Browse Source

Merge branch 'develop' into pms

pms
oogee 5 years ago
parent
commit
fb0c9efc93
  1. 1
      README.md
  2. 64
      app/build.gradle
  3. 4
      app/gradle/grgit.gradle
  4. 10
      app/src/main/AndroidManifest.xml
  5. 8
      app/src/main/assets/apache_libraries.html
  6. 90
      app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
  7. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
  8. 10
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
  9. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java
  10. 11
      app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java
  11. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksBoardFragment.java
  12. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksTopicFragment.java
  13. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/create_topic/NewTopicTask.java
  14. 74
      app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
  15. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java
  16. 11
      app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java
  17. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java
  18. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java
  19. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java
  20. 12
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
  21. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java
  22. 16
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  23. 16
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  24. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
  25. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
  26. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
  27. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
  28. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java
  29. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java
  30. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutAdapter.java
  31. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java
  32. 35
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java
  33. 15
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  34. 51
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  35. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java
  36. 9
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditResult.java
  37. 22
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java
  38. 16
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java
  39. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java
  40. 958
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java
  41. 381
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java
  42. 79
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java
  43. 191
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
  44. 8
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java
  45. 99
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java
  46. 160
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java
  47. 192
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  48. 15
      app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
  49. 1
      app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
  50. 11
      app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java
  51. 5
      app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java
  52. 1
      app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java
  53. 1
      app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java
  54. 6
      app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java
  55. 4
      app/src/main/java/gr/thmmy/mthmmy/model/Post.java
  56. 45
      app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java
  57. 12
      app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
  58. 233
      app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java
  59. 5
      app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
  60. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java
  61. 22
      app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java
  62. 3
      app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
  63. 1
      app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java
  64. 98
      app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java
  65. 14
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
  66. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java
  67. 173
      app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java
  68. 27
      app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java
  69. 5
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java
  70. 189
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java
  71. 1
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java
  72. 1
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java
  73. 17
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
  74. 4
      app/src/main/res/drawable/ic_attach_file_white_24dp.xml
  75. 4
      app/src/main/res/drawable/ic_cached_accent_24dp.xml
  76. 4
      app/src/main/res/drawable/ic_cancel_accent_24dp.xml
  77. 4
      app/src/main/res/drawable/ic_info_outline_warning_24dp.xml
  78. 14
      app/src/main/res/layout/activity_board.xml
  79. 4
      app/src/main/res/layout/activity_downloads.xml
  80. 4
      app/src/main/res/layout/activity_topic.xml
  81. 74
      app/src/main/res/layout/activity_upload.xml
  82. 2
      app/src/main/res/layout/activity_upload_fields_builder.xml
  83. 24
      app/src/main/res/layout/activity_upload_file_list_row.xml
  84. 10
      app/src/main/res/layout/activity_upload_filename_info_popup.xml
  85. 43
      app/src/main/res/layout/dialog_upload_progress.xml
  86. 37
      app/src/main/res/layout/editor_view_color_picker.xml
  87. 1
      app/src/main/res/values/colors.xml
  88. 16
      app/src/main/res/values/strings.xml
  89. 2
      app/src/main/res/values/styles.xml
  90. 158
      app/src/main/res/values/uploads_courses.xml
  91. 4
      app/src/main/res/xml-v26/app_preferences_user.xml
  92. 4
      app/src/main/res/xml/app_preferences_user.xml
  93. 10
      build.gradle
  94. 4
      gradle/wrapper/gradle-wrapper.properties

1
README.md

@ -2,6 +2,7 @@
[![API](https://img.shields.io/badge/API-19%2B-blue.svg?style=flat)](https://android-arsenal.com/api?level=19)
[![Discord Channel](https://img.shields.io/badge/discord-public@mTHMMY-738bd7.svg?style=flat)][discord-server]
![Last Commit](https://img.shields.io/github/last-commit/ThmmyNoLife/mTHMMY/develop.svg?style=flat)
![mTHMMY logo](app/src/main/res/mipmap-xhdpi/ic_launcher.png)

64
app/build.gradle

@ -6,15 +6,16 @@ apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
android {
compileSdkVersion 28
compileSdkVersion 29
buildToolsVersion = '29.0.2'
defaultConfig {
vectorDrawables.useSupportLibrary = true
applicationId "gr.thmmy.mthmmy"
minSdkVersion 19
targetSdkVersion 28
versionCode 16
versionName "1.6.1"
targetSdkVersion 29
versionCode 22
versionName "1.7.4"
archivesBaseName = "mTHMMY-v$versionName"
buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\""
buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\""
@ -41,44 +42,56 @@ android {
}
}
def firebaseReleaseProjectId = "mthmmy-release-3aef0"
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)!')
def googleServicesFile = new File("app/src/release/google-services.json")
if(googleServicesFile.exists()){
def json = new JsonSlurper().parseText(googleServicesFile.text)
if (json.project_info.project_id != firebaseReleaseProjectId)
throw new GradleException('Please supply the correct google-services.json for release in app/src/release/ directory!')
}
else
throw new GradleException('Please add the release google-services.json in app/src/release/ directory!')
})
} else if (task.name.contains("assembleDebug")) {
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 replace the release google-services.json with a debug one!')
def googleServicesFile = new File("app/src/debug/google-services.json")
if(googleServicesFile.exists()){
def json = new JsonSlurper().parseText(googleServicesFile.text)
if (json.project_info.project_id == firebaseReleaseProjectId)
throw new GradleException('Please replace google-services.json in app/src/debug/ with a debug one!')
}
else
throw new GradleException('Please add a debug google-services.json in app/src/debug/ directory!')
})
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.preference:preference:1.1.0-alpha02'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha04'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.exifinterface:exifinterface:1.1.0-beta01'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.google.firebase:firebase-core:17.0.0'
implementation 'com.google.firebase:firebase-messaging:19.0.1'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.snatik:storage:2.1.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.0' //TODO: Warning: okhttp has dropped support for Android v.19 since okhttp 3.13!
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!
implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
implementation 'com.github.franmontiel:PersistentCookieJar:1.0.1'
implementation 'com.github.PhilJay:MPAndroidChart:3.0.3'
implementation 'com.mikepenz:materialdrawer:6.1.1'
implementation 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar'
@ -86,11 +99,10 @@ dependencies {
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.1'
implementation 'ru.noties:markwon:2.0.0'
implementation 'net.gotev:uploadservice:3.4.2'
implementation 'net.gotev:uploadservice-okhttp:3.4.2'
implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.4'
//Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler
implementation 'ru.noties:markwon:2.0.2'
implementation 'net.gotev:uploadservice:3.5.2'
implementation 'net.gotev:uploadservice-okhttp:3.4.2' //TODO: Warning: v.3.5 depends on okhttp 3.13!
implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler
}
apply plugin: 'com.google.gms.google-services'

4
app/gradle/grgit.gradle

@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'org.ajoberstar.grgit:grgit-core:3.0.0'
classpath 'org.ajoberstar.grgit:grgit-core:3.1.1'
}
}
@ -48,4 +48,4 @@ ext {
getCurrentBranch = this.&getCurrentBranch
getCommitHash = this.&getCommitHash
isClean = this.&isClean
}
}

10
app/src/main/AndroidManifest.xml

@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".base.BaseApplication"
@ -110,6 +111,7 @@
</activity>
<activity
android:name=".activities.upload.UploadActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName=".activities.main.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
@ -161,6 +163,14 @@
</intent-filter>
</service>
<receiver
android:name=".services.UploadsReceiver"
android:exported="false">
<intent-filter>
<action android:name="gr.thmmy.mthmmy.uploadservice.broadcast.status" />
</intent-filter>
</receiver>
<activity
android:name=".activities.create_topic.CreateTopicActivity"
android:configChanges="orientation|screenSize"

8
app/src/main/assets/apache_libraries.html

@ -39,7 +39,7 @@
<body>
<ul>
<li>
<h5><a href="https://square.github.io/okhttp/">OkHttp</a>&nbsp;v3.12.0 (Copyright ©2016 Square, Inc.)</h5>
<h5><a href="https://square.github.io/okhttp/">OkHttp</a>&nbsp;v3.14.2 (Copyright ©2019 Square, Inc.)</h5>
</li>
<li>
<h5><a href="https://square.github.io/picasso/">Picasso</a>&nbsp;v2.5.2 (Copyright ©2013 Square, Inc.)</h5>
@ -63,16 +63,16 @@
<h5><a href="https://github.com/JakeWharton/timber">Timber</a>&nbsp;v4.7.1 (Copyright ©2013 Jake Wharton)</h5>
</li>
<li>
<h5><a href="https://github.com/gotev/android-upload-service">Android Upload Service</a>&nbsp;v3.4.2 (Copyright ©2013-2018 Aleksandar Gotev)</h5>
<h5><a href="https://github.com/gotev/android-upload-service">Android Upload Service</a>&nbsp;v3.5.2 (Copyright ©2013-2019 Aleksandar Gotev)</h5>
</li>
<li>
<h5><a href="https://github.com/noties/Markwon">Markwon</a>&nbsp;v2.0.0 (Copyright ©2017 Dimitry Ivanov)</h5>
<h5><a href="https://github.com/noties/Markwon">Markwon</a>&nbsp;v2.0.2 (Copyright ©2017 Dimitry Ivanov)</h5>
</li>
<li>
<h5><a href="https://github.com/ajoberstar/grgit">Grgit</a>&nbsp;v3.0.0 (Copyright ©2018 Andrew Oberstar)</h5>
</li>
<li>
<h5><a href=https://github.com/itkacher/OkHttpProfiler">OkHttpProfiler</a>&nbsp;v1.0.4</h5>
<h5><a href=https://github.com/itkacher/OkHttpProfiler">OkHttpProfiler</a>&nbsp;v1.0.5</h5>
</li>
</ul>

90
app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java

