diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 556dfb11..0badb7f8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,7 @@ to contribute to mTHMMY in a way that is efficient for everyone.
**Important!** Instead of creating publicly viewable issues for suspected security
vulnerabilities, please report them in private to
-`thmmynolife@gmail.com`.
+[thmmynolife@gmail.com](mailto:thmmynolife@gmail.com).
## I want to contribute!
@@ -18,7 +18,7 @@ There are many ways of contributing to mTHMMY:
- Submitting bugs and ideas to our [issue tracker][github-issues]
- Forking mTHMMY and submitting [pull requests](#pull-requests)
- Joining our core team
-- Contacting us by email at `thmmynolife@gmail.com`
+- Contacting us by email at [thmmynolife@gmail.com](mailto:thmmynolife@gmail.com)
## Issue tracker
@@ -27,17 +27,17 @@ Before creating a new issue make sure to **search the tracker** for similar ones
## Compiling
-Due to the app's integration with Firebase, a `google-services.json` is required inside the `app` directory. To get one, either [set up your own Firebase project][firebase-console] (with or without a self hosted [backend][sisyphus]), or ask us to provide you the one we use for development.
+Due to the app's integration with Firebase, a *google-services.json* file is required inside the *app* directory. To get one, either [set up your own Firebase project][firebase-console] (with or without a self hosted [backend][sisyphus]), or ask us to provide you the one we use for development.
## Pull requests
Pull 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 pull request:
+follow the workflow below to make a pull request (PR):
1. Fork the project into your personal space on Github
-1. Create a feature branch, away from `develop`
+1. Create a feature branch, away from [develop](https://github.com/ThmmyNoLife/mTHMMY/tree/develop)
1. Push the commit(s) to your fork
-1. Create a pull request (PR) targeting `develop` [at mTHMMY](https://github.com/ThmmyNoLife/mTHMMY/tree/develop)
+1. Create a PR targeting [develop at mTHMMY](https://github.com/ThmmyNoLife/mTHMMY/tree/develop)
1. Fill the PR title describing the change you want to make
1. Fill the PR description with a brief motive for your change and the method you used to achieve it
1. Submit the PR.
diff --git a/PRIVACY.md b/PRIVACY.md
new file mode 100644
index 00000000..8e69038c
--- /dev/null
+++ b/PRIVACY.md
@@ -0,0 +1,76 @@
+# Privacy Policy
+
+*Effective date: 13/10/2018*
+
+Thmmy No Life ("us", "we", or "our") developed the mTHMMY mobile app (the "App") as an open source application. It is provided by us at no cost and is intended for use as is.
+
+As a user of the App, your privacy is protected and we feel a strong commitment to protect your privacy. The purpose of this Privacy Policy is to inform you about the collection, use and disclosure of your data, and the choices you have associated with them.
+
+## Data processing
+
+To be able to offer you all functions and services of the App in the most convenient way possible and to continuously improve it, we use a number of different cloud services. This means we will transfer your data to a third party – the cloud services provider.
+
+Google Ireland Limited ("Google"), with offices at Gordon House, Barrow Street, Dublin 4, Ireland and, more specifically, Firebase, a Google subsidiary with its registered office in San Francisco, CA, U.S.A., is a third party – the cloud services provider – that stores and processes your data on our behalf (the "processor", as defined in Article 4, GDPR).
+
+### Location of processing
+
+The majority of Firebase services run on global Google infrastructure. They could process data at any of the Google Cloud Platform locations or Google data center locations, within or outside the European Union (EU).
+
+The Commission Decision (EU) 2016/1250 of 12.07.2016 allows the transfer of data from an EU controller or processor of orders to organizations in the US that have committed themselves to adhere to the framework principles of the EU-US Privacy Shield (), including the additional principles, by way of self-certification with the US Department of Commerce. Google is subject to these principles through self-certification with the U.S. Department of Commerce.
+
+### General Information
+
+* The Firebase services that we use collect and further process anonymized information, data with no personally identifiable information which can be used to trace its source so that the people whom it describes can remain anonymous. These data are not subject to the same treatment as are personal data, particularly with regards to any desire you might have for accessing it, rectifying it or erasing it (Article 11, GDPR).
+* Firebase uses the Instance ID of your mobile device to identify individual installations of this mobile app. Since each Instance ID is unique to a particular application and device, they give Firebase a way to refer to specific instances of the App.
+* Your IP address transmitted by the App in the context of Firebase will not be merged with other collected data.
+* Legal basis for the use of Firebase is Article 6 Par. 1 S. 1 letter (a) or (f), GDPR.
+
+The use of each Firebase service is explained below:
+
+### Firebase Cloud Messaging
+
+* **Purpose**: This service is essential for the funcionality of the App. It uses anonymous push notification tokens in order to determine which devices to deliver the appropriate messages to.
+* **Data collected**: Instance IDs.
+* **Consent**: You will be prompted to agree to the use of this service when you open the App for the first time.
+* **Retention**: Firebase retains Instance IDs until we make an API call for deletion. After the call, data are removed from live and backup systems within 180 days.
+
+### Firebase Crashlytics
+
+* **Purpose**: This service automatically collects and delivers analyses of errors and system crashes in real time and displays them in the Firebase Console. This helps us maintain the App and improve its stability.
+* **Data collected**: Instance IDs and crash reports with information about register codes and your device, e.g. type of device and version of operating system. For more information:
+* **Consent**: You will be prompted to choose if you want this service enabled when you open the App for the first time. After that, you can enable or disable it from the Settings inside the App.
+* **Retention**: Crash traces and their associated identifiers are kept for 90 days.
+
+### Google Analytics for Firebase
+
+* **Purpose**: This service provides analytics and attribution information for statistical purposes. The precise information collected can vary by the device and environment.
+* **Data collected**: Instance IDs, Android IDs, Analytics App Instance IDs and custom events created by us. For more information:
+* **Consent**: You will be prompted to choose if you want this service enabled when you open the App for the first time. After that, you can enable or disable (and clear all collected data) from the Settings inside the App.
+* **Retention**: ID-associated data are retained for 60 days. Aggregate reporting and campaign data are retained without automatic expiration.
+
+You can find further information on the data use by Google through Firebase following the links below:
+
+
+
+
+
+## Other data
+
+* When downloading the mobile app, the necessary information is transferred to the App Store (Google Play), i.e. in particular the name, e-mail address and customer number of your customer account, time of download, payment information and the individual device identification number. We have no influence on this data collection and shall not be responsible for it. We only process the data if it is necessary for downloading the mobile app to your mobile device.
+* The App provides functionality to login to thmmy.gr through an encrypted connection. This process generates session cookies that are stored on your device. Those cookies contain the same information as those that would be generated if you logged in using a web browser and we have no influence on them. Any other personal information that the App collects from you from thmmy.gr is only stored locally and never transmitted. You can delete all the data related to thmmy.gr anytime you wish, by logging out or clearing the App's data from your device's Settings.
+
+## About thmmy.gr
+
+We have neither influence, nor responsibility on anything you read, write, download or upload by interacting with thmmy.gr through the App. For more information you can also read the Terms of Service of thmmy.gr at .
+
+## Third-Party Links
+
+mTHMMY may contain links to third-party websites. Any access to and use of such linked websites is not governed by this Policy, but instead is governed by the privacy policies of those third party websites. We are not responsible for the information practices of such third party websites.
+
+## Policy Updates
+
+We may update this Privacy Policy from time to time and, thus, you are advised to review it periodically. The most recent version of our Privacy Policy can be found at .
+
+## Contact Us
+
+If you have any questions about our Privacy Policy, please contact us at [thmmynolife@gmail.com](mailto:thmmynolife@gmail.com).
diff --git a/README.md b/README.md
index c69fe09d..1dfb2197 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,9 @@
[](https://android-arsenal.com/api?level=19)
[][discord-server]
+
-mTHMMY is a mobile app for the [thmmy.gr](https://www.thmmy.gr) community.
+A mobile app for [thmmy.gr](https://www.thmmy.gr).
## Requirements
@@ -20,9 +21,13 @@ The latest release version is available on Google Play:
Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
+## Privacy Policy
+
+Our Privacy Policy can be found [here](/PRIVACY.md).
+
## Contact
-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].
+Do not hesitate to contact us for any matter, either by sending an email to [thmmynolife@gmail.com](mailto: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.*
diff --git a/app/build.gradle b/app/build.gradle
index 35194bae..ecbd4dbc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,43 +1,65 @@
+import groovy.json.JsonSlurper
+
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
-
android {
- compileSdkVersion 27
+ compileSdkVersion 28
defaultConfig {
vectorDrawables.useSupportLibrary = true
applicationId "gr.thmmy.mthmmy"
minSdkVersion 19
- targetSdkVersion 27
- versionCode 13
- versionName "1.4.1"
+ targetSdkVersion 28
+ versionCode 14
+ versionName "1.5.0"
archivesBaseName = "mTHMMY-v$versionName"
}
buildTypes {
release {
minifyEnabled true
+ shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
def date = new Date().format('ddMMyy_HHmmss')
archivesBaseName = archivesBaseName + "-$date"
+ // Disable fabric build ID generation for debug builds
+ ext.enableCrashlytics = false
}
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+tasks.whenTaskAdded { task ->
+ if (task.name.contains("assembleRelease")) {
+ task.getDependsOn().add({
+ def inputFile = new File("app/google-services.json")
+ def json = new JsonSlurper().parseText(inputFile.text)
+ if(json.project_info.project_id != "mthmmy-release-3aef0")
+ throw new GradleException('Please supply the correct google-services.json for release or manually change the id above!')
+ })
+ }
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:27.1.1'
- implementation 'com.android.support:design:27.1.1'
- implementation 'com.android.support:support-v4:27.1.1'
- implementation 'com.android.support:cardview-v7:27.1.1'
- implementation 'com.android.support:recyclerview-v7:27.1.1'
- implementation 'com.google.firebase:firebase-core:16.0.1'
- implementation 'com.google.firebase:firebase-messaging:17.0.0'
- implementation 'com.crashlytics.sdk.android:crashlytics:2.9.4'
- implementation 'com.squareup.okhttp3:okhttp:3.10.0'
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.android.support:design:28.0.0'
+ implementation 'com.android.support:preference-v7:28.0.0'
+ implementation 'com.android.support:preference-v14:28.0.0'
+ implementation 'com.android.support:support-v4:28.0.0'
+ implementation 'com.android.support:cardview-v7:28.0.0'
+ implementation 'com.android.support:recyclerview-v7:28.0.0'
+ implementation 'com.google.firebase:firebase-core:16.0.4'
+ implementation 'com.google.firebase:firebase-messaging:17.3.3'
+ implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5'
+ implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation 'org.jsoup:jsoup:1.10.3' //TODO: Warning: upgrading from 1.10.3 will break stuff!
@@ -48,10 +70,14 @@ dependencies {
}
implementation 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar'
- implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.12'
+ implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
implementation 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'//TODO: deprecated!
implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2'
- implementation 'com.jakewharton.timber:timber:4.7.0'
+ implementation 'com.jakewharton.timber:timber:4.7.1'
+ implementation "ru.noties:markwon:2.0.0"
+ implementation 'net.gotev:uploadservice:3.4.2'
+ implementation 'net.gotev:uploadservice-okhttp:3.4.2'
+ implementation 'android.arch.lifecycle:extensions:1.1.1'
}
apply plugin: 'com.google.gms.google-services'
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index c06e027a..9cde3150 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
-# in C:\Users\Ragnar\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
+# in sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
@@ -26,6 +26,8 @@
-dontwarn org.conscrypt.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
+# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
+-dontwarn org.codehaus.mojo.animal_sniffer.*
# Picasso
-dontwarn com.squareup.okhttp.**
@@ -40,4 +42,8 @@
-keep public class pl.droidsonroids.gif.GifIOException{(int, java.lang.String);}
# JSoup
--keep class org.jsoup.**
\ No newline at end of file
+-keep class org.jsoup.**
+
+# Markwon
+-keep class com.caverock.androidsvg.** { *; }
+-dontwarn com.caverock.androidsvg.**
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7417185a..19f47c62 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,6 +17,12 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
+
+
+
+
+
-
-
-
@@ -97,14 +100,40 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity" />
+
+
+
+
+
+
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/PRIVACY.md b/app/src/main/assets/PRIVACY.md
new file mode 100644
index 00000000..8e69038c
--- /dev/null
+++ b/app/src/main/assets/PRIVACY.md
@@ -0,0 +1,76 @@
+# Privacy Policy
+
+*Effective date: 13/10/2018*
+
+Thmmy No Life ("us", "we", or "our") developed the mTHMMY mobile app (the "App") as an open source application. It is provided by us at no cost and is intended for use as is.
+
+As a user of the App, your privacy is protected and we feel a strong commitment to protect your privacy. The purpose of this Privacy Policy is to inform you about the collection, use and disclosure of your data, and the choices you have associated with them.
+
+## Data processing
+
+To be able to offer you all functions and services of the App in the most convenient way possible and to continuously improve it, we use a number of different cloud services. This means we will transfer your data to a third party – the cloud services provider.
+
+Google Ireland Limited ("Google"), with offices at Gordon House, Barrow Street, Dublin 4, Ireland and, more specifically, Firebase, a Google subsidiary with its registered office in San Francisco, CA, U.S.A., is a third party – the cloud services provider – that stores and processes your data on our behalf (the "processor", as defined in Article 4, GDPR).
+
+### Location of processing
+
+The majority of Firebase services run on global Google infrastructure. They could process data at any of the Google Cloud Platform locations or Google data center locations, within or outside the European Union (EU).
+
+The Commission Decision (EU) 2016/1250 of 12.07.2016 allows the transfer of data from an EU controller or processor of orders to organizations in the US that have committed themselves to adhere to the framework principles of the EU-US Privacy Shield (), including the additional principles, by way of self-certification with the US Department of Commerce. Google is subject to these principles through self-certification with the U.S. Department of Commerce.
+
+### General Information
+
+* The Firebase services that we use collect and further process anonymized information, data with no personally identifiable information which can be used to trace its source so that the people whom it describes can remain anonymous. These data are not subject to the same treatment as are personal data, particularly with regards to any desire you might have for accessing it, rectifying it or erasing it (Article 11, GDPR).
+* Firebase uses the Instance ID of your mobile device to identify individual installations of this mobile app. Since each Instance ID is unique to a particular application and device, they give Firebase a way to refer to specific instances of the App.
+* Your IP address transmitted by the App in the context of Firebase will not be merged with other collected data.
+* Legal basis for the use of Firebase is Article 6 Par. 1 S. 1 letter (a) or (f), GDPR.
+
+The use of each Firebase service is explained below:
+
+### Firebase Cloud Messaging
+
+* **Purpose**: This service is essential for the funcionality of the App. It uses anonymous push notification tokens in order to determine which devices to deliver the appropriate messages to.
+* **Data collected**: Instance IDs.
+* **Consent**: You will be prompted to agree to the use of this service when you open the App for the first time.
+* **Retention**: Firebase retains Instance IDs until we make an API call for deletion. After the call, data are removed from live and backup systems within 180 days.
+
+### Firebase Crashlytics
+
+* **Purpose**: This service automatically collects and delivers analyses of errors and system crashes in real time and displays them in the Firebase Console. This helps us maintain the App and improve its stability.
+* **Data collected**: Instance IDs and crash reports with information about register codes and your device, e.g. type of device and version of operating system. For more information:
+* **Consent**: You will be prompted to choose if you want this service enabled when you open the App for the first time. After that, you can enable or disable it from the Settings inside the App.
+* **Retention**: Crash traces and their associated identifiers are kept for 90 days.
+
+### Google Analytics for Firebase
+
+* **Purpose**: This service provides analytics and attribution information for statistical purposes. The precise information collected can vary by the device and environment.
+* **Data collected**: Instance IDs, Android IDs, Analytics App Instance IDs and custom events created by us. For more information:
+* **Consent**: You will be prompted to choose if you want this service enabled when you open the App for the first time. After that, you can enable or disable (and clear all collected data) from the Settings inside the App.
+* **Retention**: ID-associated data are retained for 60 days. Aggregate reporting and campaign data are retained without automatic expiration.
+
+You can find further information on the data use by Google through Firebase following the links below:
+
+
+
+
+
+## Other data
+
+* When downloading the mobile app, the necessary information is transferred to the App Store (Google Play), i.e. in particular the name, e-mail address and customer number of your customer account, time of download, payment information and the individual device identification number. We have no influence on this data collection and shall not be responsible for it. We only process the data if it is necessary for downloading the mobile app to your mobile device.
+* The App provides functionality to login to thmmy.gr through an encrypted connection. This process generates session cookies that are stored on your device. Those cookies contain the same information as those that would be generated if you logged in using a web browser and we have no influence on them. Any other personal information that the App collects from you from thmmy.gr is only stored locally and never transmitted. You can delete all the data related to thmmy.gr anytime you wish, by logging out or clearing the App's data from your device's Settings.
+
+## About thmmy.gr
+
+We have neither influence, nor responsibility on anything you read, write, download or upload by interacting with thmmy.gr through the App. For more information you can also read the Terms of Service of thmmy.gr at .
+
+## Third-Party Links
+
+mTHMMY may contain links to third-party websites. Any access to and use of such linked websites is not governed by this Policy, but instead is governed by the privacy policies of those third party websites. We are not responsible for the information practices of such third party websites.
+
+## Policy Updates
+
+We may update this Privacy Policy from time to time and, thus, you are advised to review it periodically. The most recent version of our Privacy Policy can be found at .
+
+## Contact Us
+
+If you have any questions about our Privacy Policy, please contact us at [thmmynolife@gmail.com](mailto:thmmynolife@gmail.com).
diff --git a/app/src/main/assets/apache_libraries.html b/app/src/main/assets/apache_libraries.html
index fd866258..a8ff5d90 100644
--- a/app/src/main/assets/apache_libraries.html
+++ b/app/src/main/assets/apache_libraries.html
@@ -39,7 +39,7 @@
-
-
OkHttp v3.10.0 (Copyright ©2016 Square, Inc.)
+ OkHttp v3.11.0 (Copyright ©2016 Square, Inc.)
-
Picasso v2.5.2 (Copyright ©2013 Square, Inc.)
@@ -60,7 +60,13 @@
-
-
Timber v4.7.0 (Copyright ©2013 Jake Wharton)
+ Timber v4.7.1 (Copyright ©2013 Jake Wharton)
+
+ -
+
Android Upload Service v3.4.2 (Copyright ©2013-2018 Aleksandar Gotev)
+
+ -
+
Markwon v2.0.0 (Copyright ©2017 Dimitry Ivanov)
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
index 896a5fb5..34ed4c32 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
@@ -6,6 +6,9 @@ import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AlertDialog;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.text.style.UnderlineSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebView;
@@ -81,6 +84,13 @@ public class AboutActivity extends BaseActivity {
});
}
+ TextView privacyPolicy = findViewById(R.id.privacy_policy_header);
+ privacyPolicy.setMovementMethod(new LinkMovementMethod());
+ SpannableString spannableString = new SpannableString(privacyPolicy.getText());
+ spannableString.setSpan(new UnderlineSpan(), 0, spannableString.length(), 0);
+ privacyPolicy.setText(spannableString);
+ privacyPolicy.setOnClickListener(view -> showPrivacyPolicyDialog());
+
}
@Override
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 6f632f97..1a5ad05f 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
@@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.activities;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.AppCompatButton;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@@ -11,10 +12,12 @@ import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
+import com.google.firebase.analytics.FirebaseAnalytics;
+
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
-import timber.log.Timber;
+import gr.thmmy.mthmmy.base.BaseApplication;
import static gr.thmmy.mthmmy.session.SessionManager.BANNED_USER;
import static gr.thmmy.mthmmy.session.SessionManager.CONNECTION_ERROR;
@@ -36,12 +39,17 @@ public class LoginActivity extends BaseActivity {
/* --Graphics End-- */
private LoginTask loginTask;
+ private boolean initialRedirect;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
+ initialRedirect = getIntent().getBooleanExtra("REDIRECT", false);
+
+ PreferenceManager.setDefaultValues(this, R.xml.app_preferences_user, false);
+
//Variables initialization
inputUsername = findViewById(R.id.username);
inputPassword = findViewById(R.id.password);
@@ -49,50 +57,44 @@ public class LoginActivity extends BaseActivity {
AppCompatButton btnGuest = findViewById(R.id.btnContinueAsGuest);
//Login button Click Event
- btnLogin.setOnClickListener(new View.OnClickListener() {
-
- public void onClick(View view) {
- Timber.d("Login");
-
- //Get username and password strings
- username = inputUsername.getText().toString().trim();
- password = inputPassword.getText().toString().trim();
+ btnLogin.setOnClickListener(view -> {
- //Check for empty data in the form
- if (!validate()) {
- onLoginFailed();
- return;
- }
+ //Get username and password strings
+ username = inputUsername.getText().toString().trim();
+ password = inputPassword.getText().toString().trim();
- //Login user
- loginTask = new LoginTask();
- loginTask.execute(username, password);
+ //Check for empty data in the form
+ if (!validate()) {
+ onLoginFailed();
+ return;
}
+
+ //Login user
+ loginTask = new LoginTask();
+ loginTask.execute(username, password);
});
//Guest Button Action
- btnGuest.setOnClickListener(new View.OnClickListener() {
-
- public void onClick(View view) {
- //Session data update
- sessionManager.guestLogin();
-
- //Go to main
- Intent intent = new Intent(LoginActivity.this, MainActivity.class);
- startActivity(intent);
- finish();
- overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
- }
+ btnGuest.setOnClickListener(view -> {
+ //Session data update
+ sessionManager.guestLogin();
+
+ //Go to main
+ Intent intent = new Intent(LoginActivity.this, MainActivity.class);
+ startActivity(intent);
+ finish();
+ overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
});
}
@Override
public void onBackPressed() {
- // Disable going back to the MainActivity
- moveTaskToBack(true);
+ super.onBackPressed();
if (loginTask != null && loginTask.getStatus() == AsyncTask.Status.RUNNING) {
loginTask.cancel(true);
}
+ if(!isTaskRoot())
+ overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
}
private void onLoginFailed() {
@@ -160,9 +162,13 @@ public class LoginActivity extends BaseActivity {
Toast.makeText(getApplicationContext(),
"Welcome, " + sessionManager.getUsername() + "!", Toast.LENGTH_LONG)
.show();
- //Go to main
- Intent intent = new Intent(LoginActivity.this, MainActivity.class);
- startActivity(intent);
+ BaseApplication.getInstance().logFirebaseAnalyticsEvent(FirebaseAnalytics.Event.LOGIN, null);
+ if(initialRedirect){
+ Intent intent = new Intent(LoginActivity.this, MainActivity.class);
+ startActivity(intent);
+ } else
+ onBackPressed();
+
finish();
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
break;
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 251c7c2b..d3a74098 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
@@ -1,14 +1,15 @@
package gr.thmmy.mthmmy.activities.board;
+import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
-import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.Toast;
@@ -20,6 +21,8 @@ import java.util.ArrayList;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.activities.LoginActivity;
+import gr.thmmy.mthmmy.activities.create_content.CreateContentActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Board;
import gr.thmmy.mthmmy.model.Bookmark;
@@ -51,6 +54,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
private String boardUrl;
private String boardTitle;
private String parsedTitle;
+ private String newTopicUrl;
private int numberOfPages = -1;
private int pagesLoaded = 0;
@@ -89,43 +93,36 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
- thisPageBookmark = new Bookmark(boardTitle, ThmmyPage.getBoardId(boardUrl), false);
- setBoardBookmark((ImageButton) findViewById(R.id.bookmark));
+ thisPageBookmark = new Bookmark(boardTitle, ThmmyPage.getBoardId(boardUrl), true);
+ setBoardBookmark(findViewById(R.id.bookmark));
createDrawer();
progressBar = findViewById(R.id.progressBar);
newTopicFAB = findViewById(R.id.board_fab);
- newTopicFAB.setEnabled(false);
- newTopicFAB.hide();
- /*if (!sessionManager.isLoggedIn()) newTopicFAB.hide();
+ if (!sessionManager.isLoggedIn()) newTopicFAB.hide();
else {
- newTopicFAB.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (sessionManager.isLoggedIn()) {
- //TODO PM
- } else {
- new AlertDialog.Builder(BoardActivity.this)
- .setMessage("You need to be logged in to create a new topic!")
- .setPositiveButton("Login", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- Intent intent = new Intent(BoardActivity.this, LoginActivity.class);
- startActivity(intent);
- finish();
- overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
- }
- })
- .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- }
- })
- .show();
+ newTopicFAB.setOnClickListener(view -> {
+ if (sessionManager.isLoggedIn()) {
+ if (newTopicUrl != null) {
+ Intent intent = new Intent(this, CreateContentActivity.class);
+ intent.putExtra(CreateContentActivity.EXTRA_NEW_TOPIC_URL, newTopicUrl);
+ startActivity(intent);
}
+ } else {
+ new AlertDialog.Builder(BoardActivity.this)
+ .setMessage("You need to be logged in to create a new topic!")
+ .setPositiveButton("Login", (dialogInterface, i) -> {
+ Intent intent = new Intent(BoardActivity.this, LoginActivity.class);
+ startActivity(intent);
+ finish();
+ overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
+ })
+ .setNegativeButton("Cancel", (dialogInterface, i) -> {
+ })
+ .show();
}
});
- }*/
+ }
boardAdapter = new BoardAdapter(getApplicationContext(), parsedSubBoards, parsedTopics);
RecyclerView mainContent = findViewById(R.id.board_recycler_view);
@@ -156,7 +153,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
@Override
public void onLoadMore() {
- if (pagesLoaded < numberOfPages) {
+ if (pagesLoaded < numberOfPages && parsedTopics.get(parsedTopics.size() - 1) != null) {
parsedTopics.add(null);
boardAdapter.notifyItemInserted(parsedSubBoards.size() + parsedTopics.size());
@@ -169,7 +166,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
@Override
public void onResume() {
super.onResume();
- refreshBoardBookmark((ImageButton) findViewById(R.id.bookmark));
+ refreshBoardBookmark(findViewById(R.id.bookmark));
}
@Override
@@ -185,6 +182,9 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
* parameter!
*/
private class BoardTask extends ParseTask {
+ ArrayList tempSubboards = new ArrayList<>();
+ ArrayList tempTopics = new ArrayList<>();
+
@Override
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
@@ -193,12 +193,14 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
@Override //TODO should throw ParseException
public void parse(Document boardPage) throws ParseException {
- parsedTitle = boardPage.select("div.nav a.nav").last().text();
-
+ tempSubboards.addAll(parsedSubBoards);
+ tempTopics.addAll(parsedTopics);
//Removes loading item
if (isLoadingMore) {
- if (parsedTopics.size() > 0) parsedTopics.remove(parsedTopics.size() - 1);
+ if (tempTopics.size() > 0) tempTopics.remove(tempTopics.size() - 1);
}
+ parsedTitle = boardPage.select("div.nav a.nav").last().text();
+
//Finds number of pages
if (numberOfPages == -1) {
numberOfPages = 1;
@@ -215,6 +217,13 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
//It just means this board has only one page of topics.
}
}
+
+ //Finds the url needed to create a new topic
+ Element newTopicButton = boardPage.select("a:has(img[alt=Start new topic])").first();
+ if (newTopicButton == null)
+ newTopicButton = boardPage.select("a:has(img[alt=Νέο θέμα])").first();
+ if (newTopicButton != null) newTopicUrl = newTopicButton.attr("href");
+
{ //Finds sub boards
Elements subBoardRows = boardPage.select("div.tborder>table>tbody>tr");
if (subBoardRows != null && !subBoardRows.isEmpty()) {
@@ -254,7 +263,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
}
}
}
- parsedSubBoards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl));
+ tempSubboards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl));
}
}
}
@@ -265,7 +274,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
for (Element topicRow : topicRows) {
if (!Objects.equals(topicRow.className(), "titlebg")) {
String pTopicUrl, pSubject, pStartedBy, pLastPost, pLastPostUrl, pStats;
- boolean pLocked = false, pSticky = false;
+ boolean pLocked = false, pSticky = false, pUnread = false;
Elements topicColumns = topicRow.select(">td");
{
Element column = topicColumns.get(2);
@@ -276,6 +285,8 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
pSticky = true;
if (column.select("img[id^=lockicon]").first() != null)
pLocked = true;
+ if (column.select("a[id^=newicon]").first() != null)
+ pUnread = true;
}
pStartedBy = topicColumns.get(3).text();
pStats = "Replies " + topicColumns.get(4).text() + ", Views " + topicColumns.get(5).text();
@@ -284,13 +295,15 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
if (pLastPost.contains("by")) {
pLastPost = pLastPost.substring(0, pLastPost.indexOf("by")) +
"\n" + pLastPost.substring(pLastPost.indexOf("by"));
- } else {
+ } else if (pLastPost.contains("από")) {
pLastPost = pLastPost.substring(0, pLastPost.indexOf("από")) +
"\n" + pLastPost.substring(pLastPost.indexOf("από"));
+ } else {
+ Timber.wtf("Board parsing about to fail. pLastPost came with: %s", pLastPost);
}
pLastPostUrl = topicColumns.last().select("a:has(img)").first().attr("href");
- parsedTopics.add(new Topic(pTopicUrl, pSubject, pStartedBy, pLastPost, pLastPostUrl,
- pStats, pLocked, pSticky));
+ tempTopics.add(new Topic(pTopicUrl, pSubject, pStartedBy, pLastPost, pLastPostUrl,
+ pStats, pLocked, pSticky, pUnread));
}
}
}
@@ -299,19 +312,27 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
@Override
protected void postExecution(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), false);
+ 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), true);
+ setBoardBookmark(findViewById(R.id.bookmark));
+ }
+
+ parsedTopics.clear();
+ parsedSubBoards.clear();
+ parsedTopics.addAll(tempTopics);
+ parsedSubBoards.addAll(tempSubboards);
+ boardAdapter.notifyDataSetChanged();
+
+ //Parse was successful
+ ++pagesLoaded;
+ if (newTopicFAB.getVisibility() != View.GONE) newTopicFAB.setEnabled(true);
}
- //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/board/BoardAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
index bb43c065..09734606 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
@@ -147,10 +147,10 @@ class BoardAdapter extends RecyclerView.Adapter {
});
if (boardExpandableVisibility.get(subBoardViewHolder.getAdapterPosition() - 1)) {
subBoardViewHolder.boardExpandable.setVisibility(View.VISIBLE);
- subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up);
+ subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp);
} else {
subBoardViewHolder.boardExpandable.setVisibility(View.GONE);
- subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down);
+ subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp);
}
subBoardViewHolder.showHideExpandable.setOnClickListener(new View.OnClickListener() {
@Override
@@ -158,10 +158,10 @@ class BoardAdapter extends RecyclerView.Adapter {
final boolean visible = boardExpandableVisibility.get(subBoardViewHolder.getAdapterPosition() - 1);
if (visible) {
subBoardViewHolder.boardExpandable.setVisibility(View.GONE);
- subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down);
+ subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp);
} else {
subBoardViewHolder.boardExpandable.setVisibility(View.VISIBLE);
- subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up);
+ subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp);
}
boardExpandableVisibility.set(subBoardViewHolder.getAdapterPosition() - 1, !visible);
}
@@ -208,10 +208,10 @@ class BoardAdapter extends RecyclerView.Adapter {
if (topicExpandableVisibility.get(topicViewHolder.getAdapterPosition() - parsedSubBoards
.size() - 2)) {
topicViewHolder.topicExpandable.setVisibility(View.VISIBLE);
- topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up);
+ topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp);
} else {
topicViewHolder.topicExpandable.setVisibility(View.GONE);
- topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down);
+ topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp);
}
topicViewHolder.showHideExpandable.setOnClickListener(new View.OnClickListener() {
@Override
@@ -220,10 +220,10 @@ class BoardAdapter extends RecyclerView.Adapter {
getAdapterPosition() - parsedSubBoards.size() - 2);
if (visible) {
topicViewHolder.topicExpandable.setVisibility(View.GONE);
- topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down);
+ topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp);
} else {
topicViewHolder.topicExpandable.setVisibility(View.VISIBLE);
- topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up);
+ topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp);
}
topicExpandableVisibility.set(topicViewHolder.getAdapterPosition() -
parsedSubBoards.size() - 2, !visible);
@@ -231,12 +231,18 @@ class BoardAdapter extends RecyclerView.Adapter {
});
topicViewHolder.topicSubject.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf"));
+ topicViewHolder.topicUnreadDot.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/fontawesome-webfont.ttf"));
+ if (topic.isUnread())
+ topicViewHolder.topicUnreadDot.setVisibility(View.VISIBLE);
+ else {
+ topicViewHolder.topicUnreadDot.setVisibility(View.GONE);
+ }
String lockedSticky = topic.getSubject();
if (topic.isLocked())
lockedSticky += " " + context.getResources().getString(R.string.fa_lock);
if (topic.isSticky()) {
//topicViewHolder.topicSubject.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_pin, 0);
- lockedSticky += " " + context.getResources().getString(R.string.fa_sticky);
+ lockedSticky += " " + context.getResources().getString(R.string.fa_thumbtack);
}
topicViewHolder.topicSubject.setText(lockedSticky);
topicViewHolder.topicStartedBy.setText(context.getString(R.string.topic_started_by, topic.getStarter()));
@@ -287,7 +293,7 @@ class BoardAdapter extends RecyclerView.Adapter {
private static class TopicViewHolder extends RecyclerView.ViewHolder {
final LinearLayout topicRow, topicExpandable;
- final TextView topicSubject, topicStartedBy, topicStats, topicLastPost;
+ final TextView topicSubject, topicStartedBy, topicStats, topicLastPost, topicUnreadDot;
final ImageButton showHideExpandable;
TopicViewHolder(View topic) {
@@ -295,6 +301,7 @@ class BoardAdapter extends RecyclerView.Adapter {
topicRow = topic.findViewById(R.id.topic_row_linear);
topicExpandable = topic.findViewById(R.id.topic_expandable);
showHideExpandable = topic.findViewById(R.id.topic_expand_collapse_button);
+ topicUnreadDot = topic.findViewById(R.id.topic_unread_dot);
topicSubject = topic.findViewById(R.id.topic_subject);
topicStartedBy = topic.findViewById(R.id.topic_started_by);
topicStats = topic.findViewById(R.id.topic_stats);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BoardBookmarksFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BoardBookmarksFragment.java
index 6b7dee26..6c7db47f 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BoardBookmarksFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BoardBookmarksFragment.java
@@ -2,13 +2,16 @@ package gr.thmmy.mthmmy.activities.bookmarks;
import android.app.Activity;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -27,10 +30,14 @@ public class BoardBookmarksFragment extends Fragment {
protected static final String ARG_BOARD_BOOKMARKS = "BOARD_BOOKMARKS";
public static final String INTERACTION_CLICK_BOARD_BOOKMARK = "CLICK_BOARD_BOOKMARK";
+ public static final String INTERACTION_TOGGLE_BOARD_NOTIFICATION = "TOGGLE_BOARD_NOTIFICATION";
public static final String INTERACTION_REMOVE_BOARD_BOOKMARK= "REMOVE_BOARD_BOOKMARK";
ArrayList boardBookmarks = null;
+ private static Drawable notificationsEnabledButtonImage;
+ private static Drawable notificationsDisabledButtonImage;
+
// Required empty public constructor
public BoardBookmarksFragment() {
}
@@ -59,6 +66,16 @@ public class BoardBookmarksFragment extends Fragment {
boardBookmarks = Bookmark.arrayFromString(bundledBoardBookmarks);
}
}
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ notificationsEnabledButtonImage = getResources().getDrawable(R.drawable.ic_notification_on, null);
+ else
+ notificationsEnabledButtonImage = VectorDrawableCompat.create(getResources(), R.drawable.ic_notification_on, null);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ notificationsDisabledButtonImage = getResources().getDrawable(R.drawable.ic_notification_off, null);
+ else
+ notificationsDisabledButtonImage = VectorDrawableCompat.create(getResources(), R.drawable.ic_notification_off, null);
}
@Override
@@ -73,32 +90,43 @@ public class BoardBookmarksFragment extends Fragment {
for (final Bookmark bookmarkedBoard : boardBookmarks) {
if (bookmarkedBoard != null && bookmarkedBoard.getTitle() != null) {
final LinearLayout row = (LinearLayout) layoutInflater.inflate(
- R.layout.fragment_bookmarks_board_row, bookmarksLinearView, false);
- row.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Activity activity = getActivity();
- if (activity instanceof BookmarkActivity){
- ((BookmarkActivity) activity).onBoardInteractionListener(INTERACTION_CLICK_BOARD_BOOKMARK, bookmarkedBoard);
- }
+ R.layout.fragment_bookmarks_row, bookmarksLinearView, false);
+ row.setOnClickListener(view -> {
+ Activity activity = getActivity();
+ if (activity instanceof BookmarkActivity){
+ ((BookmarkActivity) activity).onBoardInteractionListener(INTERACTION_CLICK_BOARD_BOOKMARK, bookmarkedBoard);
}
});
((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) {
- Activity activity = getActivity();
- if (activity instanceof BookmarkActivity){
- ((BookmarkActivity) activity).onBoardInteractionListener(INTERACTION_REMOVE_BOARD_BOOKMARK, bookmarkedBoard);
- boardBookmarks.remove(bookmarkedBoard);
- }
- row.setVisibility(View.GONE);
- if (boardBookmarks.isEmpty()){
- bookmarksLinearView.addView(bookmarksListEmptyMessage());
+ final ImageButton notificationsEnabledButton = row.findViewById(R.id.toggle_notification);
+ if (!bookmarkedBoard.isNotificationsEnabled()) {
+ notificationsEnabledButton.setImageDrawable(notificationsDisabledButtonImage);
+ }
+
+ notificationsEnabledButton.setOnClickListener(view -> {
+ Activity activity = getActivity();
+ if (activity instanceof BookmarkActivity) {
+ if (((BookmarkActivity) activity).onBoardInteractionListener(INTERACTION_TOGGLE_BOARD_NOTIFICATION, bookmarkedBoard)) {
+ notificationsEnabledButton.setImageDrawable(notificationsEnabledButtonImage);
+ } else {
+ notificationsEnabledButton.setImageDrawable(notificationsDisabledButtonImage);
}
}
});
+
+ (row.findViewById(R.id.remove_bookmark)).setOnClickListener(view -> {
+ Activity activity = getActivity();
+ if (activity instanceof BookmarkActivity){
+ ((BookmarkActivity) activity).onBoardInteractionListener(INTERACTION_REMOVE_BOARD_BOOKMARK, bookmarkedBoard);
+ boardBookmarks.remove(bookmarkedBoard);
+ }
+ row.setVisibility(View.GONE);
+
+ if (boardBookmarks.isEmpty()){
+ bookmarksLinearView.addView(bookmarksListEmptyMessage());
+ }
+ });
bookmarksLinearView.addView(row);
}
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarkActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarkActivity.java
index 0408db44..164e8f3f 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarkActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarkActivity.java
@@ -1,7 +1,6 @@
package gr.thmmy.mthmmy.activities.bookmarks;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
@@ -63,39 +62,48 @@ public class BookmarkActivity extends BaseActivity {
super.onResume();
}
- public boolean onTopicInteractionListener(String interactionType, Bookmark bookmarkedTopic){
- if (interactionType.equals(TopicBookmarksFragment.INTERACTION_CLICK_TOPIC_BOOKMARK)){
- 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();
- } else if (interactionType.equals(TopicBookmarksFragment.INTERACTION_TOGGLE_TOPIC_NOTIFICATION)) {
- return toggleNotification(bookmarkedTopic);
- } else if (interactionType.equals(TopicBookmarksFragment.INTERACTION_REMOVE_TOPIC_BOOKMARK)){
- removeBookmark(bookmarkedTopic);
- Toast.makeText(BookmarkActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
+ public boolean onTopicInteractionListener(String interactionType, Bookmark bookmarkedTopic) {
+ switch (interactionType) {
+ case TopicBookmarksFragment.INTERACTION_CLICK_TOPIC_BOOKMARK:
+ 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();
+ break;
+ case TopicBookmarksFragment.INTERACTION_TOGGLE_TOPIC_NOTIFICATION:
+ return toggleNotification(bookmarkedTopic);
+ case TopicBookmarksFragment.INTERACTION_REMOVE_TOPIC_BOOKMARK:
+ removeBookmark(bookmarkedTopic);
+ Toast.makeText(BookmarkActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
+ break;
}
return true;
}
- public void onBoardInteractionListener(String interactionType, Bookmark bookmarkedBoard){
- if (interactionType.equals(BoardBookmarksFragment.INTERACTION_CLICK_BOARD_BOOKMARK)){
- 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();
- } else if (interactionType.equals(BoardBookmarksFragment.INTERACTION_REMOVE_BOARD_BOOKMARK)){
- removeBookmark(bookmarkedBoard);
- Toast.makeText(BookmarkActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
+ public boolean onBoardInteractionListener(String interactionType, Bookmark bookmarkedBoard) {
+ switch (interactionType) {
+ case BoardBookmarksFragment.INTERACTION_CLICK_BOARD_BOOKMARK:
+ 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();
+ break;
+ case BoardBookmarksFragment.INTERACTION_TOGGLE_BOARD_NOTIFICATION:
+ return toggleNotification(bookmarkedBoard);
+ case BoardBookmarksFragment.INTERACTION_REMOVE_BOARD_BOOKMARK:
+ removeBookmark(bookmarkedBoard);
+ Toast.makeText(BookmarkActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show();
+ break;
}
+ return true;
}
/**
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/TopicBookmarksFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/TopicBookmarksFragment.java
index 7679ad2f..54659baa 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/TopicBookmarksFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/TopicBookmarksFragment.java
@@ -20,13 +20,18 @@ import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Bookmark;
+/**
+ * A {@link Fragment} subclass.
+ * Use the {@link TopicBookmarksFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
public class TopicBookmarksFragment extends Fragment {
protected static final String ARG_SECTION_NUMBER = "SECTION_NUMBER";
- protected static final String ARG_TOPIC_BOOKMARKS = "BOARD_BOOKMARKS";
+ protected static final String ARG_TOPIC_BOOKMARKS = "TOPIC_BOOKMARKS";
- public static final String INTERACTION_CLICK_TOPIC_BOOKMARK = "CLICK_BOARD_BOOKMARK";
+ public static final String INTERACTION_CLICK_TOPIC_BOOKMARK = "CLICK_TOPIC_BOOKMARK";
public static final String INTERACTION_TOGGLE_TOPIC_NOTIFICATION = "TOGGLE_TOPIC_NOTIFICATION";
- public static final String INTERACTION_REMOVE_TOPIC_BOOKMARK = "REMOVE_BOARD_BOOKMARK";
+ public static final String INTERACTION_REMOVE_TOPIC_BOOKMARK = "REMOVE_TOPIC_BOOKMARK";
ArrayList topicBookmarks = null;
@@ -43,11 +48,11 @@ public class TopicBookmarksFragment extends Fragment {
*
* @return A new instance of fragment Forum.
*/
- public static TopicBookmarksFragment newInstance(int sectionNumber, String boardBookmarks) {
+ public static TopicBookmarksFragment newInstance(int sectionNumber, String topicBookmarks) {
TopicBookmarksFragment fragment = new TopicBookmarksFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
- args.putString(ARG_TOPIC_BOOKMARKS, boardBookmarks);
+ args.putString(ARG_TOPIC_BOOKMARKS, topicBookmarks);
fragment.setArguments(args);
return fragment;
}
@@ -56,23 +61,21 @@ public class TopicBookmarksFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
- String bundledBoardBookmarks = getArguments().getString(ARG_TOPIC_BOOKMARKS);
- if (bundledBoardBookmarks != null) {
- topicBookmarks = Bookmark.arrayFromString(bundledBoardBookmarks);
+ String bundledTopicBookmarks = getArguments().getString(ARG_TOPIC_BOOKMARKS);
+ if (bundledTopicBookmarks != null) {
+ topicBookmarks = Bookmark.arrayFromString(bundledTopicBookmarks);
}
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
notificationsEnabledButtonImage = getResources().getDrawable(R.drawable.ic_notification_on, null);
- } else {
+ else
notificationsEnabledButtonImage = VectorDrawableCompat.create(getResources(), R.drawable.ic_notification_on, null);
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
notificationsDisabledButtonImage = getResources().getDrawable(R.drawable.ic_notification_off, null);
- } else {
+ else
notificationsDisabledButtonImage = VectorDrawableCompat.create(getResources(), R.drawable.ic_notification_off, null);
- }
}
@Override
@@ -80,21 +83,18 @@ public class TopicBookmarksFragment extends Fragment {
Bundle savedInstanceState) {
// Inflates the layout for this fragment
final View rootView = layoutInflater.inflate(R.layout.fragment_bookmarks, container, false);
- //bookmarks_board_container
+ //bookmarks_topic_container
final LinearLayout bookmarksLinearView = rootView.findViewById(R.id.bookmarks_container);
if(this.topicBookmarks != null && !this.topicBookmarks.isEmpty()) {
for (final Bookmark bookmarkedTopic : topicBookmarks) {
if (bookmarkedTopic != null && bookmarkedTopic.getTitle() != null) {
final LinearLayout row = (LinearLayout) layoutInflater.inflate(
- R.layout.fragment_bookmarks_topic_row, bookmarksLinearView, false);
- row.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Activity activity = getActivity();
- if (activity instanceof BookmarkActivity) {
- ((BookmarkActivity) activity).onTopicInteractionListener(INTERACTION_CLICK_TOPIC_BOOKMARK, bookmarkedTopic);
- }
+ R.layout.fragment_bookmarks_row, bookmarksLinearView, false);
+ row.setOnClickListener(view -> {
+ Activity activity = getActivity();
+ if (activity instanceof BookmarkActivity) {
+ ((BookmarkActivity) activity).onTopicInteractionListener(INTERACTION_CLICK_TOPIC_BOOKMARK, bookmarkedTopic);
}
});
((TextView) row.findViewById(R.id.bookmark_title)).setText(bookmarkedTopic.getTitle());
@@ -104,32 +104,26 @@ public class TopicBookmarksFragment extends Fragment {
notificationsEnabledButton.setImageDrawable(notificationsDisabledButtonImage);
}
- notificationsEnabledButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Activity activity = getActivity();
- if (activity instanceof BookmarkActivity) {
- if (((BookmarkActivity) activity).onTopicInteractionListener(INTERACTION_TOGGLE_TOPIC_NOTIFICATION, bookmarkedTopic)) {
- notificationsEnabledButton.setImageDrawable(notificationsEnabledButtonImage);
- } else {
- notificationsEnabledButton.setImageDrawable(notificationsDisabledButtonImage);
- }
+ notificationsEnabledButton.setOnClickListener(view -> {
+ Activity activity = getActivity();
+ if (activity instanceof BookmarkActivity) {
+ if (((BookmarkActivity) activity).onTopicInteractionListener(INTERACTION_TOGGLE_TOPIC_NOTIFICATION, bookmarkedTopic)) {
+ notificationsEnabledButton.setImageDrawable(notificationsEnabledButtonImage);
+ } else {
+ notificationsEnabledButton.setImageDrawable(notificationsDisabledButtonImage);
}
}
});
- (row.findViewById(R.id.remove_bookmark)).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Activity activity = getActivity();
- if (activity instanceof BookmarkActivity) {
- ((BookmarkActivity) activity).onTopicInteractionListener(INTERACTION_REMOVE_TOPIC_BOOKMARK, bookmarkedTopic);
- topicBookmarks.remove(bookmarkedTopic);
- }
- row.setVisibility(View.GONE);
+ (row.findViewById(R.id.remove_bookmark)).setOnClickListener(view -> {
+ Activity activity = getActivity();
+ if (activity instanceof BookmarkActivity) {
+ ((BookmarkActivity) activity).onTopicInteractionListener(INTERACTION_REMOVE_TOPIC_BOOKMARK, bookmarkedTopic);
+ topicBookmarks.remove(bookmarkedTopic);
+ }
+ row.setVisibility(View.GONE);
- if (topicBookmarks.isEmpty()){
- bookmarksLinearView.addView(bookmarksListEmptyMessage());
- }
+ if (topicBookmarks.isEmpty()){
+ bookmarksLinearView.addView(bookmarksListEmptyMessage());
}
});
bookmarksLinearView.addView(row);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/CreateContentActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/CreateContentActivity.java
new file mode 100644
index 00000000..b770b7b5
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/CreateContentActivity.java
@@ -0,0 +1,110 @@
+package gr.thmmy.mthmmy.activities.create_content;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.design.widget.TextInputLayout;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Toast;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
+import gr.thmmy.mthmmy.base.BaseActivity;
+import gr.thmmy.mthmmy.editorview.EditorView;
+import gr.thmmy.mthmmy.editorview.EmojiKeyboard;
+import gr.thmmy.mthmmy.session.SessionManager;
+import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
+import timber.log.Timber;
+
+public class CreateContentActivity extends BaseActivity implements NewTopicTask.NewTopicTaskCallbacks {
+
+ public final static String EXTRA_NEW_TOPIC_URL = "new-topic-extra";
+
+ private EditorView contentEditor;
+ private EmojiKeyboard emojiKeyboard;
+ private TextInputLayout subjectInput;
+ private MaterialProgressBar progressBar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_create_content);
+
+ //Initialize toolbar
+ toolbar = findViewById(R.id.toolbar);
+ toolbar.setTitle("Create topic");
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ }
+
+ progressBar = findViewById(R.id.progressBar);
+
+ Intent callingIntent = getIntent();
+ String newTopicUrl = callingIntent.getStringExtra(EXTRA_NEW_TOPIC_URL);
+
+ emojiKeyboard = findViewById(R.id.emoji_keyboard);
+
+ subjectInput = findViewById(R.id.subject_input);
+ subjectInput.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT);
+ subjectInput.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
+
+ contentEditor = findViewById(R.id.main_content_editorview);
+ contentEditor.setEmojiKeyboard(emojiKeyboard);
+ emojiKeyboard.registerEmojiInputField(contentEditor);
+ contentEditor.setOnSubmitListener(v -> {
+ if (newTopicUrl != null) {
+ if (TextUtils.isEmpty(subjectInput.getEditText().getText())) {
+ subjectInput.setError("Required");
+ return;
+ }
+ if (TextUtils.isEmpty(contentEditor.getText())) {
+ contentEditor.setError("Required");
+ return;
+ }
+ boolean includeAppSignature = true;
+ SessionManager sessionManager = BaseActivity.getSessionManager();
+ if (sessionManager.isLoggedIn()) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ includeAppSignature = prefs.getBoolean(SettingsActivity.POSTING_APP_SIGNATURE_ENABLE_KEY, true);
+ }
+ emojiKeyboard.setVisibility(View.GONE);
+
+ new NewTopicTask(this, includeAppSignature).execute(newTopicUrl, subjectInput.getEditText().getText().toString(),
+ contentEditor.getText().toString());
+ }
+ });
+ }
+ @Override
+ public void onBackPressed() {
+ if (emojiKeyboard.getVisibility() == View.VISIBLE) {
+ emojiKeyboard.setVisibility(View.GONE);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onNewTopicTaskStarted() {
+ Timber.i("New topic creation started");
+ progressBar.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onNewTopicTaskFinished(boolean success) {
+ progressBar.setVisibility(View.INVISIBLE);
+ if (success) {
+ Timber.i("New topic created successfully");
+ finish();
+ } else {
+ Timber.w("New topic creation failed");
+ Toast.makeText(getBaseContext(), "Failed to create new topic!", Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java
new file mode 100644
index 00000000..985e0930
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java
@@ -0,0 +1,100 @@
+package gr.thmmy.mthmmy.activities.create_content;
+
+import android.os.AsyncTask;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+
+import java.io.IOException;
+
+import gr.thmmy.mthmmy.base.BaseApplication;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import timber.log.Timber;
+
+import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus;
+
+public class NewTopicTask extends AsyncTask {
+
+ private NewTopicTaskCallbacks listener;
+ private boolean includeAppSignature;
+
+ public NewTopicTask(NewTopicTaskCallbacks listener, boolean includeAppSignature){
+ this.listener = listener;
+ this.includeAppSignature = includeAppSignature;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ listener.onNewTopicTaskStarted();
+ }
+
+ @Override
+ protected Boolean doInBackground(String... strings) {
+ Request request = new Request.Builder()
+ .url(strings[0] + ";wap2")
+ .build();
+
+ OkHttpClient client = BaseApplication.getInstance().getClient();
+
+ Document document;
+ String seqnum, sc, topic, createTopicUrl;
+ try {
+ Response response = client.newCall(request).execute();
+ document = Jsoup.parse(response.body().string());
+
+ seqnum = document.select("input[name=seqnum]").first().attr("value");
+ sc = document.select("input[name=sc]").first().attr("value");
+ topic = document.select("input[name=topic]").first().attr("value");
+ createTopicUrl = document.select("form").first().attr("action");
+
+ final String appSignature = "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/" +
+ "details?id=gr.thmmy.mthmmy]mTHMMY[/url] [/i][/size][/right]";
+
+ RequestBody postBody = new MultipartBody.Builder()
+ .setType(MultipartBody.FORM)
+ .addFormDataPart("message", strings[2] + (includeAppSignature ? appSignature : ""))
+ .addFormDataPart("seqnum", seqnum)
+ .addFormDataPart("sc", sc)
+ .addFormDataPart("subject", strings[1])
+ .addFormDataPart("topic", topic)
+ .build();
+
+ Request post = new Request.Builder()
+ .url(createTopicUrl)
+ .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36")
+ .post(postBody)
+ .build();
+
+ try {
+ client.newCall(post).execute();
+ Response response2 = client.newCall(post).execute();
+ switch (replyStatus(response2)) {
+ case SUCCESSFUL:
+ BaseApplication.getInstance().logFirebaseAnalyticsEvent("new_topic_creation", null);
+ return true;
+ default:
+ Timber.e("Malformed post. Request string: %s", post.toString());
+ return false;
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ listener.onNewTopicTaskFinished(success);
+ }
+
+ public interface NewTopicTaskCallbacks {
+ void onNewTopicTaskStarted();
+ void onNewTopicTaskFinished(boolean success);
+ }
+}
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 93d92f1a..076cefe9 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
@@ -1,13 +1,14 @@
package gr.thmmy.mthmmy.activities.downloads;
+import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
-import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
import android.widget.ProgressBar;
import android.widget.Toast;
@@ -19,6 +20,7 @@ import java.util.ArrayList;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Download;
@@ -31,6 +33,8 @@ import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
+import static gr.thmmy.mthmmy.activities.upload.UploadActivity.BUNDLE_UPLOAD_CATEGORY;
+
public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener {
/**
* The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle.
@@ -42,13 +46,14 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
public static final String BUNDLE_DOWNLOADS_TITLE = "DOWNLOADS_TITLE";
private static final String downloadsIndexUrl = "https://www.thmmy.gr/smf/index.php?action=tpmod;dl;";
private String downloadsUrl;
+ private String downloadsNav;
private String downloadsTitle;
private final ArrayList parsedDownloads = new ArrayList<>();
private MaterialProgressBar progressBar;
private RecyclerView recyclerView;
private DownloadsAdapter downloadsAdapter;
- private FloatingActionButton uploadFAB;
+ //private FloatingActionButton uploadFAB;
private ParseDownloadPageTask parseDownloadPageTask;
private int numberOfPages = -1;
@@ -68,7 +73,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
if (downloadsUrl != null && !Objects.equals(downloadsUrl, "")) {
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(downloadsUrl));
if (!target.is(ThmmyPage.PageCategory.DOWNLOADS)) {
- Timber.e("Bundle came with a non downloads url!\nUrl:\n%s" , downloadsUrl);
+ Timber.e("Bundle came with a non downloads url!\nUrl:\n%s", downloadsUrl);
Toast.makeText(this, "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show();
finish();
}
@@ -114,14 +119,38 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
}
});
- uploadFAB = findViewById(R.id.download_fab);
- uploadFAB.setEnabled(false);
- uploadFAB.hide();
+// uploadFAB = findViewById(R.id.upload_fab);
+// uploadFAB.setEnabled(false);
+// uploadFAB.hide();
parseDownloadPageTask = new ParseDownloadPageTask();
parseDownloadPageTask.execute(downloadsUrl);
}
+// @Override
+// public boolean onCreateOptionsMenu(Menu menu) {
+// // Inflates the menu; this adds items to the action bar if it is present.
+// getMenuInflater().inflate(R.menu.downloads_menu, menu);
+// super.onCreateOptionsMenu(menu);
+// return true;
+// }
+//
+// @Override
+// public boolean onOptionsItemSelected(MenuItem item) {
+// // Handle presses on the action bar items
+// switch (item.getItemId()) {
+// case R.id.menu_upload:
+// Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class);
+// Bundle extras = new Bundle();
+// extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav);
+// intent.putExtras(extras);
+// startActivity(intent);
+// return true;
+// default:
+// return super.onOptionsItemSelected(item);
+// }
+// }
+
@Override
public void onLoadMore() {
if (pagesLoaded < numberOfPages) {
@@ -165,7 +194,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
* data. {@link ParseTask#postExecution(ResultCode) postExecution} method calls {@link RecyclerView#swapAdapter}
* to build graphics.
*
- *
Calling TopicTask's {@link ParseTask#execute execute} method needs to have profile's url
+ *
Calling TopicTask's {@link ParseTask#execute execute} method needs to have download's page url
* as String parameter!
*/
private class ParseDownloadPageTask extends ParseTask {
@@ -175,18 +204,22 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
@Override
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
- if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
+ //if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
}
@Override
protected void parse(Document downloadPage) throws ParseException {
- try{
+ try {
+ Element downloadsNavElement = downloadPage.select("div.nav").first();
+ downloadsNav = downloadsNavElement.text();
+
if (downloadsTitle == null || Objects.equals(downloadsTitle, ""))
- downloadsTitle = downloadPage.select("div.nav>b>a.nav").last().text();
+ downloadsTitle = downloadsNavElement.select("b>a.nav").last().text();
//Removes loading item
if (isLoadingMore) {
- if (parsedDownloads.size() > 0) parsedDownloads.remove(parsedDownloads.size() - 1);
+ if (parsedDownloads.size() > 0)
+ parsedDownloads.remove(parsedDownloads.size() - 1);
}
if (ThmmyPage.resolvePageCategory(Uri.parse(url)).is(ThmmyPage.PageCategory.DOWNLOADS_CATEGORY))
@@ -231,15 +264,15 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
}
} else {
download = new Download(type,
- rows.select("b>a").first().attr("href"),
- rows.select("b>a").first().text(),
- rows.select("div.smalltext:not(:has(a))").text(),
- rows.select("span:not(:has(a))").first().text(),
- false,
- rows.select("span:has(a)").first().text());
+ rows.select("b>a").first().attr("href"),
+ rows.select("b>a").first().text(),
+ rows.select("div.smalltext:not(:has(a))").text(),
+ rows.select("span:not(:has(a))").first().text(),
+ false,
+ rows.select("span:has(a)").first().text());
parsedDownloads.add(download);
}
- }catch(Exception e){
+ } catch (Exception e) {
throw new ParseException("Parsing failed (DownloadsActivity)");
}
}
@@ -269,7 +302,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
toolbar.setTitle(downloadsTitle);
++pagesLoaded;
- if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
+ //if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
progressBar.setVisibility(ProgressBar.INVISIBLE);
downloadsAdapter.notifyDataSetChanged();
isLoadingMore = false;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
index 5395b447..34a583e0 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
@@ -91,10 +91,10 @@ class DownloadsAdapter extends RecyclerView.Adapter {
if (downloadExpandableVisibility.get(downloadViewHolder.getAdapterPosition())) {
downloadViewHolder.informationExpandable.setVisibility(View.VISIBLE);
- downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_up);
+ downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp);
} else {
downloadViewHolder.informationExpandable.setVisibility(View.GONE);
- downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_down);
+ downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp);
}
downloadViewHolder.informationExpandableBtn.setOnClickListener(new View.OnClickListener() {
@Override
@@ -103,10 +103,10 @@ class DownloadsAdapter extends RecyclerView.Adapter {
getAdapterPosition());
if (visible) {
downloadViewHolder.informationExpandable.setVisibility(View.GONE);
- downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_down);
+ downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp);
} else {
downloadViewHolder.informationExpandable.setVisibility(View.VISIBLE);
- downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_up);
+ downloadViewHolder.informationExpandableBtn.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp);
}
downloadExpandableVisibility.set(downloadViewHolder.getAdapterPosition(), !visible);
}
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 45ea0fc9..67f99897 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
@@ -1,6 +1,7 @@
package gr.thmmy.mthmmy.activities.main;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
@@ -8,6 +9,7 @@ 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.preference.PreferenceManager;
import android.widget.Toast;
import java.util.ArrayList;
@@ -35,6 +37,7 @@ import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWN
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
+import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DEFAULT_HOME_TAB;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
@@ -42,6 +45,8 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
//-----------------------------------------CLASS VARIABLES------------------------------------------
private static final int TIME_INTERVAL = 2000;
+ private SharedPreferences sharedPrefs;
+ private static final String DRAWER_INTRO = "DRAWER_INTRO";
private long mBackPressed;
private SectionsPagerAdapter sectionsPagerAdapter;
private ViewPager viewPager;
@@ -53,9 +58,12 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
redirectToActivityFromIntent(intentFilter);
setContentView(R.layout.activity_main);
+ PreferenceManager.setDefaultValues(this, R.xml.app_preferences_user, false);
+
if (sessionManager.isLoginScreenDefault()) {
//Go to login
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
+ intent.putExtra("REDIRECT", true);
startActivity(intent);
finish();
overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
@@ -71,7 +79,6 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
if (sessionManager.isLoggedIn())
sectionsPagerAdapter.addFragment(UnreadFragment.newInstance(3), "UNREAD");
-
//Set up the ViewPager with the sections adapter.
viewPager = findViewById(R.id.container);
viewPager.setAdapter(sectionsPagerAdapter);
@@ -79,6 +86,12 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
TabLayout tabLayout = findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
+ sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ int preferredTab = Integer.parseInt(sharedPrefs.getString(DEFAULT_HOME_TAB, "0"));
+ if (preferredTab != 3 || sessionManager.isLoggedIn()) {
+ tabLayout.getTabAt(preferredTab).select();
+ }
+
setMainActivity(this);
}
@@ -92,6 +105,10 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
@Override
protected void onResume() {
drawer.setSelection(HOME_ID);
+ if(!sharedPrefs.getBoolean(DRAWER_INTRO, false)){
+ drawer.openDrawer();
+ sharedPrefs.edit().putBoolean(DRAWER_INTRO, true).apply();
+ }
updateTabs();
super.onResume();
}
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 3b6366b4..a85eca23 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
@@ -10,6 +10,7 @@ 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;
@@ -17,21 +18,26 @@ 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.BaseActivity;
+import gr.thmmy.mthmmy.base.BaseApplication;
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.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
-import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
+import okhttp3.Response;
import timber.log.Timber;
/**
@@ -83,7 +89,7 @@ public class ForumFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (categories.isEmpty()) {
- forumTask = new ForumTask();
+ forumTask = new ForumTask(this::onForumTaskStarted, this::onForumTaskFinished);
forumTask.execute();
}
@@ -106,7 +112,7 @@ public class ForumFragment extends BaseFragment {
if (BaseActivity.getSessionManager().isLoggedIn()) {
if (forumTask.getStatus() == AsyncTask.Status.RUNNING)
forumTask.cancel(true);
- forumTask = new ForumTask();
+ forumTask = new ForumTask(ForumFragment.this::onForumTaskStarted, ForumFragment.this::onForumTaskFinished);
forumTask.setUrl(categories.get(parentPosition).getCategoryURL());
forumTask.execute();
}
@@ -117,7 +123,7 @@ public class ForumFragment extends BaseFragment {
if (BaseActivity.getSessionManager().isLoggedIn()) {
if (forumTask.getStatus() == AsyncTask.Status.RUNNING)
forumTask.cancel(true);
- forumTask = new ForumTask();
+ forumTask = new ForumTask(ForumFragment.this::onForumTaskStarted, ForumFragment.this::onForumTaskFinished);
forumTask.setUrl(categories.get(parentPosition).getCategoryURL());
forumTask.execute();
}
@@ -135,16 +141,12 @@ public class ForumFragment extends BaseFragment {
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent);
- 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());
- }
+ swipeRefreshLayout.setOnRefreshListener(() -> {
+ if (forumTask != null && forumTask.getStatus() != AsyncTask.Status.RUNNING) {
+ forumTask = new ForumTask(ForumFragment.this::onForumTaskStarted, ForumFragment.this::onForumTaskFinished);
+ //forumTask.execute(SessionManager.indexUrl.toString());
+ forumTask.execute();
}
-
}
);
@@ -163,33 +165,38 @@ public class ForumFragment extends BaseFragment {
void onForumFragmentInteraction(Board board);
}
- //---------------------------------------ASYNC TASK-----------------------------------
+ public void onForumTaskStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
- private class ForumTask extends ParseTask {
- private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand
+ public void onForumTaskFinished(int resultCode, ArrayList fetchedCategories) {
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ categories.clear();
+ categories.addAll(fetchedCategories);
+ forumAdapter.notifyParentDataSetChanged(false);
+ } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) {
+ Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
+ }
- private final List fetchedCategories;
+ progressBar.setVisibility(ProgressBar.INVISIBLE);
+ swipeRefreshLayout.setRefreshing(false);
+ }
- ForumTask() {
- fetchedCategories = new ArrayList<>();
- }
+ //---------------------------------------ASYNC TASK-----------------------------------
- protected void onPreExecute() {
- progressBar.setVisibility(ProgressBar.VISIBLE);
- }
+ private class ForumTask extends NewParseTask> {
+ private HttpUrl forumUrl = SessionManager.forumUrl; //may change upon collapse/expand
- @Override
- protected Request prepareRequest(String... params) {
- return new Request.Builder()
- .url(forumUrl)
- .build();
+ public ForumTask(OnTaskStartedListener onTaskStartedListener,
+ OnNetworkTaskFinishedListener> onParseTaskFinishedListener) {
+ super(onTaskStartedListener, onParseTaskFinishedListener);
}
-
@Override
- public void parse(Document document) throws ParseException {
+ protected ArrayList parse(Document document, Response response) throws ParseException {
Elements categoryBlocks = document.select(".tborder:not([style])>table[cellpadding=5]");
if (categoryBlocks.size() != 0) {
+ ArrayList fetchedCategories = new ArrayList<>();
for (Element categoryBlock : categoryBlocks) {
Element categoryElement = categoryBlock.select("td[colspan=2]>[name]").first();
String categoryUrl = categoryElement.attr("href");
@@ -207,24 +214,26 @@ public class ForumFragment extends BaseFragment {
fetchedCategories.add(category);
}
- categories.clear();
- categories.addAll(fetchedCategories);
- fetchedCategories.clear();
+ return fetchedCategories;
} else
throw new ParseException("Parsing failed");
}
@Override
- protected void postExecution(ParseTask.ResultCode result) {
- if (result == ResultCode.SUCCESS)
- forumAdapter.notifyParentDataSetChanged(false);
+ protected Response sendRequest(OkHttpClient client, String... input) throws IOException {
+ Request request = new Request.Builder()
+ .url(forumUrl)
+ .build();
+ return client.newCall(request).execute();
+ }
- progressBar.setVisibility(ProgressBar.INVISIBLE);
- swipeRefreshLayout.setRefreshing(false);
+ @Override
+ protected int getResultCode(Response response, ArrayList data) {
+ return NetworkResultCodes.SUCCESSFUL;
}
- public void setUrl(String string) //TODO delete and simplify e.g. in prepareRequest possible?
- {
+ //TODO delete and simplify e.g. in prepareRequest possible?
+ public void setUrl(String string) {
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 aee35e37..65b0d315 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,6 +10,7 @@ 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.select.Elements;
@@ -20,13 +21,16 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CustomRecyclerView;
+import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
-import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
+import okhttp3.Response;
import timber.log.Timber;
@@ -79,7 +83,7 @@ public class RecentFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (topicSummaries.isEmpty()) {
- recentTask = new RecentTask();
+ recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished);
recentTask.execute(SessionManager.indexUrl.toString());
}
@@ -109,16 +113,11 @@ public class RecentFragment extends BaseFragment {
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent);
- swipeRefreshLayout.setOnRefreshListener(
- new SwipeRefreshLayout.OnRefreshListener() {
- @Override
- public void onRefresh() {
- if (recentTask != null && recentTask.getStatus() != AsyncTask.Status.RUNNING) {
- recentTask = new RecentTask();
- recentTask.execute(SessionManager.indexUrl.toString());
- }
+ swipeRefreshLayout.setOnRefreshListener(() -> {
+ if (recentTask != null && recentTask.getStatus() != AsyncTask.Status.RUNNING) {
+ recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished);
+ recentTask.execute(SessionManager.indexUrl.toString());
}
-
}
);
}
@@ -138,18 +137,34 @@ public class RecentFragment extends BaseFragment {
void onRecentFragmentInteraction(TopicSummary topicSummary);
}
+ private void onRecentTaskStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
+
+ private void onRecentTaskFinished(int resultCode, ArrayList fetchedRecent) {
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ topicSummaries.clear();
+ topicSummaries.addAll(fetchedRecent);
+ recentAdapter.notifyDataSetChanged();
+ } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) {
+ Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
+ }
+
+ progressBar.setVisibility(ProgressBar.INVISIBLE);
+ swipeRefreshLayout.setRefreshing(false);
+ }
+
//---------------------------------------ASYNC TASK-----------------------------------
- private class RecentTask extends ParseTask {
- private List fetchedRecent;
+ private class RecentTask extends NewParseTask> {
- @Override
- protected void onPreExecute() {
- progressBar.setVisibility(ProgressBar.VISIBLE);
- fetchedRecent = new ArrayList<>();
+ public RecentTask(OnTaskStartedListener onTaskStartedListener,
+ OnNetworkTaskFinishedListener> onParseTaskFinishedListener) {
+ super(onTaskStartedListener, onParseTaskFinishedListener);
}
@Override
- public void parse(Document document) throws ParseException {
+ protected ArrayList parse(Document document, Response response) throws ParseException {
+ ArrayList fetchedRecent = new ArrayList<>();
Elements recent = document.select("#block8 :first-child div");
if (!recent.isEmpty()) {
for (int i = 0; i < recent.size(); i += 3) {
@@ -174,7 +189,7 @@ public class RecentFragment extends BaseFragment {
dateTime.contains(" πμ") || dateTime.contains(" μμ")) {
dateTime = dateTime.replaceAll(":[0-5][0-9] ", " ");
} else {
- dateTime=dateTime.substring(0,dateTime.lastIndexOf(":"));
+ dateTime = dateTime.substring(0, dateTime.lastIndexOf(":"));
}
if (!dateTime.contains(",")) {
dateTime = dateTime.replaceAll(".+? ([0-9])", "$1");
@@ -184,22 +199,14 @@ public class RecentFragment extends BaseFragment {
fetchedRecent.add(new TopicSummary(link, title, lastUser, dateTime));
}
- return;
+ return fetchedRecent;
}
throw new ParseException("Parsing failed");
}
@Override
- protected void postExecution(ParseTask.ResultCode result) {
- if (result == ResultCode.SUCCESS)
- {
- topicSummaries.clear();
- topicSummaries.addAll(fetchedRecent);
- recentAdapter.notifyDataSetChanged();
- }
-
- progressBar.setVisibility(ProgressBar.INVISIBLE);
- swipeRefreshLayout.setRefreshing(false);
+ protected int getResultCode(Response response, ArrayList data) {
+ return NetworkResultCodes.SUCCESSFUL;
}
}
}
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
index 4b7c235b..65100922 100644
--- 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
@@ -1,6 +1,5 @@
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;
@@ -15,7 +14,6 @@ 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;
@@ -24,10 +22,9 @@ class UnreadAdapter extends RecyclerView.Adapter {
private final int VIEW_TYPE_NADA = 1;
private final int VIEW_TYPE_MARK_READ = 2;
- UnreadAdapter(Context context, @NonNull List topicSummaryList,
+ UnreadAdapter(@NonNull List topicSummaryList,
BaseFragment.FragmentInteractionListener listener,
MarkReadInteractionListener markReadInteractionListener) {
- this.context = context;
this.unreadList = topicSummaryList;
mListener = (UnreadFragment.UnreadFragmentInteractionListener) listener;
markReadListener = markReadInteractionListener;
@@ -39,8 +36,9 @@ class UnreadAdapter extends RecyclerView.Adapter {
return unreadList.get(position).getTopicUrl() == null ? VIEW_TYPE_NADA : VIEW_TYPE_ITEM;
}
+ @NonNull
@Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_unread_row, parent, false);
@@ -58,7 +56,7 @@ class UnreadAdapter extends RecyclerView.Adapter {
}
@Override
- public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
+ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof UnreadAdapter.EmptyViewHolder) {
final UnreadAdapter.EmptyViewHolder emptyViewHolder = (UnreadAdapter.EmptyViewHolder) holder;
emptyViewHolder.text.setText(unreadList.get(holder.getAdapterPosition()).getDateTimeModified());
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
index 98be97e2..0747b4bf 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
@@ -2,6 +2,7 @@ package gr.thmmy.mthmmy.activities.main.unread;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
@@ -21,14 +22,17 @@ import java.util.ArrayList;
import java.util.List;
import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CustomRecyclerView;
+import gr.thmmy.mthmmy.utils.NetworkResultCodes;
+import gr.thmmy.mthmmy.utils.parsing.NewParseTask;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
-import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.Request;
+import okhttp3.Response;
import timber.log.Timber;
/**
@@ -42,13 +46,14 @@ import timber.log.Timber;
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 int numberOfPages = 0;
+ private int loadedPages = 0;
private UnreadTask unreadTask;
private MarkReadTask markReadTask;
@@ -82,7 +87,8 @@ public class UnreadFragment extends BaseFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (topicSummaries.isEmpty()) {
- unreadTask = new UnreadTask();
+ unreadTask = new UnreadTask(this::onUnreadTaskStarted, this::onUnreadTaskFinished);
+ assert SessionManager.unreadUrl != null;
unreadTask.execute(SessionManager.unreadUrl.toString());
}
markReadTask = new MarkReadTask();
@@ -91,7 +97,7 @@ public class UnreadFragment extends BaseFragment {
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View rootView = inflater.inflate(R.layout.fragment_unread, container, false);
@@ -99,16 +105,13 @@ public class UnreadFragment extends BaseFragment {
// Set the adapter
if (rootView instanceof RelativeLayout) {
progressBar = 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);
- }
- }
- });
+ unreadAdapter = new UnreadAdapter(topicSummaries,
+ fragmentInteractionListener, markReadLinkUrl -> {
+ if (markReadTask != null && markReadTask.getStatus() != AsyncTask.Status.RUNNING) {
+ markReadTask = new MarkReadTask();
+ markReadTask.execute(markReadLinkUrl);
+ }
+ });
CustomRecyclerView recyclerView = rootView.findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(recyclerView.getContext());
@@ -122,15 +125,15 @@ public class UnreadFragment extends BaseFragment {
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent);
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());
- }
+ () -> {
+ if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) {
+ topicSummaries.clear();
+ numberOfPages = 0;
+ loadedPages = 0;
+ unreadTask = new UnreadTask(this::onUnreadTaskStarted, this::onUnreadTaskFinished);
+ assert SessionManager.unreadUrl != null;
+ unreadTask.execute(SessionManager.unreadUrl.toString());
}
-
}
);
}
@@ -152,16 +155,45 @@ public class UnreadFragment extends BaseFragment {
}
//---------------------------------------ASYNC TASK-----------------------------------
- private class UnreadTask extends ParseTask {
- protected void onPreExecute() {
- progressBar.setVisibility(ProgressBar.VISIBLE);
+
+ private void onUnreadTaskStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
+
+ private void onUnreadTaskFinished(int resultCode, Void data) {
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ unreadAdapter.notifyDataSetChanged();
+
+ ++loadedPages;
+ if (loadedPages < numberOfPages) {
+ unreadTask = new UnreadTask(this::onUnreadTaskStarted, this::onUnreadTaskFinished);
+ assert SessionManager.unreadUrl != null;
+ unreadTask.execute(SessionManager.unreadUrl.toString() + ";start=" + loadedPages * 20);
+ }
+ else {
+ progressBar.setVisibility(ProgressBar.INVISIBLE);
+ swipeRefreshLayout.setRefreshing(false);
+ }
+ }
+ else{
+ progressBar.setVisibility(ProgressBar.INVISIBLE);
+ swipeRefreshLayout.setRefreshing(false);
+ if (resultCode == NetworkResultCodes.NETWORK_ERROR)
+ Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private class UnreadTask extends NewParseTask {
+
+ UnreadTask(OnTaskStartedListener onTaskStartedListener, OnNetworkTaskFinishedListener onParseTaskFinishedListener) {
+ super(onTaskStartedListener, onParseTaskFinishedListener);
}
@Override
- public void parse(Document document) throws ParseException {
+ protected Void parse(Document document, Response response) throws ParseException {
Elements unread = document.select("table.bordercolor[cellspacing=1] tr:not(.titlebg)");
if (!unread.isEmpty()) {
- topicSummaries.clear();
+ //topicSummaries.clear();
for (Element row : unread) {
Elements information = row.select("td");
String link = information.last().select("a").first().attr("href");
@@ -178,7 +210,7 @@ public class UnreadFragment extends BaseFragment {
dateTime.contains(" πμ") || dateTime.contains(" μμ")) {
dateTime = dateTime.replaceAll(":[0-5][0-9] ", " ");
} else {
- dateTime=dateTime.substring(0,dateTime.lastIndexOf(":"));
+ dateTime = dateTime.substring(0, dateTime.lastIndexOf(":"));
}
if (!dateTime.contains(",")) {
dateTime = dateTime.replaceAll(".+? ([0-9])", "$1");
@@ -186,9 +218,26 @@ public class UnreadFragment extends BaseFragment {
topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime));
}
- Element markRead = document.select("table:not(.bordercolor):not([width])").select("a")
- .first();
- if (markRead != null)
+ Element topBar = document.select("table:not(.bordercolor):not(#bodyarea):has(td.middletext)").first();
+
+ Element pagesElement = null, markRead = null;
+ if (topBar != null) {
+ pagesElement = topBar.select("td.middletext").first();
+
+ markRead = document.select("table:not(.bordercolor):not([width])").select("a")
+ .first();
+ }
+
+ if (numberOfPages == 0 && pagesElement != null) {
+ Elements pages = pagesElement.select("a");
+ if (!pages.isEmpty()) {
+ numberOfPages = Integer.parseInt(pages.last().text());
+ } else {
+ numberOfPages = 1;
+ }
+ }
+
+ if (markRead != null && loadedPages == numberOfPages - 1)
topicSummaries.add(new TopicSummary(markRead.attr("href"), markRead.text(), null,
null));
} else {
@@ -201,15 +250,12 @@ public class UnreadFragment extends BaseFragment {
}
topicSummaries.add(new TopicSummary(null, null, null, message));
}
+ return null;
}
@Override
- protected void postExecution(ParseTask.ResultCode result) {
- if (result == ResultCode.SUCCESS)
- unreadAdapter.notifyDataSetChanged();
-
- progressBar.setVisibility(ProgressBar.INVISIBLE);
- swipeRefreshLayout.setRefreshing(false);
+ protected int getResultCode(Response response, Void data) {
+ return NetworkResultCodes.SUCCESSFUL;
}
}
@@ -253,7 +299,8 @@ public class UnreadFragment extends BaseFragment {
, "Fatal error!\n Task aborted...", Toast.LENGTH_LONG).show();
} else {
if (unreadTask != null && unreadTask.getStatus() != AsyncTask.Status.RUNNING) {
- unreadTask = new UnreadTask();
+ unreadTask = new UnreadTask(UnreadFragment.this::onUnreadTaskStarted, UnreadFragment.this::onUnreadTaskFinished);
+ assert SessionManager.unreadUrl != null;
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 a83fc610..19680329 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
@@ -5,6 +5,7 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
@@ -13,6 +14,7 @@ import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatDelegate;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
@@ -92,6 +94,13 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
private String username;
private int tabSelect;
+ //Fix for vector drawables on android <21
+ static {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -124,9 +133,9 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
.resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
.centerCrop()
.error(ResourcesCompat.getDrawable(this.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
.placeholder(ResourcesCompat.getDrawable(this.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
.transform(new CircleTransform())
.into(thumbnailView);
usernameView = findViewById(R.id.profile_activity_username);
@@ -310,9 +319,9 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
.resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
.centerCrop()
.error(ResourcesCompat.getDrawable(getResources()
- , R.drawable.ic_default_user_thumbnail, null))
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
.placeholder(ResourcesCompat.getDrawable(getResources()
- , R.drawable.ic_default_user_thumbnail, null))
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
.transform(new CircleTransform())
.into(thumbnailView);
if (personalText != null) {
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 8993fcf0..76380576 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
@@ -180,8 +180,6 @@ public class SummaryFragment extends Fragment {
if (profileSummaryRow.contains("@") &&
(profileSummaryRow.contains("Email") || profileSummaryRow.contains("E-mail"))) {
- Timber.d("mpika");
- Timber.d(profileSummaryRow);
String email = profileSummaryRow.substring(profileSummaryRow.indexOf(": ") + 6);
profileSummaryRow = profileSummaryRow.replace(email,
"" + email + "");
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java
new file mode 100644
index 00000000..4383732b
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java
@@ -0,0 +1,48 @@
+package gr.thmmy.mthmmy.activities.settings;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.base.BaseActivity;
+
+public class SettingsActivity extends BaseActivity {
+ public static final String DEFAULT_HOME_TAB = "pref_app_main_default_tab_key";
+ public static final String NOTIFICATION_LED_KEY = "pref_notification_led_enable_key";
+ public static final String NOTIFICATION_VIBRATION_KEY = "pref_notification_vibration_enable_key";
+ public static final String POSTING_APP_SIGNATURE_ENABLE_KEY = "pref_posting_app_signature_enable_key";
+ public static final String UPLOADING_APP_SIGNATURE_ENABLE_KEY = "pref_uploading_app_signature_enable_key";
+
+ private SettingsFragment preferenceFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+
+ //Initialize toolbar
+ toolbar = findViewById(R.id.toolbar);
+ toolbar.setTitle("Settings");
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ }
+
+ createDrawer();
+ drawer.setSelection(SETTINGS_ID);
+
+ preferenceFragment = SettingsFragment.newInstance(sessionManager.isLoggedIn());
+ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
+ fragmentTransaction.add(R.id.pref_container, preferenceFragment);
+ fragmentTransaction.commit();
+ }
+
+ @Override
+ protected void onResume() {
+ drawer.setSelection(SETTINGS_ID);
+ super.onResume();
+ if (preferenceFragment != null)
+ preferenceFragment.updateUserLoginState(sessionManager.isLoggedIn());
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java
new file mode 100644
index 00000000..df97ad07
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java
@@ -0,0 +1,214 @@
+package gr.thmmy.mthmmy.activities.settings;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.view.View;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.base.BaseApplication;
+import timber.log.Timber;
+
+import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DEFAULT_HOME_TAB;
+
+public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
+ private enum PREFS_TYPE {
+ NOT_SET, USER, GUEST
+ }
+
+ public static final String ARG_IS_LOGGED_IN = "selectedRingtoneKey";
+
+ //Preferences xml keys
+ private static final String SELECTED_NOTIFICATIONS_SOUND = "pref_notifications_select_sound_key";
+ private static final String POSTING_CATEGORY = "pref_category_posting_key";
+ private static final String UPLOADING_CATEGORY = "pref_category_uploading_key";
+
+ //SharedPreferences keys
+ private static final int REQUEST_CODE_ALERT_RINGTONE = 2;
+ public static final String SETTINGS_SHARED_PREFS = "settingsSharedPrefs";
+ public static final String SELECTED_RINGTONE = "selectedRingtoneKey";
+ private static final String SILENT_SELECTED = "STFU";
+
+ private SharedPreferences settingsFile;
+
+ private PREFS_TYPE prefs_type = PREFS_TYPE.NOT_SET;
+ private boolean isLoggedIn = false;
+ private ArrayList defaultHomeTabEntries = new ArrayList<>();
+ private ArrayList defaultHomeTabValues = new ArrayList<>();
+
+ public static SettingsFragment newInstance(boolean isLoggedIn) {
+ SettingsFragment fragment = new SettingsFragment();
+ Bundle args = new Bundle();
+ args.putBoolean(ARG_IS_LOGGED_IN, isLoggedIn);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ public SettingsFragment() {
+ defaultHomeTabEntries.add("Recent");
+ defaultHomeTabEntries.add("Forum");
+
+ defaultHomeTabValues.add("0");
+ defaultHomeTabValues.add("1");
+
+ if(isLoggedIn = BaseApplication.getInstance().getSessionManager().isLoggedIn()){
+ defaultHomeTabEntries.add("Unread");
+ defaultHomeTabValues.add("2");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle args = getArguments();
+
+ if (args != null)
+ isLoggedIn = args.getBoolean(ARG_IS_LOGGED_IN, false);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle bundle, String rootKey) {
+ isLoggedIn = BaseApplication.getInstance().getSessionManager().isLoggedIn(); //Ensures it stays updated
+ // Add the Preferences from the XML file if needed
+ if(isLoggedIn&&(prefs_type==PREFS_TYPE.GUEST||prefs_type==PREFS_TYPE.NOT_SET)){
+ prefs_type = PREFS_TYPE.USER;
+ addPreferencesFromResource(R.xml.app_preferences_user);
+ }
+ else if(!isLoggedIn&&(prefs_type==PREFS_TYPE.USER||prefs_type==PREFS_TYPE.NOT_SET)){
+ prefs_type = PREFS_TYPE.GUEST;
+ addPreferencesFromResource(R.xml.app_preferences_guest);
+ }
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ updatePreferenceVisibility();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ if (preference.getKey().equals(SELECTED_NOTIFICATIONS_SOUND)) {
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI);
+
+ Activity activity = this.getActivity();
+ settingsFile = activity != null
+ ? activity.getSharedPreferences(SETTINGS_SHARED_PREFS, Context.MODE_PRIVATE)
+ : null;
+ String existingValue = settingsFile != null
+ ? settingsFile.getString(SELECTED_RINGTONE, null)
+ : null;
+ if (existingValue != null) {
+ if (existingValue.equals(SILENT_SELECTED)) {
+ //Selects "Silent"
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, (Uri) null);
+ } else {
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(existingValue));
+ }
+ } else {
+ //No ringtone has been selected, set to the default
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Settings.System.DEFAULT_NOTIFICATION_URI);
+ }
+
+ startActivityForResult(intent, REQUEST_CODE_ALERT_RINGTONE);
+ return true;
+ } else {
+ return super.onPreferenceTreeClick(preference);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ALERT_RINGTONE && data != null) {
+ Uri ringtone = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+ SharedPreferences.Editor editor = settingsFile.edit();
+ if (ringtone != null) {
+ editor.putString(SELECTED_RINGTONE, ringtone.toString()).apply();
+ } else {
+ //"Silent" was selected
+ editor.putString(SELECTED_RINGTONE, SILENT_SELECTED).apply();
+ }
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ public void updateUserLoginState(boolean isLoggedIn) {
+ this.isLoggedIn = isLoggedIn;
+ updatePreferenceVisibility();
+ }
+
+ private void updatePreferenceVisibility(){
+ if(isLoggedIn&& prefs_type==PREFS_TYPE.GUEST) {
+ prefs_type = PREFS_TYPE.USER;
+ setPreferencesFromResource(R.xml.app_preferences_user, getPreferenceScreen().getKey());
+ if(!defaultHomeTabEntries.contains("Unread")){
+ defaultHomeTabEntries.add("Unread");
+ defaultHomeTabValues.add("2");
+ }
+ }
+ else if(!isLoggedIn&&prefs_type==PREFS_TYPE.USER){
+ prefs_type = PREFS_TYPE.GUEST;
+ setPreferencesFromResource(R.xml.app_preferences_guest,getPreferenceScreen().getKey());
+ if(defaultHomeTabEntries.contains("Unread")){
+ defaultHomeTabEntries.remove("Unread");
+ defaultHomeTabValues.remove("2");
+ }
+ }
+
+ CharSequence[] tmpCs = defaultHomeTabEntries.toArray(new CharSequence[defaultHomeTabEntries.size()]);
+ ((ListPreference) findPreference(DEFAULT_HOME_TAB)).setEntries(tmpCs);
+
+ tmpCs = defaultHomeTabValues.toArray(new CharSequence[defaultHomeTabValues.size()]);
+ ((ListPreference) findPreference(DEFAULT_HOME_TAB)).setEntryValues(tmpCs);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ boolean enabled;
+ if (key.equals(getString(R.string.pref_privacy_crashlytics_enable_key))) {
+ enabled = sharedPreferences.getBoolean(key, false);
+ if(enabled)
+ BaseApplication.getInstance().startFirebaseCrashlyticsCollection();
+ else {
+ Timber.i("Crashlytics collection will be disabled after restarting.");
+ Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "This change will take effect once you restart the app.", Toast.LENGTH_SHORT).show();
+ }
+ } else if (key.equals(getString(R.string.pref_privacy_analytics_enable_key))) {
+ enabled = sharedPreferences.getBoolean(key, false);
+ BaseApplication.getInstance().setFirebaseAnalyticsCollection(enabled);
+ if(enabled)
+ Timber.i("Analytics collection enabled.");
+ else
+ Timber.i("Analytics collection disabled.");
+ }
+ }
+}
\ No newline at end of file
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 cc08f17d..88c2f8a3 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,11 +11,11 @@ import timber.log.Timber;
/**
* This is a utility class containing a collection of static methods to help with topic replying.
*/
-class Posting {
+public class Posting {
/**
* {@link REPLY_STATUS} enum defines the different possible outcomes of a topic reply request.
*/
- enum REPLY_STATUS {
+ public enum REPLY_STATUS {
/**
* The request was successful
*/
@@ -54,7 +54,7 @@ class Posting {
* @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 {
+ public static REPLY_STATUS replyStatus(Response response) throws IOException {
if (response.code() == 404) return REPLY_STATUS.NOT_FOUND;
if (response.code() < 200 || response.code() >= 400) return REPLY_STATUS.OTHER_ERROR;
String finalUrl = response.request().url().toString();
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 cffbc290..4d03649d 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
@@ -2,24 +2,26 @@ package gr.thmmy.mthmmy.activities.topic;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
+import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
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.design.widget.Snackbar;
+import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.RecyclerView;
-import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
-import android.text.style.ClickableSpan;
-import android.text.style.URLSpan;
-import android.util.SparseArray;
+import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -32,39 +34,29 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.jsoup.nodes.Element;
-import org.jsoup.select.Selector;
-
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Objects;
import gr.thmmy.mthmmy.R;
-import gr.thmmy.mthmmy.activities.board.BoardActivity;
-import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
+import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
+import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask;
+import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply;
+import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask;
+import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask;
import gr.thmmy.mthmmy.base.BaseActivity;
+import gr.thmmy.mthmmy.base.BaseApplication;
+import gr.thmmy.mthmmy.editorview.EmojiKeyboard;
import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyPage;
+import gr.thmmy.mthmmy.model.TopicItem;
import gr.thmmy.mthmmy.utils.CustomLinearLayoutManager;
-import gr.thmmy.mthmmy.utils.parsing.ParseException;
+import gr.thmmy.mthmmy.utils.HTMLUtils;
+import gr.thmmy.mthmmy.utils.NetworkResultCodes;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
+import gr.thmmy.mthmmy.viewmodel.TopicViewModel;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
-import okhttp3.MultipartBody;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
import timber.log.Timber;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE;
-import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL;
-import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL;
-import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
-import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
-import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus;
import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG;
/**
@@ -74,7 +66,7 @@ import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG;
* key {@link #BUNDLE_TOPIC_TITLE} for faster title rendering.
*/
@SuppressWarnings("unchecked")
-public class TopicActivity extends BaseActivity {
+public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFocusChangeListener {
//Activity's variables
/**
* The key to use when putting topic's url String to {@link TopicActivity}'s Bundle.
@@ -84,80 +76,18 @@ public class TopicActivity extends BaseActivity {
* The key to use when putting topic's title String to {@link TopicActivity}'s Bundle.
*/
public static final String BUNDLE_TOPIC_TITLE = "TOPIC_TITLE";
- private static TopicTask topicTask;
private MaterialProgressBar progressBar;
private TextView toolbarTitle;
- /**
- * Holds this topic's base url. For example a topic with url similar to
- * "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or
- * "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1"
- * has the base url "https://www.thmmy.gr/smf/index.php?topic=1"
- */
- private static String base_url = "";
- /**
- * Holds this topic's title. At first this gets the value of the topic title that came with
- * bundle and is rendered in the toolbar while parsing this topic. Later, after topic's parsing
- * is done, it gets the value of {@link #parsedTitle} if bundle title and parsed title differ.
- */
- private String topicTitle;
- /**
- * Holds this topic's title as parsed from the html source. If this (parsed) title is different
- * than the one that came with activity's bundle then the parsed title is preferred over the
- * bundle one and gets rendered in the toolbar.
- */
- private String parsedTitle;
- private String topicPageUrl;
private RecyclerView recyclerView;
- /**
- * Holds the url of this page
- */
- private String loadedPageUrl = "";
- /**
- * Holds the topicId of this page
- */
- private int loadedPageTopicId = -1;
- /**
- * Becomes true after user has posted in this topic and the page is being reloaded and false
- * when topic's reloading is done
- */
- private boolean reloadingPage = false;
//Posts related
private TopicAdapter topicAdapter;
/**
* Holds a list of this topic's posts
*/
- private ArrayList postsList;
- /**
- * Gets assigned to {@link #postFocus} when there is no post focus information in the url
- */
- private static final int NO_POST_FOCUS = -1;
- /**
- * Holds the index of the post that has focus
- */
- private int postFocus = NO_POST_FOCUS;
- /**
- * Holds the position in the {@link #postsList} of the post with focus
- */
- private static int postFocusPosition = 0;
+ private ArrayList topicItems;
//Reply related
private FloatingActionButton replyFAB;
- /**
- * Holds this topic's reply url
- */
- private String replyPageUrl = null;
//Topic's pages related
- /**
- * Holds current page's index (starting from 1, not 0)
- */
- private int thisPage = 1;
- /**
- * Holds this topic's number of pages
- */
- private int numberOfPages = 1;
- /**
- * Holds a list of this topic's pages urls
- */
- private final SparseArray pagesUrls = new SparseArray<>();
//Page select related
/**
* Used for handling bottom navigation bar's buttons long click user interactions
@@ -179,11 +109,7 @@ public class TopicActivity extends BaseActivity {
* long click is held in either first or last buttons
*/
private static final int LARGE_STEP = 10;
- /**
- * Holds the value (index) of the page to be requested when a user interaction with bottom
- * navigation bar occurs
- */
- private Integer pageRequestValue;
+
//Bottom navigation bar graphics related
private LinearLayout bottomNavBar;
private ImageButton firstPage;
@@ -191,19 +117,28 @@ public class TopicActivity extends BaseActivity {
private TextView pageIndicator;
private ImageButton nextPage;
private ImageButton lastPage;
- //Topic's info related
- private SpannableStringBuilder topicTreeAndMods = new SpannableStringBuilder("Loading..."),
- topicViewers = new SpannableStringBuilder("Loading...");
-
+ private Snackbar snackbar;
+ private TopicViewModel viewModel;
+ private EmojiKeyboard emojiKeyboard;
+
+ //Fix for vector drawables on android <21
+ static {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_topic);
+ // get TopicViewModel instance
+ viewModel = ViewModelProviders.of(this).get(TopicViewModel.class);
+ subscribeUI();
Bundle extras = getIntent().getExtras();
- topicTitle = extras.getString(BUNDLE_TOPIC_TITLE);
- topicPageUrl = extras.getString(BUNDLE_TOPIC_URL);
+ String topicTitle = extras.getString(BUNDLE_TOPIC_TITLE);
+ String topicPageUrl = extras.getString(BUNDLE_TOPIC_URL);
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(
Uri.parse(topicPageUrl));
if (!target.is(ThmmyPage.PageCategory.TOPIC)) {
@@ -213,7 +148,6 @@ public class TopicActivity extends BaseActivity {
}
topicPageUrl = ThmmyPage.sanitizeTopicUrl(topicPageUrl);
-
thisPageBookmark = new Bookmark(topicTitle, ThmmyPage.getTopicId(topicPageUrl), true);
//Initializes graphics
@@ -233,40 +167,28 @@ public class TopicActivity extends BaseActivity {
createDrawer();
progressBar = findViewById(R.id.progressBar);
+ emojiKeyboard = findViewById(R.id.emoji_keyboard);
- postsList = new ArrayList<>();
+ topicItems = new ArrayList<>();
recyclerView = findViewById(R.id.topic_recycler_view);
recyclerView.setHasFixedSize(true);
- recyclerView.setOnTouchListener(
- new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- v.performClick();
- return topicTask != null && topicTask.getStatus() == AsyncTask.Status.RUNNING;
- }
- }
- );
//LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(
- getApplicationContext(), loadedPageUrl);
+ getApplicationContext(), topicPageUrl);
+
recyclerView.setLayoutManager(layoutManager);
- topicAdapter = new TopicAdapter(this, postsList, base_url, topicTask);
+ topicAdapter = new TopicAdapter(this, emojiKeyboard, topicItems);
recyclerView.setAdapter(topicAdapter);
replyFAB = findViewById(R.id.topic_fab);
- replyFAB.setEnabled(false);
+ replyFAB.hide();
bottomNavBar = findViewById(R.id.bottom_navigation_bar);
if (!sessionManager.isLoggedIn()) replyFAB.hide();
else {
- replyFAB.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (sessionManager.isLoggedIn()) {
- PrepareForReply prepareForReply = new PrepareForReply();
- prepareForReply.execute(topicAdapter.getToQuoteList());
- }
- }
+ replyFAB.setOnClickListener(view -> {
+ if (sessionManager.isLoggedIn())
+ viewModel.prepareForReply();
});
}
@@ -281,11 +203,11 @@ public class TopicActivity extends BaseActivity {
initDecrementButton(previousPage, SMALL_STEP);
initIncrementButton(nextPage, SMALL_STEP);
initIncrementButton(lastPage, LARGE_STEP);
+
paginationEnabled(false);
- //Gets posts
- topicTask = new TopicTask();
- topicTask.execute(topicPageUrl); //Attempt data parsing
+ Timber.i("Starting initial topic load");
+ viewModel.loadUrl(topicPageUrl);
}
@Override
@@ -305,27 +227,34 @@ public class TopicActivity extends BaseActivity {
topicMenuBookmarkClick();
return true;
case R.id.menu_info:
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AppCompatAlertDialogStyleAccent);
LayoutInflater inflater = this.getLayoutInflater();
LinearLayout infoDialog = (LinearLayout) inflater.inflate(R.layout.dialog_topic_info
, null);
TextView treeAndMods = infoDialog.findViewById(R.id.topic_tree_and_mods);
- treeAndMods.setText(topicTreeAndMods);
+ treeAndMods.setText(new SpannableStringBuilder("Loading..."));
treeAndMods.setMovementMethod(LinkMovementMethod.getInstance());
TextView usersViewing = infoDialog.findViewById(R.id.users_viewing);
- usersViewing.setText(topicViewers);
+ usersViewing.setText(new SpannableStringBuilder("Loading..."));
usersViewing.setMovementMethod(LinkMovementMethod.getInstance());
-
+ viewModel.getTopicTreeAndMods().observe(this, topicTreeAndMods -> {
+ if (topicTreeAndMods == null) return;
+ treeAndMods.setText(HTMLUtils.getSpannableFromHtml(this, topicTreeAndMods));
+ });
+ viewModel.getTopicViewers().observe(this, topicViewers -> {
+ if (topicViewers == null) return;
+ usersViewing.setText(HTMLUtils.getSpannableFromHtml(this, topicViewers));
+ });
builder.setView(infoDialog);
AlertDialog dialog = builder.create();
dialog.show();
return true;
case R.id.menu_share:
- Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
+ Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
sendIntent.setType("text/plain");
- sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, topicPageUrl);
+ sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, viewModel.getTopicUrl());
startActivity(Intent.createChooser(sendIntent, "Share via"));
- return true;
+ return true; //invalidateOptionsMenu();
default:
return super.onOptionsItemSelected(item);
}
@@ -336,16 +265,24 @@ public class TopicActivity extends BaseActivity {
if (drawer.isDrawerOpen()) {
drawer.closeDrawer();
return;
- }
- else if(postsList!=null && postsList.size()>0 && postsList.get(postsList.size()-1)==null)
- {
- postsList.remove(postsList.size() - 1);
- topicAdapter.notifyItemRemoved(postsList.size());
+ } else if (emojiKeyboard.getVisibility() == View.VISIBLE) {
+ emojiKeyboard.setVisibility(View.GONE);
+ return;
+ } else if (viewModel.isWritingReply()) {
+ topicItems.remove(topicItems.size() - 1);
+ topicAdapter.notifyItemRemoved(topicItems.size());
+ topicAdapter.setBackButtonHidden();
+ viewModel.setWritingReply(false);
+ replyFAB.show();
+ bottomNavBar.setVisibility(View.VISIBLE);
+ return;
+ } else if (viewModel.isEditingPost()) {
+ ((Post) topicItems.get(viewModel.getPostBeingEditedPosition())).setPostType(Post.TYPE_POST);
+ topicAdapter.notifyItemChanged(viewModel.getPostBeingEditedPosition());
topicAdapter.setBackButtonHidden();
- replyFAB.setVisibility(View.INVISIBLE);
- bottomNavBar.setVisibility(View.INVISIBLE);
- paginationEnabled(true);
- replyFAB.setEnabled(true);
+ viewModel.setEditingPost(false);
+ replyFAB.show();
+ bottomNavBar.setVisibility(View.VISIBLE);
return;
}
super.onBackPressed();
@@ -362,8 +299,12 @@ public class TopicActivity extends BaseActivity {
protected void onDestroy() {
super.onDestroy();
recyclerView.setAdapter(null);
- if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING)
- topicTask.cancel(true);
+ viewModel.stopLoading();
+ }
+
+ @Override
+ public void onPostFocusChange(int position) {
+ recyclerView.scrollToPosition(position);
}
//--------------------------------------BOTTOM NAV BAR METHODS----------------------------------
@@ -385,10 +326,10 @@ public class TopicActivity extends BaseActivity {
public void run() {
long REPEAT_DELAY = 250;
if (autoIncrement) {
- incrementPageRequestValue(step);
+ viewModel.incrementPageRequestValue(step, false);
repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), REPEAT_DELAY);
} else if (autoDecrement) {
- decrementPageRequestValue(step);
+ viewModel.decrementPageRequestValue(step, false);
repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), REPEAT_DELAY);
}
}
@@ -426,26 +367,21 @@ public class TopicActivity extends BaseActivity {
@SuppressLint("ClickableViewAccessibility")
private void initIncrementButton(ImageButton increment, final int step) {
// Increment once for a click
- increment.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- if (!autoIncrement && step == LARGE_STEP) {
- changePage(numberOfPages - 1);
- } else if (!autoIncrement) {
- incrementPageRequestValue(step);
- changePage(pageRequestValue - 1);
- }
+ increment.setOnClickListener(v -> {
+ if (!autoIncrement && step == LARGE_STEP) {
+ viewModel.setPageIndicatorIndex(viewModel.getPageCount(), true);
+ } else if (!autoIncrement) {
+ viewModel.incrementPageRequestValue(step, true);
}
});
// Auto increment for a long click
increment.setOnLongClickListener(
- new View.OnLongClickListener() {
- public boolean onLongClick(View arg0) {
- paginationDisable(arg0);
- autoIncrement = true;
- repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY);
- return false;
- }
+ arg0 -> {
+ paginationDisable(arg0);
+ autoIncrement = true;
+ repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY);
+ return false;
}
);
@@ -459,11 +395,11 @@ public class TopicActivity extends BaseActivity {
} else if (rect != null && event.getAction() == MotionEvent.ACTION_UP && autoIncrement) {
autoIncrement = false;
paginationEnabled(true);
- changePage(pageRequestValue - 1);
+ viewModel.loadPageIndicated();
} else if (rect != null && event.getAction() == MotionEvent.ACTION_MOVE) {
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
autoIncrement = false;
- decrementPageRequestValue(pageRequestValue - thisPage);
+ viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false);
paginationEnabled(true);
}
}
@@ -475,26 +411,21 @@ public class TopicActivity extends BaseActivity {
@SuppressLint("ClickableViewAccessibility")
private void initDecrementButton(ImageButton decrement, final int step) {
// Decrement once for a click
- decrement.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- if (!autoDecrement && step == LARGE_STEP) {
- changePage(0);
- } else if (!autoDecrement) {
- decrementPageRequestValue(step);
- changePage(pageRequestValue - 1);
- }
+ decrement.setOnClickListener(v -> {
+ if (!autoDecrement && step == LARGE_STEP) {
+ viewModel.setPageIndicatorIndex(1, true);
+ } else if (!autoDecrement) {
+ viewModel.decrementPageRequestValue(step, true);
}
});
// Auto decrement for a long click
decrement.setOnLongClickListener(
- new View.OnLongClickListener() {
- public boolean onLongClick(View arg0) {
- paginationDisable(arg0);
- autoDecrement = true;
- repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY);
- return false;
- }
+ arg0 -> {
+ paginationDisable(arg0);
+ autoDecrement = true;
+ repeatUpdateHandler.postDelayed(new RepetitiveUpdater(step), INITIAL_DELAY);
+ return false;
}
);
@@ -508,12 +439,12 @@ public class TopicActivity extends BaseActivity {
} else if (event.getAction() == MotionEvent.ACTION_UP && autoDecrement) {
autoDecrement = false;
paginationEnabled(true);
- changePage(pageRequestValue - 1);
+ viewModel.loadPageIndicated();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (rect != null &&
!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
autoIncrement = false;
- incrementPageRequestValue(thisPage - pageRequestValue);
+ viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false);
paginationEnabled(true);
}
}
@@ -522,446 +453,306 @@ public class TopicActivity extends BaseActivity {
});
}
- private void incrementPageRequestValue(int step) {
- if (pageRequestValue < numberOfPages - step) {
- pageRequestValue = pageRequestValue + step;
- } else
- pageRequestValue = numberOfPages;
- pageIndicator.setText(pageRequestValue + "/" + String.valueOf(numberOfPages));
- }
-
- private void decrementPageRequestValue(int step) {
- if (pageRequestValue > step)
- pageRequestValue = pageRequestValue - step;
- else
- pageRequestValue = 1;
- pageIndicator.setText(pageRequestValue + "/" + String.valueOf(numberOfPages));
- }
-
- private void changePage(int pageRequested) {
- if (pageRequested != thisPage - 1) {
- if (topicTask != null && topicTask.getStatus() != AsyncTask.Status.RUNNING)
- topicTask.cancel(true);
-
- topicTask = new TopicTask();
- topicTask.execute(pagesUrls.get(pageRequested)); //Attempt data parsing
-
- }
- }
-//------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
- private enum ResultCode {
- SUCCESS, NETWORK_ERROR, PARSING_ERROR, OTHER_ERROR, SAME_PAGE, UNAUTHORIZED
- }
-
+ //------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
/**
- * An {@link AsyncTask} that handles asynchronous fetching of this topic page and parsing of its
- * data.
- * TopicTask's {@link AsyncTask#execute execute} method needs a topic's url as String
- * parameter.
+ * Binds the UI to its data
*/
- class TopicTask extends AsyncTask {
- ArrayList localPostsList;
+ private void subscribeUI() {
+ // Implement async task callbacks
+ viewModel.setTopicTaskObserver(new TopicTask.TopicTaskObserver() {
+ @Override
+ public void onTopicTaskStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ if (snackbar != null) snackbar.dismiss();
+ }
- @Override
- protected void onPreExecute() {
- progressBar.setVisibility(ProgressBar.VISIBLE);
- paginationEnabled(false);
- if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(false);
- }
+ @Override
+ public void onTopicTaskCancelled() {
+ progressBar.setVisibility(ProgressBar.GONE);
+ }
+ });
+ viewModel.setDeleteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE));
+ viewModel.setDeleteTaskFinishedListener((resultCode, data) -> {
+ progressBar.setVisibility(ProgressBar.GONE);
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ Timber.i("Post deleted successfully");
+ viewModel.reloadPage();
+ } else {
+ Timber.w("Failed to delete post");
+ Toast.makeText(getBaseContext(), "Delete failed!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ viewModel.setReplyFinishListener(new ReplyTask.ReplyTaskCallbacks() {
+ @Override
+ public void onReplyTaskStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
- protected ResultCode doInBackground(String... strings) {
- Document document = null;
- String newPageUrl = strings[0];
-
- //Finds the index of message focus if present
- {
- postFocus = NO_POST_FOCUS;
- if (newPageUrl.contains("msg")) {
- String tmp = newPageUrl.substring(newPageUrl.indexOf("msg") + 3);
- if (tmp.contains(";"))
- postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf(";")));
- else if (tmp.contains("#"))
- postFocus = Integer.parseInt(tmp.substring(0, tmp.indexOf("#")));
+ @Override
+ public void onReplyTaskFinished(Posting.REPLY_STATUS replyStatus) {
+ View view = getCurrentFocus();
+ if (view != null) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
- }
- //Checks if the page to be loaded is the one already shown
- if (!reloadingPage && !Objects.equals(loadedPageUrl, "") && newPageUrl.contains(base_url)) {
- if (newPageUrl.contains("topicseen#new") || newPageUrl.contains("#new"))
- if (thisPage == numberOfPages)
- return ResultCode.SAME_PAGE;
- if (newPageUrl.contains("msg")) {
- String tmpUrlSbstr = newPageUrl.substring(newPageUrl.indexOf("msg") + 3);
- if (tmpUrlSbstr.contains("msg"))
- tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1);
- int testAgainst = Integer.parseInt(tmpUrlSbstr);
- for (Post post : postsList) {
- if (post.getPostIndex() == testAgainst) {
- return ResultCode.SAME_PAGE;
+
+ progressBar.setVisibility(ProgressBar.GONE);
+
+ switch (replyStatus) {
+ case SUCCESSFUL:
+ BaseApplication.getInstance().logFirebaseAnalyticsEvent("post_creation", null);
+ Timber.i("Post reply successful");
+ replyFAB.show();
+ bottomNavBar.setVisibility(View.VISIBLE);
+ viewModel.setWritingReply(false);
+ if ((((Post) topicItems.get(topicItems.size() - 1)).getPostNumber() + 1) % 15 == 0) {
+ Timber.i("Reply was posted in new page. Switching to last page.");
+ viewModel.loadUrl(ParseHelpers.getBaseURL(viewModel.getTopicUrl()) + "." + 2147483647);
+ } else {
+ viewModel.reloadPage();
}
- }
- } else if ((Objects.equals(newPageUrl, base_url) && thisPage == 1) ||
- Integer.parseInt(newPageUrl.substring(base_url.length() + 1)) / 15 + 1 == thisPage)
- return ResultCode.SAME_PAGE;
- } else if (!Objects.equals(loadedPageUrl, "")) topicTitle = null;
- if (reloadingPage) reloadingPage = !reloadingPage;
-
- loadedPageUrl = newPageUrl;
- if (strings[0].substring(0, strings[0].lastIndexOf(".")).contains("topic="))
- base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //New topic's base url
- replyPageUrl = null;
- Request request = new Request.Builder()
- .url(newPageUrl)
- .build();
- try {
- Response response = client.newCall(request).execute();
- document = Jsoup.parse(response.body().string());
- localPostsList = parse(document);
-
- loadedPageTopicId = Integer.parseInt(ThmmyPage.getTopicId(loadedPageUrl));
-
- //Finds the position of the focused message if present
- for (int i = 0; i < localPostsList.size(); ++i) {
- if (localPostsList.get(i).getPostIndex() == postFocus) {
- postFocusPosition = i;
break;
- }
+ case NEW_REPLY_WHILE_POSTING:
+ Timber.i("New reply while writing a reply");
+ TopicAdapter.QuickReplyViewHolder replyHolder = (TopicAdapter.QuickReplyViewHolder)
+ recyclerView.findViewHolderForAdapterPosition(topicItems.size() - 1);
+ String subject = replyHolder.quickReplySubject.getText().toString();
+ String message = replyHolder.replyEditor.getText().toString();
+ Runnable addReply = () -> {
+ viewModel.setWritingReply(true);
+ topicItems.add(Post.newQuickReply());
+ topicAdapter.notifyItemInserted(topicItems.size());
+ recyclerView.scrollToPosition(topicItems.size() - 1);
+ replyFAB.hide();
+ bottomNavBar.setVisibility(View.GONE);
+ TopicAdapter.QuickReplyViewHolder newReplyHolder = (TopicAdapter.QuickReplyViewHolder)
+ recyclerView.findViewHolderForAdapterPosition(topicItems.size() - 1);
+ newReplyHolder.quickReplySubject.setText(subject);
+ newReplyHolder.replyEditor.setText(message);
+ AlertDialog.Builder builder = new AlertDialog.Builder(TopicActivity.this,
+ R.style.AppCompatAlertDialogStyleAccent);
+ builder.setMessage("A new reply was posted before you completed your new post." +
+ " Please review it and send your reply again")
+ .setNeutralButton(getString(R.string.ok), (dialog, which) -> dialog.dismiss())
+ .show();
+ };
+ viewModel.reloadPageThen(addReply);
+ break;
+ default:
+ Timber.w("Post reply unsuccessful");
+ Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show();
+ recyclerView.getChildAt(topicItems.size() - 1).setAlpha(1);
+ recyclerView.getChildAt(topicItems.size() - 1).setEnabled(true);
}
- return ResultCode.SUCCESS;
- } catch (IOException e) {
- Timber.i(e, "IO Exception");
- return ResultCode.NETWORK_ERROR;
- } catch (ParseException e) {
- if(isUnauthorized(document))
- return ResultCode.UNAUTHORIZED;
- Timber.e(e, "Parsing Error");
- return ResultCode.PARSING_ERROR;
- } catch (Exception e) {
- Timber.e(e, "Exception");
- return ResultCode.OTHER_ERROR;
}
- }
-
- protected void onPostExecute(ResultCode parseResult) {
- switch (parseResult) {
- case SUCCESS:
- if (topicTitle == null || Objects.equals(topicTitle, "")
- || !Objects.equals(topicTitle, parsedTitle)) {
- toolbarTitle.setText(parsedTitle);
- topicTitle = parsedTitle;
- thisPageBookmark = new Bookmark(parsedTitle, Integer.toString(loadedPageTopicId), true);
- invalidateOptionsMenu();
- }
-
- if (!postsList.isEmpty()) {
- recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug
- postsList.clear();
- topicAdapter.notifyItemRangeRemoved(0, postsList.size() - 1);
- }
- postsList.addAll(localPostsList);
- topicAdapter.notifyItemRangeInserted(0, postsList.size());
- progressBar.setVisibility(ProgressBar.INVISIBLE);
-
- if (replyPageUrl == null) {
- replyFAB.hide();
- topicAdapter.resetTopic(base_url, new TopicTask(), false);
- } else topicAdapter.resetTopic(base_url, new TopicTask(), true);
+ });
+ viewModel.setPrepareForEditCallbacks(new PrepareForEditTask.PrepareForEditCallbacks() {
+ @Override
+ public void onPrepareEditStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
- if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
+ @Override
+ public void onPrepareEditCancelled() {
+ progressBar.setVisibility(ProgressBar.GONE);
+ }
+ });
+ viewModel.setEditTaskCallbacks(new EditTask.EditTaskCallbacks() {
+ @Override
+ public void onEditTaskStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
- //Set current page
- pageIndicator.setText(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages));
- pageRequestValue = thisPage;
+ @Override
+ public void onEditTaskFinished(boolean result, int position) {
+ View view = getCurrentFocus();
+ if (view != null) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
- if(thisPage==numberOfPages){
- NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- if(notificationManager!=null)
- notificationManager.cancel(NEW_POST_TAG, loadedPageTopicId);
- }
+ progressBar.setVisibility(ProgressBar.GONE);
+
+ if (result) {
+ Timber.i("Post edit successful");
+ ((Post) topicItems.get(position)).setPostType(Post.TYPE_POST);
+ topicAdapter.notifyItemChanged(position);
+ replyFAB.show();
+ bottomNavBar.setVisibility(View.VISIBLE);
+ viewModel.setEditingPost(false);
+ viewModel.reloadPage();
+ } else {
+ Timber.i("Post edit unsuccessful");
+ Toast.makeText(getBaseContext(), "Edit failed!", Toast.LENGTH_SHORT).show();
+ recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1);
+ recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true);
+ }
+ }
+ });
+ viewModel.setPrepareForReplyCallbacks(new PrepareForReply.PrepareForReplyCallbacks() {
+ @Override
+ public void onPrepareForReplyStarted() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
+ @Override
+ public void onPrepareForReplyCancelled() {
+ progressBar.setVisibility(ProgressBar.GONE);
+ }
+ });
+ viewModel.setVoteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE));
+ viewModel.setVoteTaskFinishedListener((resultCode, data) -> {
+ progressBar.setVisibility(View.GONE);
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ Timber.i("Vote sent");
+ viewModel.resetPage();
+ }
+ else {
+ Timber.w("Failed to send vote");
+ Toast.makeText(this, "Failed to send vote", Toast.LENGTH_LONG).show();
+ }
+ });
+ viewModel.setRemoveVoteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE));
+ viewModel.setRemoveVoteTaskFinishedListener((resultCode, data) -> {
+ progressBar.setVisibility(View.GONE);
+ if (resultCode == NetworkResultCodes.SUCCESSFUL) {
+ Timber.i("Vote removed");
+ viewModel.resetPage();
+ }
+ else {
+ Timber.w("Failed to remove vote");
+ Toast.makeText(this, "Failed to remove vote", Toast.LENGTH_LONG).show();
+ }
+ });
+ // observe the chages in data
+ viewModel.getPageIndicatorIndex().observe(this, pageIndicatorIndex -> {
+ if (pageIndicatorIndex == null) return;
+ pageIndicator.setText(String.valueOf(pageIndicatorIndex) + "/" +
+ String.valueOf(viewModel.getPageCount()));
+ });
+ viewModel.getTopicTitle().observe(this, newTopicTitle -> {
+ if (newTopicTitle == null) return;
+ if (!TextUtils.equals(toolbarTitle.getText(), newTopicTitle))
+ toolbarTitle.setText(newTopicTitle);
+ });
+ viewModel.getPageTopicId().observe(this, pageTopicId -> {
+ if (pageTopicId == null) return;
+ if (viewModel.getCurrentPageIndex() == viewModel.getPageCount()) {
+ NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager != null)
+ notificationManager.cancel(NEW_POST_TAG, pageTopicId);
+ }
+ });
+ viewModel.getReplyPageUrl().observe(this, replyPageUrl -> {
+ if (replyPageUrl == null)
+ replyFAB.hide();
+ else
+ replyFAB.show();
+ });
+ viewModel.getTopicItems().observe(this, postList -> {
+ if (postList == null) progressBar.setVisibility(ProgressBar.VISIBLE);
+ recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug
+ topicItems.clear();
+ topicItems.addAll(postList);
+ topicAdapter.notifyDataSetChanged();
+ });
+ /*viewModel.getFocusedPostIndex().observe(this, focusedPostIndex -> {
+ if (focusedPostIndex == null) return;
+ recyclerView.scrollToPosition(focusedPostIndex);
+ });*/
+ viewModel.getTopicTaskResultCode().observe(this, resultCode -> {
+ if (resultCode == null) return;
+ progressBar.setVisibility(ProgressBar.GONE);
+ switch (resultCode) {
+ case SUCCESS:
+ Timber.i("Successfully loaded topic with URL %s", viewModel.getTopicUrl());
paginationEnabled(true);
break;
case NETWORK_ERROR:
- Toast.makeText(getBaseContext(), "Network Error", Toast.LENGTH_SHORT).show();
- break;
- case SAME_PAGE:
- stopLoading();
- Toast.makeText(getBaseContext(), "That's the same page", Toast.LENGTH_SHORT).show();
- //TODO change focus
+ Timber.w("Network error on loaded page");
+ if (viewModel.getTopicItems().getValue() == null) {
+ // no page has been loaded yet. Give user the ability to refresh
+ recyclerView.setVisibility(View.GONE);
+ TextView errorTextview = findViewById(R.id.error_textview);
+
+ Spannable errorText = new SpannableString(getString(R.string.network_error_retry_prompt));
+ errorText.setSpan(
+ new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.accent, null)),
+ errorText.toString().indexOf("Tap to retry"),
+ errorText.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ errorTextview.setText(errorText);
+ errorTextview.setVisibility(View.VISIBLE);
+ errorTextview.setOnClickListener(view -> {
+ viewModel.reloadPage();
+ errorTextview.setVisibility(View.GONE);
+ recyclerView.setVisibility(View.VISIBLE);
+ });
+ } else {
+ // a page has already been loaded
+ viewModel.setPageIndicatorIndex(viewModel.getCurrentPageIndex(), false);
+ snackbar = Snackbar.make(findViewById(R.id.main_content),
+ R.string.generic_network_error, Snackbar.LENGTH_INDEFINITE);
+ snackbar.setAction(R.string.retry, view -> viewModel.reloadPage());
+ snackbar.show();
+ }
break;
case UNAUTHORIZED:
- stopLoading();
- Toast.makeText(getBaseContext(), "This topic is either missing or off limits to you", Toast.LENGTH_SHORT).show();
+ Timber.w("Requested topic was unauthorized");
+ recyclerView.setVisibility(View.GONE);
+ TextView errorTextview = findViewById(R.id.error_textview);
+
+ Spannable errorText = new SpannableString(getString(R.string.unauthorized_topic_error));
+ errorText.setSpan(
+ //TODO: maybe change the color to a red in order to indicate the error nature of the message
+ new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.accent, null)),
+ 0,
+ errorText.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ errorTextview.setText(getString(R.string.unauthorized_topic_error));
+ errorTextview.setVisibility(View.VISIBLE);
break;
default:
//Parse failed - should never happen
- Timber.d("Parse failed!"); //TODO report ParseException!!!
+ Timber.wtf("Parse failed!"); //TODO report ParseException!!!
Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show();
finish();
break;
}
- }
-
- private void stopLoading(){
- progressBar.setVisibility(ProgressBar.INVISIBLE);
- if (replyPageUrl == null) {
+ });
+ viewModel.getPrepareForReplyResult().observe(this, prepareForReplyResult -> {
+ progressBar.setVisibility(ProgressBar.GONE);
+ if (prepareForReplyResult != null && prepareForReplyResult.isSuccessful()) {
+ Timber.i("Prepare for reply successful");
+ //prepare for a reply
+ viewModel.setWritingReply(true);
+ topicItems.add(Post.newQuickReply());
+ topicAdapter.notifyItemInserted(topicItems.size());
+ recyclerView.scrollToPosition(topicItems.size() - 1);
replyFAB.hide();
- topicAdapter.resetTopic(base_url, new TopicTask(), false);
- } else topicAdapter.resetTopic(base_url, new TopicTask(), true);
- if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
- paginationEnabled(true);
- }
-
- /**
- * All the parsing a topic needs.
- *
- * @param topic {@link Document} object containing this topic's source code
- * @see org.jsoup.Jsoup Jsoup
- */
- private ArrayList parse(Document topic) throws ParseException{
- try {
- ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic);
-
- //Finds topic's tree, mods and users viewing
- {
- topicTreeAndMods = getSpannableFromHtml(topic.select("div.nav").first().html());
- topicViewers = getSpannableFromHtml(TopicParser.parseUsersViewingThisTopic(topic, language));
- }
-
- //Finds reply page url
- {
- Element replyButton = topic.select("a:has(img[alt=Reply])").first();
- if (replyButton == null)
- replyButton = topic.select("a:has(img[alt=Απάντηση])").first();
- if (replyButton != null) replyPageUrl = replyButton.attr("href");
- }
-
- //Finds topic title if missing
- {
- parsedTitle = topic.select("td[id=top_subject]").first().text();
- if (parsedTitle.contains("Topic:")) {
- parsedTitle = parsedTitle.substring(parsedTitle.indexOf("Topic:") + 7
- , parsedTitle.indexOf("(Read") - 2);
- } else {
- parsedTitle = parsedTitle.substring(parsedTitle.indexOf("Θέμα:") + 6
- , parsedTitle.indexOf("(Αναγνώστηκε") - 2);
- Timber.d("Parsed title: %s", parsedTitle);
- }
- }
-
- { //Finds current page's index
- thisPage = TopicParser.parseCurrentPageIndex(topic, language);
- }
- { //Finds number of pages
- numberOfPages = TopicParser.parseTopicNumberOfPages(topic, thisPage, language);
-
- for (int i = 0; i < numberOfPages; i++) {
- //Generate each page's url from topic's base url +".15*numberOfPage"
- pagesUrls.put(i, base_url + "." + String.valueOf(i * 15));
- }
- }
-
- return TopicParser.parseTopic(topic, language);
- } catch (Exception e) {
- throw new ParseException("Parsing failed (TopicTask)");
- }
- }
-
- private boolean isUnauthorized(Document document) {
- return document != null && document.select("body:contains(The topic or board you" +
- " are looking for appears to be either missing or off limits to you.)," +
- "body:contains(Το θέμα ή πίνακας που ψάχνετε ή δεν υπάρχει ή δεν " +
- "είναι προσβάσιμο από εσάς.)").size() > 0;
- }
-
- private void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span) {
- int start = strBuilder.getSpanStart(span);
- int end = strBuilder.getSpanEnd(span);
- int flags = strBuilder.getSpanFlags(span);
- ClickableSpan clickable = new ClickableSpan() {
- @Override
- public void onClick(View view) {
- ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(span.getURL()));
- if (target.is(ThmmyPage.PageCategory.BOARD)) {
- Intent intent = new Intent(getApplicationContext(), BoardActivity.class);
- Bundle extras = new Bundle();
- extras.putString(BUNDLE_BOARD_URL, span.getURL());
- extras.putString(BUNDLE_BOARD_TITLE, "");
- intent.putExtras(extras);
- intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
- getApplicationContext().startActivity(intent);
- } else if (target.is(ThmmyPage.PageCategory.PROFILE)) {
- Intent intent = new Intent(getApplicationContext(), ProfileActivity.class);
- Bundle extras = new Bundle();
- extras.putString(BUNDLE_PROFILE_URL, span.getURL());
- extras.putString(BUNDLE_PROFILE_THUMBNAIL_URL, "");
- extras.putString(BUNDLE_PROFILE_USERNAME, "");
- intent.putExtras(extras);
- intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
- getApplicationContext().startActivity(intent);
- } else if (target.is(ThmmyPage.PageCategory.INDEX))
- finish();
- }
- };
- strBuilder.setSpan(clickable, start, end, flags);
- strBuilder.removeSpan(span);
- }
-
- private SpannableStringBuilder getSpannableFromHtml(String html) {
- CharSequence sequence;
- if (Build.VERSION.SDK_INT >= 24) {
- sequence = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
+ bottomNavBar.setVisibility(View.GONE);
} else {
- //noinspection deprecation
- sequence = Html.fromHtml(html);
- }
- SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
- URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
- for (URLSpan span : urls) {
- makeLinkClickable(strBuilder, span);
+ Timber.i("Prepare for reply unsuccessful");
+ Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show();
}
- return strBuilder;
- }
- }
-
- class PrepareForReply extends AsyncTask, Void, Boolean> {
- String numReplies, seqnum, sc, topic, buildedQuotes = "";
-
- @Override
- protected void onPreExecute() {
- progressBar.setVisibility(ProgressBar.VISIBLE);
- paginationEnabled(false);
- replyFAB.setEnabled(false);
- replyFAB.hide();
- bottomNavBar.setVisibility(View.GONE);
- }
-
- @Override
- protected Boolean doInBackground(ArrayList... quoteList) {
- Document document;
- Request request = new Request.Builder()
- .url(replyPageUrl + ";wap2")
- .build();
-
- try {
- Response response = client.newCall(request).execute();
- document = Jsoup.parse(response.body().string());
-
- numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12);
- seqnum = document.select("input[name=seqnum]").first().attr("value");
- sc = document.select("input[name=sc]").first().attr("value");
- topic = document.select("input[name=topic]").first().attr("value");
- } catch (IOException | Selector.SelectorParseException e) {
- Timber.e(e, "Prepare failed.");
- return false;
- }
-
- for (Integer quotePosition : quoteList[0]) {
- request = new Request.Builder()
- .url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" +
- postsList.get(quotePosition).getPostIndex() +
- ";" + "sesc=" + sc + ";xml")
- .build();
-
- try {
- Response response = client.newCall(request).execute();
- String body = response.body().string();
- buildedQuotes += body.substring(body.indexOf("") + 7, body.indexOf("
"));
- buildedQuotes += "\n\n";
- } catch (IOException | Selector.SelectorParseException e) {
- Timber.e(e, "Quote building failed.");
- return false;
- }
- }
- return true;
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- postsList.add(null);
- topicAdapter.notifyItemInserted(postsList.size());
- topicAdapter.prepareForReply(new ReplyTask(), topicTitle, numReplies, seqnum, sc,
- topic, buildedQuotes);
- recyclerView.scrollToPosition(postsList.size() - 1);
- progressBar.setVisibility(ProgressBar.GONE);
- }
- }
-
- class ReplyTask extends AsyncTask {
-
- @Override
- protected void onPreExecute() {
- progressBar.setVisibility(ProgressBar.VISIBLE);
- paginationEnabled(false);
- replyFAB.setEnabled(false);
- }
-
- @Override
- protected Boolean doInBackground(String... args) {
- final String sentFrommTHMMY = "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy]mTHMMY[/url] [/i][/size][/right]";
- RequestBody postBody = new MultipartBody.Builder()
- .setType(MultipartBody.FORM)
- .addFormDataPart("message", args[1] + sentFrommTHMMY)
- .addFormDataPart("num_replies", args[2])
- .addFormDataPart("seqnum", args[3])
- .addFormDataPart("sc", args[4])
- .addFormDataPart("subject", args[0])
- .addFormDataPart("topic", args[5])
- .build();
- Request post = new Request.Builder()
- .url("https://www.thmmy.gr/smf/index.php?action=post2")
- .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36")
- .post(postBody)
- .build();
-
- try {
- client.newCall(post).execute();
- Response response = client.newCall(post).execute();
- switch (replyStatus(response)) {
- case SUCCESSFUL:
- return true;
- case NEW_REPLY_WHILE_POSTING:
- //TODO this...
- return true;
- default:
- Timber.e("Malformed post. Request string: %s", post.toString());
- return true;
- }
- } catch (IOException e) {
- Timber.e(e, "Post failed.");
- return false;
- }
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- View view = getCurrentFocus();
- if (view != null) {
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
-
- postsList.remove(postsList.size() - 1);
- topicAdapter.notifyItemRemoved(postsList.size());
-
+ });
+ viewModel.getPrepareForEditResult().observe(this, result -> {
progressBar.setVisibility(ProgressBar.GONE);
- replyFAB.setVisibility(View.VISIBLE);
- bottomNavBar.setVisibility(View.VISIBLE);
-
- if (!result)
- Toast.makeText(TopicActivity.this, "Post failed!", Toast.LENGTH_SHORT).show();
- paginationEnabled(true);
- replyFAB.setEnabled(true);
-
- if (result) {
- topicTask = new TopicTask();
- if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0)
- topicTask.execute(base_url + "." + 2147483647);
- else {
- reloadingPage = true;
- topicTask.execute(loadedPageUrl);
- }
+ if (result != null && result.isSuccessful()) {
+ Timber.i("Prepare for edit successful");
+ viewModel.setEditingPost(true);
+ ((Post) topicItems.get(result.getPosition())).setPostType(Post.TYPE_EDIT);
+ topicAdapter.notifyItemChanged(result.getPosition());
+ recyclerView.scrollToPosition(result.getPosition());
+ replyFAB.hide();
+ bottomNavBar.setVisibility(View.GONE);
+ } else {
+ Timber.i("Prepare for edit unsuccessful");
+ Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show();
}
- }
+ });
}
}
\ No newline at end of file
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
index 54560b58..bfc26961 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
@@ -2,34 +2,51 @@ package gr.thmmy.mthmmy.activities.topic;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
-import android.support.v7.widget.AppCompatImageButton;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.content.res.AppCompatResources;
+import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.RecyclerView;
-import android.text.Editable;
+import android.text.Html;
+import android.text.InputType;
import android.text.TextUtils;
-import android.text.TextWatcher;
+import android.text.method.LinkMovementMethod;
+import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.github.mikephil.charting.charts.HorizontalBarChart;
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.BarDataSet;
+import com.github.mikephil.charting.data.BarEntry;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
@@ -40,10 +57,16 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
+import gr.thmmy.mthmmy.editorview.EditorView;
+import gr.thmmy.mthmmy.editorview.IEmojiKeyboard;
+import gr.thmmy.mthmmy.model.Poll;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.model.ThmmyPage;
+import gr.thmmy.mthmmy.model.TopicItem;
import gr.thmmy.mthmmy.utils.CircleTransform;
+import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
+import gr.thmmy.mthmmy.viewmodel.TopicViewModel;
import timber.log.Timber;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -66,305 +89,377 @@ class TopicAdapter extends RecyclerView.Adapter {
*/
private static int THUMBNAIL_SIZE;
private final Context context;
- private String topicTitle;
- private String baseUrl;
- 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 #isUserExtraInfoVisibile} and
- * {@link #isQuoteButtonChecked}.
- */
- private final ArrayList viewProperties = new ArrayList<>();
- /**
- * Index of state indicator in the boolean array. If true user's extra info are expanded and
- * visible.
- */
- 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 = 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 final String[] replyDataHolder = new String[2];
- private final int replySubject = 0, replyText = 1;
- private String numReplies, seqnum, sc, topic, buildedQuotes;
- private boolean canReply = false;
+ private final OnPostFocusChangeListener postFocusListener;
+ private final IEmojiKeyboard emojiKeyboard;
+ private final List topicItems;
+ private TopicViewModel viewModel;
/**
- * @param context the context of the {@link RecyclerView}
- * @param postsList List of {@link Post} objects to use
+ * @param context the context of the {@link RecyclerView}
+ * @param topicItems List of {@link Post} objects to use
*/
- TopicAdapter(Context context, List postsList, String baseUrl,
- TopicActivity.TopicTask topicTask) {
+ TopicAdapter(TopicActivity context, IEmojiKeyboard emojiKeyboard, List topicItems) {
this.context = context;
- this.postsList = postsList;
- this.baseUrl = baseUrl;
+ this.topicItems = topicItems;
+ this.postFocusListener = context;
+ this.emojiKeyboard = emojiKeyboard;
- THUMBNAIL_SIZE = (int) context.getResources().getDimension(R.dimen.thumbnail_size);
- for (int i = 0; i < postsList.size(); ++i) {
- //Initializes properties, array's values will be false by default
- viewProperties.add(new boolean[3]);
- }
- this.topicTask = topicTask;
- }
+ viewModel = ViewModelProviders.of(context).get(TopicViewModel.class);
- ArrayList getToQuoteList(){
- return toQuoteList;
- }
-
- void prepareForReply(TopicActivity.ReplyTask replyTask, String topicTitle, String numReplies,
- String seqnum, String sc, String topic, String buildedQuotes) {
- this.replyTask = replyTask;
- this.topicTitle = topicTitle;
- this.numReplies = numReplies;
- this.seqnum = seqnum;
- this.sc = sc;
- this.topic = topic;
- this.buildedQuotes = buildedQuotes;
+ THUMBNAIL_SIZE = (int) context.getResources().getDimension(R.dimen.thumbnail_size);
}
@Override
public int getItemViewType(int position) {
- return postsList.get(position) == null ? VIEW_TYPE_QUICK_REPLY : VIEW_TYPE_POST;
+ if (topicItems.get(position) instanceof Poll) return Poll.TYPE_POLL;
+ return ((Post) topicItems.get(position)).getPostType();
}
+ @NonNull
@Override
- public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- if (viewType == VIEW_TYPE_POST) {
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ if (viewType == Post.TYPE_POST) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.activity_topic_post_row, parent, false);
return new PostViewHolder(itemView);
- } else if (viewType == VIEW_TYPE_QUICK_REPLY) {
+ } else if (viewType == Post.TYPE_QUICK_REPLY) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.activity_topic_quick_reply_row, parent, false);
- view.findViewById(R.id.quick_reply_submit).setEnabled(true);
- final EditText quickReplyText = view.findViewById(R.id.quick_reply_text);
+ final EditText quickReplyText = ((EditorView) view.findViewById(R.id.reply_editorview)).getEditText();
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.setOnFocusChangeListener((v, hasFocus) -> quickReplyText.post(() -> {
+ 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
- if (!Objects.equals(buildedQuotes, ""))
- replyDataHolder[replyText] = buildedQuotes;
- return new QuickReplyViewHolder(view, new CustomEditTextListener(replySubject),
- new CustomEditTextListener(replyText));
+ return new QuickReplyViewHolder(view);
+ } else if (viewType == Post.TYPE_EDIT) {
+ View view = LayoutInflater.from(parent.getContext()).
+ inflate(R.layout.activity_topic_edit_row, parent, false);
+
+ final EditText editPostEdittext = ((EditorView) view.findViewById(R.id.edit_editorview)).getEditText();
+ editPostEdittext.setFocusableInTouchMode(true);
+ editPostEdittext.setOnFocusChangeListener((v, hasFocus) -> editPostEdittext.post(() -> {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(editPostEdittext, InputMethodManager.SHOW_IMPLICIT);
+ }));
+ editPostEdittext.requestFocus();
+
+ return new EditMessageViewHolder(view);
+ } else if (viewType == Poll.TYPE_POLL) {
+ View view = LayoutInflater.from(parent.getContext()).
+ inflate(R.layout.activity_topic_poll, parent, false);
+ return new PollViewHolder(view);
+ } else {
+ throw new IllegalArgumentException("Unknown view type");
}
- return null;
}
@SuppressLint({"SetJavaScriptEnabled", "SetTextI18n"})
@Override
- public void onBindViewHolder(final RecyclerView.ViewHolder currentHolder,
+ public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder currentHolder,
final int position) {
- if (currentHolder instanceof PostViewHolder) {
- final Post currentPost = postsList.get(position);
- final PostViewHolder holder = (PostViewHolder) currentHolder;
-
- //Post's WebView parameters
- holder.post.setClickable(true);
- holder.post.setWebViewClient(new LinkLauncher());
-
- //Avoids errors about layout having 0 width/height
- holder.thumbnail.setMinimumWidth(1);
- holder.thumbnail.setMinimumHeight(1);
- //Sets thumbnail size
- holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE);
- holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE);
-
- //noinspection ConstantConditions
- Picasso.with(context)
- .load(currentPost.getThumbnailURL())
- .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
- .centerCrop()
- .error(ResourcesCompat.getDrawable(context.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
- .placeholder(ResourcesCompat.getDrawable(context.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
- .transform(new CircleTransform())
- .into(holder.thumbnail);
-
- //Sets username,submit date, index number, subject, post's and attached files texts
- holder.username.setText(currentPost.getAuthor());
- holder.postDate.setText(currentPost.getPostDate());
- if (currentPost.getPostNumber() != 0)
- holder.postNum.setText(context.getString(
- R.string.user_number_of_posts, currentPost.getPostNumber()));
- else
- holder.postNum.setText("");
- holder.subject.setText(currentPost.getSubject());
- holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null);
- if ((currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0)
- || (currentPost.getLastEdit() != null)) {
- holder.bodyFooterDivider.setVisibility(View.VISIBLE);
- holder.postFooter.removeAllViews();
-
- if (currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) {
- int filesTextColor;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- filesTextColor = context.getResources().getColor(R.color.accent, null);
- } else //noinspection deprecation
- filesTextColor = context.getResources().getColor(R.color.accent);
-
- for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) {
- final TextView attached = new TextView(context);
- attached.setTextSize(10f);
- attached.setClickable(true);
- attached.setTypeface(Typeface.createFromAsset(context.getAssets()
- , "fonts/fontawesome-webfont.ttf"));
- attached.setText(faIconFromFilename(attachedFile.getFilename()) + " "
- + attachedFile.getFilename() + attachedFile.getFileInfo());
- attached.setTextColor(filesTextColor);
- attached.setPadding(0, 3, 0, 3);
-
- attached.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- ((BaseActivity) context).downloadFile(attachedFile);
- }
- });
-
- holder.postFooter.addView(attached);
+ if (currentHolder.getItemViewType() == Poll.TYPE_POLL) {
+ Poll poll = (Poll) topicItems.get(position);
+ Poll.Entry[] entries = poll.getEntries();
+ PollViewHolder holder = (PollViewHolder) currentHolder;
+ holder.question.setText(poll.getQuestion());
+ holder.optionsLayout.removeAllViews();
+ holder.errorTooManySelected.setVisibility(View.GONE);
+ if (poll.getAvailableVoteCount() > 1) {
+ for (Poll.Entry entry : entries) {
+ LinearLayout container = new LinearLayout(context);
+ container.setOrientation(LinearLayout.HORIZONTAL);
+ CheckBox checkBox = new CheckBox(context);
+ TextView label = new TextView(context);
+ label.setTextColor(context.getResources().getColor(R.color.primary_text));
+ label.setMovementMethod(LinkMovementMethod.getInstance());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ label.setText(Html.fromHtml(entry.getEntryName(), Html.FROM_HTML_MODE_LEGACY));
+ } else {
+ //noinspection deprecation
+ label.setText(Html.fromHtml(entry.getEntryName()));
}
+ checkBox.setTextColor(context.getResources().getColor(R.color.primary_text));
+ container.addView(checkBox);
+ container.addView(label);
+ holder.optionsLayout.addView(container);
}
- if (currentPost.getLastEdit() != null && currentPost.getLastEdit().length() > 0) {
- int lastEditTextColor;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- lastEditTextColor = context.getResources().getColor(R.color.white, null);
- } else //noinspection deprecation
- lastEditTextColor = context.getResources().getColor(R.color.white);
-
- final TextView lastEdit = new TextView(context);
- lastEdit.setTextSize(12f);
- lastEdit.setText(currentPost.getLastEdit());
- lastEdit.setTextColor(lastEditTextColor);
- lastEdit.setPadding(0, 3, 0, 3);
- holder.postFooter.addView(lastEdit);
+ holder.voteChart.setVisibility(View.GONE);
+ holder.optionsLayout.setVisibility(View.VISIBLE);
+ } else if (poll.getAvailableVoteCount() == 1) {
+ RadioGroup radioGroup = new RadioGroup(context);
+ for (int i = 0; i < entries.length; i++) {
+ RadioButton radioButton = new RadioButton(context);
+ radioButton.setId(i);
+ radioButton.setMovementMethod(LinkMovementMethod.getInstance());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ radioButton.setText(Html.fromHtml(entries[i].getEntryName(), Html.FROM_HTML_MODE_LEGACY));
+ } else {
+ //noinspection deprecation
+ radioButton.setText(Html.fromHtml(entries[i].getEntryName()));
+ }
+ radioButton.setTextColor(context.getResources().getColor(R.color.primary_text));
+ radioGroup.addView(radioButton);
}
+ holder.optionsLayout.addView(radioGroup);
+ holder.voteChart.setVisibility(View.GONE);
+ holder.optionsLayout.setVisibility(View.VISIBLE);
} else {
- holder.bodyFooterDivider.setVisibility(View.GONE);
- holder.postFooter.removeAllViews();
- }
-
- String mSpecialRank, mRank, mGender, mNumberOfPosts, mPersonalText;
- int mNumberOfStars, mUserColor;
-
- if (!currentPost.isDeleted()) { //Sets user's extra info
- mSpecialRank = currentPost.getSpecialRank();
- mRank = currentPost.getRank();
- mGender = currentPost.getGender();
- mNumberOfPosts = currentPost.getNumberOfPosts();
- mPersonalText = currentPost.getPersonalText();
- mNumberOfStars = currentPost.getNumberOfStars();
- } else {
- mSpecialRank = null;
- mRank = null;
- mGender = null;
- mNumberOfPosts = null;
- mPersonalText = null;
- mNumberOfStars = 0;
- }
- mUserColor = currentPost.getUserColor();
-
- if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) {
- holder.specialRank.setText(mSpecialRank);
- holder.specialRank.setVisibility(View.VISIBLE);
- } else
- holder.specialRank.setVisibility(View.GONE);
- if (!Objects.equals(mRank, "") && mRank != null) {
- holder.rank.setText(mRank);
- holder.rank.setVisibility(View.VISIBLE);
- } else
- holder.rank.setVisibility(View.GONE);
- if (!Objects.equals(mGender, "") && mGender != null) {
- holder.gender.setText(mGender);
- holder.gender.setVisibility(View.VISIBLE);
- } else
- holder.gender.setVisibility(View.GONE);
- if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) {
- holder.numberOfPosts.setText(mNumberOfPosts);
- holder.numberOfPosts.setVisibility(View.VISIBLE);
- } else
- holder.numberOfPosts.setVisibility(View.GONE);
- if (!Objects.equals(mPersonalText, "") && mPersonalText != null) {
- holder.personalText.setText("\"" + mPersonalText + "\"");
- holder.personalText.setVisibility(View.VISIBLE);
- } else
- holder.personalText.setVisibility(View.GONE);
- if (mUserColor != USER_COLOR_YELLOW) {
- holder.username.setTextColor(mUserColor);
- } else {
- holder.username.setTextColor(USER_COLOR_WHITE);
+ //Showing results
+ holder.optionsLayout.setVisibility(View.GONE);
+ List valuesToCompare = new ArrayList<>();
+ for (int i = 0; i < entries.length; i++) {
+ valuesToCompare.add(new BarEntry(i, entries[i].getVotes()));
+ }
+ BarDataSet data = new BarDataSet(valuesToCompare, "Vote Results");
+ data.setColor(context.getResources().getColor(R.color.accent));
+
+ YAxis yAxisLeft = holder.voteChart.getAxisLeft();
+ yAxisLeft.setGranularity(1);
+ yAxisLeft.setTextColor(context.getResources().getColor(R.color.primary_text));
+ yAxisLeft.setAxisMinimum(0);
+ YAxis yAxisRight = holder.voteChart.getAxisRight();
+ yAxisRight.setEnabled(false);
+
+ XAxis xAxis = holder.voteChart.getXAxis();
+ xAxis.setValueFormatter((value, axis) -> Html.fromHtml(entries[(int) value].getEntryName()).toString());
+ xAxis.setTextColor(context.getResources().getColor(R.color.primary_text));
+ xAxis.setGranularity(1f);
+ xAxis.setLabelCount(entries.length);
+ xAxis.setDrawGridLines(false);
+ xAxis.setDrawAxisLine(false);
+ xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
+
+ BarData barData = new BarData(data);
+ barData.setValueTextColor(context.getResources().getColor(R.color.accent));
+ holder.voteChart.setData(barData);
+ holder.voteChart.getLegend().setEnabled(false);
+ holder.voteChart.getDescription().setEnabled(false);
+ int chartHeightdp = 10 + 30 * entries.length;
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ holder.voteChart.setMinimumHeight((int) (chartHeightdp * (metrics.densityDpi / 160f)));
+ holder.voteChart.invalidate();
+ holder.voteChart.setVisibility(View.VISIBLE);
}
- if (mNumberOfStars > 0) {
- holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets()
- , "fonts/fontawesome-webfont.ttf"));
-
- String aStar = context.getResources().getString(R.string.fa_icon_star);
- String usersStars = "";
- for (int i = 0; i < mNumberOfStars; ++i) {
- usersStars += aStar;
+ if (poll.getRemoveVoteUrl() != null) {
+ holder.removeVotesButton.setOnClickListener(v -> viewModel.removeVote());
+ holder.removeVotesButton.setVisibility(View.VISIBLE);
+ } else holder.removeVotesButton.setVisibility(View.GONE);
+ if (poll.getShowVoteResultsUrl() != null) {
+ holder.showPollResultsButton.setOnClickListener(v -> viewModel.loadUrl(poll.getShowVoteResultsUrl()));
+ holder.showPollResultsButton.setVisibility(View.VISIBLE);
+ } else holder.showPollResultsButton.setVisibility(View.GONE);
+
+ if (poll.getShowOptionsUrl() != null) {
+ holder.hidePollResultsButton.setOnClickListener(v -> viewModel.loadUrl(poll.getShowOptionsUrl()));
+ holder.hidePollResultsButton.setVisibility(View.VISIBLE);
+ } else holder.hidePollResultsButton.setVisibility(View.GONE);
+ if (poll.getPollFormUrl() != null) {
+ holder.submitButton.setOnClickListener(v -> {
+ if (!viewModel.submitVote(holder.optionsLayout)) {
+ holder.errorTooManySelected.setText(context.getResources()
+ .getQuantityString(R.plurals.error_too_many_checked, poll.getAvailableVoteCount(),
+ poll.getAvailableVoteCount()));
+ holder.errorTooManySelected.setVisibility(View.VISIBLE);
+ }
+ });
+ holder.submitButton.setVisibility(View.VISIBLE);
+ } else holder.submitButton.setVisibility(View.GONE);
+ } else {
+ Post currentPost = (Post) topicItems.get(position);
+ if (currentHolder instanceof PostViewHolder) {
+ final PostViewHolder holder = (PostViewHolder) currentHolder;
+
+ //Post's WebView parameters
+ holder.post.setClickable(true);
+ holder.post.setWebViewClient(new LinkLauncher());
+
+ //Avoids errors about layout having 0 width/height
+ holder.thumbnail.setMinimumWidth(1);
+ holder.thumbnail.setMinimumHeight(1);
+ //Sets thumbnail size
+ holder.thumbnail.setMaxWidth(THUMBNAIL_SIZE);
+ holder.thumbnail.setMaxHeight(THUMBNAIL_SIZE);
+
+ //noinspection ConstantConditions
+ Picasso.with(context)
+ .load(currentPost.getThumbnailURL())
+ .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
+ .centerCrop()
+ .error(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
+ .placeholder(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
+ .transform(new CircleTransform())
+ .into(holder.thumbnail);
+
+ //Sets username,submit date, index number, subject, post's and attached files texts
+ holder.username.setText(currentPost.getAuthor());
+ holder.postDate.setText(currentPost.getPostDate());
+ if (currentPost.getPostNumber() != 0)
+ holder.postNum.setText(context.getString(
+ R.string.user_number_of_posts, currentPost.getPostNumber()));
+ else
+ holder.postNum.setText("");
+ holder.subject.setText(currentPost.getSubject());
+ holder.post.loadDataWithBaseURL("file:///android_asset/", currentPost.getContent(), "text/html", "UTF-8", null);
+ if ((currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0)
+ || (currentPost.getLastEdit() != null)) {
+ holder.bodyFooterDivider.setVisibility(View.VISIBLE);
+ holder.postFooter.removeAllViews();
+
+ if (currentPost.getAttachedFiles() != null && currentPost.getAttachedFiles().size() != 0) {
+ int filesTextColor;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ filesTextColor = context.getResources().getColor(R.color.accent, null);
+ } else //noinspection deprecation
+ filesTextColor = context.getResources().getColor(R.color.accent);
+
+ for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) {
+ final TextView attached = new TextView(context);
+ attached.setTextSize(10f);
+ attached.setClickable(true);
+ attached.setTypeface(Typeface.createFromAsset(context.getAssets()
+ , "fonts/fontawesome-webfont.ttf"));
+ attached.setText(faIconFromFilename(attachedFile.getFilename()) + " "
+ + attachedFile.getFilename() + attachedFile.getFileInfo());
+ attached.setTextColor(filesTextColor);
+ attached.setPadding(0, 3, 0, 3);
+
+ attached.setOnClickListener(view -> ((BaseActivity) context).downloadFile(attachedFile));
+
+ holder.postFooter.addView(attached);
+ }
+ }
+ if (currentPost.getLastEdit() != null && currentPost.getLastEdit().length() > 0) {
+ int lastEditTextColor;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ lastEditTextColor = context.getResources().getColor(R.color.white, null);
+ } else //noinspection deprecation
+ lastEditTextColor = context.getResources().getColor(R.color.white);
+
+ final TextView lastEdit = new TextView(context);
+ lastEdit.setTextSize(12f);
+ lastEdit.setText(currentPost.getLastEdit());
+ lastEdit.setTextColor(lastEditTextColor);
+ lastEdit.setPadding(0, 3, 0, 3);
+ holder.postFooter.addView(lastEdit);
+ }
+ } else {
+ holder.bodyFooterDivider.setVisibility(View.GONE);
+ holder.postFooter.removeAllViews();
}
- holder.stars.setText(usersStars);
- holder.stars.setTextColor(mUserColor);
- holder.stars.setVisibility(View.VISIBLE);
- } else
- holder.stars.setVisibility(View.GONE);
- //Special card for special member of the month!
- if (mUserColor == TopicParser.USER_COLOR_PINK) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- holder.cardChildLinear.setBackground(context.getResources().
- getDrawable(R.drawable.member_of_the_month_card, null));
- } else //noinspection deprecation
- holder.cardChildLinear.setBackground(context.getResources().
- getDrawable(R.drawable.member_of_the_month_card));
- } else holder.cardChildLinear.setBackground(null);
-
- //Avoid's view's visibility recycling
- if (!currentPost.isDeleted() && viewProperties.get(position)[isUserExtraInfoVisibile]) {
- holder.userExtraInfo.setVisibility(View.VISIBLE);
- holder.userExtraInfo.setAlpha(1.0f);
-
- 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
- holder.thumbnail.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
+ String mSpecialRank, mRank, mGender, mNumberOfPosts, mPersonalText;
+ int mNumberOfStars, mUserColor;
+
+ if (!currentPost.isDeleted()) { //Sets user's extra info
+ mSpecialRank = currentPost.getSpecialRank();
+ mRank = currentPost.getRank();
+ mGender = currentPost.getGender();
+ mNumberOfPosts = currentPost.getNumberOfPosts();
+ mPersonalText = currentPost.getPersonalText();
+ mNumberOfStars = currentPost.getNumberOfStars();
+ } else {
+ mSpecialRank = null;
+ mRank = null;
+ mGender = null;
+ mNumberOfPosts = null;
+ mPersonalText = null;
+ mNumberOfStars = 0;
+ }
+ mUserColor = currentPost.getUserColor();
+
+ if (!Objects.equals(mSpecialRank, "") && mSpecialRank != null) {
+ holder.specialRank.setText(mSpecialRank);
+ holder.specialRank.setVisibility(View.VISIBLE);
+ } else
+ holder.specialRank.setVisibility(View.GONE);
+ if (!Objects.equals(mRank, "") && mRank != null) {
+ holder.rank.setText(mRank);
+ holder.rank.setVisibility(View.VISIBLE);
+ } else
+ holder.rank.setVisibility(View.GONE);
+ if (!Objects.equals(mGender, "") && mGender != null) {
+ holder.gender.setText(mGender);
+ holder.gender.setVisibility(View.VISIBLE);
+ } else
+ holder.gender.setVisibility(View.GONE);
+ if (!Objects.equals(mNumberOfPosts, "") && mNumberOfPosts != null) {
+ holder.numberOfPosts.setText(mNumberOfPosts);
+ holder.numberOfPosts.setVisibility(View.VISIBLE);
+ } else
+ holder.numberOfPosts.setVisibility(View.GONE);
+ if (!Objects.equals(mPersonalText, "") && mPersonalText != null) {
+ holder.personalText.setText("\"" + mPersonalText + "\"");
+ holder.personalText.setVisibility(View.VISIBLE);
+ } else
+ holder.personalText.setVisibility(View.GONE);
+ if (mUserColor != USER_COLOR_YELLOW) {
+ holder.username.setTextColor(mUserColor);
+ } else {
+ holder.username.setTextColor(USER_COLOR_WHITE);
+ }
+ if (mNumberOfStars > 0) {
+ holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets()
+ , "fonts/fontawesome-webfont.ttf"));
+
+ String aStar = context.getResources().getString(R.string.fa_icon_star);
+ StringBuilder usersStars = new StringBuilder();
+ for (int i = 0; i < mNumberOfStars; ++i) {
+ usersStars.append(aStar);
+ }
+ holder.stars.setText(usersStars.toString());
+ holder.stars.setTextColor(mUserColor);
+ holder.stars.setVisibility(View.VISIBLE);
+ } else
+ holder.stars.setVisibility(View.GONE);
+
+ if (currentPost.isUserMentionedInPost()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ holder.cardChildLinear.setBackground(context.getResources().
+ getDrawable(R.drawable.mention_card, null));
+ } else //noinspection deprecation
+ holder.cardChildLinear.setBackground(context.getResources().
+ getDrawable(R.drawable.mention_card));
+ } else if (mUserColor == TopicParser.USER_COLOR_PINK) {
+ //Special card for special member of the month!
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ holder.cardChildLinear.setBackground(context.getResources().
+ getDrawable(R.drawable.member_of_the_month_card, null));
+ } else //noinspection deprecation
+ holder.cardChildLinear.setBackground(context.getResources().
+ getDrawable(R.drawable.member_of_the_month_card));
+ } else holder.cardChildLinear.setBackground(null);
+
+ //Avoid's view's visibility recycling
+ if (!currentPost.isDeleted() && viewModel.isUserExtraInfoVisible(holder.getAdapterPosition())) {
+ 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
+ holder.thumbnail.setOnClickListener(view -> {
//Clicking the thumbnail opens user's profile
Intent intent = new Intent(context, ProfileActivity.class);
Bundle extras = new Bundle();
@@ -377,133 +472,198 @@ class TopicAdapter extends RecyclerView.Adapter {
intent.putExtras(extras);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
- }
- });
- holder.header.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
+ });
+ holder.header.setOnClickListener(v -> {
//Clicking the header makes it expand/collapse
- boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- tmp[isUserExtraInfoVisibile] = !tmp[isUserExtraInfoVisibile];
- viewProperties.set(holder.getAdapterPosition(), tmp);
+ viewModel.toggleUserInfo(holder.getAdapterPosition());
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
- holder.userExtraInfo.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- tmp[isUserExtraInfoVisibile] = false;
- viewProperties.set(holder.getAdapterPosition(), tmp);
-
+ });
+ //Clicking the expanded part of a header (the extra info) makes it collapse
+ holder.userExtraInfo.setOnClickListener(v -> {
+ viewModel.hideUserInfo(holder.getAdapterPosition());
TopicAnimations.animateUserExtraInfoVisibility(holder.username,
holder.subject, Color.parseColor("#FFFFFF"),
Color.parseColor("#757575"), (LinearLayout) v);
+ });
+ } else {
+ holder.header.setOnClickListener(null);
+ holder.userExtraInfo.setOnClickListener(null);
+ }
+
+ holder.overflowButton.setOnClickListener(view -> {
+ //Inflates the popup menu content
+ LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ if (layoutInflater == null) {
+ return;
+ }
+ View popUpContent = layoutInflater.inflate(R.layout.activity_topic_overflow_menu, null);
+
+ //Creates the PopupWindow
+ final PopupWindow popUp = new PopupWindow(holder.overflowButton.getContext());
+ popUp.setContentView(popUpContent);
+ popUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
+ popUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
+ popUp.setFocusable(true);
+
+ TextView shareButton = popUpContent.findViewById(R.id.post_share_button);
+ Drawable shareStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_share_white_24dp);
+ shareButton.setCompoundDrawablesRelativeWithIntrinsicBounds(shareStartDrawable, null, null, null);
+ shareButton.setOnClickListener(v -> {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.setType("text/plain");
+ sendIntent.putExtra(Intent.EXTRA_TEXT, currentPost.getPostURL());
+ context.startActivity(Intent.createChooser(sendIntent, "Share via"));
+ popUp.dismiss();
+ });
+
+ final TextView editPostButton = popUpContent.findViewById(R.id.edit_post);
+ Drawable editStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_edit_white_24dp);
+ editPostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(editStartDrawable, null, null, null);
+
+ if (viewModel.isEditingPost() || currentPost.getPostEditURL() == null || currentPost.getPostEditURL().equals("")) {
+ editPostButton.setVisibility(View.GONE);
+ } else {
+ editPostButton.setOnClickListener(v -> {
+ viewModel.prepareForEdit(position, currentPost.getPostEditURL());
+ popUp.dismiss();
+ });
+ }
+
+ TextView deletePostButton = popUpContent.findViewById(R.id.delete_post);
+
+ if (currentPost.getPostDeleteURL() == null || currentPost.getPostDeleteURL().equals("")) {
+ deletePostButton.setVisibility(View.GONE);
+ } else {
+ Drawable deleteStartDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_white_24dp);
+ deletePostButton.setCompoundDrawablesRelativeWithIntrinsicBounds(deleteStartDrawable, null, null, null);
+ popUpContent.findViewById(R.id.delete_post).setOnClickListener(v -> {
+ new AlertDialog.Builder(holder.overflowButton.getContext())
+ .setTitle("Delete post")
+ .setMessage("Do you really want to delete this post?")
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(android.R.string.yes, (dialog, whichButton) -> viewModel.deletePost(currentPost.getPostDeleteURL()))
+ .setNegativeButton(android.R.string.no, null).show();
+ popUp.dismiss();
+ });
}
+
+ //Displays the popup
+ popUp.showAsDropDown(holder.overflowButton);
});
- } else {
- holder.header.setOnClickListener(null);
- holder.userExtraInfo.setOnClickListener(null);
- }
- holder.sharePostButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
- sendIntent.setType("text/plain");
- sendIntent.putExtra(android.content.Intent.EXTRA_TEXT, currentPost.getPostURL());
- context.startActivity(Intent.createChooser(sendIntent, "Share via"));
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (!BaseActivity.getSessionManager().isLoggedIn() || !viewModel.canReply()) {
+ holder.quoteToggle.setVisibility(View.GONE);
+ } else {
+ if (viewModel.getToQuoteList().contains(currentPost.getPostIndex()))
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp);
+ else
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp);
+ //Sets graphics behavior
+ holder.quoteToggle.setOnClickListener(view -> {
+ viewModel.postIndexToggle(currentPost.getPostIndex());
+ if (viewModel.getToQuoteList().contains(currentPost.getPostIndex()))
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp);
+ else
+ holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp);
+ });
}
- });
+ } else if (currentHolder instanceof QuickReplyViewHolder) {
+ final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder;
+
+ //noinspection ConstantConditions
+ Picasso.with(context)
+ .load(getSessionManager().getAvatarLink())
+ .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
+ .centerCrop()
+ .error(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
+ .placeholder(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
+ .transform(new CircleTransform())
+ .into(holder.thumbnail);
+ holder.username.setText(getSessionManager().getUsername());
+ holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle().getValue());
+ holder.quickReplySubject.setRawInputType(InputType.TYPE_CLASS_TEXT);
+ holder.quickReplySubject.setImeOptions(EditorInfo.IME_ACTION_DONE);
+
+ holder.replyEditor.setEmojiKeyboard(emojiKeyboard);
+ holder.replyEditor.requestEditTextFocus();
+ emojiKeyboard.registerEmojiInputField(holder.replyEditor);
+
+ holder.replyEditor.setText(viewModel.getBuildedQuotes());
+ holder.replyEditor.setOnSubmitListener(view -> {
+ if (holder.quickReplySubject.getText().toString().isEmpty()) return;
+ if (holder.replyEditor.getText().toString().isEmpty()) {
+ holder.replyEditor.setError("Required");
+ return;
+ }
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ holder.itemView.setAlpha(0.5f);
+ holder.itemView.setEnabled(false);
+ emojiKeyboard.hide();
+
+ viewModel.postReply(context, holder.quickReplySubject.getText().toString(),
+ holder.replyEditor.getText().toString());
+ });
+ holder.replyEditor.setOnClickListener(view -> holder.replyEditor.setError(null));
- //noinspection PointlessBooleanExpression,ConstantConditions
- if (!BaseActivity.getSessionManager().isLoggedIn() || !canReply) {
- holder.quoteToggle.setVisibility(View.GONE);
- } else {
- if (viewProperties.get(position)[isQuoteButtonChecked])
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked);
- else
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked);
- //Sets graphics behavior
- holder.quoteToggle.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- boolean[] tmp = viewProperties.get(holder.getAdapterPosition());
- if (tmp[isQuoteButtonChecked]) {
- if (toQuoteList.contains(postsList.indexOf(currentPost))) {
- toQuoteList.remove(toQuoteList.indexOf(postsList.indexOf(currentPost)));
- } else
- Timber.i("An error occurred while trying to exclude post fromtoQuoteList, post wasn't there!");
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked);
- } else {
- toQuoteList.add(postsList.indexOf(currentPost));
- holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked);
- }
- tmp[isQuoteButtonChecked] = !tmp[isQuoteButtonChecked];
- viewProperties.set(holder.getAdapterPosition(), tmp);
+ if (backPressHidden) {
+ holder.replyEditor.requestFocus();
+ backPressHidden = false;
+ }
+ } else if (currentHolder instanceof EditMessageViewHolder) {
+ final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder;
+
+ //noinspection ConstantConditions
+ Picasso.with(context)
+ .load(getSessionManager().getAvatarLink())
+ .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
+ .centerCrop()
+ .error(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
+ .placeholder(ResourcesCompat.getDrawable(context.getResources()
+ , R.drawable.ic_default_user_thumbnail_white_24dp, null))
+ .transform(new CircleTransform())
+ .into(holder.thumbnail);
+ holder.username.setText(getSessionManager().getUsername());
+ holder.editSubject.setText(currentPost.getSubject());
+ holder.editSubject.setRawInputType(InputType.TYPE_CLASS_TEXT);
+ holder.editSubject.setImeOptions(EditorInfo.IME_ACTION_DONE);
+
+ holder.editEditor.setEmojiKeyboard(emojiKeyboard);
+ holder.editEditor.requestEditTextFocus();
+ emojiKeyboard.registerEmojiInputField(holder.editEditor);
+ holder.editEditor.setText(viewModel.getPostBeingEditedText());
+ holder.editEditor.setOnSubmitListener(view -> {
+ if (holder.editSubject.getText().toString().isEmpty()) return;
+ if (holder.editEditor.getText().toString().isEmpty()) {
+ holder.editEditor.setError("Required");
+ return;
}
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ holder.itemView.setAlpha(0.5f);
+ holder.itemView.setEnabled(false);
+ emojiKeyboard.hide();
+
+ viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString());
});
- }
- } else if (currentHolder instanceof QuickReplyViewHolder) {
- final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder;
-
- //noinspection ConstantConditions
- Picasso.with(context)
- .load(getSessionManager().getAvatarLink())
- .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
- .centerCrop()
- .error(ResourcesCompat.getDrawable(context.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
- .placeholder(ResourcesCompat.getDrawable(context.getResources()
- , R.drawable.ic_default_user_thumbnail, null))
- .transform(new CircleTransform())
- .into(holder.thumbnail);
- holder.username.setText(getSessionManager().getUsername());
- holder.quickReplySubject.setText(replyDataHolder[replySubject]);
-
- if (replyDataHolder[replyText] != null && !Objects.equals(replyDataHolder[replyText], ""))
- holder.quickReply.setText(replyDataHolder[replyText]);
-
- holder.submitButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (holder.quickReplySubject.getText().toString().isEmpty()) return;
- if (holder.quickReply.getText().toString().isEmpty()) return;
- holder.submitButton.setEnabled(false);
- replyTask.execute(holder.quickReplySubject.getText().toString(),
- holder.quickReply.getText().toString(), numReplies, seqnum, sc, topic);
-
- holder.quickReplySubject.getText().clear();
- holder.quickReplySubject.setText("Re: " + topicTitle);
- holder.quickReply.getText().clear();
- holder.submitButton.setEnabled(true);
+
+ if (backPressHidden) {
+ holder.editEditor.requestFocus();
+ backPressHidden = false;
}
- });
- if(backPressHidden)
- {
- holder.quickReply.requestFocus();
- backPressHidden = false;
}
}
}
- void resetTopic(String baseUrl, TopicActivity.TopicTask topicTask, boolean canReply) {
- this.baseUrl = baseUrl;
- 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]);
- }
- }
-
@Override
public int getItemCount() {
- return postsList.size();
+ return topicItems.size();
}
/**
@@ -514,7 +674,7 @@ class TopicAdapter extends RecyclerView.Adapter {
final TextView postDate, postNum, username, subject;
final ImageView thumbnail;
final public WebView post;
- final ImageButton quoteToggle, sharePostButton;
+ final ImageButton quoteToggle, overflowButton;
final RelativeLayout header;
final LinearLayout userExtraInfo;
final View bodyFooterDivider;
@@ -535,7 +695,7 @@ class TopicAdapter extends RecyclerView.Adapter {
post = view.findViewById(R.id.post);
post.setBackgroundColor(Color.argb(1, 255, 255, 255));
quoteToggle = view.findViewById(R.id.toggle_quote_button);
- sharePostButton = view.findViewById(R.id.post_share_button);
+ overflowButton = view.findViewById(R.id.post_overflow_menu);
bodyFooterDivider = view.findViewById(R.id.body_footer_divider);
postFooter = view.findViewById(R.id.post_footer);
@@ -561,22 +721,55 @@ class TopicAdapter extends RecyclerView.Adapter {
/**
* Custom {@link RecyclerView.ViewHolder} implementation
*/
- private static class QuickReplyViewHolder extends RecyclerView.ViewHolder {
+ static class QuickReplyViewHolder extends RecyclerView.ViewHolder {
final ImageView thumbnail;
final TextView username;
- final EditText quickReply, quickReplySubject;
- final AppCompatImageButton submitButton;
+ final EditText quickReplySubject;
+ final EditorView replyEditor;
- QuickReplyViewHolder(View quickReply, CustomEditTextListener replySubject
- , CustomEditTextListener replyText) {
+ QuickReplyViewHolder(View quickReply) {
super(quickReply);
thumbnail = quickReply.findViewById(R.id.thumbnail);
username = quickReply.findViewById(R.id.username);
- this.quickReply = quickReply.findViewById(R.id.quick_reply_text);
- this.quickReply.addTextChangedListener(replyText);
quickReplySubject = quickReply.findViewById(R.id.quick_reply_subject);
- quickReplySubject.addTextChangedListener(replySubject);
- submitButton = quickReply.findViewById(R.id.quick_reply_submit);
+ replyEditor = quickReply.findViewById(R.id.reply_editorview);
+ }
+ }
+
+ static class EditMessageViewHolder extends RecyclerView.ViewHolder {
+ final ImageView thumbnail;
+ final TextView username;
+ final EditText editSubject;
+ final EditorView editEditor;
+
+ EditMessageViewHolder(View editView) {
+ super(editView);
+
+ thumbnail = editView.findViewById(R.id.thumbnail);
+ username = editView.findViewById(R.id.username);
+ editSubject = editView.findViewById(R.id.edit_message_subject);
+ editEditor = editView.findViewById(R.id.edit_editorview);
+ }
+ }
+
+ static class PollViewHolder extends RecyclerView.ViewHolder {
+ final TextView question, errorTooManySelected;
+ final LinearLayout optionsLayout;
+ final AppCompatButton submitButton;
+ final AppCompatButton removeVotesButton, showPollResultsButton, hidePollResultsButton;
+ final HorizontalBarChart voteChart;
+
+ PollViewHolder(View itemView) {
+ super(itemView);
+
+ question = itemView.findViewById(R.id.question_textview);
+ optionsLayout = itemView.findViewById(R.id.options_layout);
+ submitButton = itemView.findViewById(R.id.submit_button);
+ removeVotesButton = itemView.findViewById(R.id.remove_vote_button);
+ showPollResultsButton = itemView.findViewById(R.id.show_poll_results_button);
+ hidePollResultsButton = itemView.findViewById(R.id.show_poll_options_button);
+ errorTooManySelected = itemView.findViewById(R.id.error_too_many_checked);
+ voteChart = itemView.findViewById(R.id.vote_chart);
}
}
@@ -605,26 +798,39 @@ class TopicAdapter extends RecyclerView.Adapter {
final String uriString = uri.toString();
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(uri);
+ viewModel.stopLoading();
if (target.is(ThmmyPage.PageCategory.TOPIC)) {
//This url points to a topic
- //Checks if this is the current topic
- if (Objects.equals(uriString.substring(0, uriString.lastIndexOf(".")), baseUrl)) {
- //Gets uri's targeted message's index number
- String msgIndexReq = uriString.substring(uriString.indexOf("msg") + 3);
- if (msgIndexReq.contains("#"))
- msgIndexReq = msgIndexReq.substring(0, msgIndexReq.indexOf("#"));
- else
- msgIndexReq = msgIndexReq.substring(0, msgIndexReq.indexOf(";"));
-
- //Checks if this post is in the current topic's page
- for (Post post : postsList) {
- if (post.getPostIndex() == Integer.parseInt(msgIndexReq)) {
- // TODO Don't restart Activity, Just change post focus
+ //Checks if the page to be loaded is the one already shown
+ if (uriString.contains(ParseHelpers.getBaseURL(viewModel.getTopicUrl()))) {
+ if (uriString.contains("topicseen#new") || uriString.contains("#new")) {
+ if (viewModel.getCurrentPageIndex() == viewModel.getPageCount()) {
+ //same page
+ postFocusListener.onPostFocusChange(getItemCount() - 1);
+ Timber.e("new");
return true;
}
}
-
- topicTask.execute(uri.toString());
+ if (uriString.contains("msg")) {
+ String tmpUrlSbstr = uriString.substring(uriString.indexOf("msg") + 3);
+ if (tmpUrlSbstr.contains("msg"))
+ tmpUrlSbstr = tmpUrlSbstr.substring(0, tmpUrlSbstr.indexOf("msg") - 1);
+ int testAgainst = Integer.parseInt(tmpUrlSbstr);
+ for (int i = 0; i < topicItems.size(); i++) {
+ if (topicItems.get(i) instanceof Post && ((Post) topicItems.get(i)).getPostIndex() == testAgainst) {
+ //same page
+ Timber.e(Integer.toString(i));
+ postFocusListener.onPostFocusChange(i);
+ return true;
+ }
+ }
+ } else if ((Objects.equals(uriString, ParseHelpers.getBaseURL(viewModel.getTopicUrl())) &&
+ viewModel.getCurrentPageIndex() == 1) ||
+ Integer.parseInt(uriString.substring(ParseHelpers.getBaseURL(viewModel.getTopicUrl()).length() + 1)) / 15 + 1 ==
+ viewModel.getCurrentPageIndex()) {
+ //same page
+ return true;
+ }
}
Intent intent = new Intent(context, TopicActivity.class);
@@ -665,25 +871,9 @@ class TopicAdapter extends RecyclerView.Adapter {
}
- private class CustomEditTextListener implements TextWatcher {
- private final int positionInDataHolder;
-
- CustomEditTextListener(int positionInDataHolder) {
- this.positionInDataHolder = positionInDataHolder;
- }
-
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
- }
-
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
- replyDataHolder[positionInDataHolder] = charSequence.toString();
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- }
+ //we need to set a callback to topic activity to scroll the recyclerview when post focus is requested
+ public interface OnPostFocusChangeListener {
+ void onPostFocusChange(int position);
}
/**
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 97f134ae..5a411e12 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
@@ -5,6 +5,7 @@ import android.graphics.Color;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
+import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;
import java.net.MalformedURLException;
@@ -13,9 +14,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import gr.thmmy.mthmmy.base.BaseActivity;
+import gr.thmmy.mthmmy.model.Poll;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyFile;
+import gr.thmmy.mthmmy.model.TopicItem;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;
@@ -28,7 +34,11 @@ import timber.log.Timber;
* {@link #parseTopicNumberOfPages(Document, int, ParseHelpers.Language)}
* {@link #parseTopic(Document, ParseHelpers.Language)}
*/
-class TopicParser {
+public class TopicParser {
+ private static Pattern mentionsPattern = Pattern.
+ compile("