@ -11,17 +11,21 @@ import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.appbar.AppBarLayout;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.appbar.AppBarLayout;
import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
public class AboutActivity extends BaseActivity {
private static final int TIME_INTERVAL = 1000;
@ -31,8 +35,9 @@ public class AboutActivity extends BaseActivity {
private AppBarLayout appBar;
private CoordinatorLayout coordinatorLayout;
private ScrollView mainContent;
private AlertDialog alertDialog;
private FrameLayout trollGif;
private FrameLayout easterEggImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -69,41 +74,41 @@ public class AboutActivity extends BaseActivity {
createDrawer();
drawer.setSelection(ABOUT_ID);
final ScrollView mainContent = findViewById(R.id.scrollview);
trollGif = findViewById(R.id.trollPicFrame);
mainContent = findViewById(R.id.scrollview);
easterEggImage = findViewById(R.id.trollPicFrame);
// Set Easter egg on logo image
ImageView logoImageView = findViewById(R.id.logoView);
logoImageView.setOnClickListener(view -> {
if (mVersionLastPressedTime + TIME_INTERVAL > System.currentTimeMillis()) {
if (mVersionPressedCounter == TIMES_TO_PRESS)
showEasterEgg();
mVersionLastPressedTime = System.currentTimeMillis();
++mVersionPressedCounter;
} else {
mVersionLastPressedTime = System.currentTimeMillis();
mVersionPressedCounter = 0;
}
});
TextView tv = findViewById(R.id.version);
if (tv != null) {
TextView versionTextView = findViewById(R.id.version);
if (versionTextView != null) {
if (BuildConfig.DEBUG)
tv.setText(getString(R.string.version, versionName + versionInfo));
versionTextView.setText(getString(R.string.version, versionName + versionInfo));
else
tv.setText(getString(R.string.version, versionName));
versionTextView.setText(getString(R.string.version, versionName));
if(BuildConfig.DEBUG && gitExists){
tv.setOnClickListener(view -> {
if(gitExists){
versionTextView.setOnClickListener(view -> {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/ThmmyNoLife/mTHMMY/commit/" + BuildConfig.COMMIT_HASH));
startActivity(intent);
});
}
else{ // Easter Egg
tv.setOnClickListener(view -> {
if (mVersionLastPressedTime + TIME_INTERVAL > System.currentTimeMillis()) {
if (mVersionPressedCounter == TIMES_TO_PRESS) {
appBar.setVisibility(View.INVISIBLE);
mainContent.setVisibility(View.INVISIBLE);
trollGif.setVisibility(View.VISIBLE);
drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
mVersionLastPressedTime = System.currentTimeMillis();
++mVersionPressedCounter;
} else {
mVersionLastPressedTime = System.currentTimeMillis();
mVersionPressedCounter = 0;
}
});
}
versionTextView.setOnLongClickListener(view -> {
Toast.makeText(getApplicationContext(), BaseApplication.getFirebaseProjectId(), Toast.LENGTH_SHORT).show();
return true;
});
}
TextView privacyPolicy = findViewById(R.id.privacy_policy_header);
@ -121,6 +126,14 @@ public class AboutActivity extends BaseActivity {
super.onResume();
}
@Override
public void onBackPressed() {
if(easterEggImage.getVisibility()==View.INVISIBLE)
super.onBackPressed();
else
hideEasterEgg();
}
public void displayApacheLibraries(View v) {
LayoutInflater inflater = LayoutInflater.from(this);
WebView webView = (WebView) inflater.inflate(R.layout.dialog_licenses, coordinatorLayout, false);
@ -151,4 +164,21 @@ public class AboutActivity extends BaseActivity {
alertDialog.getWindow().setLayout(width, height);
}
private void showEasterEgg(){
if(getResources().getConfiguration().orientation==ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
appBar.setVisibility(View.INVISIBLE);
mainContent.setVisibility(View.INVISIBLE);
easterEggImage.setVisibility(View.VISIBLE);
drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
}
private void hideEasterEgg(){
appBar.setVisibility(View.VISIBLE);
mainContent.setVisibility(View.VISIBLE);
easterEggImage.setVisibility(View.INVISIBLE);
drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}

5
app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java

@ -10,10 +10,11 @@ import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
import com.google.firebase.analytics.FirebaseAnalytics;
import androidx.appcompat.widget.AppCompatButton;
import androidx.preference.PreferenceManager;
import com.google.firebase.analytics.FirebaseAnalytics;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.base.BaseActivity;

10
app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java

@ -8,6 +8,11 @@ import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.jsoup.nodes.Document;
@ -17,10 +22,6 @@ import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Objects;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.create_topic.CreateTopicActivity;
@ -100,6 +101,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
progressBar = findViewById(R.id.progressBar);
newTopicFAB = findViewById(R.id.board_fab);
newTopicFAB.setTag(true);
if (!sessionManager.isLoggedIn()) newTopicFAB.hide();
else {
newTopicFAB.setOnClickListener(view -> {

3
app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java

@ -12,10 +12,11 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Objects;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.model.Board;

11
app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java

@ -4,16 +4,17 @@ import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.topic.TopicActivity;

5
app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksBoardFragment.java

@ -12,11 +12,12 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Bookmark;

5
app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksTopicFragment.java

@ -12,11 +12,12 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Bookmark;

1
app/src/main/java/gr/thmmy/mthmmy/activities/create_topic/NewTopicTask.java

@ -61,6 +61,7 @@ public class NewTopicTask extends AsyncTask<String, Void, Boolean> {
.addFormDataPart("sc", sc)
.addFormDataPart("subject", strings[1])
.addFormDataPart("topic", topic)
.addFormDataPart("icon", "xx")
.build();
Request post = new Request.Builder()

74
app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java

@ -1,11 +1,21 @@
package gr.thmmy.mthmmy.activities.downloads;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
@ -13,10 +23,8 @@ import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Objects;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
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;
@ -29,6 +37,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.
@ -47,7 +57,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
private MaterialProgressBar progressBar;
private RecyclerView recyclerView;
private DownloadsAdapter downloadsAdapter;
//private FloatingActionButton uploadFAB;
private FloatingActionButton uploadFAB;
private ParseDownloadPageTask parseDownloadPageTask;
private int numberOfPages = -1;
@ -113,37 +123,37 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
}
});
// uploadFAB = findViewById(R.id.upload_fab);
// uploadFAB.setEnabled(false);
// uploadFAB.hide();
uploadFAB = findViewById(R.id.upload_fab);
uploadFAB.setEnabled(false);
uploadFAB.hide();
parseDownloadPageTask = new ParseDownloadPageTask();
parseDownloadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 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 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() {
@ -198,7 +208,7 @@ 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
@ -296,7 +306,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;

3
app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java

@ -12,12 +12,13 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Objects;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Download;

11
app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java

@ -7,11 +7,6 @@ import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@ -19,6 +14,12 @@ import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import java.util.ArrayList;
import java.util.List;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.board.BoardActivity;

5
app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java

@ -6,14 +6,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bignerdranch.expandablerecyclerview.ChildViewHolder;
import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
import com.bignerdranch.expandablerecyclerview.ParentViewHolder;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.Board;

7
app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java

@ -9,6 +9,10 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
import org.jsoup.nodes.Document;
@ -19,9 +23,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;

5
app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java

@ -6,10 +6,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;

12
app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java

@ -9,6 +9,10 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
@ -17,10 +21,8 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
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;
@ -145,9 +147,9 @@ public class RecentFragment extends BaseFragment {
topicSummaries.addAll(fetchedRecent);
recentAdapter.notifyDataSetChanged();
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) {
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show();
Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getContext(), "Unexpected error," +
Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Unexpected error," +
" please contact the developers with the details", Toast.LENGTH_LONG).show();
}

5
app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java

@ -5,10 +5,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;

16
app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java

@ -10,6 +10,11 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
@ -18,10 +23,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;
@ -142,11 +143,12 @@ public class UnreadFragment extends BaseFragment {
@Override
public void onDestroy() {
super.onDestroy();
if (unreadTask.isRunning())
if (unreadTask!=null && unreadTask.isRunning())
unreadTask.cancel(true);
if (markReadTask.isRunning())
if (markReadTask!=null && markReadTask.isRunning())
markReadTask.cancel(true);
topicSummaries.clear();
if(topicSummaries!=null)
topicSummaries.clear();
}
public interface UnreadFragmentInteractionListener extends FragmentInteractionListener {

16
app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java

@ -19,6 +19,13 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.squareup.picasso.Picasso;
@ -33,12 +40,6 @@ import java.util.List;
import java.util.Objects;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.create_pm.CreatePMActivity;
@ -61,6 +62,7 @@ import timber.log.Timber;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.emojiTagToHtml;
/**
* Activity for user profile. When creating an Intent of this activity you need to bundle a <b>String</b>
@ -271,7 +273,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
{ //Finds personal text
Element tmpEl = profilePage.select("td.windowbg:nth-child(2)").first();
if (tmpEl != null) {
personalText = tmpEl.text().trim();
personalText = emojiTagToHtml(tmpEl.text().trim());
} else {
//Should never get here!
//Something is wrong.

3
app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java

@ -8,9 +8,10 @@ import android.webkit.WebView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.PostSummary;

7
app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java

@ -8,6 +8,10 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -17,9 +21,6 @@ import java.util.ArrayList;
import javax.net.ssl.SSLHandshakeException;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseFragment;

3
app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java

@ -12,6 +12,8 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import com.github.mikephil.charting.charts.HorizontalBarChart;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.AxisBase;
@ -37,7 +39,6 @@ import java.util.List;
import javax.net.ssl.SSLHandshakeException;
import androidx.fragment.app.Fragment;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;

5
app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java

@ -13,6 +13,8 @@ import android.webkit.WebView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -21,7 +23,6 @@ import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Objects;
import androidx.fragment.app.Fragment;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;
@ -144,7 +145,7 @@ public class SummaryFragment extends Fragment {
|| summaryRow.text().contains("Κατάσταση")) continue;
else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) {
//This needs special handling since it may have css
pHtml = ParseHelpers.youtubeEmbeddedFix(summaryRow);
pHtml = ParseHelpers.emojiTagToHtml(ParseHelpers.youtubeEmbeddedFix(summaryRow));
//Add stuff to make it work in WebView
//style.css
pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n" +

1
app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java

@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.activities.settings;
import android.os.Bundle;
import androidx.fragment.app.FragmentTransaction;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;

5
app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java

@ -11,12 +11,13 @@ import android.provider.Settings;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import timber.log.Timber;

1
app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutAdapter.java

@ -16,6 +16,7 @@ import android.webkit.WebViewClient;
import android.widget.TextView;
import androidx.annotation.NonNull;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;

3
app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java

@ -16,7 +16,9 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.editorview.EditorView;
import gr.thmmy.mthmmy.editorview.EmojiKeyboard;
import gr.thmmy.mthmmy.model.Shout;
@ -133,6 +135,7 @@ public class ShoutboxFragment extends Fragment {
progressBar.setVisibility(View.INVISIBLE);
if (resultCode == NetworkResultCodes.SUCCESSFUL) {
Timber.i("Shout was sent successfully");
BaseApplication.getInstance().logFirebaseAnalyticsEvent("shout", null);
editorView.getEditText().getText().clear();
shoutboxViewModel.loadShoutbox(true);
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) {

35
app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java

@ -2,6 +2,7 @@ package gr.thmmy.mthmmy.activities.topic;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.IOException;
@ -60,22 +61,24 @@ public class Posting {
String finalUrl = response.request().url().toString();
if (finalUrl.contains("action=post")) {
Document postErrorPage = Jsoup.parse(response.body().string());
String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first()
.toString().split("<br>");
for (int i = 0; i < errors.length; ++i) { //TODO test
Timber.d(String.valueOf(i));
Timber.d(errors[i]);
}
for (String error : errors) {
if (error.contains("Your session timed out while posting") ||
error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή"))
return REPLY_STATUS.SESSION_ENDED;
if (error.contains("No subject was filled in")
|| error.contains("Δεν δόθηκε τίτλος"))
return REPLY_STATUS.NO_SUBJECT;
if (error.contains("The message body was left empty")
|| error.contains("Δεν δόθηκε κείμενο για το μήνυμα"))
return REPLY_STATUS.EMPTY_BODY;
Element errorsElement = postErrorPage.select("tr[id=errors] div[id=error_list]").first();
if(errorsElement!=null){
String[] errors = errorsElement.toString().split("<br>");
for (int i = 0; i < errors.length; ++i) { //TODO test
Timber.d(String.valueOf(i));
Timber.d(errors[i]);
}
for (String error : errors) {
if (error.contains("Your session timed out while posting") ||
error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή"))
return REPLY_STATUS.SESSION_ENDED;
if (error.contains("No subject was filled in")
|| error.contains("Δεν δόθηκε τίτλος"))
return REPLY_STATUS.NO_SUBJECT;
if (error.contains("The message body was left empty")
|| error.contains("Δεν δόθηκε κείμενο για το μήνυμα"))
return REPLY_STATUS.EMPTY_BODY;
}
}
return REPLY_STATUS.NEW_REPLY_WHILE_POSTING;
}

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

@ -28,20 +28,21 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
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.PrepareForReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask;
import gr.thmmy.mthmmy.base.BaseActivity;
@ -617,7 +618,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
}
}
});
viewModel.setPrepareForReplyCallbacks(new PrepareForReply.PrepareForReplyCallbacks() {
viewModel.setPrepareForReplyCallbacks(new PrepareForReplyTask.PrepareForReplyCallbacks() {
@Override
public void onPrepareForReplyStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE);

51
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java

@ -39,6 +39,14 @@ import android.widget.RadioGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatButton;
import androidx.core.content.res.ResourcesCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import com.github.mikephil.charting.charts.HorizontalBarChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
@ -53,13 +61,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatButton;
import androidx.core.content.res.ResourcesCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
@ -88,6 +89,7 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.USER_COLOR_WHITE;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.USER_COLOR_YELLOW;
import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
import static gr.thmmy.mthmmy.utils.FileUtils.faIconFromFilename;
/**
* Custom {@link RecyclerView.Adapter} used for topics.
@ -393,7 +395,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
attached.setClickable(true);
attached.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf"));
attached.setText(faIconFromFilename(attachedFile.getFilename()) + " "
attached.setText(faIconFromFilename(context, attachedFile.getFilename()) + " "
+ attachedFile.getFilename() + attachedFile.getFileInfo());
attached.setTextColor(filesTextColor);
attached.setPadding(0, 3, 0, 3);
@ -1036,37 +1038,4 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public interface OnPostFocusChangeListener {
void onPostFocusChange(int position);
}
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.
*
* @param filename String with filename <b>containing file's extension</b>
* @return FontAwesome character according to file's type
* @see <a href="http://fontawesome.io/">FontAwesome</a>
*/
@NonNull
private String faIconFromFilename(String filename) {
filename = filename.toLowerCase();
if (filename.contains("jpg") || filename.contains("gif") || filename.contains("jpeg")
|| filename.contains("png"))
return context.getResources().getString(R.string.fa_file_image_o);
else if (filename.contains("pdf"))
return context.getResources().getString(R.string.fa_file_pdf_o);
else if (filename.contains("zip") || filename.contains("rar") || filename.contains("tar.gz"))
return context.getResources().getString(R.string.fa_file_zip_o);
else if (filename.contains("txt"))
return context.getResources().getString(R.string.fa_file_text_o);
else if (filename.contains("doc") || filename.contains("docx"))
return context.getResources().getString(R.string.fa_file_word_o);
else if (filename.contains("xls") || filename.contains("xlsx"))
return context.getResources().getString(R.string.fa_file_excel_o);
else if (filename.contains("pps"))
return context.getResources().getString(R.string.fa_file_powerpoint_o);
else if (filename.contains("mpg"))
return context.getResources().getString(R.string.fa_file_video_o);
return context.getResources().getString(R.string.fa_file);
}
}

1
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java

@ -38,6 +38,7 @@ public class EditTask extends AsyncTask<String, Void, Boolean> {
.addFormDataPart("sc", strings[4])
.addFormDataPart("subject", strings[5])
.addFormDataPart("topic", strings[6])
.addFormDataPart("icon", strings[7])
.build();
Request post = new Request.Builder()
.url(strings[0])

9
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditResult.java

@ -1,18 +1,19 @@
package gr.thmmy.mthmmy.activities.topic.tasks;
public class PrepareForEditResult {
private final String postText, commitEditUrl, numReplies, seqnum, sc, topic;
private final String postText, commitEditUrl, numReplies, seqnum, sc, topic, icon;
private int position;
private boolean successful;
public PrepareForEditResult(String postText, String commitEditUrl, String numReplies, String seqnum,
String sc, String topic, int position, boolean successful) {
String sc, String topic, String icon, int position, boolean successful) {
this.postText = postText;
this.commitEditUrl = commitEditUrl;
this.numReplies = numReplies;
this.seqnum = seqnum;
this.sc = sc;
this.topic = topic;
this.icon = icon;
this.position = position;
this.successful = successful;
}
@ -41,6 +42,10 @@ public class PrepareForEditResult {
return topic;
}
public String getIcon() {
return icon;
}
public int getPosition() {
return position;
}

22
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java

@ -38,28 +38,32 @@ public class PrepareForEditTask extends AsyncTask<String, Void, PrepareForEditRe
Document document;
String url = strings[0];
Request request = new Request.Builder()
.url(url + ";wap2")
/*.url(url + ";wap2")*/
.url(url)
.build();
try {
String postText, commitEditURL, numReplies, seqnum, sc, topic;
String postText, commitEditURL, numReplies, seqnum, sc, topic, icon;
OkHttpClient client = BaseApplication.getInstance().getClient();
Response response = client.newCall(request).execute();
document = ParseHelpers.parse(response.body().string());
Element message = document.select("textarea").first();
Element form = document.select("form#postmodify").first();
Element message = form.select("textarea").first();
postText = message.text();
commitEditURL = document.select("form").first().attr("action");
commitEditURL = form.attr("action");
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");
seqnum = form.select("input[name=seqnum]").first().attr("value");
sc = form.select("input[name=sc]").first().attr("value");
topic = form.select("input[name=topic]").first().attr("value");
icon = form.select("select[name=icon]>option[selected]").first().attr("value");
return new PrepareForEditResult(postText, commitEditURL, numReplies, seqnum, sc, topic, position, true);
return new PrepareForEditResult(postText, commitEditURL, numReplies, seqnum, sc, topic, icon, position, true);
} catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Prepare failed.");
return new PrepareForEditResult(null, null, null, null, null, null, position, false);
return new PrepareForEditResult(null, null, null, null, null, null, null, position, false);
}
}

16
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java → app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java

@ -15,13 +15,13 @@ import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyResult> {
public class PrepareForReplyTask extends AsyncTask<Integer, Void, PrepareForReplyResult> {
private PrepareForReplyCallbacks listener;
private OnPrepareForReplyFinished finishListener;
private String replyPageUrl;
public PrepareForReply(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener,
String replyPageUrl) {
public PrepareForReplyTask(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener,
String replyPageUrl) {
this.listener = listener;
this.finishListener = finishListener;
this.replyPageUrl = replyPageUrl;
@ -49,12 +49,16 @@ public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyRes
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.");
} catch (NullPointerException e) {
// TODO: Convert this task to (New)ParseTask (?) / handle parsing errors in a better way
Timber.e(e, "Prepare failed (1)");
return new PrepareForReplyResult(false, null, null, null, null, null);
} catch (IOException | Selector.SelectorParseException e){
Timber.e(e, "Prepare failed (2)");
return new PrepareForReplyResult(false, null, null, null, null, null);
}
StringBuilder buildedQuotes = new StringBuilder("");
StringBuilder buildedQuotes = new StringBuilder();
for (Integer postIndex : postIndices) {
request = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" +

1
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java

@ -42,6 +42,7 @@ public class ReplyTask extends AsyncTask<String, Void, Posting.REPLY_STATUS> {
.addFormDataPart("sc", args[4])
.addFormDataPart("subject", args[0])
.addFormDataPart("topic", args[5])
.addFormDataPart("icon", "xx")
.build();
Request post = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=post2")

958
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java

File diff suppressed because it is too large

381
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java

@ -11,23 +11,27 @@ import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.annotation.Nullable;
import java.util.Calendar;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import timber.log.Timber;
public class UploadFieldsBuilderActivity extends AppCompatActivity {
static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE";
public class UploadFieldsBuilderActivity extends BaseActivity {
static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME";
static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME";
static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME";
static final String BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER";
static final String RESULT_FILENAME = "RESULT_FILENAME";
static final String RESULT_TITLE = "RESULT_TITLE";
static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION";
private String course, semester;
private String courseName, courseMinifiedName, courseGreeklishName, semester;
private boolean isValidYear;
private LinearLayout semesterChooserLinear;
private RadioGroup typeRadio, semesterRadio;
@ -38,32 +42,26 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String working = s.toString();
boolean isValid;
if (working.length() == 4) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
int inputYear = Integer.parseInt(working);
isValid = inputYear <= currentYear && inputYear > 2000;
} else {
isValid = false;
}
isValidYear = inputYear <= currentYear && inputYear > 1980;
} else
isValidYear = false;
if (!isValid) {
if (!isValidYear)
year.setError("Please enter a valid year");
} else {
else
year.setError(null);
}
}
@Override
public void afterTextChanged(Editable s) {
}
public void afterTextChanged(Editable s) { }
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
};
@Override
@ -73,9 +71,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
Bundle extras = getIntent().getExtras();
if (extras != null) {
course = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE);
courseName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME);
courseMinifiedName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME);
courseGreeklishName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME);
semester = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER);
if (course == null || course.equals("") || semester == null || semester.equals("")) {
if (courseName == null || courseName.equals("") || semester == null || semester.equals("")) {
Toast.makeText(this, "Something went wrong!", Toast.LENGTH_SHORT).show();
Timber.e("Bundle came empty in %s", UploadFieldsBuilderActivity.class.getSimpleName());
@ -86,7 +86,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
}
//Initialize toolbar
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.upload_fields_builder_toolbar_title);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
@ -94,6 +94,9 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
createDrawer();
drawer.setSelection(UPLOAD_ID, false);
semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester);
semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group);
semesterRadio.check(Integer.parseInt(semester) % 2 == 0
@ -121,8 +124,8 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
} else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) {
Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show();
return;
} else if (year.getText().toString().isEmpty()) {
Toast.makeText(view.getContext(), "Please choose a year for the upload", Toast.LENGTH_SHORT).show();
} else if (year.getText().toString().isEmpty() || !isValidYear) {
Toast.makeText(view.getContext(), "Please choose a valid year for the upload", Toast.LENGTH_SHORT).show();
return;
}
@ -139,11 +142,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildFilename() {
switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams:
return getGreeklishCourseName() + "_" + getGreeklishPeriod() + "_" + year.getText().toString();
return courseGreeklishName + "_" + getGreeklishPeriod() + "_" + year.getText().toString();
case R.id.upload_fields_builder_radio_button_exam_solutions:
return getGreeklishCourseName() + "_" + getGreeklishPeriod() + "_" + year.getText().toString() + "_Lyseis";
return courseGreeklishName + "_" + getGreeklishPeriod() + "_" + year.getText().toString() + "_Lyseis";
case R.id.upload_fields_builder_radio_button_notes:
return getGreeklishCourseName() + "_" + year.getText().toString() + "_Shmeiwseis";
return courseGreeklishName + "_" + year.getText().toString() + "_Simeioseis";
default:
return null;
}
@ -153,11 +156,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildTitle() {
switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams:
return getMinifiedCourseName() + " - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString();
return "[" + courseMinifiedName + "] - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString();
case R.id.upload_fields_builder_radio_button_exam_solutions:
return getMinifiedCourseName() + " - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString();
return "[" + courseMinifiedName + "] - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString();
case R.id.upload_fields_builder_radio_button_notes:
return getMinifiedCourseName() + " - " + "Σημειώσεις παραδόσεων " + year.getText().toString();
return "[" + courseMinifiedName + "] - " + "Σημειώσεις παραδόσεων " + year.getText().toString();
default:
return null;
}
@ -166,11 +169,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildDescription() {
switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams:
return "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + course + "\"";
return "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + courseName + "\"";
case R.id.upload_fields_builder_radio_button_exam_solutions:
return "Λύσεις των θεμάτων των εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + course + "\"";
return "Λύσεις των θεμάτων των εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + courseName + "\"";
case R.id.upload_fields_builder_radio_button_notes:
return "Σημειώσεις των παραδόσεων του μαθήματος \"" + course + "\" από το " + year.getText().toString();
return "Σημειώσεις των παραδόσεων του μαθήματος \"" + courseName + "\" από το " + year.getText().toString();
default:
return null;
}
@ -201,316 +204,4 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
return null;
}
}
@Nullable
private String getGreeklishCourseName() {
return getGreeklishOrMinifiedCourseName(true);
}
@Nullable
private String getMinifiedCourseName() {
return getGreeklishOrMinifiedCourseName(false);
}
@Nullable
private String getGreeklishOrMinifiedCourseName(boolean greeklish) {
if (course.contains("Ψηφιακή Επεξεργασία Σήματος")) {
return greeklish ? "PSES" : "ΨΕΣ";
} else if (course.contains("Ψηφιακή Επεξεργασία Εικόνας")) {
return greeklish ? "psee" : "ΨΕΕ";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες ΙΙ")) {
return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες Ι")) {
return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1";
} else if (course.contains("Ψηφιακά Φίλτρα")) {
return greeklish ? "filtra" : "Φίλτρα";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙΙ")) {
return greeklish ? "pshfiaka_III" : "Ψηφιακά 3";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙ")) {
return greeklish ? "pshfiaka_II" : "Ψηφιακά 2";
} else if (course.contains("Ψηφιακά Συστήματα Ι")) {
return greeklish ? "pshfiaka_I" : "Ψηφιακά 1";
} else if (course.contains("Φωτονική Τεχνολογία")) {
return greeklish ? "fwtonikh" : "Φωτονική";
} else if (course.contains("Φυσική Ι")) {
return greeklish ? "fysikh_I" : "Φυσική 1";
} else if (course.contains("Υψηλές Τάσεις ΙΙΙ")) {
return greeklish ? "ypshles_III" : "Υψηλές 3";
} else if (course.contains("Υψηλές Τάσεις ΙΙ")) {
return greeklish ? "ypshles_II" : "Υψηλές 2";
} else if (course.contains("Υψηλές Τάσεις Ι")) {
return greeklish ? "ypshles_I" : "Υψηλές 1";
} else if (course.contains("Υψηλές Τάσεις 4")) {
return greeklish ? "ypshles_IV" : "Υψηλές 4";
} else if (course.contains("Υπολογιστικός Ηλεκτρομαγνητισμός")) {
return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ";
} else if (course.contains("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα")) {
return greeklish ? "ymes" : "ΥΜΕΣ";
} else if (course.contains("Τηλεπικοινωνιακή Ηλεκτρονική")) {
return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ.";
} else if (course.contains("Τηλεοπτικά Συστήματα")) {
return greeklish ? "tileoptika" : "Τηλεοπτικά";
} else if (course.contains("Τεχνολογία Λογισμικού")) {
return greeklish ? "SE" : "Τεχνολογία Λογισμικού";
} else if (course.contains("Τεχνολογία Ηλεκτροτεχνικών Υλικών")) {
return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά";
} else if (course.contains("Τεχνολογία Ήχου και Εικόνας")) {
return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας";
} else if (course.contains("Τεχνική Μηχανική")) {
return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν.";
} else if (course.contains("Τεχνικές μη Καταστρεπτικών Δοκιμών")) {
return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές";
} else if (course.contains("Τεχνικές Σχεδίασης με Η/Υ")) {
return greeklish ? "sxedio" : "Σχέδιο";
} else if (course.contains("Τεχνικές Κωδικοποίησης")) {
return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης";
} else if (course.contains("Τεχνικές Βελτιστοποίησης")) {
return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση";
} else if (course.contains("Σύνθεση Τηλεπικοινωνιακών Διατάξεων")) {
return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων";
} else if (course.contains("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων")) {
return greeklish ? "synthesh" : "Σύνθεση";
} else if (course.contains("Σχεδίαση Συστημάτων VLSI")) {
return greeklish ? "VLSI" : "VLSI";
} else if (course.contains("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)")) {
return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών";
} else if (course.contains("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα")) {
return greeklish ? "polymesa" : "Πολυμέσα";
} else if (course.contains("Συστήματα Μικροϋπολογιστών")) {
return greeklish ? "mikro_I" : "Μίκρο 1";
} else if (course.contains("Συστήματα Ηλεκτροκίνησης")) {
return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙΙ")) {
return greeklish ? "SHE_III" : "ΣΗΕ 3";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙ")) {
return greeklish ? "SHE_II" : "ΣΗΕ 2";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας Ι")) {
return greeklish ? "SHE_I" : "ΣΗΕ 1";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙI")) {
return greeklish ? "SAE_III" : "ΣΑΕ 3";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙ")) {
return greeklish ? "SAE_II" : "ΣΑΕ 2";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου Ι")) {
return greeklish ? "SAE_1" : "ΣΑΕ 1";
} else if (course.contains("Στοχαστικό Σήμα")) {
return greeklish ? "stox_shma" : "Στοχ. Σήμα";
} else if (course.contains("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας")) {
return greeklish ? "SPHE" : "ΣΠΗΕ";
} else if (course.contains("Σερβοκινητήρια Συστήματα")) {
return greeklish ? "servo" : "Σέρβο";
} else if (course.contains("Σήματα και Συστήματα")) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Ρομποτική")) {
return greeklish ? "rompotikh" : "Ρομποτική";
} else if (course.contains("Προσομοίωση και Μοντελοποίηση Συστημάτων")) {
return greeklish ? "montelopoihsh" : "Μοντελοποίηση";
} else if (course.contains("Προηγμένες Τεχνικές Επεξεργασίας Σήματος")) {
return greeklish ? "ptes" : "ΠΤΕΣ";
} else if (course.contains("Προγραμματιστικές Τεχνικές")) {
return greeklish ? "cpp" : "Προγραμματ. Τεχν.";
} else if (course.contains("Προγραμματιζόμενα Κυκλώματα ASIC")) {
return greeklish ? "asic" : "ASIC";
} else if (course.contains("Παράλληλα και Κατανεμημένα Συστήματα")) {
return greeklish ? "parallhla" : "Παράλληλα";
} else if (course.contains("Οργάνωση και Διοίκηση Εργοστασίων")) {
return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων";
} else if (course.contains("Οργάνωση Υπολογιστών")) {
return greeklish ? "org_ypol" : "Οργάνωση Υπολ.";
} else if (course.contains("Οπτική ΙΙ")) {
return greeklish ? "optikh_II" : "Οπτική 2";
} else if (course.contains("Οπτική Ι")) {
return greeklish ? "optikh_I" : "Οπτική 1";
} else if (course.contains("Οπτικές Επικοινωνίες")) {
return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ.";
} else if (course.contains("Μικροκύματα II")) {
return greeklish ? "mikrokymata_II" : "Μικροκύματα 2";
} else if (course.contains("Μικροκύματα I")) {
return greeklish ? "mikrokymata_I" : "Μικροκύματα 1";
} else if (course.contains("Μικροκυματική Τηλεπισκόπηση")) {
return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση";
} else if (course.contains("Μικροεπεξεργαστές και Περιφερειακά")) {
return greeklish ? "mikro_II" : "Μίκρο 2";
} else if (course.contains("Μετάδοση Θερμότητας")) {
return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ.";
} else if (course.contains("Λογισμός ΙΙ")) {
return greeklish ? "logismos_II" : "Λογισμός 2";
} else if (course.contains("Λογισμός Ι")) {
return greeklish ? "logismos_I" : "Λογισμός 1";
} else if (course.contains("Λογική Σχεδίαση")) {
return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση";
} else if (course.contains("Λειτουργικά Συστήματα")) {
return greeklish ? "OS" : "Λειτουργικά";
} else if (course.contains("Κινητές και Δορυφορικές Επικοινωνίες")) {
return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες";
} else if (course.contains("Κβαντική Φυσική")) {
return greeklish ? "kvantikh" : "Κβαντική";
} else if (course.contains("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων")) {
return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων";
} else if (course.contains("Θεωρία Υπολογισμών και Αλγορίθμων")) {
return greeklish ? "thya" : "ΘΥΑ";
} else if (course.contains("Θεωρία Σκέδασης")) {
return greeklish ? "skedash" : "Σκέδαση";
} else if (course.contains("Θεωρία Σημάτων και Γραμμικών Συστημάτων")) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Θεωρία Πληροφοριών")) {
return greeklish ? "theoria_plir" : "Θεωρία Πληρ.";
} else if (course.contains("Θεωρία Πιθανοτήτων και Στατιστική")) {
return greeklish ? "pithanothtes" : "Πιθανότητες";
} else if (course.contains("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις")) {
return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά";
} else if (course.contains("Ηλεκτρονική ΙΙΙ")) {
return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3";
} else if (course.contains("Ηλεκτρονική ΙΙ")) {
return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2";
} else if (course.contains("Ηλεκτρονική Ι")) {
return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1";
} else if (course.contains("Ηλεκτρονικές Διατάξεις και Μετρήσεις")) {
return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις";
} else if (course.contains("Ηλεκτρονικά Ισχύος ΙΙ")) {
return greeklish ? "isxyos_II" : "Ισχύος 2";
} else if (course.contains("Ηλεκτρονικά Ισχύος Ι")) {
return greeklish ? "isxyos_I" : "Ισχύος 1";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο ΙΙ")) {
return greeklish ? "pedio_II" : "Πεδίο 2";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο Ι")) {
return greeklish ? "pedio_I" : "Πεδίο 1";
} else if (course.contains("Ηλεκτρομαγνητική Συμβατότητα")) {
return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα";
} else if (course.contains("Ηλεκτρολογικά Υλικά")) {
return greeklish ? "ylika" : "Ηλεκτρ. Υλικά";
} else if (course.contains("Ηλεκτρική Οικονομία")) {
return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία";
} else if (course.contains("Ηλεκτρικές Μηχανές Γ'")) {
return greeklish ? "mhxanes_C" : "Μηχανές Γ";
} else if (course.contains("Ηλεκτρικές Μηχανές Β'")) {
return greeklish ? "mhxanes_B" : "Μηχανές Β";
} else if (course.contains("Ηλεκτρικές Μηχανές Α'")) {
return greeklish ? "mhxanes_A" : "Μηχανές Α";
} else if (course.contains("Ηλεκτρικές Μετρήσεις ΙΙ")) {
return greeklish ? "metrhseis_II" : "Μετρήσεις 2";
} else if (course.contains("Ηλεκτρικές Μετρήσεις Ι")) {
return greeklish ? "metrhseis_1" : "Μετρήσεις 1";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙΙ")) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 3";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙ")) {
return greeklish ? "kyklwmata_II" : "Κυκλώματα 2";
} else if (course.contains("Ηλεκτρικά Κυκλώματα Ι")) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 1";
} else if (course.contains("Ηλεκτρακουστική ΙΙ")) {
return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2";
} else if (course.contains("Ηλεκτρακουστική Ι")) {
return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1";
} else if (course.contains("Εφαρμοσμένη Θερμοδυναμική")) {
return greeklish ? "thermodynamikh" : "Θερμοδυναμική";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά ΙΙ")) {
return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά Ι")) {
return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1";
} else if (course.contains("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων")) {
return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων";
} else if (course.contains("Ευφυή Συστήματα Ρομπότ")) {
return greeklish ? "eufuh" : "Ευφυή";
} else if (course.contains("Ευρυζωνικά Δίκτυα")) {
return greeklish ? "eyryzwnika" : "Ευρυζωνικά";
} else if (course.contains("Επιχειρησιακή Έρευνα")) {
return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα";
} else if (course.contains("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου")) {
return greeklish ? "enswmatwmena" : "Ενσωματωμένα";
} else if (course.contains("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας")) {
return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν.";
} else if (course.contains("Εισαγωγή στην Πολιτική Οικονομία")) {
return greeklish ? "polit_oik" : "Πολιτική Οικονομία";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία ΙΙ")) {
return greeklish ? "EET_2" : "ΕΕΤ2";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία Ι")) {
return greeklish ? "EET_I" : "ΕΕΤ 1";
} else if (course.contains("Ειδικές Κεραίες, Σύνθεση Κεραιών")) {
return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών";
} else if (course.contains("Ειδικές Αρχιτεκτονικές Υπολογιστών")) {
return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών";
} else if (course.contains("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "ekshe" : "ΕΚΣΗΕ";
} else if (course.contains("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι")) {
return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι";
} else if (course.contains("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων")) {
return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων";
} else if (course.contains("Δομημένος Προγραμματισμός")) {
return greeklish ? "C" : "Δομ. Προγραμμ.";
} else if (course.contains("Δομές Δεδομένων")) {
return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ.";
} else if (course.contains("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "dshe" : "ΔΣΗΕ";
} else if (course.contains("Διαφορικές Εξισώσεις")) {
return greeklish ? "diaforikes" : "Διαφορικές";
} else if (course.contains("Διανεμημένη Παραγωγή")) {
return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή";
} else if (course.contains("Διακριτά μαθηματικά")) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Διακριτά Μαθηματικά")) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Διάδοση Ηλεκτρομαγνητικού Κύματος Ι (πρώην Πεδίο ΙΙΙ)")) {
return greeklish ? "diadosi_1" : "Διάδοση 1";
} else if (course.contains("Διάδοση Η/Μ Κύματος ΙΙ")) {
return greeklish ? "diadosi_II" : "Διάδοση 2";
} else if (course.contains("Δίκτυα Υπολογιστών ΙΙ")) {
return greeklish ? "diktya_II" : "Δίκτυα 2";
} else if (course.contains("Δίκτυα Υπολογιστών Ι")) {
return greeklish ? "diktya_I" : "Δίκτυα 1";
} else if (course.contains("Δίκτυα Τηλεπικοινωνιών")) {
return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ.";
} else if (course.contains("Γραφική με Υπολογιστές")) {
return greeklish ? "grafikh" : "Γραφική";
} else if (course.contains("Γραμμική Άλγεβρα")) {
return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ.";
} else if (course.contains("Γεωηλεκτρομαγνητισμός")) {
return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός";
} else if (course.contains("Βιοϊατρική Τεχνολογία")) {
return greeklish ? "vioiatriki" : "Βιοιατρική";
} else if (course.contains("Βιομηχανική Πληροφορική")) {
return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ";
} else if (course.contains("Βιομηχανικά Ηλεκτρονικά")) {
return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά";
} else if (course.contains("Βάσεις Δεδομένων")) {
return greeklish ? "vaseis" : "Βάσεις";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία ΙΙ")) {
return greeklish ? "asyrmatos_II" : "Ασύρματος 2";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία Ι")) {
return greeklish ? "asyrmatos_I" : "Ασύρματος 1";
} else if (course.contains("Ασφάλεια Πληροφοριακών Συστημάτων")) {
return greeklish ? "asfaleia" : "Ασφάλεια";
} else if (course.contains("Ασαφή Συστήματα")) {
return greeklish ? "asafh" : "Ασαφή";
} else if (course.contains("Αρχιτεκτονική Υπολογιστών")) {
return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών";
} else if (course.contains("Αρχές Παράλληλης Επεξεργασίας")) {
return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας";
} else if (course.contains("Αρχές Οικονομίας")) {
return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας";
} else if (course.contains("Αριθμητική Ανάλυση")) {
return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση";
} else if (course.contains("Αξιοπιστία Συστημάτων")) {
return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων";
} else if (course.contains("Αντικειμενοστραφής Προγραμματισμός")) {
return greeklish ? "OOP" : "Αντικειμενοστραφής";
} else if (course.contains("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα Ι)")) {
return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ.";
} else if (course.contains("Αναγνώριση Προτύπων")) {
return greeklish ? "protipa" : "Αναγνώριση Προτύπων";
} else if (course.contains("Ανάλυση και Σχεδίαση Αλγορίθμων")) {
return greeklish ? "algorithms" : "Αλγόριθμοι";
} else if (course.contains("Ανάλυση Χρονοσειρών")) {
return greeklish ? "xronoseires" : "Χρονοσειρές";
} else if (course.contains("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "ASHE" : "ΑΣΗΕ";
} else if (course.contains("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή")) {
return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή";
} else if (course.contains("Ακουστική ΙΙ")) {
return greeklish ? "akoystikh_II" : "Ακουστική 2";
} else if (course.contains("Ακουστική Ι")) {
return greeklish ? "akoystikh_I" : "Ακουστική 1";
} else {
return null;
}
}
}

79
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java

@ -0,0 +1,79 @@
package gr.thmmy.mthmmy.activities.upload;
import android.os.Bundle;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gr.thmmy.mthmmy.base.BaseApplication;
import timber.log.Timber;
class UploadsCourse {
private String name;
private String minifiedName;
private String greeklishName;
private UploadsCourse(String fullName, String minifiedName, String greeklishName) {
this.name = fullName;
this.minifiedName = minifiedName;
this.greeklishName = greeklishName;
}
String getName() {
return name;
}
String getMinifiedName() {
return minifiedName;
}
String getGreeklishName() {
return greeklishName;
}
static Map<String, UploadsCourse> generateUploadsCourses(String[] uploadsCoursesRes){
Map<String, UploadsCourse> uploadsCourses = new HashMap<>();
for(String uploadsCourseStr:uploadsCoursesRes) {
String[] split = uploadsCourseStr.split(":");
UploadsCourse uploadsCourse = new UploadsCourse(split[0], split[1], split[2]);
uploadsCourses.put(uploadsCourse.getName(),uploadsCourse);
}
return uploadsCourses;
}
static UploadsCourse findCourse(String retrievedCourse,
Map<String, UploadsCourse> uploadsCourses){
retrievedCourse = normalizeGreekNumbers(retrievedCourse);
UploadsCourse uploadsCourse = uploadsCourses.get(retrievedCourse);
if(uploadsCourse != null) return uploadsCourse;
String foundKey = null;
for (Map.Entry<String, UploadsCourse> entry : uploadsCourses.entrySet()) {
String key = entry.getKey();
if ((key.contains(retrievedCourse) || retrievedCourse.contains(key))
&& (foundKey==null || key.length()>foundKey.length()))
foundKey = key;
}
if(foundKey==null){
Timber.w("Couldn't find course that matches %s", retrievedCourse);
Bundle bundle = new Bundle();
bundle.putString("course_name", retrievedCourse);
BaseApplication.getInstance().logFirebaseAnalyticsEvent("unsupported_uploads_course", bundle);
return null;
}
return uploadsCourses.get(foundKey);
}
private static String normalizeGreekNumbers(String stringWithGreekNumbers) {
StringBuilder normalizedStrBuilder = new StringBuilder(stringWithGreekNumbers);
Pattern pattern = Pattern.compile("(Ι+)(?:\\s|\\(|\\)|$)");
Matcher matcher = pattern.matcher(stringWithGreekNumbers);
while (matcher.find())
normalizedStrBuilder.replace(matcher.start(1), matcher.end(1), matcher.group(1).replaceAll("Ι", "I"));
return normalizedStrBuilder.toString();
}
}

191
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java

@ -1,71 +1,48 @@
package gr.thmmy.mthmmy.activities.upload;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
import android.provider.OpenableColumns;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import com.snatik.storage.Storage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import gr.thmmy.mthmmy.utils.FileUtils;
import timber.log.Timber;
class UploadsHelper {
private static final int DEFAULT_MIN_WIDTH_QUALITY = 400;
private static final String CACHE_IMAGE_NAME = "tempUploadFile.jpg";
@NonNull
static String filenameFromUri(Context context, Uri uri) {
String filename = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
}
}
if (filename == null) {
filename = uri.getPath();
int cut = filename.lastIndexOf('/');
if (cut != -1) {
filename = filename.substring(cut + 1);
}
}
return filename;
}
public class UploadsHelper {
private static final int BUFFER = 4096;
private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads";
@SuppressWarnings("ResultOfMethodCallIgnored")
@Nullable
static String createTempFile(Context context, Uri fileUri, String newFilename) {
String oldFilename = filenameFromUri(context, fileUri);
String fileExtension = oldFilename.substring(oldFilename.indexOf("."));
static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) {
String oldFilename = FileUtils.filenameFromUri(context, fileUri);
String fileExtension = oldFilename.substring(oldFilename.indexOf('.'));
String destinationFilename = Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads" + File.separatorChar + newFilename + fileExtension;
File.separatorChar + TEMP_FILES_DIRECTORY + File.separatorChar + newFilename + fileExtension;
File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads");
File.separatorChar + TEMP_FILES_DIRECTORY);
if (!tempDirectory.exists()) {
if (!tempDirectory.mkdirs()) {
Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName());
Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show();
return null;
}
if (!tempDirectory.exists() && !tempDirectory.mkdirs()) {
Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName());
Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show();
return null;
}
InputStream inputStream;
@ -97,103 +74,63 @@ class UploadsHelper {
}
}
return destinationFilename;
}
return FileProvider.getUriForFile(context, context.getPackageName() +
".provider", storage.getFile(destinationFilename));
static File getCacheFile(Context context) {
File imageFile = new File(context.getExternalCacheDir(), CACHE_IMAGE_NAME);
//noinspection ResultOfMethodCallIgnored
imageFile.getParentFile().mkdirs();
return imageFile;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
static void deleteTempFiles() {
File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads");
if (tempFilesDirectory.isDirectory()) {
String[] tempFilesArray = tempFilesDirectory.list();
for (String tempFile : tempFilesArray) {
new File(tempFilesDirectory, tempFile).delete();
}
tempFilesDirectory.delete();
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
static void deleteCacheFiles(Context context) {
File cacheFilesDirectory = context.getExternalCacheDir();
assert cacheFilesDirectory != null;
String[] tempFilesArray = cacheFilesDirectory.list();
for (String tempFile : tempFilesArray) {
new File(cacheFilesDirectory, tempFile).delete();
}
}
/**
* Resize to avoid using too much memory loading big images (e.g.: 2560*1920)
**/
static Bitmap getImageResized(Context context, Uri selectedImage) {
Bitmap bm;
int[] sampleSizes = new int[]{5, 3, 2, 1};
int i = 0;
do {
bm = decodeBitmap(context, selectedImage, sampleSizes[i]);
i++;
} while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length);
return bm;
}
private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
@Nullable
static File createZipFile(@NonNull String zipFilename) {
// Create a zip file name
File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) +
File.separator + "mTHMMY");
AssetFileDescriptor fileDescriptor = null;
try {
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
if (!zipFolder.exists() && !zipFolder.mkdirs()) {
Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName());
return null;
}
assert fileDescriptor != null;
return BitmapFactory.decodeFileDescriptor(
fileDescriptor.getFileDescriptor(), null, options);
return new File(zipFolder, zipFilename);
}
static int getRotation(Context context, Uri imageUri) {
int rotation = 0;
static void zip(Context context, Uri[] files, Uri zipFile) {
try {
context.getContentResolver().notifyChange(imageUri, null);
ExifInterface exif = new ExifInterface(imageUri.getPath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotation = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotation = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90;
break;
BufferedInputStream origin;
OutputStream dest = context.getContentResolver().openOutputStream(zipFile);
assert dest != null;
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
byte[] data = new byte[BUFFER];
for (Uri file : files) {
InputStream inputStream = context.getContentResolver().openInputStream(file);
assert inputStream != null;
origin = new BufferedInputStream(inputStream, BUFFER);
ZipEntry entry = new ZipEntry(FileUtils.filenameFromUri(context, file));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return rotation;
}
static Bitmap rotate(Bitmap bm, int rotation) {
if (rotation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
public static void deleteTempFiles(Storage storage) {
File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + TEMP_FILES_DIRECTORY);
if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) {
for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) {
storage.deleteFile(tempFile.getAbsolutePath());
}
storage.deleteDirectory(tempFilesDirectory.getAbsolutePath());
}
return bm;
}
}

8
app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java

@ -0,0 +1,8 @@
package gr.thmmy.mthmmy.activities.upload.multipart;
public class MultipartUploadException extends RuntimeException {
public MultipartUploadException(String message) {
super(message);
}
}

99
app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java

@ -0,0 +1,99 @@
package gr.thmmy.mthmmy.activities.upload.multipart;
import android.content.Context;
import android.content.Intent;
import net.gotev.uploadservice.HttpUploadRequest;
import net.gotev.uploadservice.Logger;
import net.gotev.uploadservice.UploadFile;
import net.gotev.uploadservice.UploadTask;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
/**
From MultipartUploadRequest gotev/android-upload-service in order to use the local custom
MultipartUploadTask.
*/
public class MultipartUploadRequest extends HttpUploadRequest<MultipartUploadRequest> {
private static final String LOG_TAG = MultipartUploadRequest.class.getSimpleName();
private boolean isUtf8Charset = false;
public MultipartUploadRequest(final Context context, final String uploadId, final String serverUrl)
throws IllegalArgumentException, MalformedURLException {
super(context, uploadId, serverUrl);
}
public MultipartUploadRequest(final Context context, final String serverUrl)
throws MalformedURLException, IllegalArgumentException {
this(context, null, serverUrl);
}
@Override
protected void initializeIntent(Intent intent) {
super.initializeIntent(intent);
intent.putExtra(MultipartUploadTask.PARAM_UTF8_CHARSET, isUtf8Charset);
}
@Override
protected Class<? extends UploadTask> getTaskClass() {
return MultipartUploadTask.class;
}
public MultipartUploadRequest addFileToUpload(String filePath,
String parameterName,
String fileName, String contentType)
throws FileNotFoundException, IllegalArgumentException {
UploadFile file = new UploadFile(filePath);
filePath = file.getPath();
if (parameterName == null || "".equals(parameterName)) {
throw new IllegalArgumentException("Please specify parameterName value for file: "
+ filePath);
}
file.setProperty(MultipartUploadTask.PROPERTY_PARAM_NAME, parameterName);
if (contentType == null || contentType.isEmpty()) {
contentType = file.getContentType(context);
Logger.debug(LOG_TAG, "Auto-detected MIME type for " + filePath
+ " is: " + contentType);
} else {
Logger.debug(LOG_TAG, "Content Type set for " + filePath
+ " is: " + contentType);
}
file.setProperty(MultipartUploadTask.PROPERTY_CONTENT_TYPE, contentType);
if (fileName == null || "".equals(fileName)) {
fileName = file.getName(context);
Logger.debug(LOG_TAG, "Using original file name: " + fileName);
} else {
Logger.debug(LOG_TAG, "Using custom file name: " + fileName);
}
file.setProperty(MultipartUploadTask.PROPERTY_REMOTE_FILE_NAME, fileName);
params.files.add(file);
return this;
}
public MultipartUploadRequest addFileToUpload(final String path, final String parameterName,
final String fileName)
throws FileNotFoundException, IllegalArgumentException {
return addFileToUpload(path, parameterName, fileName, null);
}
public MultipartUploadRequest addFileToUpload(final String path, final String parameterName)
throws FileNotFoundException, IllegalArgumentException {
return addFileToUpload(path, parameterName, null, null);
}
public MultipartUploadRequest setUtf8Charset() {
isUtf8Charset = true;
return this;
}
}

160
app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java

@ -0,0 +1,160 @@
package gr.thmmy.mthmmy.activities.upload.multipart;
import android.content.Intent;
import net.gotev.uploadservice.HttpUploadTask;
import net.gotev.uploadservice.NameValue;
import net.gotev.uploadservice.UploadFile;
import net.gotev.uploadservice.UploadService;
import net.gotev.uploadservice.http.BodyWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
/**
Extended MultipartUploadTask from gotev/android-upload-service to include a fix for the parameter
tp_dluploadpic. Also changed Connection to keep-alive.
*/
public class MultipartUploadTask extends HttpUploadTask {
static final String PARAM_UTF8_CHARSET = "multipartUtf8Charset";
private static final String BOUNDARY_SIGNATURE = "-------AndroidUploadService";
private static final Charset US_ASCII = Charset.forName("US-ASCII");
private static final String NEW_LINE = "\r\n";
private static final String TWO_HYPHENS = "--";
// properties associated to each file
static final String PROPERTY_REMOTE_FILE_NAME = "httpRemoteFileName";
static final String PROPERTY_CONTENT_TYPE = "httpContentType";
static final String PROPERTY_PARAM_NAME = "httpParamName";
private byte[] boundaryBytes;
private byte[] trailerBytes;
private Charset charset;
@Override
protected void init(UploadService service, Intent intent) throws IOException {
super.init(service, intent);
String boundary = BOUNDARY_SIGNATURE + System.nanoTime();
boundaryBytes = (TWO_HYPHENS + boundary + NEW_LINE).getBytes(US_ASCII);
trailerBytes = (TWO_HYPHENS + boundary + TWO_HYPHENS + NEW_LINE).getBytes(US_ASCII);
charset = intent.getBooleanExtra(PARAM_UTF8_CHARSET, false) ?
Charset.forName("UTF-8") : US_ASCII;
httpParams.addHeader("Connection", "Keep-Alive");
httpParams.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
}
@Override
protected long getBodyLength() throws UnsupportedEncodingException {
return (getRequestParametersLength() + getFilesLength() + trailerBytes.length);
}
@Override
public void onBodyReady(BodyWriter bodyWriter) throws IOException {
//reset uploaded bytes when the body is ready to be written
//because sometimes this gets invoked when network changes
uploadedBytes = 0;
writeRequestParameters(bodyWriter);
writeFiles(bodyWriter);
bodyWriter.write(trailerBytes);
uploadedBytes += trailerBytes.length;
broadcastProgress(uploadedBytes, totalBytes);
}
private long getFilesLength() throws UnsupportedEncodingException {
long total = 0;
for (UploadFile file : params.files) {
total += getTotalMultipartBytes(file);
}
return total;
}
private long getRequestParametersLength() throws UnsupportedEncodingException {
long parametersBytes = 0;
if (!httpParams.getRequestParameters().isEmpty()) {
for (final NameValue parameter : httpParams.getRequestParameters()) {
// the bytes needed for every parameter are the sum of the boundary bytes
// and the bytes occupied by the parameter
parametersBytes += boundaryBytes.length + getMultipartBytes(parameter).length;
}
}
return parametersBytes;
}
private byte[] getMultipartBytes(NameValue parameter) throws UnsupportedEncodingException {
if(parameter.getName().equals("tp_dluploadpic")){
String header = "Content-Disposition: form-data; name=\"" +
parameter.getName() + "\"; filename=\"\"" + NEW_LINE +
"Content-Type: application/octet-stream" + NEW_LINE + NEW_LINE;
return header.getBytes(charset);
}
else
return ("Content-Disposition: form-data; name=\"" + parameter.getName() + "\""
+ NEW_LINE + NEW_LINE + parameter.getValue() + NEW_LINE).getBytes(charset);
}
private byte[] getMultipartHeader(UploadFile file)
throws UnsupportedEncodingException {
String header = "Content-Disposition: form-data; name=\"" +
file.getProperty(PROPERTY_PARAM_NAME) + "\"; filename=\"" +
file.getProperty(PROPERTY_REMOTE_FILE_NAME) + "\"" + NEW_LINE +
"Content-Type: " + file.getProperty(PROPERTY_CONTENT_TYPE) +
NEW_LINE + NEW_LINE;
return header.getBytes(charset);
}
private long getTotalMultipartBytes(UploadFile file)
throws UnsupportedEncodingException {
return boundaryBytes.length + getMultipartHeader(file).length + file.length(service)
+ NEW_LINE.getBytes(charset).length;
}
private void writeRequestParameters(BodyWriter bodyWriter) throws IOException {
if (!httpParams.getRequestParameters().isEmpty()) {
for (final NameValue parameter : httpParams.getRequestParameters()) {
bodyWriter.write(boundaryBytes);
byte[] formItemBytes = getMultipartBytes(parameter);
bodyWriter.write(formItemBytes);
uploadedBytes += boundaryBytes.length + formItemBytes.length;
broadcastProgress(uploadedBytes, totalBytes);
}
}
}
private void writeFiles(BodyWriter bodyWriter) throws IOException {
for (UploadFile file : params.files) {
if (!shouldContinue)
break;
bodyWriter.write(boundaryBytes);
byte[] headerBytes = getMultipartHeader(file);
bodyWriter.write(headerBytes);
uploadedBytes += boundaryBytes.length + headerBytes.length;
broadcastProgress(uploadedBytes, totalBytes);
bodyWriter.writeStream(file.getStream(service), this);
byte[] newLineBytes = NEW_LINE.getBytes(charset);
bodyWriter.write(newLineBytes);
uploadedBytes += newLineBytes.length;
}
}
@Override
protected void onSuccessfulUpload() {
addAllFilesToSuccessfullyUploadedFiles();
}
}

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

@ -2,21 +2,34 @@ package gr.thmmy.mthmmy.base;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.preference.PreferenceManager;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.firebase.messaging.FirebaseMessaging;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
@ -28,6 +41,9 @@ import com.mikepenz.materialdrawer.Drawer;
import com.mikepenz.materialdrawer.DrawerBuilder;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
import com.snatik.storage.Storage;
import net.gotev.uploadservice.UploadService;
import java.io.BufferedReader;
import java.io.File;
@ -35,14 +51,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.AboutActivity;
import gr.thmmy.mthmmy.activities.LoginActivity;
@ -53,12 +61,15 @@ import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxActivity;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper;
import gr.thmmy.mthmmy.services.UploadsReceiver;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.viewmodel.BaseViewModel;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient;
import ru.noties.markwon.LinkResolverDef;
import ru.noties.markwon.Markwon;
@ -73,6 +84,7 @@ import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_
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.services.DownloadHelper.SAVE_DIR;
import static gr.thmmy.mthmmy.services.UploadsReceiver.UPLOAD_ID_KEY;
import static gr.thmmy.mthmmy.session.SessionManager.SUCCESS;
import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType;
@ -83,6 +95,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//SessionManager
protected static SessionManager sessionManager;
//Storage manager
protected Storage storage;
//Bookmarks
public static final String BOOKMARKS_SHARED_PREFS = "bookmarksSharedPrefs";
public static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey";
@ -97,6 +112,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//Common UI elements
protected Toolbar toolbar;
protected Drawer drawer;
//Uploads progress dialog
UploadsShowDialogReceiver uploadsShowDialogReceiver;
AlertDialog uploadsProgressDialog;
private MainActivity mainActivity;
private boolean isMainActivity;
@ -124,16 +142,25 @@ public abstract class BaseActivity extends AppCompatActivity {
BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton));
storage = new Storage(getApplicationContext());
}
@Override
protected void onResume() {
super.onResume();
updateDrawer();
if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown){
isUserConsentDialogShown=true;
if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown) {
isUserConsentDialogShown = true;
showUserConsentDialog();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (uploadsShowDialogReceiver == null) {
uploadsShowDialogReceiver = new UploadsShowDialogReceiver(this);
}
this.registerReceiver(uploadsShowDialogReceiver, new IntentFilter(UploadsReceiver.ACTION_COMBINED_UPLOAD));
}
}
@Override
@ -141,6 +168,10 @@ public abstract class BaseActivity extends AppCompatActivity {
super.onPause();
if (drawer != null) //close drawer animation after returning to activity
drawer.closeDrawer();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && uploadsShowDialogReceiver != null) {
this.unregisterReceiver(uploadsShowDialogReceiver);
}
}
@ -152,6 +183,10 @@ public abstract class BaseActivity extends AppCompatActivity {
return sessionManager;
}
public Storage getStorage() {
return storage;
}
//TODO: move stuff below (?)
//------------------------------------------DRAWER STUFF----------------------------------------
protected static final int HOME_ID = 0;
@ -257,14 +292,15 @@ public abstract class BaseActivity extends AppCompatActivity {
.withName(R.string.downloads)
.withIcon(downloadsIcon)
.withSelectedIcon(downloadsIconSelected);
// uploadItem = new PrimaryDrawerItem()
// .withTextColor(primaryColor)
// .withSelectedColor(selectedPrimaryColor)
// .withSelectedTextColor(selectedSecondaryColor)
// .withIdentifier(UPLOAD_ID)
// .withName(R.string.upload)
// .withIcon(uploadIcon)
// .withSelectedIcon(uploadIconSelected);
uploadItem = new PrimaryDrawerItem()
.withTextColor(primaryColor)
.withSelectedColor(selectedPrimaryColor)
.withSelectedTextColor(selectedSecondaryColor)
.withIdentifier(UPLOAD_ID)
.withName(R.string.upload)
.withIcon(uploadIcon)
.withSelectedIcon(uploadIconSelected);
shoutboxItem = new PrimaryDrawerItem()
.withTextColor(primaryColor)
@ -397,11 +433,11 @@ public abstract class BaseActivity extends AppCompatActivity {
intent.putExtras(extras);
startActivity(intent);
}
// } else if (drawerItem.equals(UPLOAD_ID)) {
// if (!(BaseActivity.this instanceof UploadActivity)) {
// Intent intent = new Intent(BaseActivity.this, UploadActivity.class);
// startActivity(intent);
// }
} else if (drawerItem.equals(UPLOAD_ID)) {
if (!(BaseActivity.this instanceof UploadActivity)) {
Intent intent = new Intent(BaseActivity.this, UploadActivity.class);
startActivity(intent);
}
} else if (drawerItem.equals(BOOKMARKS_ID)) {
if (!(BaseActivity.this instanceof BookmarksActivity)) {
Intent intent = new Intent(BaseActivity.this, BookmarksActivity.class);
@ -452,7 +488,7 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{
drawer.removeItem(DOWNLOADS_ID);
// drawer.removeItem(UPLOAD_ID);
drawer.removeItem(UPLOAD_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
profileDrawerItem.withName(sessionManager.getUsername());
setDefaultAvatar();
@ -460,9 +496,9 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!drawer.getDrawerItems().contains(downloadsItem)) {
drawer.addItemAtPosition(downloadsItem, 4);
}
// if (!drawer.getDrawerItems().contains(uploadItem)) {
// drawer.addItemAtPosition(uploadItem, 5);
// }
if (!drawer.getDrawerItems().contains(uploadItem)) {
drawer.addItemAtPosition(uploadItem, 5);
}
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername());
if (sessionManager.hasAvatar())
@ -687,10 +723,10 @@ public abstract class BaseActivity extends AppCompatActivity {
//-------------------------------------------BOOKMARKS END------------------------------------------
//-------PERMS---------
private static final int PERMISSIONS_REQUEST_CODE = 69;
private static final int DOWNLOAD_REQUEST_CODE = 69; //Arbitrary, application specific
//True if permissions are OK
private boolean checkPerms() {
protected boolean checkPerms() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
@ -703,13 +739,13 @@ public abstract class BaseActivity extends AppCompatActivity {
}
//Display popup for user to grant permission
private void requestPerms() { //Runtime permissions request for devices with API >= 23
protected void requestPerms(int code) { //Runtime permissions request for devices with API >= 23
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(PERMISSIONS_STORAGE, PERMISSIONS_REQUEST_CODE);
requestPermissions(PERMISSIONS_STORAGE, code);
}
}
@ -718,8 +754,9 @@ public abstract class BaseActivity extends AppCompatActivity {
public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions
, @NonNull int[] grantResults) {
switch (permsRequestCode) {
case PERMISSIONS_REQUEST_CODE:
downloadFile();
case DOWNLOAD_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
prepareDownload(tempThmmyFile);
break;
}
}
@ -733,7 +770,7 @@ public abstract class BaseActivity extends AppCompatActivity {
prepareDownload(thmmyFile);
else {
tempThmmyFile = thmmyFile;
requestPerms();
requestPerms(DOWNLOAD_REQUEST_CODE);
}
}
@ -855,6 +892,93 @@ public abstract class BaseActivity extends AppCompatActivity {
editor.putBoolean(getString(R.string.pref_privacy_analytics_enable_key), enabled).apply();
}
//------------------------------------------ UPLOADS -------------------------------------------
private class UploadsShowDialogReceiver extends BroadcastReceiver {
private final Context activityContext;
UploadsShowDialogReceiver(Context activityContext) {
this.activityContext = activityContext;
}
@Override
public void onReceive(Context context, Intent intent) {
Bundle intentBundle = intent.getExtras();
if (intentBundle == null) {
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
String dialogUploadID = intentBundle.getString(UPLOAD_ID_KEY);
/*String retryFilename = intentBundle.getString(UPLOAD_RETRY_FILENAME);
String retryCategory = intentBundle.getString(UPLOAD_RETRY_CATEGORY);
String retryTitleText = intentBundle.getString(UPLOAD_RETRY_TITLE);
String retryDescription = intentBundle.getString(UPLOAD_RETRY_DESCRIPTION);
String retryIcon = intentBundle.getString(UPLOAD_RETRY_ICON);
String retryUploaderProfile = intentBundle.getString(UPLOAD_RETRY_UPLOADER);
Uri retryFileUri = (Uri) intentBundle.get(UPLOAD_RETRY_FILE_URI);
Intent retryIntent = new Intent(context, UploadsReceiver.class);
retryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
retryIntent.setAction(UploadsReceiver.ACTION_RETRY_UPLOAD);
retryIntent.putExtra(UPLOAD_RETRY_FILENAME, retryFilename);
retryIntent.putExtra(UPLOAD_RETRY_CATEGORY, retryCategory);
retryIntent.putExtra(UPLOAD_RETRY_TITLE, retryTitleText);
retryIntent.putExtra(UPLOAD_RETRY_DESCRIPTION, retryDescription);
retryIntent.putExtra(UPLOAD_RETRY_ICON, retryIcon);
retryIntent.putExtra(UPLOAD_RETRY_UPLOADER, retryUploaderProfile);
retryIntent.putExtra(UPLOAD_RETRY_FILE_URI, retryFileUri);*/
if (uploadsProgressDialog == null) {
AlertDialog.Builder progressDialogBuilder = new AlertDialog.Builder(activityContext);
LayoutInflater inflater = LayoutInflater.from(activityContext);
LinearLayout progressDialogLayout = (LinearLayout) inflater.inflate(R.layout.dialog_upload_progress, null);
MaterialProgressBar dialogProgressBar = progressDialogLayout.findViewById(R.id.dialogProgressBar);
dialogProgressBar.setMax(100);
progressDialogBuilder.setView(progressDialogLayout);
uploadsProgressDialog = progressDialogBuilder.create();
if (!UploadService.getTaskList().contains(dialogUploadID)) {
//Upload probably failed at this point
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "", (progressDialog, progressWhich) -> {
/*LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context.getApplicationContext());
localBroadcastManager.sendBroadcast(multipartUploadRetryIntent);*/
//uploadsProgressDialog.dismiss();
//context.sendBroadcast(retryIntent);
});
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), (progressDialog, progressWhich) -> {
uploadsProgressDialog.dismiss();
});
TextView dialogProgressText = progressDialogLayout.findViewById(R.id.dialog_upload_progress_text);
dialogProgressBar.setVisibility(View.GONE);
dialogProgressText.setText(getString(R.string.upload_failed));
uploadsProgressDialog.show();
} else {
//Empty buttons are needed, they are updated with correct values in the receiver
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "placeholder", (progressDialog, progressWhich) -> {
});
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "placeholder", (progressDialog, progressWhich) -> {
});
UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, null);
//UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, retryIntent);
uploadsProgressDialog.show();
}
} else {
UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, null);
//UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, retryIntent);
uploadsProgressDialog.show();
}
}
}
}
//----------------------------------MISC----------------------
protected void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;

15
app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java

@ -10,11 +10,15 @@ import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.google.firebase.FirebaseApp;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor;
import com.jakewharton.picasso.OkHttp3Downloader;
@ -33,8 +37,6 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.session.SessionManager;
@ -50,7 +52,8 @@ import timber.log.Timber;
public class BaseApplication extends Application {
private static BaseApplication baseApplication; //BaseApplication singleton
//Firebase Analytics
//Firebase
private static String firebaseProjectId;
private FirebaseAnalytics firebaseAnalytics;
//Client & SessionManager
@ -62,7 +65,6 @@ public class BaseApplication extends Application {
//Display Metrics
private static float dpWidth;
public static BaseApplication getInstance() {
return baseApplication;
}
@ -86,6 +88,7 @@ public class BaseApplication extends Application {
else
Timber.i("Starting app with Crashlytics disabled.");
firebaseProjectId = FirebaseApp.getInstance().getOptions().getProjectId();
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
boolean enableAnalytics = settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_analytics_enable_key), false);
firebaseAnalytics.setAnalyticsCollectionEnabled(enableAnalytics);
@ -215,4 +218,8 @@ public class BaseApplication extends Application {
} else
Timber.i("Crashlytics were already initialized for this app session.");
}
public static String getFirebaseProjectId(){
return firebaseProjectId;
}
}

1
app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java

@ -5,6 +5,7 @@ import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import okhttp3.OkHttpClient;
public abstract class BaseFragment extends Fragment {

11
app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java

@ -26,16 +26,17 @@ import android.widget.PopupWindow;
import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import java.util.Objects;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
import timber.log.Timber;

5
app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java

@ -9,11 +9,12 @@ import android.view.MotionEvent;
import android.view.inputmethod.InputConnection;
import android.widget.LinearLayout;
import java.util.HashSet;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.HashSet;
import gr.thmmy.mthmmy.R;
public class EmojiKeyboard extends LinearLayout implements IEmojiKeyboard {

1
app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java

@ -8,6 +8,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
public class EmojiKeyboardAdapter extends RecyclerView.Adapter<EmojiKeyboardAdapter.EmojiViewHolder> {

1
app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java

@ -7,6 +7,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
public class FormatButtonsAdapter extends RecyclerView.Adapter<FormatButtonsAdapter.FormatButtonViewHolder> {

6
app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java

@ -1,11 +1,11 @@
package gr.thmmy.mthmmy.model;
import java.util.ArrayList;
import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Objects;
public class Bookmark implements java.io.Serializable {
private final String title, id;
private boolean isNotificationsEnabled;

4
app/src/main/java/gr/thmmy/mthmmy/model/Post.java

@ -1,10 +1,10 @@
package gr.thmmy.mthmmy.model;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Objects;
import androidx.annotation.Nullable;
/**
* Class that defines a topic's post. All member variables are declared final (thus no setters are
* supplied). Class has two constructors and getter methods for all variables.

45
app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java

@ -0,0 +1,45 @@
package gr.thmmy.mthmmy.model;
import android.net.Uri;
import androidx.annotation.Nullable;
import java.io.File;
public class UploadFile {
private final boolean isCameraPhoto;
private Uri fileUri;
private File photoFile;
private UploadFile() {
isCameraPhoto = false;
fileUri = null;
photoFile = null;
}
public UploadFile(boolean isCameraPhoto, Uri fileUri, @Nullable File photoFile) {
this.isCameraPhoto = isCameraPhoto;
this.fileUri = fileUri;
this.photoFile = photoFile;
}
public boolean isCameraPhoto() {
return isCameraPhoto;
}
public Uri getFileUri() {
return fileUri;
}
public File getPhotoFile() {
return photoFile;
}
public void setFileUri(Uri fileUri) {
this.fileUri = fileUri;
}
public void setPhotoFile(File photoFile) {
this.photoFile = photoFile;
}
}

12
app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java

@ -13,6 +13,10 @@ import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.preference.PreferenceManager;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
@ -23,9 +27,6 @@ import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
@ -260,10 +261,9 @@ public class NotificationService extends FirebaseMessagingService {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since Android Oreo notification channel is needed.
if (buildVersion >= Build.VERSION_CODES.O){
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null)
if (buildVersion >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(CHANNEL_ID) == null)
notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH));
}
notificationManager.notify(NEW_POST_TAG, notificationId, notificationBuilder.build());

233
app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java

@ -0,0 +1,233 @@
package gr.thmmy.mthmmy.services;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import com.snatik.storage.Storage;
import net.gotev.uploadservice.ServerResponse;
import net.gotev.uploadservice.UploadInfo;
import net.gotev.uploadservice.UploadService;
import net.gotev.uploadservice.UploadServiceBroadcastReceiver;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.upload.UploadsHelper;
import gr.thmmy.mthmmy.activities.upload.multipart.MultipartUploadException;
import gr.thmmy.mthmmy.base.BaseApplication;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import timber.log.Timber;
public class UploadsReceiver extends UploadServiceBroadcastReceiver {
public static final String UPLOAD_ID_KEY = "UPLOAD_ID_KEY";
public static final String ACTION_COMBINED_UPLOAD = "ACTION_COMBINED_UPLOAD";
public static final String ACTION_CANCEL_UPLOAD = "ACTION_CANCEL_UPLOAD";
public static final String ACTION_RETRY_UPLOAD = "ACTION_RETRY_UPLOAD";
/*public static final String UPLOAD_RETRY_FILENAME = "UPLOAD_RETRY_FILENAME";
public static final String UPLOAD_RETRY_CATEGORY = "UPLOAD_RETRY_CATEGORY";
public static final String UPLOAD_RETRY_TITLE = "UPLOAD_RETRY_TITLE";
public static final String UPLOAD_RETRY_DESCRIPTION = "UPLOAD_RETRY_DESCRIPTION";
public static final String UPLOAD_RETRY_ICON = "UPLOAD_RETRY_ICON";
public static final String UPLOAD_RETRY_UPLOADER = "UPLOAD_RETRY_UPLOADER";
public static final String UPLOAD_RETRY_FILE_URI = "UPLOAD_RETRY_FILE_URI";*/
private Storage storage;
private static AlertDialog uploadProgressDialog;
private static String dialogUploadID;
//private static Intent multipartUploadRetryIntent;
@Override
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
Bundle intentBundle = intent.getExtras();
if (intentAction == null || intentBundle == null) {
super.onReceive(context, intent);
return;
}
switch (intentAction) {
case ACTION_CANCEL_UPLOAD:
String uploadID = intentBundle.getString(UPLOAD_ID_KEY);
Timber.d("Received ACTION_CANCEL_UPLOAD (id: %s)", uploadID);
UploadService.stopUpload(uploadID);
break;
/*case ACTION_RETRY_UPLOAD:
String retryFilename = intentBundle.getString(UPLOAD_RETRY_FILENAME);
String retryCategory = intentBundle.getString(UPLOAD_RETRY_CATEGORY);
String retryTitleText = intentBundle.getString(UPLOAD_RETRY_TITLE);
String retryDescription = intentBundle.getString(UPLOAD_RETRY_DESCRIPTION);
String retryIcon = intentBundle.getString(UPLOAD_RETRY_ICON);
String retryUploaderProfile = intentBundle.getString(UPLOAD_RETRY_UPLOADER);
Uri retryFileUri = (Uri) intentBundle.get(UPLOAD_RETRY_FILE_URI);
String retryUploadID = UUID.randomUUID().toString();
UploadActivity.uploadFile(context, retryUploadID,
UploadActivity.getConfigForUpload(context, retryUploadID, retryFilename, retryCategory,
retryTitleText, retryDescription, retryIcon, retryUploaderProfile, retryFileUri),
retryCategory, retryTitleText, retryDescription, retryIcon,
retryUploaderProfile, retryFileUri);
break;*/
default:
super.onReceive(context, intent);
break;
}
}
@Override
public void onProgress(Context context, UploadInfo uploadInfo) {
Timber.i("Upload in progress (id: %s)",uploadInfo.getUploadId());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP &&
uploadInfo.getUploadId().equals(dialogUploadID) &&
uploadProgressDialog != null) {
Button alertDialogNeutral = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
alertDialogNeutral.setText(R.string.upload_resume_in_background);
alertDialogNeutral.setOnClickListener(v -> uploadProgressDialog.dismiss());
Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
alertDialogNegative.setText(R.string.cancel);
alertDialogNegative.setOnClickListener(v -> {
Timber.d("Cancelling upload (id: %s)", dialogUploadID);
UploadService.stopUpload(dialogUploadID);
uploadProgressDialog.dismiss();
});
if (uploadProgressDialog.isShowing()) {
Window progressWindow = uploadProgressDialog.getWindow();
if (progressWindow != null) {
MaterialProgressBar dialogProgressBar = progressWindow.findViewById(R.id.dialogProgressBar);
TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text);
dialogProgressBar.setProgress(uploadInfo.getProgressPercent());
dialogProgressText.setText(context.getResources().getString(
R.string.upload_progress_dialog_bytes_uploaded,
(float) uploadInfo.getUploadRate(),
(int) uploadInfo.getUploadedBytes() / 1000,
(int) uploadInfo.getTotalBytes() / 1000));
}
if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) {
uploadProgressDialog.dismiss();
}
}
}
}
@Override
public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse,
Exception exception) {
Timber.i("Error while uploading (id: %s)",uploadInfo.getUploadId());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP &&
uploadInfo.getUploadId().equals(dialogUploadID) &&
uploadProgressDialog != null) {
/*Button alertDialogNeutral = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
alertDialogNeutral.setText("Retry");
alertDialogNeutral.setOnClickListener(v -> {
if (multipartUploadRetryIntent != null) {
context.sendBroadcast(multipartUploadRetryIntent);
}
uploadProgressDialog.dismiss();
});*/
Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
alertDialogNegative.setText(R.string.cancel);
alertDialogNegative.setOnClickListener(v -> {
cancelNotification(context, uploadInfo.getNotificationID());
UploadsHelper.deleteTempFiles(storage);
uploadProgressDialog.dismiss();
});
if (uploadProgressDialog.isShowing()) {
Window progressWindow = uploadProgressDialog.getWindow();
if (progressWindow != null) {
MaterialProgressBar dialogProgressBar = progressWindow.findViewById(R.id.dialogProgressBar);
TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text);
dialogProgressBar.setVisibility(View.GONE);
dialogProgressText.setText(R.string.upload_failed);
}
if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) {
uploadProgressDialog.dismiss();
}
}
} else {
cancelNotification(context, uploadInfo.getNotificationID());
Intent combinedActionsIntent = new Intent(UploadsReceiver.ACTION_COMBINED_UPLOAD);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadInfo.getUploadId());
context.sendBroadcast(combinedActionsIntent);
}
Toast.makeText(context.getApplicationContext(), R.string.upload_failed, Toast.LENGTH_SHORT).show();
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
}
@Override
public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
uploadProgressDialog = null;
dialogUploadID = null;
}
String response = serverResponse.getBodyAsString();
if(response.contains("Η προσθήκη του αρχείου ήταν επιτυχημένη.")||response.contains("The upload was successful.")){
Timber.i("Upload completed successfully (id: %s)",uploadInfo.getUploadId());
Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show();
BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null);
}
else {
MultipartUploadException multipartUploadException = new MultipartUploadException(response);
Timber.e(multipartUploadException);
onError(context,uploadInfo,serverResponse,multipartUploadException);
}
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
UploadsHelper.deleteTempFiles(storage);
}
@Override
public void onCancelled(Context context, UploadInfo uploadInfo) {
Timber.i("Upload cancelled (id: %s)", uploadInfo.getUploadId());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
uploadProgressDialog = null;
dialogUploadID = null;
}
Toast.makeText(context.getApplicationContext(), R.string.upload_cancelled, Toast.LENGTH_SHORT).show();
if (storage == null)
storage = new Storage(context.getApplicationContext());
//cancelNotification(context, uploadInfo.getNotificationID());
UploadsHelper.deleteTempFiles(storage);
}
public static void setDialogDisplay(AlertDialog uploadProgressDialog, String dialogUploadID,
Intent multipartUploadRetryIntent) {
UploadsReceiver.uploadProgressDialog = uploadProgressDialog;
UploadsReceiver.dialogUploadID = dialogUploadID;
//UploadsReceiver.multipartUploadRetryIntent = multipartUploadRetryIntent;
}
private void cancelNotification(Context context, int notificationId){
NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null)
notificationManager.cancel(notificationId);
}
}

5
app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java

@ -2,6 +2,9 @@ package gr.thmmy.mthmmy.session;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
@ -14,8 +17,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import okhttp3.Cookie;

4
app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java

@ -10,12 +10,12 @@ import android.widget.AdapterView;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatSpinner;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import androidx.appcompat.widget.AppCompatSpinner;
public class AppCompatSpinnerWithoutDefault extends AppCompatSpinner {
public AppCompatSpinnerWithoutDefault(Context context) {
super(context);

22
app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java

@ -17,8 +17,8 @@ public class CrashReporter {
public static void reportForumInfo(Document document) {
ParseHelpers.Theme theme = ParseHelpers.parseTheme(document);
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document);
String themeKey = "forum theme", themeValue = null;
String languageKey = "forum language", languageValue = null;
String themeKey = "forum theme", themeValue;
String languageKey = "forum language", languageValue;
switch (theme) {
case SCRIBBLES2:
themeValue = "Scribbles2";
@ -32,19 +32,17 @@ public class CrashReporter {
case HELIOS_MULTI:
themeValue = "Helios_Multi";
break;
case THEME_UNKNOWN:
default:
themeValue = "Unknown theme";
break;
}
switch (language) {
case GREEK:
languageValue = "Greek";
break;
case ENGLISH:
languageValue = "English";
break;
}
if (language == ParseHelpers.Language.GREEK)
languageValue = "Greek";
else if (language == ParseHelpers.Language.ENGLISH)
languageValue = "English";
else
languageValue = "Unknown";
Crashlytics.setString(themeKey, themeValue);
Crashlytics.setString(languageKey, languageValue);
Crashlytics.setBool("isLoggedIn", BaseApplication.getInstance().getSessionManager().isLoggedIn());

3
app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java

@ -27,8 +27,7 @@ public class CrashReportingTree extends DebugTree {
Crashlytics.log(level + "/" + tag + ": " + message);
if(priority == Log.ERROR)
{
if(priority == Log.ERROR) {
if (t!=null)
Crashlytics.logException(t);
else

1
app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java

@ -4,6 +4,7 @@ import android.content.Context;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import timber.log.Timber;
public class CustomLinearLayoutManager extends LinearLayoutManager {

98
app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java

@ -1,10 +1,17 @@
package gr.thmmy.mthmmy.utils;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import androidx.annotation.NonNull;
import gr.thmmy.mthmmy.R;
import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR;
@ -21,7 +28,94 @@ public class FileUtils {
return type;
}
public static boolean fileNameExists (String fileName) {
public static boolean fileNameExists(String fileName) {
return fileName != null && (new File(SAVE_DIR.getAbsolutePath(), fileName)).isFile();
}
@Nullable
public static String getFileExtension(@NonNull String filename) {
String fileExtension;
if (!filename.contains(".")) {
return null;
}
if (filename.toLowerCase().endsWith(".tar.gz")) {
fileExtension = filename.substring(filename.length() - 7);
} else {
fileExtension = filename.substring(filename.lastIndexOf("."));
}
return fileExtension;
}
public static String getFilenameWithoutExtension(String filename) {
String fileExtension = getFileExtension(filename);
return fileExtension == null
? filename
: filename.substring(0, filename.indexOf(fileExtension));
}
@NonNull
public static String filenameFromUri(Context context, Uri uri) {
String filename = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
}
}
if (filename == null) {
filename = uri.getPath();
int cut = filename.lastIndexOf('/');
if (cut != -1) {
filename = filename.substring(cut + 1);
}
}
return filename;
}
public static long sizeFromUri(Context context, @NonNull Uri uri) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
}
}
return -1;
}
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.
*
* @param filename String with filename <b>containing file's extension</b>
* @return FontAwesome character according to file's type
* @see <a href="http://fontawesome.io/">FontAwesome</a>
*/
@NonNull
public static String faIconFromFilename(Context context, String filename) {
filename = filename.toLowerCase();
if (filename.contains("jpg") || filename.contains("gif") || filename.contains("jpeg")
|| filename.contains("png"))
return context.getResources().getString(R.string.fa_file_image_o);
else if (filename.contains("pdf"))
return context.getResources().getString(R.string.fa_file_pdf_o);
else if (filename.contains("zip") || filename.contains("rar") || filename.contains("tar.gz"))
return context.getResources().getString(R.string.fa_file_zip_o);
else if (filename.contains("txt"))
return context.getResources().getString(R.string.fa_file_text_o);
else if (filename.contains("doc") || filename.contains("docx"))
return context.getResources().getString(R.string.fa_file_word_o);
else if (filename.contains("xls") || filename.contains("xlsx"))
return context.getResources().getString(R.string.fa_file_excel_o);
else if (filename.contains("pps"))
return context.getResources().getString(R.string.fa_file_powerpoint_o);
else if (filename.contains("mpg"))
return context.getResources().getString(R.string.fa_file_video_o);
return context.getResources().getString(R.string.fa_file);
}
}

14
app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java

@ -4,13 +4,13 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
/**
* Extends FloatingActionButton's behavior so the button will hide when scrolling down and show
* otherwise. It also lifts the {@link FloatingActionButton} when a {@link Snackbar} is shown.
@ -47,7 +47,13 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingA
});
} else if (child.getTag() != null && (boolean) child.getTag() && (dyConsumed < 0 ||
!target.canScrollVertically(-1) && dyUnconsumed < -50)) {
child.show();
child.show(new FloatingActionButton.OnVisibilityChangedListener() {
@Override
public void onShown(FloatingActionButton fab) {
super.onShown(fab);
fab.setVisibility(View.VISIBLE);
}
});
}
}

4
app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java

@ -6,13 +6,13 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewPropertyAnimator;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.google.android.material.snackbar.Snackbar;
/**
* Extends LinearLayout's behavior. Used for bottom navigation bar.
* <p>When a nested ScrollView is scrolled down, the view will disappear.

173
app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java

@ -0,0 +1,173 @@
package gr.thmmy.mthmmy.utils;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import androidx.exifinterface.media.ExifInterface;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import timber.log.Timber;
public class TakePhoto {
private static final int DEFAULT_MIN_WIDTH_QUALITY = 400;
private static final String IMAGE_CONTENT_DESCRIPTION = "mTHMMY uploads image";
@Nullable
public static Intent getIntent(Context context, @NonNull File photoFile) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//Ensures that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) {
Uri photoURI = FileProvider.getUriForFile(context, context.getPackageName() +
".provider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
//Grants necessary permissions for Gallery to use the Uri
List<ResolveInfo> resInfoList = context.getPackageManager().
queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, photoURI,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return takePictureIntent;
}
return null;
}
public static Uri processResult(Context context, File photoFile) {
Bitmap bitmap;
Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", photoFile);
bitmap = getImageResized(context, fileUri);
int rotation = getRotation(context, fileUri);
bitmap = rotate(bitmap, rotation);
try {
FileOutputStream out = new FileOutputStream(photoFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return fileUri;
}
@Nullable
public static File createImageFile(Context context) {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date());
String imageFileName = "mTHMMY_" + timeStamp + ".jpg";
File imageFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) +
File.separator + "mTHMMY");
if (!imageFolder.exists()&&!imageFolder.mkdirs()) {
Timber.w("Photos folder build returned false in %s", TakePhoto.class.getSimpleName());
Toast.makeText(context, "Couldn't create photos directory", Toast.LENGTH_SHORT).show();
return null;
}
return new File(imageFolder, imageFileName);
}
public static void galleryAddPic(Context context, File photoFile) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, photoFile.getName());
values.put(MediaStore.Images.Media.DESCRIPTION, IMAGE_CONTENT_DESCRIPTION);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.ImageColumns.BUCKET_ID, photoFile.toString().toLowerCase(Locale.US).hashCode());
values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, photoFile.getName().toLowerCase(Locale.US));
values.put("_data", photoFile.getAbsolutePath());
ContentResolver cr = context.getContentResolver();
cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
private static Bitmap getImageResized(Context context, Uri selectedImage) {
Bitmap bm;
int[] sampleSizes = new int[]{5, 3, 2, 1};
int i = 0;
do {
bm = decodeBitmap(context, selectedImage, sampleSizes[i]);
i++;
} while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length);
return bm;
}
private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
AssetFileDescriptor fileDescriptor = null;
try {
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
assert fileDescriptor != null;
return BitmapFactory.decodeFileDescriptor(
fileDescriptor.getFileDescriptor(), null, options);
}
private static int getRotation(Context context, Uri imageUri) {
int rotation = 0;
try {
context.getContentResolver().notifyChange(imageUri, null);
ExifInterface exif = new ExifInterface(imageUri.getPath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotation = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotation = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return rotation;
}
private static Bitmap rotate(Bitmap bm, int rotation) {
if (rotation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
}
return bm;
}
}

27
app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java

@ -0,0 +1,27 @@
package gr.thmmy.mthmmy.utils;
import android.content.Context;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatButton;
public class ToggledBackgroundButton extends AppCompatButton {
public ToggledBackgroundButton(Context context) {
super(context);
}
public ToggledBackgroundButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ToggledBackgroundButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setEnabled(boolean enabled) {
setAlpha(enabled ? 1 : 0.5f);
super.setEnabled(enabled);
}
}

5
app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java

@ -1,13 +1,12 @@
package gr.thmmy.mthmmy.utils.parsing;
/**
* ParseException is to be used for errors while parsing.
* Use ParseException for errors while parsing.
*/
public class ParseException extends RuntimeException {
public ParseException() {}
public ParseException(String message)
{
public ParseException(String message) {
super(message);
}

189
app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java

@ -9,6 +9,7 @@ import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -59,7 +60,7 @@ public class ParseHelpers {
/**
* An enum describing a forum page's language by defining the types:<ul>
* <li>{@link #PAGE_INCOMPLETE}</li>
* <li>{@link #UNDEFINED_LANGUAGE}</li>
* <li>{@link #UNKNOWN_LANGUAGE}</li>
* <li>{@link #ENGLISH}</li>
* <li>{@link #GREEK}</li>
* </ul>
@ -78,9 +79,9 @@ public class ParseHelpers {
*/
PAGE_INCOMPLETE,
/**
* Page language is not (yet) supported.
* Page language could not be determined/ not (yet) supported.
*/
UNDEFINED_LANGUAGE;
UNKNOWN_LANGUAGE;
/**
* Returns one of the supported forum languages.
@ -93,13 +94,12 @@ public class ParseHelpers {
Element welcoming = page.select("h3").first();
if (welcoming == null) {
Element welcomingGuest = page.select("div[id=myuser]").first();
if (welcomingGuest != null) {
if (welcomingGuest.text().contains("Welcome")) return ENGLISH;
}
if (welcomingGuest != null && welcomingGuest.text().contains("Welcome"))
return ENGLISH;
return PAGE_INCOMPLETE;
} else if (welcoming.text().contains("Καλώς ορίσατε")) return GREEK;
else if (welcoming.text().contains("Hey")) return ENGLISH;
else return UNDEFINED_LANGUAGE;
else return UNKNOWN_LANGUAGE;
}
}
@ -113,16 +113,17 @@ public class ParseHelpers {
public static Theme parseTheme(Document page) {
Element stylesheet = page.select("link[rel=stylesheet]").first();
if (stylesheet.attr("href").contains("scribbles2"))
return Theme.SCRIBBLES2;
else if (stylesheet.attr("href").contains("helios_multi"))
return Theme.HELIOS_MULTI;
else if (stylesheet.attr("href").contains("smfone"))
return Theme.SMFONE_BLUE;
else if (stylesheet.attr("href").contains("default"))
return Theme.SMF_DEFAULT;
else
return Theme.THEME_UNKNOWN;
if(stylesheet!=null){
if (stylesheet.attr("href").contains("scribbles2"))
return Theme.SCRIBBLES2;
else if (stylesheet.attr("href").contains("helios_multi"))
return Theme.HELIOS_MULTI;
else if (stylesheet.attr("href").contains("smfone"))
return Theme.SMFONE_BLUE;
else if (stylesheet.attr("href").contains("default"))
return Theme.SMF_DEFAULT;
}
return Theme.THEME_UNKNOWN;
}
/**
@ -307,7 +308,7 @@ public class ParseHelpers {
* @param html html to parse
* @return a document with deobfuscated emails
*/
public static Document parse(String html){
public static Document parse(String html) {
Document document = Jsoup.parse(html);
deobfuscateElements(document.select("span.__cf_email__,a.__cf_email__"), true);
return document;
@ -316,19 +317,18 @@ public class ParseHelpers {
/**
* Use this method instead of parse() if you are targeting specific elements
*/
public static void deobfuscateElements(Elements elements, boolean found){
if(!found)
public static void deobfuscateElements(Elements elements, boolean found) {
if (!found)
elements = elements.select("span.__cf_email__,a.__cf_email__");
for (Element obfuscatedElement : elements) {
String deobfuscatedEmail = deobfuscateEmail(obfuscatedElement.attr("data-cfemail"));
if(obfuscatedElement.is("span")){
if (obfuscatedElement.is("span")) {
Element parent = obfuscatedElement.parent();
if (parent.is("a")&&parent.attr("href").contains("email-protection"))
parent.attr("href", "mailto:"+deobfuscatedEmail);
}
else if (obfuscatedElement.attr("href").contains("email-protection"))
obfuscatedElement.attr("href", "mailto:"+deobfuscatedEmail);
if (parent.is("a") && parent.attr("href").contains("email-protection"))
parent.attr("href", "mailto:" + deobfuscatedEmail);
} else if (obfuscatedElement.attr("href").contains("email-protection"))
obfuscatedElement.attr("href", "mailto:" + deobfuscatedEmail);
obfuscatedElement.replaceWith(new TextNode(deobfuscatedEmail, ""));
}
@ -339,7 +339,7 @@ public class ParseHelpers {
* @param obfuscatedEmail CloudFlare-obfuscated email
* @return deobfuscated email
*/
private static String deobfuscateEmail(String obfuscatedEmail){
private static String deobfuscateEmail(String obfuscatedEmail) {
//Deobfuscate
final StringBuilder stringBuilder = new StringBuilder();
final int r = Integer.parseInt(obfuscatedEmail.substring(0, 2), 16);
@ -348,7 +348,140 @@ public class ParseHelpers {
stringBuilder.append(Character.toString((char) i));
}
Timber.i("Email deobfuscated.");
Timber.d("Email deobfuscated.");
return stringBuilder.toString();
}
public static String emojiTagToHtml(String emojiTagedString) {
HashMap<Pattern, String> tagToHtmlMap = new HashMap<>();
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":)"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/smiley.gif\" alt=\"Smiley\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(";)"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/wink.gif\" alt=\"Wink\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":D"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/cheesy.gif\" alt=\"Cheesy\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(";D"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/grin.gif\" alt=\"Grin\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("&gt;:("), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/angry.gif\" alt=\"Angry\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":("), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/sad.gif\" alt=\"Sad\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":o"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/shocked.gif\" alt=\"Shocked\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("8))"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/cool.gif\" alt=\"Cool\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":???:"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/huh.gif\" alt=\"Huh\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":P"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/tongue.gif\" alt=\"Tongue\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-["), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/embarrassed.gif\" alt=\"Embarrassed\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-X"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/lipsrsealed.gif\" alt=\"Lips Sealed\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-\\"), Pattern.MULTILINE), "<img src =\"https://www.thmmy.gr/smf/Smileys/default_dither/undecided.gif\" alt=\"Undecided\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-*"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/kiss.gif\" alt=\"Kiss\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":'("), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/cry.gif\" alt=\"Cry\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("&lt;3"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/heart.gif\" alt=\"heart\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^locked^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/locked.gif\" alt=\"kleidaria\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rollover^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/roll_over.gif\" alt=\"roll_over\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^redface^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/redface.gif\" alt=\"redface\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^confused^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/confused.gif\" alt=\"confused\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^innocent^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/innocent.gif\" alt=\"innocent\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sleep^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/sleep.gif\" alt=\"sleep\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sealed^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/lips_sealed.gif\" alt=\"lips_sealed\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^cool^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/cool.bmp\" alt=\"cool\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^crazy^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/crazy.jpg\" alt=\"crazy\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^mad^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/mad.jpg\" alt=\"mad\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^wav^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/wav.gif\" alt=\"wav\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^binkybaby^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/binkybaby.gif\" alt=\"BinkyBaby\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^Police^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/Police.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^dontknow^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/dontknow.gif\" alt=\"DontKnow\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote(":angry4:"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/angry4.gif\" alt=\"angry4\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^angryhot^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/angry_hot.gif\" alt=\"angryAndHot\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^angry^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/angry.gif\" alt=\"angry\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^fouska^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/foyska.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^nysta^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/nista.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sfinaki^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/10_7_3.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^banghead^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/BangHead.gif\" alt=\"bang_head\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^crybaby^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/crybaby.gif\" alt=\"CryBaby\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^hello^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/hello.gif\" alt=\"Hello\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^jerk^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/jerk.gif\" alt=\"jerk\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^nono^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/nono.gif\" alt=\"NoNo\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^notworthy^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/notworthy.gif\" alt=\"NotWorthy\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^off-topic^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/off -topic.gif\" alt=\"Off-topic\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^puke^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/puke.gif\" alt=\"Puke\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shout^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/shout.gif\" alt=\"Shout\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^slurp^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/slurp.gif\" alt=\"Slurp\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^superconfused^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/superconfused.gif\" alt=\"SuperConfused\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^superinnocent^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/superinnocent.gif\" alt=\"SuperInnocent\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^cellPhone^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/cellPhone.gif\" alt=\"CellPhone\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^idiot^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/idiot.gif\" alt=\"Idiot\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^knuppel^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/knuppel.gif\" alt=\"Knuppel\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^tickedOff^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/tickedoff.gif\" alt=\"TickedOff\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^peace^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/peace.gif\" alt=\"Peace\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^suspicious^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/suspicious.gif\" alt=\"Suspicious\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^caffine^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/caffine.gif\" alt=\"Caffine\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^argue^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/argue.gif\" alt=\"argue\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^banned2^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/banned2.gif\" alt=\"banned2\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^banned^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/banned.gif\" alt=\"banned\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bath^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/bath.gif\" alt=\"bath\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^beg^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/beg.gif\" alt=\"beg\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bluescreen^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/bluescreen.gif\" alt=\"bluescreen\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^boil^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/boil.gif\" alt=\"boil\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bye^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/bye.gif\" alt=\"bye\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^callmerip^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/callmerip.gif\" alt=\"callmerip\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^carnaval^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/carnaval.gif\" alt=\"carnaval\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^clap^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/clap.gif\" alt=\"clap\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^coffepot^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/coffeepot.gif\" alt=\"coffepot\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^crap^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/crap.gif\" alt=\"crap\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^curses^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/curses.gif\" alt=\"curses\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^funny^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/funny.gif\" alt=\"funny\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^guitar^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/guitar1.gif\" alt=\"guitar\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^kissy^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/icon_kissy.gif\" alt=\"kissy\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^band^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/band.gif\" alt=\"band\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^ivres^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/ivres.gif\" alt=\"ivres\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^kaloe^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/kaloe.gif\" alt=\"kaloe\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^kremala^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/kremala.gif\" alt=\"kremala\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^moon^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/moon.gif\" alt=\"moon\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^mopping^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/mopping.gif\" alt=\"mopping\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^mountza^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/mountza.gif\" alt=\"mountza\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^pcsleep^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/pcsleep.gif\" alt=\"pcsleep\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^pinokio^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/pinokio.gif\" alt=\"pinokio\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^poke^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/poke.gif\" alt=\"poke\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^seestars^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/seestars.gif\" alt=\"seestars\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sfyri^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/sfyri.gif\" alt=\"sfyri\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^spam^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/spam2.gif\" alt=\"spam\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^super^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/super.gif\" alt=\"super\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^tafos^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/tafos.gif\" alt=\"tafos\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^tomato^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/tomatomourh.gif\" alt=\"tomato\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^ytold^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/ytold.gif\" alt=\"ytold\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^beer^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/beer2.gif\" alt=\"beer\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^yue^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/yu.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^eatpaper^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/a-eatpaper.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^fritz^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/fritz.gif\" alt=\"ο fritz!!!\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^wade^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/wade.gif\" alt=\"o Wade!!!\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^lypi^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/lypi.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^aytoxeir^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/megashok1wq.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^victory^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/victory.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^filarakia^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/filarakia.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^hat^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/bonjour-97213.gif\" alt=\"bonjour\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^miss^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/curtseyqi9.gif\" alt=\"bonjour2\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rolfmao^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/rofl.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^lock^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/locked.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^que^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/question.gif\" alt=\"question\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shifty^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/shifty.gif\" alt=\"shifty\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shy^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/shy.png\" alt=\"shy\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^music_listen^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/music.gif\" alt=\"music_listenning\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bagface^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/shamed_bag.jpg\" alt=\"bag_face\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rotate^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/rotfl.gif\" alt=\"rotation\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^love^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/love.jpg\" alt=\"love\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^speech^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/speech.gif\" alt=\"speech\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^facepalm^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/facepalm.gif\" alt=\"\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shocked^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/shocked.png\" alt=\"shocked\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^ex_shocked^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/extremely_shocked.png\" alt=\"extremely_shocked\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^smurf^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/smurf.gif\" alt=\"smurf\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^monster^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/monster.bmp\" alt=\"monster\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^pig^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/noffe.gif\" alt=\"pig\" border=\"0\">");
tagToHtmlMap.put(Pattern.compile(Pattern.quote("^lol^"), Pattern.MULTILINE), "<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/LoL.jpg\" alt=\"lol\" border=\"0\">");
//Needs priority over the rest tags
final Pattern pattern = Pattern.compile(Pattern.quote("::)"), Pattern.MULTILINE);
Matcher matcher = pattern.matcher(emojiTagedString);
emojiTagedString = matcher.replaceAll("<img src=\"https://www.thmmy.gr/smf/Smileys/default_dither/rolleyes.gif\" alt=\"Roll Eyes\" border=\"0\">");
for (Pattern patternKey : tagToHtmlMap.keySet()) {
matcher = patternKey.matcher(emojiTagedString);
emojiTagedString = matcher.replaceAll(tagToHtmlMap.get(patternKey));
}
return emojiTagedString;
}
}

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

@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import gr.thmmy.mthmmy.model.Bookmark;
public class BaseViewModel extends ViewModel {

1
app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java

@ -4,6 +4,7 @@ import android.os.AsyncTask;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import gr.thmmy.mthmmy.activities.shoutbox.SendShoutTask;
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxTask;
import gr.thmmy.mthmmy.model.Shoutbox;

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

@ -8,16 +8,17 @@ import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList;
import androidx.lifecycle.MutableLiveData;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask;
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyResult;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.RemoveVoteTask;
import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.SubmitVoteTask;
@ -34,7 +35,7 @@ import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;
public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted,
PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished {
PrepareForReplyTask.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished {
/**
* topic state
*/
@ -55,7 +56,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
private TopicTask currentTopicTask;
private PrepareForEditTask currentPrepareForEditTask;
private PrepareForReply currentPrepareForReplyTask;
private PrepareForReplyTask currentPrepareForReplyTask;
//callbacks for topic activity
private TopicTask.TopicTaskObserver topicTaskObserver;
@ -64,7 +65,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
private ReplyTask.ReplyTaskCallbacks replyFinishListener;
private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks;
private EditTask.EditTaskCallbacks editTaskCallbacks;
private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks;
private PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks;
private ExternalAsyncTask.OnTaskStartedListener voteTaskStartedListener;
private NetworkTask.OnNetworkTaskFinishedListener<Void> voteTaskFinishedListener;
private ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener;
@ -176,7 +177,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
stopLoading();
setPageIndicatorIndex(pageCount, true);
Timber.i("Preparing for reply");
currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this,
currentPrepareForReplyTask = new PrepareForReplyTask(prepareForReplyCallbacks, this,
replyPageUrl.getValue());
currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0]));
}
@ -219,7 +220,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
PrepareForEditResult editResult = prepareForEditResult.getValue();
Timber.i("Editing post");
new EditTask(editTaskCallbacks, position).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editResult.getCommitEditUrl(), message,
editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic());
editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic(), editResult.getIcon());
}
/**
@ -423,7 +424,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
this.editTaskCallbacks = editTaskCallbacks;
}
public void setPrepareForReplyCallbacks(PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks) {
public void setPrepareForReplyCallbacks(PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks) {
this.prepareForReplyCallbacks = prepareForReplyCallbacks;
}

4
app/src/main/res/drawable/ic_attach_file_white_24dp.xml

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z"/>
</vector>

4
app/src/main/res/drawable/ic_cached_accent_24dp.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#26A69A" android:pathData="M19,8l-4,4h3c0,3.31 -2.69,6 -6,6 -1.01,0 -1.97,-0.25 -2.8,-0.7l-1.46,1.46C8.97,19.54 10.43,20 12,20c4.42,0 8,-3.58 8,-8h3l-4,-4zM6,12c0,-3.31 2.69,-6 6,-6 1.01,0 1.97,0.25 2.8,0.7l1.46,-1.46C15.03,4.46 13.57,4 12,4c-4.42,0 -8,3.58 -8,8H1l4,4 4,-4H6z"/>
</vector>

4
app/src/main/res/drawable/ic_cancel_accent_24dp.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#26A69A" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
</vector>

4
app/src/main/res/drawable/ic_info_outline_warning_24dp.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#FF9800" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>

14
app/src/main/res/layout/activity_board.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
@ -31,7 +30,7 @@
android:layout_marginEnd="19dp"
android:background="@null"
android:contentDescription="@string/bookmark"
app:srcCompat="@drawable/ic_bookmark_false_accent_24dp"/>
app:srcCompat="@drawable/ic_bookmark_false_accent_24dp" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
@ -43,8 +42,7 @@
android:layout_marginTop="64dp"
android:background="@color/background"
android:scrollbars="none"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity">
</androidx.recyclerview.widget.RecyclerView>
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar"
@ -56,7 +54,7 @@
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|center"
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
app:mpb_progressStyle="horizontal" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/board_fab"
@ -65,7 +63,5 @@
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_add_fab"/>
app:srcCompat="@drawable/ic_add_fab" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

4
app/src/main/res/layout/activity_downloads.xml

@ -48,7 +48,7 @@
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
<!--<com.google.android.material.floatingactionbutton.FloatingActionButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/upload_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -56,5 +56,5 @@
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload_white_24dp"/>-->
app:srcCompat="@drawable/ic_file_upload_white_24dp"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

4
app/src/main/res/layout/activity_topic.xml

@ -45,8 +45,8 @@
android:layout_below="@id/appbar"
android:layout_gravity="top|start"
android:clipToPadding="false"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
@ -146,8 +146,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="50dp"
android:layout_marginEnd="@dimen/fab_margins"
android:layout_marginBottom="50dp"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_reply" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

74
app/src/main/res/layout/activity_upload.xml

@ -28,16 +28,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|start"
android:background="@color/background"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:background="@color/primary_light"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.upload.UploadActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background"
android:background="@color/primary_light"
android:focusableInTouchMode="true"
android:orientation="vertical">
@ -45,15 +45,16 @@
android:id="@+id/upload_spinners"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background"
android:layout_marginTop="8dp"
android:background="@color/primary_light"
android:orientation="vertical">
<gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault
android:id="@+id/upload_spinner_category_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:popupBackground="@color/primary"
android:prompt="@string/upload_spinners_hint" />
</LinearLayout>
@ -61,8 +62,8 @@
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp">
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp">
<EditText
android:id="@+id/upload_title"
@ -73,11 +74,41 @@
android:maxLength="500" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:layout_weight="1">
<EditText
android:id="@+id/upload_filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/upload_filename"
android:inputType="textNoSuggestions"
android:maxLength="500" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/upload_filename_info"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:theme="@style/LightBackgroundColoredButton"
app:srcCompat="@drawable/ic_info_outline_white_24dp" />
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp">
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp">
<EditText
android:id="@+id/upload_description"
@ -87,7 +118,7 @@
android:inputType="textMultiLine" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton
<gr.thmmy.mthmmy.utils.ToggledBackgroundButton
android:id="@+id/upload_title_description_builder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -97,16 +128,13 @@
android:textAlignment="center"
android:textColor="@color/accent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/upload_filename"
<LinearLayout
android:id="@+id/upload_files_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:drawablePadding="5dp"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="@color/primary_text"
android:layout_marginBottom="6dp"
android:orientation="vertical"
android:visibility="gone" />
<LinearLayout
@ -129,11 +157,6 @@
android:text="@string/upload_select_file"
android:textColor="@color/primary_text" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/upload_take_photo_button"
android:layout_width="wrap_content"
@ -144,6 +167,11 @@
android:gravity="center_vertical"
android:text="@string/upload_take_photo"
android:textColor="@color/primary_text" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
@ -165,8 +193,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins"
android:layout_marginBottom="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload_white_24dp" />

2
app/src/main/res/layout/activity_upload_fields_builder.xml

@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|start"
android:background="@color/background"
android:background="@color/primary_light"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"

24
app/src/main/res/layout/activity_upload_file_list_row.xml

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/upload_file_item_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="@color/primary_text" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/upload_file_item_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/LightBackgroundColoredButton"
app:srcCompat="@drawable/ic_delete_accent_24dp" />
</LinearLayout>

10
app/src/main/res/layout/activity_upload_filename_info_popup.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/upload_filename_info_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/primary_light"
android:linksClickable="true"
android:padding="8dp"
android:text="@string/upload_filename_info"
android:textColor="@color/primary_text" />

43
app/src/main/res/layout/dialog_upload_progress.xml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingEnd="24dp"
android:paddingStart="24dp">
<TextView
android:id="@+id/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="5dp"
android:text="@string/upload_progress_dialog_title"
android:textColor="@color/accent"
android:textSize="20sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="15dp"
android:background="@color/secondary_text" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/dialogProgressBar"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent"
android:layout_height="@dimen/progress_bar_height"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
app:mpb_progressTint="@color/accent" />
<TextView
android:id="@+id/dialog_upload_progress_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginTop="12dp"
android:minEms="64"
android:textColor="@color/white" />
</LinearLayout>

37
app/src/main/res/layout/editor_view_color_picker.xml

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="@color/card_background">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@ -14,84 +13,84 @@
android:id="@+id/black"
style="@style/PopupMenuItem.TopItem"
android:text="@string/black"
android:textColor="@color/black"/>
android:textColor="@color/black" />
<TextView
android:id="@+id/red"
style="@style/PopupMenuItem"
android:text="@string/red"
android:textColor="@color/red"/>
android:textColor="@color/red" />
<TextView
android:id="@+id/yellow"
style="@style/PopupMenuItem"
android:text="@string/yellow"
android:textColor="@color/yellow"/>
android:textColor="@color/yellow" />
<TextView
android:id="@+id/pink"
style="@style/PopupMenuItem"
android:text="@string/pink"
android:textColor="@color/pink"/>
android:textColor="@color/pink" />
<TextView
android:id="@+id/green"
style="@style/PopupMenuItem"
android:text="@string/green"
android:textColor="@color/green"/>
android:textColor="@color/green" />
<TextView
android:id="@+id/orange"
style="@style/PopupMenuItem"
android:text="@string/orange"
android:textColor="@color/orange"/>
android:textColor="@color/orange" />
<TextView
android:id="@+id/purple"
style="@style/PopupMenuItem"
android:text="@string/purple"
android:textColor="@color/purple"/>
android:textColor="@color/purple" />
<TextView
android:id="@+id/blue"
style="@style/PopupMenuItem"
android:text="@string/blue"
android:textColor="@color/blue"/>
android:textColor="@color/blue" />
<TextView
android:id="@+id/beige"
style="@style/PopupMenuItem"
android:text="@string/beige"
android:textColor="@color/beige"/>
android:textColor="@color/beige" />
<TextView
android:id="@+id/brown"
style="@style/PopupMenuItem"
android:text="@string/brown"
android:textColor="@color/brown"/>
android:textColor="@color/brown" />
<TextView
android:id="@+id/teal"
style="@style/PopupMenuItem"
android:text="@string/teal"
android:textColor="@color/teal"/>
android:textColor="@color/teal" />
<TextView
android:id="@+id/navy"
style="@style/PopupMenuItem"
android:text="@string/navy"
android:textColor="@color/navy"/>
android:textColor="@color/navy" />
<TextView
android:id="@+id/maroon"
style="@style/PopupMenuItem"
android:text="@string/maroon"
android:textColor="@color/maroon"/>
android:textColor="@color/maroon" />
<TextView
android:id="@+id/lime_green"
style="@style/PopupMenuItem.BottomItem"
android:text="@string/lime_green"
android:textColor="@color/lime_green"/>
android:textColor="@color/lime_green" />
</LinearLayout>
</ScrollView>

1
app/src/main/res/values/colors.xml

@ -18,6 +18,7 @@
<color name="divider">#8B8B8B</color>
<color name="link_color">#FF9800</color>
<color name="mention_color">#FAA61A</color>
<color name="error_red">#890d0d</color>
<color name="white">#FFFFFF</color>
<color name="iron">#CCCCCC</color>

16
app/src/main/res/values/strings.xml

@ -15,7 +15,7 @@
<string name="inbox">Inbox</string>
<string name="info">Info</string>
<string name="ok">OK</string>
<string name="cancel">Cancel</string>
<string name="cancel">"Cancel"</string>
<string name="user_agreement_dialog_text">"To use mTHMMY you have to agree to our Privacy Policy by choosing one of the options below. Choose \"Yes, I want to help\", if you consent to the collection of anonymized data that will help us improve the app. Otherwise, choose \"Nope, leave me alone\". You can change your preferences any time through the app's Settings.</string>
<string name="user_consent_shared_preference_key">user_consent_shared_preference_key</string>
@ -133,12 +133,22 @@
<string name="upload_button">Upload</string>
<!--Upload Activity-->
<string name="upload_title_description_builder">Generate title and description</string>
<string name="upload_title_description_builder">Generate fields</string>
<string name="upload_title_hint">Title</string>
<string name="upload_filename">Upload as (filename)</string>
<string name="upload_description_hint">Description</string>
<string name="upload_select_file">Select file</string>
<string name="upload_select_file">Add files</string>
<string name="upload_take_photo">Take photo</string>
<string name="upload_spinners_hint">Select a category</string>
<string name="upload_filename_info">Please follow the filename rules as\ndescribed
in&#160;<a href="https://www.thmmy.gr/smf/index.php?topic=34294.0">this topic</a>.\n
\nThis does not rename your local files.</string>
<string name="upload_progress_dialog_title">Uploading</string>
<string name="upload_progress_dialog_bytes_uploaded">Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes</string>
<string name="upload_failed">"Upload failed"</string>
<string name="upload_cancelled">"Upload canceled"</string>
<string name="upload_resume_in_background">"Resume in background"</string>
<string name="upload_retry">"Retry"</string>
<!--Upload Fields Builder Activity-->
<string name="upload_fields_builder_type_radio_buttons_title">Select type of upload</string>

2
app/src/main/res/values/styles.xml

@ -31,7 +31,7 @@
<style name="AppTheme.NoActionBar" parent="BaseAppTheme.NoActionBar"/>
<style name="AppTheme.PreferenceTheme" parent="AppTheme.NoActionBar">
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
<item name="colorControlActivated">@color/accent</item>
</style>

158
app/src/main/res/values/uploads_courses.xml

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--Format: Original:Minified:Greeklish-->
<string-array name="string_array_uploads_courses">
<item>Ακουστική I:Ακουστική 1:Akoustiki_I</item>
<item>Ακουστική II:Ακουστική 2:Akoustiki_II</item>
<item>Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή:Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή:Analysi_Ilektr_Kykl</item>
<item>Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας:ΑΣΗΕ:ASHE</item>
<item>Ανάλυση Χρονοσειρών:Χρονοσειρές:Xronoseires</item>
<item>Ανάλυση και Σχεδίαση Αλγορίθμων:Αλγόριθμοι:Algorithms</item>
<item>Αναγνώριση Προτύπων:Αναγνώριση Προτύπων:protipa</item>
<item>Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I):Αναλογικές Τηλεπ.:Anal_Tilep</item>
<item>Αντικειμενοστραφής Προγραμματισμός:Αντικειμενοστραφής:OOP</item>
<item>Αξιοπιστία Συστημάτων:Αξιοπιστία Συστημάτων:Aksiopistia_Systimaton</item>
<item>Αριθμητική Ανάλυση:Αριθμ. Ανάλυση:Arith_Anal</item>
<item>Αρχές Οικονομίας:Αρχές Οικονομίας:Arx_Oikonomias</item>
<item>Αρχές Παράλληλης Επεξεργασίας:Αρχές Παράλληλης Επεξεργασίας:Arxes_Parall_Epeksergasias</item>
<item>Αρχιτεκτονική Υπολογιστών:Αρχ. Υπολογιστών:Arx_Ypologiston</item>
<item>Ασαφή Συστήματα:Ασαφή:Asafi</item>
<item>Ασφάλεια Πληροφοριακών Συστημάτων:Ασφάλεια:Asfaleia</item>
<item>Ασύρματος Τηλεπικοινωνία I:Ασύρματος 1:Asyrmatos_I</item>
<item>Ασύρματος Τηλεπικοινωνία II:Ασύρματος 2:Asyrmatos_II</item>
<item>Βάσεις Δεδομένων:Βάσεις:Vaseis</item>
<item>Βιομηχανικά Ηλεκτρονικά:Βιομηχανικά Ηλεκτρονικά:Viomix_Ilektronika</item>
<item>Βιομηχανική Πληροφορική:Βιομηχανική Πληρ:Viomix_Plir</item>
<item>Βιοϊατρική Τεχνολογία:Βιοιατρική:Vioiatriki</item>
<item>Γεωηλεκτρομαγνητισμός:Γεωηλεκτρομαγνητισμός:Geoilektromagnitismos</item>
<item>Γραμμική Άλγεβρα:Γραμμ. Άλγεβρ.:Grammiki_Algevra</item>
<item>Γραφική με Υπολογιστές:Γραφική:Grafiki</item>
<item>Δίκτυα Τηλεπικοινωνιών:Δίκτυα Τηλέπ.:Diktya_Tilep</item>
<item>Δίκτυα Υπολογιστών I:Δίκτυα 1:Diktya_I</item>
<item>Δίκτυα Υπολογιστών II:Δίκτυα 2:Diktya_II</item>
<item>Διάδοση Η/Μ Κύματος II:Διάδοση 2:Diadosi_II</item>
<item>Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III):Διάδοση 1:Diadosi_I</item>
<item>Διακριτά Μαθηματικά:Διακριτά Μαθηματικά:Diakrita</item>
<item>Διακριτά μαθηματικά:Διακριτά Μαθηματικά:Diakrita</item>
<item>Διανεμημένη Παραγωγή:Διανεμημένη Παραγωγή:Dian_Paragogi</item>
<item>Διατάξεις Υψηλών Συχνοτήτων:ΔΥΣ:DYS</item>
<item>Διαφορικές Εξισώσεις:Διαφορικές:Diaforikes</item>
<item>Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας:ΔΣΗΕ:DSHE</item>
<item>Δομές Δεδομένων:Δομ. Δεδομ.:Domes_Dedomenon</item>
<item>Δομημένος Προγραμματισμός:Δομ. Προγραμμ.:C</item>
<item>Ειδικά Κεφάλαια Διαφορικών Εξισώσεων:Ειδικά Κεφάλαια Διαφορικών Εξισώσεων:Eidika_Kef_Diaf_Eksis</item>
<item>Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I:Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I:Eidika_Kef_HM_Pediou_I</item>
<item>Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας:ΕΚΣΗΕ:EKSHE</item>
<item>Ειδικές Αρχιτεκτονικές Υπολογιστών:Ειδικές Αρχιτεκτονικές Υπολογιστών:Eidikes_Arx_Ypolog</item>
<item>Ειδικές Κεραίες, Σύνθεση Κεραιών:Ειδικές Κεραίες, Σύνθεση Κεραιών:Eidikes_Keraies</item>
<item>Εισαγωγή στην Ενεργειακή Τεχνολογία I:ΕΕΤ 1:EET_I</item>
<item>Εισαγωγή στην Ενεργειακή Τεχνολογία II:ΕΕΤ2:EET_II</item>
<item>Εισαγωγή στην Πολιτική Οικονομία:Πολιτική Οικονομία:Polit_Oik</item>
<item>Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας:Εισ. Πυρηνικη Τεχν.:Intro_Pyriniki_Texn</item>
<item>Ενσωματωμένα Συστήματα Πραγματικού Χρόνου:Ενσωματωμένα:Enswmatwmena</item>
<item>Επιχειρησιακή Έρευνα:Επιχειρησιακή Έρευνα:Epixeirisiaki</item>
<item>Ευρυζωνικά Δίκτυα:Ευρυζωνικά:Evryzonika</item>
<item>Ευφυή Συστήματα Ρομπότ:Ευφυή:eufuh</item>
<item>Εφαρμογές Τηλεπικοινωνιακών Διατάξεων:Εφαρμογές Τηλεπ. Διατάξεων:Efarm_Tilep_Diatakseon</item>
<item>Εφαρμοσμένα Μαθηματικά I:Εφαρμοσμένα 1:Efarmosmena_Math_I</item>
<item>Εφαρμοσμένα Μαθηματικά II:Εφαρμοσμένα 2:Efarmosmena_Math_II</item>
<item>Εφαρμοσμένη Θερμοδυναμική:Θερμοδυναμική:Thermodynamiki</item>
<item>Ηλεκτρακουστική I:Ηλεκτρακουστική 1:Ilektrakoustiki_I</item>
<item>Ηλεκτρακουστική II:Ηλεκτρακουστική 2:Ilektrakoustiki_II</item>
<item>Ηλεκτρικά Κυκλώματα I:Κυκλώματα 1:Kyklomata_I</item>
<item>Ηλεκτρικά Κυκλώματα II:Κυκλώματα 2:Kyklomata_II</item>
<item>Ηλεκτρικά Κυκλώματα III:Κυκλώματα 3:Kyklomata_I</item>
<item>Ηλεκτρικές Μετρήσεις I:Μετρήσεις 1:Metriseis_I</item>
<item>Ηλεκτρικές Μετρήσεις II:Μετρήσεις 2:Metriseis_II</item>
<item>Ηλεκτρικές Μηχανές I:Μηχανές I:Mixanes_I</item>
<item>Ηλεκτρικές Μηχανές Α\':Μηχανές Α:Mixanes_A</item>
<item>Ηλεκτρικές Μηχανές Β\':Μηχανές Β:Mixanes_B</item>
<item>Ηλεκτρικές Μηχανές Γ\':Μηχανές Γ:Mixanes_C</item>
<item>Ηλεκτρική Οικονομία:Ηλεκτρική Οικονομία:Ilektr_Oikonomia</item>
<item>Ηλεκτρολογικά Υλικά:Ηλεκτρ. Υλικά:Ylika</item>
<item>Ηλεκτρομαγνητική Συμβατότητα:H/M Συμβατότητα:HM_Symvatotita</item>
<item>Ηλεκτρομαγνητικό Πεδίο I:Πεδίο 1:Pedio_I</item>
<item>Ηλεκτρομαγνητικό Πεδίο II:Πεδίο 2:Pedio_II</item>
<item>Ηλεκτρονικά Ισχύος I:Ισχύος 1:Isxyos_I</item>
<item>Ηλεκτρονικά Ισχύος II:Ισχύος 2:Isxyos_II</item>
<item>Ηλεκτρονικές Διατάξεις και Μετρήσεις:Ηλεκτρονικές Διατάξεις και Μετρήσεις:Ilektron_Diatakseis_Metriseis</item>
<item>Ηλεκτρονική I:Ηλεκτρονική 1:Ilektroniki_I</item>
<item>Ηλεκτρονική II:Ηλεκτρονική 2:Ilektroniki_II</item>
<item>Ηλεκτρονική III:Ηλεκτρονική 3:Ilektroniki_III</item>
<item>Ημιαγωγά Υλικά: Θεωρία-Διατάξεις:Ημιαγωγά Υλικά:Imiagoga_Ylika</item>
<item>Θεωρία Πιθανοτήτων και Στατιστική:Πιθανότητες:Pithanotites</item>
<item>Θεωρία Πληροφοριών:Θεωρία Πληρ.:Theoria_Plir</item>
<item>Θεωρία Σημάτων και Γραμμικών Συστημάτων:Σήματα &amp; Συστήματα:Analog_Sima</item>
<item>Θεωρία Σκέδασης:Σκέδαση:Skedasi</item>
<item>Θεωρία Υπολογισμών και Αλγορίθμων:ΘΥΑ:THYA</item>
<item>Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων:Τεχνολογία Αντιδραστήρων:Texn_Antidrasthron</item>
<item>Κβαντική Φυσική:Κβαντική:Kvantiki</item>
<item>Κινητές και Δορυφορικές Επικοινωνίες:Κινητές &amp; Δορυφορικές Επικοινωνίες:Kinites_Doryforikes_Epik</item>
<item>Λειτουργικά Συστήματα:Λειτουργικά:OS</item>
<item>Λογική Σχεδίαση:Λογική Σχεδίαση:Logiki_Sxediasi</item>
<item>Λογισμός I:Λογισμός 1:Logismos_I</item>
<item>Λογισμός II:Λογισμός 2:Logismos_II</item>
<item>Μετάδοση Θερμότητας:Μετάδοση Θερμ.:Metadosi_Therm</item>
<item>Μικροεπεξεργαστές και Περιφερειακά:Μίκρο 2:Mikro_II</item>
<item>Μικροκυματική Τηλεπισκόπηση:Τηλεπισκόπηση:Tilepiskopisi</item>
<item>Μικροκύματα I:Μικροκύματα 1:Mikrokymata_I</item>
<item>Μικροκύματα II:Μικροκύματα 2:Mikrokymata_II</item>
<item>Οπτικές Επικοινωνίες:Οπτικές Τηλεπ.:Optikes_Tilep</item>
<item>Οπτική I:Οπτική 1:Optiki_I</item>
<item>Οπτική II:Οπτική 2:Optiki_II</item>
<item>Οργάνωση Υπολογιστών:Οργάνωση Υπολ.:Org_Ypol</item>
<item>Οργάνωση και Διοίκηση Εργοστασίων:Οργάνωση και Διοίκηση Εργοστασίων:Organ_Dioik_Ergostasion</item>
<item>Παράλληλα και Κατανεμημένα Συστήματα:Παράλληλα:Parallila</item>
<item>Προγραμματιζόμενα Κυκλώματα ASIC:ASIC:ASIC</item>
<item>Προγραμματιστικές Τεχνικές:Προγραμματ. Τεχν.:CPP</item>
<item>Προηγμένες Τεχνικές Επεξεργασίας Σήματος:ΠΤΕΣ:PTES</item>
<item>Προσομοίωση και Μοντελοποίηση Συστημάτων:Μοντελοποίηση:Montelopoiisi</item>
<item>Ρομποτική:Ρομποτική:Robotiki</item>
<item>Σήματα και Συστήματα:Σήματα &amp; Συστήματα:Analog_Sima</item>
<item>Σερβοκινητήρια Συστήματα:Σέρβο:Servo</item>
<item>Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας:ΣΠΗΕ:SPHE</item>
<item>Στοχαστικά Σήματα και Διαδικασίες:Στοχαστικό:Stochastic</item>
<item>Στοχαστικό Σήμα:Στοχαστικό:Stochastic</item>
<item>Συστήματα Αυτομάτου Ελέγχου I:ΣΑΕ 1:SAE_I</item>
<item>Συστήματα Αυτομάτου Ελέγχου II:ΣΑΕ 2:SAE_II</item>
<item>Συστήματα Αυτομάτου Ελέγχου III:ΣΑΕ 3:SAE_III</item>
<item>Συστήματα Ηλεκτρικής Ενέργειας I:ΣΗΕ 1:SHE_I</item>
<item>Συστήματα Ηλεκτρικής Ενέργειας II:ΣΗΕ 2:SHE_II</item>
<item>Συστήματα Ηλεκτρικής Ενέργειας III:ΣΗΕ 3:SHE_III</item>
<item>Συστήματα Ηλεκτροκίνησης:Ηλεκτροκίνηση:Ilektrokinisi</item>
<item>Συστήματα Μικροϋπολογιστών:Μίκρο 1:Mikro_I</item>
<item>Συστήματα Πολυμέσων και Εικονική Πραγματικότητα:Πολυμέσα:Polymesa</item>
<item>Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα):Συσ. Υπολογιστών:Sys_Ypologiston</item>
<item>Σχεδίαση Συστημάτων VLSI:VLSI:VLSI</item>
<item>Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων:Σύνθεση:Synthesi</item>
<item>Σύνθεση Τηλεπικοινωνιακών Διατάξεων:Σύνθεση Τηλεπ. Διατάξεων:Synth_Tilep_Diatakseon</item>
<item>Τεχνικές Βελτιστοποίησης:Βελτιστοποίηση:Veltistopoiisi</item>
<item>Τεχνικές Κωδικοποίησης:Τεχνικές Κωδικοποίησης:Texn_Kodikopoiisis</item>
<item>Τεχνικές Σχεδίασης με Η/Υ:Σχέδιο:sxedio</item>
<item>Τεχνικές μη Καταστρεπτικών Δοκιμών:Μη Καταστρεπτικές Δοκιμές:Non_Destructive_Tests</item>
<item>Τεχνική Μηχανική:Τεχν. Μηχαν.:Texn_Mixan</item>
<item>Τεχνολογία Ήχου και Εικόνας:Τεχνολογία Ήχου και Εικόνας:Texn_Ixou_Eikonas</item>
<item>Τεχνολογία Ηλεκτροτεχνικών Υλικών:Ηλεκτροτεχνικά Υλικά:Ilektrotexnika_Ylika</item>
<item>Τεχνολογία Λογισμικού:Τεχνολογία Λογισμικού:SE</item>
<item>Τηλεοπτικά Συστήματα:Τηλεοπτικά:Tileoptika</item>
<item>Τηλεπικοινωνιακά Συστήματα I:Τηλεπικοινωνιακά I:Tilepikoinoniaka_I</item>
<item>Τηλεπικοινωνιακά Συστήματα II:Τηλεπικοινωνιακά II:Tilepikoinoniaka_II</item>
<item>Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr</item>
<item>Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES</item>
<item>Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM</item>
<item>Υψηλές Τάσεις I:Υψηλές 1:Ypsiles_I</item>
<item>Υψηλές Τάσεις II:Υψηλές 2:Ypsiles_II</item>
<item>Υψηλές Τάσεις III:Υψηλές 3:Ypsiles_III</item>
<item>Υψηλές Τάσεις 4:Υψηλές 4:Ypsiles_IV</item>
<item>Φυσική I:Φυσική 1:Fysiki_I</item>
<item>Φωτονική Τεχνολογία:Φωτονική:Fotoniki</item>
<item>Ψηφιακά Συστήματα I:Ψηφιακά 1:Psifiaka_I</item>
<item>Ψηφιακά Συστήματα II:Ψηφιακά 2:Psifiaka_II</item>
<item>Ψηφιακά Συστήματα III:Ψηφιακά 3:Psifiaka_III</item>
<item>Ψηφιακά Φίλτρα:Φίλτρα:Filtra</item>
<item>Ψηφιακές Τηλεπικοινωνίες I:Ψηφιακές Τηλεπ. 1:Psif_Tilep_I</item>
<item>Ψηφιακές Τηλεπικοινωνίες II:Ψηφιακές Τηλεπ. 2:Psif_Tilep_II</item>
<item>Ψηφιακή Επεξεργασία Εικόνας:ΨΕΕ:PSEE</item>
<item>Ψηφιακή Επεξεργασία Σήματος:ΨΕΣ:PSES</item>
</string-array>
</resources>

4
app/src/main/res/xml-v26/app_preferences_user.xml

@ -28,7 +28,7 @@
app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>
<!--<androidx.preference.PreferenceCategory
<androidx.preference.PreferenceCategory
android:key="pref_category_uploading_key"
android:title="@string/pref_category_uploading"
app:iconSpaceReserved="false">
@ -38,7 +38,7 @@
android:title="@string/pref_title_uploading_app_signature_enable"
android:summary="@string/pref_summary_uploading_app_signature_enable"
app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>-->
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:key="pref_category_privacy_key"

4
app/src/main/res/xml/app_preferences_user.xml

@ -49,7 +49,7 @@
app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>
<!--<androidx.preference.PreferenceCategory
<androidx.preference.PreferenceCategory
android:key="pref_category_uploading_key"
android:title="@string/pref_category_uploading"
app:iconSpaceReserved="false">
@ -59,7 +59,7 @@
android:title="@string/pref_title_uploading_app_signature_enable"
android:summary="@string/pref_summary_uploading_app_signature_enable"
app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>-->
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
android:key="pref_category_privacy_key"

10
build.gradle

@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: "com.github.ben-manes.versions"
buildscript {
repositories {
@ -8,10 +9,11 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.26.1'
classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' // Also change in app/gradle/grgit.gradle
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.google.gms:google-services:4.3.2'
classpath 'io.fabric.tools:gradle:1.29.0'
classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle
classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0"
}
}

4
gradle/wrapper/gradle-wrapper.properties

@ -1,6 +1,6 @@
#Sat Feb 09 12:35:50 EET 2019
#Tue Sep 17 12:32:34 EEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

Loading…
Cancel
Save