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. 2
      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) [![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] [![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) ![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' apply plugin: 'io.fabric'
android { android {
compileSdkVersion 28 compileSdkVersion 29
buildToolsVersion = '29.0.2'
defaultConfig { defaultConfig {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId "gr.thmmy.mthmmy" applicationId "gr.thmmy.mthmmy"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 28 targetSdkVersion 29
versionCode 16 versionCode 22
versionName "1.6.1" versionName "1.7.4"
archivesBaseName = "mTHMMY-v$versionName" archivesBaseName = "mTHMMY-v$versionName"
buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\""
buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\""
@ -41,44 +42,56 @@ android {
} }
} }
def firebaseReleaseProjectId = "mthmmy-release-3aef0"
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->
if (task.name.contains("assembleRelease")) { if (task.name.contains("assembleRelease")) {
task.getDependsOn().add({ task.getDependsOn().add({
def inputFile = new File("app/google-services.json") def googleServicesFile = new File("app/src/release/google-services.json")
def json = new JsonSlurper().parseText(inputFile.text) if(googleServicesFile.exists()){
if (json.project_info.project_id != "mthmmy-release-3aef0") def json = new JsonSlurper().parseText(googleServicesFile.text)
throw new GradleException('Please supply the correct google-services.json for release (or manually change the id above)!') 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")) { } else if (task.name.contains("assembleDebug")) {
task.getDependsOn().add({ task.getDependsOn().add({
def inputFile = new File("app/google-services.json") def googleServicesFile = new File("app/src/debug/google-services.json")
def json = new JsonSlurper().parseText(inputFile.text) if(googleServicesFile.exists()){
if (json.project_info.project_id == "mthmmy-release-3aef0") def json = new JsonSlurper().parseText(googleServicesFile.text)
throw new GradleException('Please replace the release google-services.json with a debug one!') 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 { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0-alpha02' implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview: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.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.android.material:material:1.0.0'
implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-core:17.0.0'
implementation 'com.google.firebase:firebase-messaging:17.3.4' implementation 'com.google.firebase:firebase-messaging:19.0.1'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.squareup.okhttp3:okhttp:3.12.0' 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.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' 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 '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.franmontiel:PersistentCookieJar:1.0.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3' implementation 'com.github.PhilJay:MPAndroidChart:3.0.3'
implementation 'com.mikepenz:materialdrawer:6.1.1' implementation 'com.mikepenz:materialdrawer:6.1.1'
implementation 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar' implementation 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@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 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'//TODO: deprecated!
implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2' implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2'
implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'ru.noties:markwon:2.0.0' implementation 'ru.noties:markwon:2.0.2'
implementation 'net.gotev:uploadservice:3.4.2' implementation 'net.gotev:uploadservice:3.5.2'
implementation 'net.gotev:uploadservice-okhttp:3.4.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.4' implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler
//Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'

2
app/gradle/grgit.gradle

@ -6,7 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' classpath 'org.ajoberstar.grgit:grgit-core:3.1.1'
} }
} }

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

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

@ -39,7 +39,7 @@
<body> <body>
<ul> <ul>
<li> <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>
<li> <li>
<h5><a href="https://square.github.io/picasso/">Picasso</a>&nbsp;v2.5.2 (Copyright ©2013 Square, Inc.)</h5> <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> <h5><a href="https://github.com/JakeWharton/timber">Timber</a>&nbsp;v4.7.1 (Copyright ©2013 Jake Wharton)</h5>
</li> </li>
<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>
<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>
<li> <li>
<h5><a href="https://github.com/ajoberstar/grgit">Grgit</a>&nbsp;v3.0.0 (Copyright ©2018 Andrew Oberstar)</h5> <h5><a href="https://github.com/ajoberstar/grgit">Grgit</a>&nbsp;v3.0.0 (Copyright ©2018 Andrew Oberstar)</h5>
</li> </li>
<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> </li>
</ul> </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.view.View;
import android.webkit.WebView; import android.webkit.WebView;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ScrollView; import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.appbar.AppBarLayout;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.drawerlayout.widget.DrawerLayout; import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.appbar.AppBarLayout;
import gr.thmmy.mthmmy.BuildConfig; import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
public class AboutActivity extends BaseActivity { public class AboutActivity extends BaseActivity {
private static final int TIME_INTERVAL = 1000; private static final int TIME_INTERVAL = 1000;
@ -31,8 +35,9 @@ public class AboutActivity extends BaseActivity {
private AppBarLayout appBar; private AppBarLayout appBar;
private CoordinatorLayout coordinatorLayout; private CoordinatorLayout coordinatorLayout;
private ScrollView mainContent;
private AlertDialog alertDialog; private AlertDialog alertDialog;
private FrameLayout trollGif; private FrameLayout easterEggImage;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -69,41 +74,41 @@ public class AboutActivity extends BaseActivity {
createDrawer(); createDrawer();
drawer.setSelection(ABOUT_ID); drawer.setSelection(ABOUT_ID);
final ScrollView mainContent = findViewById(R.id.scrollview); mainContent = findViewById(R.id.scrollview);
trollGif = findViewById(R.id.trollPicFrame); 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); TextView versionTextView = findViewById(R.id.version);
if (tv != null) { if (versionTextView != null) {
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
tv.setText(getString(R.string.version, versionName + versionInfo)); versionTextView.setText(getString(R.string.version, versionName + versionInfo));
else else
tv.setText(getString(R.string.version, versionName)); versionTextView.setText(getString(R.string.version, versionName));
if(BuildConfig.DEBUG && gitExists){ if(gitExists){
tv.setOnClickListener(view -> { versionTextView.setOnClickListener(view -> {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/ThmmyNoLife/mTHMMY/commit/" + BuildConfig.COMMIT_HASH)); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/ThmmyNoLife/mTHMMY/commit/" + BuildConfig.COMMIT_HASH));
startActivity(intent); startActivity(intent);
}); });
} }
else{ // Easter Egg
tv.setOnClickListener(view -> { versionTextView.setOnLongClickListener(view -> {
if (mVersionLastPressedTime + TIME_INTERVAL > System.currentTimeMillis()) { Toast.makeText(getApplicationContext(), BaseApplication.getFirebaseProjectId(), Toast.LENGTH_SHORT).show();
if (mVersionPressedCounter == TIMES_TO_PRESS) { return true;
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;
}
});
}
} }
TextView privacyPolicy = findViewById(R.id.privacy_policy_header); TextView privacyPolicy = findViewById(R.id.privacy_policy_header);
@ -121,6 +126,14 @@ public class AboutActivity extends BaseActivity {
super.onResume(); super.onResume();
} }
@Override
public void onBackPressed() {
if(easterEggImage.getVisibility()==View.INVISIBLE)
super.onBackPressed();
else
hideEasterEgg();
}
public void displayApacheLibraries(View v) { public void displayApacheLibraries(View v) {
LayoutInflater inflater = LayoutInflater.from(this); LayoutInflater inflater = LayoutInflater.from(this);
WebView webView = (WebView) inflater.inflate(R.layout.dialog_licenses, coordinatorLayout, false); 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); 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.ScrollView;
import android.widget.Toast; import android.widget.Toast;
import com.google.firebase.analytics.FirebaseAnalytics;
import androidx.appcompat.widget.AppCompatButton; import androidx.appcompat.widget.AppCompatButton;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.firebase.analytics.FirebaseAnalytics;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.main.MainActivity; import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.base.BaseActivity; 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.ProgressBar;
import android.widget.Toast; 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 com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
@ -17,10 +22,6 @@ import org.jsoup.select.Elements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; 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.R;
import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.create_topic.CreateTopicActivity; 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); progressBar = findViewById(R.id.progressBar);
newTopicFAB = findViewById(R.id.board_fab); newTopicFAB = findViewById(R.id.board_fab);
newTopicFAB.setTag(true);
if (!sessionManager.isLoggedIn()) newTopicFAB.hide(); if (!sessionManager.isLoggedIn()) newTopicFAB.hide();
else { else {
newTopicFAB.setOnClickListener(view -> { 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.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.model.Board; 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.os.Bundle;
import android.widget.Toast; 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.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager; 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.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity; import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.topic.TopicActivity; 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.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Bookmark; 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.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import java.util.ArrayList;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.model.Bookmark; 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("sc", sc)
.addFormDataPart("subject", strings[1]) .addFormDataPart("subject", strings[1])
.addFormDataPart("topic", topic) .addFormDataPart("topic", topic)
.addFormDataPart("icon", "xx")
.build(); .build();
Request post = new Request.Builder() 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; package gr.thmmy.mthmmy.activities.downloads;
import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Toast; 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.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -13,10 +23,8 @@ import org.jsoup.select.Elements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; 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.R;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Download; import gr.thmmy.mthmmy.model.Download;
@ -29,6 +37,8 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
import static gr.thmmy.mthmmy.activities.upload.UploadActivity.BUNDLE_UPLOAD_CATEGORY;
public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener { public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener {
/** /**
* The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle. * 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 MaterialProgressBar progressBar;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private DownloadsAdapter downloadsAdapter; private DownloadsAdapter downloadsAdapter;
//private FloatingActionButton uploadFAB; private FloatingActionButton uploadFAB;
private ParseDownloadPageTask parseDownloadPageTask; private ParseDownloadPageTask parseDownloadPageTask;
private int numberOfPages = -1; private int numberOfPages = -1;
@ -113,37 +123,37 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
} }
}); });
// uploadFAB = findViewById(R.id.upload_fab); uploadFAB = findViewById(R.id.upload_fab);
// uploadFAB.setEnabled(false); uploadFAB.setEnabled(false);
// uploadFAB.hide(); uploadFAB.hide();
parseDownloadPageTask = new ParseDownloadPageTask(); parseDownloadPageTask = new ParseDownloadPageTask();
parseDownloadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, downloadsUrl); parseDownloadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, downloadsUrl);
} }
// @Override @Override
// public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// // Inflates the menu; this adds items to the action bar if it is present. // Inflates the menu; this adds items to the action bar if it is present.
// getMenuInflater().inflate(R.menu.downloads_menu, menu); getMenuInflater().inflate(R.menu.downloads_menu, menu);
// super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
// return true; return true;
// } }
//
// @Override @Override
// public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
// // Handle presses on the action bar items // Handle presses on the action bar items
// switch (item.getItemId()) { switch (item.getItemId()) {
// case R.id.menu_upload: case R.id.menu_upload:
// Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class); Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class);
// Bundle extras = new Bundle(); Bundle extras = new Bundle();
// extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav); extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav);
// intent.putExtras(extras); intent.putExtras(extras);
// startActivity(intent); startActivity(intent);
// return true; return true;
// default: default:
// return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
// } }
// } }
@Override @Override
public void onLoadMore() { public void onLoadMore() {
@ -198,7 +208,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE); if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
//if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false); if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
} }
@Override @Override
@ -296,7 +306,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
toolbar.setTitle(downloadsTitle); toolbar.setTitle(downloadsTitle);
++pagesLoaded; ++pagesLoaded;
//if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true); if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
progressBar.setVisibility(ProgressBar.INVISIBLE); progressBar.setVisibility(ProgressBar.INVISIBLE);
downloadsAdapter.notifyDataSetChanged(); downloadsAdapter.notifyDataSetChanged();
isLoadingMore = false; 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.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Download; 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.os.Bundle;
import android.widget.Toast; 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.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@ -19,6 +14,12 @@ import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager; 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.R;
import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.board.BoardActivity; 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.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bignerdranch.expandablerecyclerview.ChildViewHolder; import com.bignerdranch.expandablerecyclerview.ChildViewHolder;
import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter; import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
import com.bignerdranch.expandablerecyclerview.ParentViewHolder; import com.bignerdranch.expandablerecyclerview.ParentViewHolder;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.Board; 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.RelativeLayout;
import android.widget.Toast; 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 com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
@ -19,9 +23,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication; 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.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; 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.RelativeLayout;
import android.widget.Toast; 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.nodes.Document;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -17,10 +21,8 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
@ -145,9 +147,9 @@ public class RecentFragment extends BaseFragment {
topicSummaries.addAll(fetchedRecent); topicSummaries.addAll(fetchedRecent);
recentAdapter.notifyDataSetChanged(); recentAdapter.notifyDataSetChanged();
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { } 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 { } else {
Toast.makeText(getContext(), "Unexpected error," + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Unexpected error," +
" please contact the developers with the details", Toast.LENGTH_LONG).show(); " 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.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; 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.RelativeLayout;
import android.widget.Toast; 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.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -18,10 +23,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.R;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
@ -142,11 +143,12 @@ public class UnreadFragment extends BaseFragment {
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
if (unreadTask.isRunning()) if (unreadTask!=null && unreadTask.isRunning())
unreadTask.cancel(true); unreadTask.cancel(true);
if (markReadTask.isRunning()) if (markReadTask!=null && markReadTask.isRunning())
markReadTask.cancel(true); markReadTask.cancel(true);
topicSummaries.clear(); if(topicSummaries!=null)
topicSummaries.clear();
} }
public interface UnreadFragmentInteractionListener extends FragmentInteractionListener { 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.TextView;
import android.widget.Toast; 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.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
@ -33,12 +40,6 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import androidx.appcompat.app.AlertDialog; 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.R;
import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.create_pm.CreatePMActivity; 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_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL; 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> * 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 { //Finds personal text
Element tmpEl = profilePage.select("td.windowbg:nth-child(2)").first(); Element tmpEl = profilePage.select("td.windowbg:nth-child(2)").first();
if (tmpEl != null) { if (tmpEl != null) {
personalText = tmpEl.text().trim(); personalText = emojiTagToHtml(tmpEl.text().trim());
} else { } else {
//Should never get here! //Should never get here!
//Something is wrong. //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.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList; import java.util.ArrayList;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.PostSummary; 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.ProgressBar;
import android.widget.Toast; 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.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
@ -17,9 +21,6 @@ import java.util.ArrayList;
import javax.net.ssl.SSLHandshakeException; 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.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseFragment; 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.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.fragment.app.Fragment;
import com.github.mikephil.charting.charts.HorizontalBarChart; import com.github.mikephil.charting.charts.HorizontalBarChart;
import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.components.AxisBase;
@ -37,7 +39,6 @@ import java.util.List;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import androidx.fragment.app.Fragment;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; 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.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.fragment.app.Fragment;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
@ -21,7 +23,6 @@ import org.jsoup.select.Elements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
import androidx.fragment.app.Fragment;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber; import timber.log.Timber;
@ -144,7 +145,7 @@ public class SummaryFragment extends Fragment {
|| summaryRow.text().contains("Κατάσταση")) continue; || summaryRow.text().contains("Κατάσταση")) continue;
else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) { else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) {
//This needs special handling since it may have css //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 //Add stuff to make it work in WebView
//style.css //style.css
pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n" + 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 android.os.Bundle;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; 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.view.View;
import android.widget.Toast; import android.widget.Toast;
import java.util.ArrayList;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
import timber.log.Timber; 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 android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity; import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; 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.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.editorview.EditorView; import gr.thmmy.mthmmy.editorview.EditorView;
import gr.thmmy.mthmmy.editorview.EmojiKeyboard; import gr.thmmy.mthmmy.editorview.EmojiKeyboard;
import gr.thmmy.mthmmy.model.Shout; import gr.thmmy.mthmmy.model.Shout;
@ -133,6 +135,7 @@ public class ShoutboxFragment extends Fragment {
progressBar.setVisibility(View.INVISIBLE); progressBar.setVisibility(View.INVISIBLE);
if (resultCode == NetworkResultCodes.SUCCESSFUL) { if (resultCode == NetworkResultCodes.SUCCESSFUL) {
Timber.i("Shout was sent successfully"); Timber.i("Shout was sent successfully");
BaseApplication.getInstance().logFirebaseAnalyticsEvent("shout", null);
editorView.getEditText().getText().clear(); editorView.getEditText().getText().clear();
shoutboxViewModel.loadShoutbox(true); shoutboxViewModel.loadShoutbox(true);
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { } 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.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.IOException; import java.io.IOException;
@ -60,22 +61,24 @@ public class Posting {
String finalUrl = response.request().url().toString(); String finalUrl = response.request().url().toString();
if (finalUrl.contains("action=post")) { if (finalUrl.contains("action=post")) {
Document postErrorPage = Jsoup.parse(response.body().string()); Document postErrorPage = Jsoup.parse(response.body().string());
String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first() Element errorsElement = postErrorPage.select("tr[id=errors] div[id=error_list]").first();
.toString().split("<br>"); if(errorsElement!=null){
for (int i = 0; i < errors.length; ++i) { //TODO test String[] errors = errorsElement.toString().split("<br>");
Timber.d(String.valueOf(i)); for (int i = 0; i < errors.length; ++i) { //TODO test
Timber.d(errors[i]); Timber.d(String.valueOf(i));
} Timber.d(errors[i]);
for (String error : errors) { }
if (error.contains("Your session timed out while posting") || for (String error : errors) {
error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή")) if (error.contains("Your session timed out while posting") ||
return REPLY_STATUS.SESSION_ENDED; error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή"))
if (error.contains("No subject was filled in") return REPLY_STATUS.SESSION_ENDED;
|| error.contains("Δεν δόθηκε τίτλος")) if (error.contains("No subject was filled in")
return REPLY_STATUS.NO_SUBJECT; || error.contains("Δεν δόθηκε τίτλος"))
if (error.contains("The message body was left empty") return REPLY_STATUS.NO_SUBJECT;
|| error.contains("Δεν δόθηκε κείμενο για το μήνυμα")) if (error.contains("The message body was left empty")
return REPLY_STATUS.EMPTY_BODY; || error.contains("Δεν δόθηκε κείμενο για το μήνυμα"))
return REPLY_STATUS.EMPTY_BODY;
}
} }
return REPLY_STATUS.NEW_REPLY_WHILE_POSTING; 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.TextView;
import android.widget.Toast; 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.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat; import androidx.core.content.res.ResourcesCompat;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView; 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.R;
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; 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.ReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask; import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask;
import gr.thmmy.mthmmy.base.BaseActivity; 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 @Override
public void onPrepareForReplyStarted() { public void onPrepareForReplyStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE); 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.RelativeLayout;
import android.widget.TextView; 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.charts.HorizontalBarChart;
import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis;
@ -53,13 +61,6 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; 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.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity; import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; 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_WHITE;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.USER_COLOR_YELLOW; import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.USER_COLOR_YELLOW;
import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager; import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
import static gr.thmmy.mthmmy.utils.FileUtils.faIconFromFilename;
/** /**
* Custom {@link RecyclerView.Adapter} used for topics. * Custom {@link RecyclerView.Adapter} used for topics.
@ -393,7 +395,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
attached.setClickable(true); attached.setClickable(true);
attached.setTypeface(Typeface.createFromAsset(context.getAssets() attached.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf")); , "fonts/fontawesome-webfont.ttf"));
attached.setText(faIconFromFilename(attachedFile.getFilename()) + " " attached.setText(faIconFromFilename(context, attachedFile.getFilename()) + " "
+ attachedFile.getFilename() + attachedFile.getFileInfo()); + attachedFile.getFilename() + attachedFile.getFileInfo());
attached.setTextColor(filesTextColor); attached.setTextColor(filesTextColor);
attached.setPadding(0, 3, 0, 3); attached.setPadding(0, 3, 0, 3);
@ -1036,37 +1038,4 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public interface OnPostFocusChangeListener { public interface OnPostFocusChangeListener {
void onPostFocusChange(int position); 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("sc", strings[4])
.addFormDataPart("subject", strings[5]) .addFormDataPart("subject", strings[5])
.addFormDataPart("topic", strings[6]) .addFormDataPart("topic", strings[6])
.addFormDataPart("icon", strings[7])
.build(); .build();
Request post = new Request.Builder() Request post = new Request.Builder()
.url(strings[0]) .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; package gr.thmmy.mthmmy.activities.topic.tasks;
public class PrepareForEditResult { 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 int position;
private boolean successful; private boolean successful;
public PrepareForEditResult(String postText, String commitEditUrl, String numReplies, String seqnum, 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.postText = postText;
this.commitEditUrl = commitEditUrl; this.commitEditUrl = commitEditUrl;
this.numReplies = numReplies; this.numReplies = numReplies;
this.seqnum = seqnum; this.seqnum = seqnum;
this.sc = sc; this.sc = sc;
this.topic = topic; this.topic = topic;
this.icon = icon;
this.position = position; this.position = position;
this.successful = successful; this.successful = successful;
} }
@ -41,6 +42,10 @@ public class PrepareForEditResult {
return topic; return topic;
} }
public String getIcon() {
return icon;
}
public int getPosition() { public int getPosition() {
return position; 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; Document document;
String url = strings[0]; String url = strings[0];
Request request = new Request.Builder() Request request = new Request.Builder()
.url(url + ";wap2") /*.url(url + ";wap2")*/
.url(url)
.build(); .build();
try { try {
String postText, commitEditURL, numReplies, seqnum, sc, topic; String postText, commitEditURL, numReplies, seqnum, sc, topic, icon;
OkHttpClient client = BaseApplication.getInstance().getClient(); OkHttpClient client = BaseApplication.getInstance().getClient();
Response response = client.newCall(request).execute(); Response response = client.newCall(request).execute();
document = ParseHelpers.parse(response.body().string()); 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(); postText = message.text();
commitEditURL = document.select("form").first().attr("action"); commitEditURL = form.attr("action");
numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12); numReplies = replyPageUrl.substring(replyPageUrl.indexOf("num_replies=") + 12);
seqnum = document.select("input[name=seqnum]").first().attr("value"); seqnum = form.select("input[name=seqnum]").first().attr("value");
sc = document.select("input[name=sc]").first().attr("value"); sc = form.select("input[name=sc]").first().attr("value");
topic = document.select("input[name=topic]").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) { } catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Prepare failed."); 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 okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyResult> { public class PrepareForReplyTask extends AsyncTask<Integer, Void, PrepareForReplyResult> {
private PrepareForReplyCallbacks listener; private PrepareForReplyCallbacks listener;
private OnPrepareForReplyFinished finishListener; private OnPrepareForReplyFinished finishListener;
private String replyPageUrl; private String replyPageUrl;
public PrepareForReply(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, public PrepareForReplyTask(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener,
String replyPageUrl) { String replyPageUrl) {
this.listener = listener; this.listener = listener;
this.finishListener = finishListener; this.finishListener = finishListener;
this.replyPageUrl = replyPageUrl; this.replyPageUrl = replyPageUrl;
@ -49,12 +49,16 @@ public class PrepareForReply extends AsyncTask<Integer, Void, PrepareForReplyRes
seqnum = document.select("input[name=seqnum]").first().attr("value"); seqnum = document.select("input[name=seqnum]").first().attr("value");
sc = document.select("input[name=sc]").first().attr("value"); sc = document.select("input[name=sc]").first().attr("value");
topic = document.select("input[name=topic]").first().attr("value"); topic = document.select("input[name=topic]").first().attr("value");
} catch (IOException | Selector.SelectorParseException e) { } catch (NullPointerException e) {
Timber.e(e, "Prepare failed."); // 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); return new PrepareForReplyResult(false, null, null, null, null, null);
} }
StringBuilder buildedQuotes = new StringBuilder(""); StringBuilder buildedQuotes = new StringBuilder();
for (Integer postIndex : postIndices) { for (Integer postIndex : postIndices) {
request = new Request.Builder() request = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" + .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("sc", args[4])
.addFormDataPart("subject", args[0]) .addFormDataPart("subject", args[0])
.addFormDataPart("topic", args[5]) .addFormDataPart("topic", args[5])
.addFormDataPart("icon", "xx")
.build(); .build();
Request post = new Request.Builder() Request post = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=post2") .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.RadioGroup;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable;
import java.util.Calendar; 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.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import timber.log.Timber; import timber.log.Timber;
public class UploadFieldsBuilderActivity extends AppCompatActivity { public class UploadFieldsBuilderActivity extends BaseActivity {
static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE";
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 BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER";
static final String RESULT_FILENAME = "RESULT_FILENAME"; static final String RESULT_FILENAME = "RESULT_FILENAME";
static final String RESULT_TITLE = "RESULT_TITLE"; static final String RESULT_TITLE = "RESULT_TITLE";
static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION"; static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION";
private String course, semester; private String courseName, courseMinifiedName, courseGreeklishName, semester;
private boolean isValidYear;
private LinearLayout semesterChooserLinear; private LinearLayout semesterChooserLinear;
private RadioGroup typeRadio, semesterRadio; private RadioGroup typeRadio, semesterRadio;
@ -38,32 +42,26 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
String working = s.toString(); String working = s.toString();
boolean isValid;
if (working.length() == 4) { if (working.length() == 4) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR); int currentYear = Calendar.getInstance().get(Calendar.YEAR);
int inputYear = Integer.parseInt(working); int inputYear = Integer.parseInt(working);
isValid = inputYear <= currentYear && inputYear > 2000; isValidYear = inputYear <= currentYear && inputYear > 1980;
} else { } else
isValid = false; isValidYear = false;
}
if (!isValid) { if (!isValidYear)
year.setError("Please enter a valid year"); year.setError("Please enter a valid year");
} else { else
year.setError(null); year.setError(null);
}
} }
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) { }
}
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
}
}; };
@Override @Override
@ -73,9 +71,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
Bundle extras = getIntent().getExtras(); Bundle extras = getIntent().getExtras();
if (extras != null) { 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); 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(); Toast.makeText(this, "Something went wrong!", Toast.LENGTH_SHORT).show();
Timber.e("Bundle came empty in %s", UploadFieldsBuilderActivity.class.getSimpleName()); Timber.e("Bundle came empty in %s", UploadFieldsBuilderActivity.class.getSimpleName());
@ -86,7 +86,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
} }
//Initialize toolbar //Initialize toolbar
Toolbar toolbar = findViewById(R.id.toolbar); toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.upload_fields_builder_toolbar_title); toolbar.setTitle(R.string.upload_fields_builder_toolbar_title);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
@ -94,6 +94,9 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
createDrawer();
drawer.setSelection(UPLOAD_ID, false);
semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester); semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester);
semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group); semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group);
semesterRadio.check(Integer.parseInt(semester) % 2 == 0 semesterRadio.check(Integer.parseInt(semester) % 2 == 0
@ -121,8 +124,8 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
} else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) { } else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) {
Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show();
return; return;
} else if (year.getText().toString().isEmpty()) { } else if (year.getText().toString().isEmpty() || !isValidYear) {
Toast.makeText(view.getContext(), "Please choose a year for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(), "Please choose a valid year for the upload", Toast.LENGTH_SHORT).show();
return; return;
} }
@ -139,11 +142,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildFilename() { private String buildFilename() {
switch (typeRadio.getCheckedRadioButtonId()) { switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams: 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: 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: case R.id.upload_fields_builder_radio_button_notes:
return getGreeklishCourseName() + "_" + year.getText().toString() + "_Shmeiwseis"; return courseGreeklishName + "_" + year.getText().toString() + "_Simeioseis";
default: default:
return null; return null;
} }
@ -153,11 +156,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildTitle() { private String buildTitle() {
switch (typeRadio.getCheckedRadioButtonId()) { switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams: 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: 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: case R.id.upload_fields_builder_radio_button_notes:
return getMinifiedCourseName() + " - " + "Σημειώσεις παραδόσεων " + year.getText().toString(); return "[" + courseMinifiedName + "] - " + "Σημειώσεις παραδόσεων " + year.getText().toString();
default: default:
return null; return null;
} }
@ -166,11 +169,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildDescription() { private String buildDescription() {
switch (typeRadio.getCheckedRadioButtonId()) { switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams: 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: 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: case R.id.upload_fields_builder_radio_button_notes:
return "Σημειώσεις των παραδόσεων του μαθήματος \"" + course + "\" από το " + year.getText().toString(); return "Σημειώσεις των παραδόσεων του μαθήματος \"" + courseName + "\" από το " + year.getText().toString();
default: default:
return null; return null;
} }
@ -201,316 +204,4 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
return null; 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; package gr.thmmy.mthmmy.activities.upload;
import android.content.Context; 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.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.OpenableColumns;
import android.widget.Toast; 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.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import androidx.annotation.NonNull; import gr.thmmy.mthmmy.utils.FileUtils;
import androidx.annotation.Nullable;
import timber.log.Timber; import timber.log.Timber;
class UploadsHelper { public class UploadsHelper {
private static final int DEFAULT_MIN_WIDTH_QUALITY = 400; private static final int BUFFER = 4096;
private static final String CACHE_IMAGE_NAME = "tempUploadFile.jpg"; private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads";
@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;
}
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
@Nullable @Nullable
static String createTempFile(Context context, Uri fileUri, String newFilename) { static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) {
String oldFilename = filenameFromUri(context, fileUri); String oldFilename = FileUtils.filenameFromUri(context, fileUri);
String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); String fileExtension = oldFilename.substring(oldFilename.indexOf('.'));
String destinationFilename = Environment.getExternalStorageDirectory().getPath() + 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 tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads"); File.separatorChar + TEMP_FILES_DIRECTORY);
if (!tempDirectory.exists()) { if (!tempDirectory.exists() && !tempDirectory.mkdirs()) {
if (!tempDirectory.mkdirs()) { Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName());
Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show();
Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); return null;
return null;
}
} }
InputStream inputStream; 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") @Nullable
static void deleteTempFiles() { static File createZipFile(@NonNull String zipFilename) {
File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() + // Create a zip file name
File.separatorChar + "~tmp_mThmmy_uploads"); File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) +
File.separator + "mTHMMY");
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;
AssetFileDescriptor fileDescriptor = null; if (!zipFolder.exists() && !zipFolder.mkdirs()) {
try { Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName());
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r"); return null;
} catch (FileNotFoundException e) {
e.printStackTrace();
} }
assert fileDescriptor != null; return new File(zipFolder, zipFilename);
return BitmapFactory.decodeFileDescriptor(
fileDescriptor.getFileDescriptor(), null, options);
} }
static int getRotation(Context context, Uri imageUri) { static void zip(Context context, Uri[] files, Uri zipFile) {
int rotation = 0;
try { try {
BufferedInputStream origin;
context.getContentResolver().notifyChange(imageUri, null); OutputStream dest = context.getContentResolver().openOutputStream(zipFile);
ExifInterface exif = new ExifInterface(imageUri.getPath()); assert dest != null;
int orientation = exif.getAttributeInt( ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
ExifInterface.TAG_ORIENTATION, byte[] data = new byte[BUFFER];
ExifInterface.ORIENTATION_NORMAL);
for (Uri file : files) {
switch (orientation) { InputStream inputStream = context.getContentResolver().openInputStream(file);
case ExifInterface.ORIENTATION_ROTATE_270: assert inputStream != null;
rotation = 270; origin = new BufferedInputStream(inputStream, BUFFER);
break;
case ExifInterface.ORIENTATION_ROTATE_180: ZipEntry entry = new ZipEntry(FileUtils.filenameFromUri(context, file));
rotation = 180; out.putNextEntry(entry);
break; int count;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90; while ((count = origin.read(data, 0, BUFFER)) != -1) {
break; out.write(data, 0, count);
}
origin.close();
} }
out.close();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return rotation;
} }
static Bitmap rotate(Bitmap bm, int rotation) { public static void deleteTempFiles(Storage storage) {
if (rotation != 0) { File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
Matrix matrix = new Matrix(); File.separatorChar + TEMP_FILES_DIRECTORY);
matrix.postRotate(rotation);
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); 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.Manifest;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; 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.android.material.bottomsheet.BottomSheetDialog;
import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessaging;
import com.mikepenz.fontawesome_typeface_library.FontAwesome; 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.DrawerBuilder;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.ProfileDrawerItem; import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
import com.snatik.storage.Storage;
import net.gotev.uploadservice.UploadService;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -35,14 +51,6 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; 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.R;
import gr.thmmy.mthmmy.activities.AboutActivity; import gr.thmmy.mthmmy.activities.AboutActivity;
import gr.thmmy.mthmmy.activities.LoginActivity; 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.profile.ProfileActivity;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxActivity; 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.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper; import gr.thmmy.mthmmy.services.DownloadHelper;
import gr.thmmy.mthmmy.services.UploadsReceiver;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.FileUtils; import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.viewmodel.BaseViewModel; import gr.thmmy.mthmmy.viewmodel.BaseViewModel;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import ru.noties.markwon.LinkResolverDef; import ru.noties.markwon.LinkResolverDef;
import ru.noties.markwon.Markwon; 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.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DEFAULT_HOME_TAB; 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.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.session.SessionManager.SUCCESS;
import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType; import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType;
@ -83,6 +95,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//SessionManager //SessionManager
protected static SessionManager sessionManager; protected static SessionManager sessionManager;
//Storage manager
protected Storage storage;
//Bookmarks //Bookmarks
public static final String BOOKMARKS_SHARED_PREFS = "bookmarksSharedPrefs"; public static final String BOOKMARKS_SHARED_PREFS = "bookmarksSharedPrefs";
public static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey"; public static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey";
@ -97,6 +112,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//Common UI elements //Common UI elements
protected Toolbar toolbar; protected Toolbar toolbar;
protected Drawer drawer; protected Drawer drawer;
//Uploads progress dialog
UploadsShowDialogReceiver uploadsShowDialogReceiver;
AlertDialog uploadsProgressDialog;
private MainActivity mainActivity; private MainActivity mainActivity;
private boolean isMainActivity; private boolean isMainActivity;
@ -124,16 +142,25 @@ public abstract class BaseActivity extends AppCompatActivity {
BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class); BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton)); baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton));
storage = new Storage(getApplicationContext());
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
updateDrawer(); updateDrawer();
if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown){ if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown) {
isUserConsentDialogShown=true; isUserConsentDialogShown = true;
showUserConsentDialog(); 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 @Override
@ -141,6 +168,10 @@ public abstract class BaseActivity extends AppCompatActivity {
super.onPause(); super.onPause();
if (drawer != null) //close drawer animation after returning to activity if (drawer != null) //close drawer animation after returning to activity
drawer.closeDrawer(); 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; return sessionManager;
} }
public Storage getStorage() {
return storage;
}
//TODO: move stuff below (?) //TODO: move stuff below (?)
//------------------------------------------DRAWER STUFF---------------------------------------- //------------------------------------------DRAWER STUFF----------------------------------------
protected static final int HOME_ID = 0; protected static final int HOME_ID = 0;
@ -257,14 +292,15 @@ public abstract class BaseActivity extends AppCompatActivity {
.withName(R.string.downloads) .withName(R.string.downloads)
.withIcon(downloadsIcon) .withIcon(downloadsIcon)
.withSelectedIcon(downloadsIconSelected); .withSelectedIcon(downloadsIconSelected);
// uploadItem = new PrimaryDrawerItem()
// .withTextColor(primaryColor) uploadItem = new PrimaryDrawerItem()
// .withSelectedColor(selectedPrimaryColor) .withTextColor(primaryColor)
// .withSelectedTextColor(selectedSecondaryColor) .withSelectedColor(selectedPrimaryColor)
// .withIdentifier(UPLOAD_ID) .withSelectedTextColor(selectedSecondaryColor)
// .withName(R.string.upload) .withIdentifier(UPLOAD_ID)
// .withIcon(uploadIcon) .withName(R.string.upload)
// .withSelectedIcon(uploadIconSelected); .withIcon(uploadIcon)
.withSelectedIcon(uploadIconSelected);
shoutboxItem = new PrimaryDrawerItem() shoutboxItem = new PrimaryDrawerItem()
.withTextColor(primaryColor) .withTextColor(primaryColor)
@ -397,11 +433,11 @@ public abstract class BaseActivity extends AppCompatActivity {
intent.putExtras(extras); intent.putExtras(extras);
startActivity(intent); startActivity(intent);
} }
// } else if (drawerItem.equals(UPLOAD_ID)) { } else if (drawerItem.equals(UPLOAD_ID)) {
// if (!(BaseActivity.this instanceof UploadActivity)) { if (!(BaseActivity.this instanceof UploadActivity)) {
// Intent intent = new Intent(BaseActivity.this, UploadActivity.class); Intent intent = new Intent(BaseActivity.this, UploadActivity.class);
// startActivity(intent); startActivity(intent);
// } }
} else if (drawerItem.equals(BOOKMARKS_ID)) { } else if (drawerItem.equals(BOOKMARKS_ID)) {
if (!(BaseActivity.this instanceof BookmarksActivity)) { if (!(BaseActivity.this instanceof BookmarksActivity)) {
Intent intent = new Intent(BaseActivity.this, BookmarksActivity.class); 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 if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{ {
drawer.removeItem(DOWNLOADS_ID); drawer.removeItem(DOWNLOADS_ID);
// drawer.removeItem(UPLOAD_ID); drawer.removeItem(UPLOAD_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
profileDrawerItem.withName(sessionManager.getUsername()); profileDrawerItem.withName(sessionManager.getUsername());
setDefaultAvatar(); setDefaultAvatar();
@ -460,9 +496,9 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!drawer.getDrawerItems().contains(downloadsItem)) { if (!drawer.getDrawerItems().contains(downloadsItem)) {
drawer.addItemAtPosition(downloadsItem, 4); drawer.addItemAtPosition(downloadsItem, 4);
} }
// if (!drawer.getDrawerItems().contains(uploadItem)) { if (!drawer.getDrawerItems().contains(uploadItem)) {
// drawer.addItemAtPosition(uploadItem, 5); drawer.addItemAtPosition(uploadItem, 5);
// } }
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername()); profileDrawerItem.withName(sessionManager.getUsername());
if (sessionManager.hasAvatar()) if (sessionManager.hasAvatar())
@ -687,10 +723,10 @@ public abstract class BaseActivity extends AppCompatActivity {
//-------------------------------------------BOOKMARKS END------------------------------------------ //-------------------------------------------BOOKMARKS END------------------------------------------
//-------PERMS--------- //-------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 //True if permissions are OK
private boolean checkPerms() { protected boolean checkPerms() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = { String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
@ -703,13 +739,13 @@ public abstract class BaseActivity extends AppCompatActivity {
} }
//Display popup for user to grant permission //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) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = { String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_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 public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions
, @NonNull int[] grantResults) { , @NonNull int[] grantResults) {
switch (permsRequestCode) { switch (permsRequestCode) {
case PERMISSIONS_REQUEST_CODE: case DOWNLOAD_REQUEST_CODE:
downloadFile(); if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
prepareDownload(tempThmmyFile);
break; break;
} }
} }
@ -733,7 +770,7 @@ public abstract class BaseActivity extends AppCompatActivity {
prepareDownload(thmmyFile); prepareDownload(thmmyFile);
else { else {
tempThmmyFile = thmmyFile; 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(); 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---------------------- //----------------------------------MISC----------------------
protected void setMainActivity(MainActivity mainActivity) { protected void setMainActivity(MainActivity mainActivity) {
this.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.util.DisplayMetrics;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore; import com.crashlytics.android.core.CrashlyticsCore;
import com.franmontiel.persistentcookiejar.PersistentCookieJar; import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache; import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.google.firebase.FirebaseApp;
import com.google.firebase.analytics.FirebaseAnalytics; import com.google.firebase.analytics.FirebaseAnalytics;
import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor; import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor;
import com.jakewharton.picasso.OkHttp3Downloader; import com.jakewharton.picasso.OkHttp3Downloader;
@ -33,8 +37,6 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.BuildConfig; import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
@ -50,7 +52,8 @@ import timber.log.Timber;
public class BaseApplication extends Application { public class BaseApplication extends Application {
private static BaseApplication baseApplication; //BaseApplication singleton private static BaseApplication baseApplication; //BaseApplication singleton
//Firebase Analytics //Firebase
private static String firebaseProjectId;
private FirebaseAnalytics firebaseAnalytics; private FirebaseAnalytics firebaseAnalytics;
//Client & SessionManager //Client & SessionManager
@ -62,7 +65,6 @@ public class BaseApplication extends Application {
//Display Metrics //Display Metrics
private static float dpWidth; private static float dpWidth;
public static BaseApplication getInstance() { public static BaseApplication getInstance() {
return baseApplication; return baseApplication;
} }
@ -86,6 +88,7 @@ public class BaseApplication extends Application {
else else
Timber.i("Starting app with Crashlytics disabled."); Timber.i("Starting app with Crashlytics disabled.");
firebaseProjectId = FirebaseApp.getInstance().getOptions().getProjectId();
firebaseAnalytics = FirebaseAnalytics.getInstance(this); firebaseAnalytics = FirebaseAnalytics.getInstance(this);
boolean enableAnalytics = settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_analytics_enable_key), false); boolean enableAnalytics = settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_analytics_enable_key), false);
firebaseAnalytics.setAnalyticsCollectionEnabled(enableAnalytics); firebaseAnalytics.setAnalyticsCollectionEnabled(enableAnalytics);
@ -215,4 +218,8 @@ public class BaseApplication extends Application {
} else } else
Timber.i("Crashlytics were already initialized for this app session."); 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.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
public abstract class BaseFragment extends Fragment { 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.ScrollView;
import android.widget.TextView; 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.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageButton;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; 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 gr.thmmy.mthmmy.R;
import timber.log.Timber; 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.view.inputmethod.InputConnection;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import java.util.HashSet;
import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.HashSet;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
public class EmojiKeyboard extends LinearLayout implements IEmojiKeyboard { 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.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
public class EmojiKeyboardAdapter extends RecyclerView.Adapter<EmojiKeyboardAdapter.EmojiViewHolder> { 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.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton; import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
public class FormatButtonsAdapter extends RecyclerView.Adapter<FormatButtonsAdapter.FormatButtonViewHolder> { 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; package gr.thmmy.mthmmy.model;
import java.util.ArrayList;
import java.util.Objects;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Objects;
public class Bookmark implements java.io.Serializable { public class Bookmark implements java.io.Serializable {
private final String title, id; private final String title, id;
private boolean isNotificationsEnabled; private boolean isNotificationsEnabled;

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

@ -1,10 +1,10 @@
package gr.thmmy.mthmmy.model; package gr.thmmy.mthmmy.model;
import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; 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 * 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. * 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.os.Bundle;
import android.service.notification.StatusBarNotification; 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.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
@ -23,9 +27,6 @@ import java.util.ArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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.R;
import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
@ -260,10 +261,9 @@ public class NotificationService extends FirebaseMessagingService {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since Android Oreo notification channel is needed. // Since Android Oreo notification channel is needed.
if (buildVersion >= Build.VERSION_CODES.O){ if (buildVersion >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(CHANNEL_ID) == null)
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null)
notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH)); notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH));
}
notificationManager.notify(NEW_POST_TAG, notificationId, notificationBuilder.build()); 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 android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.franmontiel.persistentcookiejar.PersistentCookieJar; import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
@ -14,8 +17,6 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import okhttp3.Cookie; 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.SpinnerAdapter;
import android.widget.TextView; import android.widget.TextView;
import androidx.appcompat.widget.AppCompatSpinner;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import androidx.appcompat.widget.AppCompatSpinner;
public class AppCompatSpinnerWithoutDefault extends AppCompatSpinner { public class AppCompatSpinnerWithoutDefault extends AppCompatSpinner {
public AppCompatSpinnerWithoutDefault(Context context) { public AppCompatSpinnerWithoutDefault(Context context) {
super(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) { public static void reportForumInfo(Document document) {
ParseHelpers.Theme theme = ParseHelpers.parseTheme(document); ParseHelpers.Theme theme = ParseHelpers.parseTheme(document);
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document); ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document);
String themeKey = "forum theme", themeValue = null; String themeKey = "forum theme", themeValue;
String languageKey = "forum language", languageValue = null; String languageKey = "forum language", languageValue;
switch (theme) { switch (theme) {
case SCRIBBLES2: case SCRIBBLES2:
themeValue = "Scribbles2"; themeValue = "Scribbles2";
@ -32,19 +32,17 @@ public class CrashReporter {
case HELIOS_MULTI: case HELIOS_MULTI:
themeValue = "Helios_Multi"; themeValue = "Helios_Multi";
break; break;
case THEME_UNKNOWN: default:
themeValue = "Unknown theme"; 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(themeKey, themeValue);
Crashlytics.setString(languageKey, languageValue); Crashlytics.setString(languageKey, languageValue);
Crashlytics.setBool("isLoggedIn", BaseApplication.getInstance().getSessionManager().isLoggedIn()); 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); Crashlytics.log(level + "/" + tag + ": " + message);
if(priority == Log.ERROR) if(priority == Log.ERROR) {
{
if (t!=null) if (t!=null)
Crashlytics.logException(t); Crashlytics.logException(t);
else 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.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import timber.log.Timber; import timber.log.Timber;
public class CustomLinearLayoutManager extends LinearLayoutManager { 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; 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 android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File; import java.io.File;
import androidx.annotation.NonNull; import gr.thmmy.mthmmy.R;
import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR;
@ -21,7 +28,94 @@ public class FileUtils {
return type; return type;
} }
public static boolean fileNameExists (String fileName) { public static boolean fileNameExists(String fileName) {
return fileName != null && (new File(SAVE_DIR.getAbsolutePath(), fileName)).isFile(); 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.util.AttributeSet;
import android.view.View; import android.view.View;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat; 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 * 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. * 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 || } else if (child.getTag() != null && (boolean) child.getTag() && (dyConsumed < 0 ||
!target.canScrollVertically(-1) && dyUnconsumed < -50)) { !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.View;
import android.view.ViewPropertyAnimator; import android.view.ViewPropertyAnimator;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.google.android.material.snackbar.Snackbar;
/** /**
* Extends LinearLayout's behavior. Used for bottom navigation bar. * Extends LinearLayout's behavior. Used for bottom navigation bar.
* <p>When a nested ScrollView is scrolled down, the view will disappear. * <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; 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 class ParseException extends RuntimeException {
public ParseException() {} public ParseException() {}
public ParseException(String message) public ParseException(String message) {
{
super(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 org.jsoup.select.Elements;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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> * An enum describing a forum page's language by defining the types:<ul>
* <li>{@link #PAGE_INCOMPLETE}</li> * <li>{@link #PAGE_INCOMPLETE}</li>
* <li>{@link #UNDEFINED_LANGUAGE}</li> * <li>{@link #UNKNOWN_LANGUAGE}</li>
* <li>{@link #ENGLISH}</li> * <li>{@link #ENGLISH}</li>
* <li>{@link #GREEK}</li> * <li>{@link #GREEK}</li>
* </ul> * </ul>
@ -78,9 +79,9 @@ public class ParseHelpers {
*/ */
PAGE_INCOMPLETE, 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. * Returns one of the supported forum languages.
@ -93,13 +94,12 @@ public class ParseHelpers {
Element welcoming = page.select("h3").first(); Element welcoming = page.select("h3").first();
if (welcoming == null) { if (welcoming == null) {
Element welcomingGuest = page.select("div[id=myuser]").first(); Element welcomingGuest = page.select("div[id=myuser]").first();
if (welcomingGuest != null) { if (welcomingGuest != null && welcomingGuest.text().contains("Welcome"))
if (welcomingGuest.text().contains("Welcome")) return ENGLISH; return ENGLISH;
}
return PAGE_INCOMPLETE; return PAGE_INCOMPLETE;
} else if (welcoming.text().contains("Καλώς ορίσατε")) return GREEK; } else if (welcoming.text().contains("Καλώς ορίσατε")) return GREEK;
else if (welcoming.text().contains("Hey")) return ENGLISH; 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) { public static Theme parseTheme(Document page) {
Element stylesheet = page.select("link[rel=stylesheet]").first(); Element stylesheet = page.select("link[rel=stylesheet]").first();
if (stylesheet.attr("href").contains("scribbles2")) if(stylesheet!=null){
return Theme.SCRIBBLES2; if (stylesheet.attr("href").contains("scribbles2"))
else if (stylesheet.attr("href").contains("helios_multi")) return Theme.SCRIBBLES2;
return Theme.HELIOS_MULTI; else if (stylesheet.attr("href").contains("helios_multi"))
else if (stylesheet.attr("href").contains("smfone")) return Theme.HELIOS_MULTI;
return Theme.SMFONE_BLUE; else if (stylesheet.attr("href").contains("smfone"))
else if (stylesheet.attr("href").contains("default")) return Theme.SMFONE_BLUE;
return Theme.SMF_DEFAULT; else if (stylesheet.attr("href").contains("default"))
else return Theme.SMF_DEFAULT;
return Theme.THEME_UNKNOWN; }
return Theme.THEME_UNKNOWN;
} }
/** /**
@ -307,7 +308,7 @@ public class ParseHelpers {
* @param html html to parse * @param html html to parse
* @return a document with deobfuscated emails * @return a document with deobfuscated emails
*/ */
public static Document parse(String html){ public static Document parse(String html) {
Document document = Jsoup.parse(html); Document document = Jsoup.parse(html);
deobfuscateElements(document.select("span.__cf_email__,a.__cf_email__"), true); deobfuscateElements(document.select("span.__cf_email__,a.__cf_email__"), true);
return document; return document;
@ -316,19 +317,18 @@ public class ParseHelpers {
/** /**
* Use this method instead of parse() if you are targeting specific elements * Use this method instead of parse() if you are targeting specific elements
*/ */
public static void deobfuscateElements(Elements elements, boolean found){ public static void deobfuscateElements(Elements elements, boolean found) {
if(!found) if (!found)
elements = elements.select("span.__cf_email__,a.__cf_email__"); elements = elements.select("span.__cf_email__,a.__cf_email__");
for (Element obfuscatedElement : elements) { for (Element obfuscatedElement : elements) {
String deobfuscatedEmail = deobfuscateEmail(obfuscatedElement.attr("data-cfemail")); String deobfuscatedEmail = deobfuscateEmail(obfuscatedElement.attr("data-cfemail"));
if(obfuscatedElement.is("span")){ if (obfuscatedElement.is("span")) {
Element parent = obfuscatedElement.parent(); Element parent = obfuscatedElement.parent();
if (parent.is("a")&&parent.attr("href").contains("email-protection")) if (parent.is("a") && parent.attr("href").contains("email-protection"))
parent.attr("href", "mailto:"+deobfuscatedEmail); parent.attr("href", "mailto:" + deobfuscatedEmail);
} } else if (obfuscatedElement.attr("href").contains("email-protection"))
else if (obfuscatedElement.attr("href").contains("email-protection")) obfuscatedElement.attr("href", "mailto:" + deobfuscatedEmail);
obfuscatedElement.attr("href", "mailto:"+deobfuscatedEmail);
obfuscatedElement.replaceWith(new TextNode(deobfuscatedEmail, "")); obfuscatedElement.replaceWith(new TextNode(deobfuscatedEmail, ""));
} }
@ -339,7 +339,7 @@ public class ParseHelpers {
* @param obfuscatedEmail CloudFlare-obfuscated email * @param obfuscatedEmail CloudFlare-obfuscated email
* @return deobfuscated email * @return deobfuscated email
*/ */
private static String deobfuscateEmail(String obfuscatedEmail){ private static String deobfuscateEmail(String obfuscatedEmail) {
//Deobfuscate //Deobfuscate
final StringBuilder stringBuilder = new StringBuilder(); final StringBuilder stringBuilder = new StringBuilder();
final int r = Integer.parseInt(obfuscatedEmail.substring(0, 2), 16); final int r = Integer.parseInt(obfuscatedEmail.substring(0, 2), 16);
@ -348,7 +348,140 @@ public class ParseHelpers {
stringBuilder.append(Character.toString((char) i)); stringBuilder.append(Character.toString((char) i));
} }
Timber.i("Email deobfuscated."); Timber.d("Email deobfuscated.");
return stringBuilder.toString(); 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.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Bookmark;
public class BaseViewModel extends ViewModel { 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.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import gr.thmmy.mthmmy.activities.shoutbox.SendShoutTask; import gr.thmmy.mthmmy.activities.shoutbox.SendShoutTask;
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxTask; import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxTask;
import gr.thmmy.mthmmy.model.Shoutbox; 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.LinearLayout;
import android.widget.RadioGroup; import android.widget.RadioGroup;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList; import java.util.ArrayList;
import androidx.lifecycle.MutableLiveData;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask; import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask;
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; 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.PrepareForReplyResult;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.RemoveVoteTask; import gr.thmmy.mthmmy.activities.topic.tasks.RemoveVoteTask;
import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask;
import gr.thmmy.mthmmy.activities.topic.tasks.SubmitVoteTask; import gr.thmmy.mthmmy.activities.topic.tasks.SubmitVoteTask;
@ -34,7 +35,7 @@ import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber; import timber.log.Timber;
public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted, public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted,
PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { PrepareForReplyTask.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished {
/** /**
* topic state * topic state
*/ */
@ -55,7 +56,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
private TopicTask currentTopicTask; private TopicTask currentTopicTask;
private PrepareForEditTask currentPrepareForEditTask; private PrepareForEditTask currentPrepareForEditTask;
private PrepareForReply currentPrepareForReplyTask; private PrepareForReplyTask currentPrepareForReplyTask;
//callbacks for topic activity //callbacks for topic activity
private TopicTask.TopicTaskObserver topicTaskObserver; private TopicTask.TopicTaskObserver topicTaskObserver;
@ -64,7 +65,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
private ReplyTask.ReplyTaskCallbacks replyFinishListener; private ReplyTask.ReplyTaskCallbacks replyFinishListener;
private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks; private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks;
private EditTask.EditTaskCallbacks editTaskCallbacks; private EditTask.EditTaskCallbacks editTaskCallbacks;
private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks; private PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks;
private ExternalAsyncTask.OnTaskStartedListener voteTaskStartedListener; private ExternalAsyncTask.OnTaskStartedListener voteTaskStartedListener;
private NetworkTask.OnNetworkTaskFinishedListener<Void> voteTaskFinishedListener; private NetworkTask.OnNetworkTaskFinishedListener<Void> voteTaskFinishedListener;
private ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener; private ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener;
@ -176,7 +177,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
stopLoading(); stopLoading();
setPageIndicatorIndex(pageCount, true); setPageIndicatorIndex(pageCount, true);
Timber.i("Preparing for reply"); Timber.i("Preparing for reply");
currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this, currentPrepareForReplyTask = new PrepareForReplyTask(prepareForReplyCallbacks, this,
replyPageUrl.getValue()); replyPageUrl.getValue());
currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0])); currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0]));
} }
@ -219,7 +220,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
PrepareForEditResult editResult = prepareForEditResult.getValue(); PrepareForEditResult editResult = prepareForEditResult.getValue();
Timber.i("Editing post"); Timber.i("Editing post");
new EditTask(editTaskCallbacks, position).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editResult.getCommitEditUrl(), message, 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; this.editTaskCallbacks = editTaskCallbacks;
} }
public void setPrepareForReplyCallbacks(PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks) { public void setPrepareForReplyCallbacks(PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks) {
this.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"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content" android:id="@+id/main_content"
@ -31,7 +30,7 @@
android:layout_marginEnd="19dp" android:layout_marginEnd="19dp"
android:background="@null" android:background="@null"
android:contentDescription="@string/bookmark" android:contentDescription="@string/bookmark"
app:srcCompat="@drawable/ic_bookmark_false_accent_24dp"/> app:srcCompat="@drawable/ic_bookmark_false_accent_24dp" />
</androidx.appcompat.widget.Toolbar> </androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
@ -43,8 +42,7 @@
android:layout_marginTop="64dp" android:layout_marginTop="64dp"
android:background="@color/background" android:background="@color/background"
android:scrollbars="none" android:scrollbars="none"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity"> tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
</androidx.recyclerview.widget.RecyclerView>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar <me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
@ -56,7 +54,7 @@
app:layout_anchor="@id/appbar" app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|center" app:layout_anchorGravity="bottom|center"
app:mpb_indeterminateTint="@color/accent" app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/> app:mpb_progressStyle="horizontal" />
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/board_fab" android:id="@+id/board_fab"
@ -65,7 +63,5 @@
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margins" android:layout_margin="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_add_fab"/> app:srcCompat="@drawable/ic_add_fab" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

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

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

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

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

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

@ -28,16 +28,16 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="top|start" 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" android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.upload.UploadActivity"> tools:context="gr.thmmy.mthmmy.activities.upload.UploadActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background" android:background="@color/primary_light"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:orientation="vertical"> android:orientation="vertical">
@ -45,15 +45,16 @@
android:id="@+id/upload_spinners" android:id="@+id/upload_spinners"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background" android:layout_marginTop="8dp"
android:background="@color/primary_light"
android:orientation="vertical"> android:orientation="vertical">
<gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault <gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault
android:id="@+id/upload_spinner_category_root" android:id="@+id/upload_spinner_category_root"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:popupBackground="@color/primary" android:popupBackground="@color/primary"
android:prompt="@string/upload_spinners_hint" /> android:prompt="@string/upload_spinners_hint" />
</LinearLayout> </LinearLayout>
@ -61,8 +62,8 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp" android:layout_marginTop="6dp"
android:layout_marginTop="6dp"> android:layout_marginBottom="6dp">
<EditText <EditText
android:id="@+id/upload_title" android:id="@+id/upload_title"
@ -73,11 +74,41 @@
android:maxLength="500" /> android:maxLength="500" />
</com.google.android.material.textfield.TextInputLayout> </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 <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp" android:layout_marginTop="6dp"
android:layout_marginTop="6dp"> android:layout_marginBottom="6dp">
<EditText <EditText
android:id="@+id/upload_description" android:id="@+id/upload_description"
@ -87,7 +118,7 @@
android:inputType="textMultiLine" /> android:inputType="textMultiLine" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatButton <gr.thmmy.mthmmy.utils.ToggledBackgroundButton
android:id="@+id/upload_title_description_builder" android:id="@+id/upload_title_description_builder"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -97,16 +128,13 @@
android:textAlignment="center" android:textAlignment="center"
android:textColor="@color/accent" /> android:textColor="@color/accent" />
<androidx.appcompat.widget.AppCompatTextView <LinearLayout
android:id="@+id/upload_filename" android:id="@+id/upload_files_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:drawablePadding="5dp" android:layout_marginBottom="6dp"
android:ellipsize="marquee" android:orientation="vertical"
android:singleLine="true"
android:textColor="@color/primary_text"
android:visibility="gone" /> android:visibility="gone" />
<LinearLayout <LinearLayout
@ -129,11 +157,6 @@
android:text="@string/upload_select_file" android:text="@string/upload_select_file"
android:textColor="@color/primary_text" /> android:textColor="@color/primary_text" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/upload_take_photo_button" android:id="@+id/upload_take_photo_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -144,6 +167,11 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/upload_take_photo" android:text="@string/upload_take_photo"
android:textColor="@color/primary_text" /> android:textColor="@color/primary_text" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
@ -165,8 +193,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins" android:layout_marginEnd="@dimen/fab_margins"
android:layout_marginBottom="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload_white_24dp" /> 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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="top|start" android:layout_gravity="top|start"
android:background="@color/background" android:background="@color/primary_light"
android:paddingEnd="@dimen/activity_horizontal_margin" android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin" android:paddingStart="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior" 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"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:background="@color/card_background">
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
@ -14,84 +13,84 @@
android:id="@+id/black" android:id="@+id/black"
style="@style/PopupMenuItem.TopItem" style="@style/PopupMenuItem.TopItem"
android:text="@string/black" android:text="@string/black"
android:textColor="@color/black"/> android:textColor="@color/black" />
<TextView <TextView
android:id="@+id/red" android:id="@+id/red"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/red" android:text="@string/red"
android:textColor="@color/red"/> android:textColor="@color/red" />
<TextView <TextView
android:id="@+id/yellow" android:id="@+id/yellow"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/yellow" android:text="@string/yellow"
android:textColor="@color/yellow"/> android:textColor="@color/yellow" />
<TextView <TextView
android:id="@+id/pink" android:id="@+id/pink"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/pink" android:text="@string/pink"
android:textColor="@color/pink"/> android:textColor="@color/pink" />
<TextView <TextView
android:id="@+id/green" android:id="@+id/green"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/green" android:text="@string/green"
android:textColor="@color/green"/> android:textColor="@color/green" />
<TextView <TextView
android:id="@+id/orange" android:id="@+id/orange"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/orange" android:text="@string/orange"
android:textColor="@color/orange"/> android:textColor="@color/orange" />
<TextView <TextView
android:id="@+id/purple" android:id="@+id/purple"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/purple" android:text="@string/purple"
android:textColor="@color/purple"/> android:textColor="@color/purple" />
<TextView <TextView
android:id="@+id/blue" android:id="@+id/blue"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/blue" android:text="@string/blue"
android:textColor="@color/blue"/> android:textColor="@color/blue" />
<TextView <TextView
android:id="@+id/beige" android:id="@+id/beige"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/beige" android:text="@string/beige"
android:textColor="@color/beige"/> android:textColor="@color/beige" />
<TextView <TextView
android:id="@+id/brown" android:id="@+id/brown"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/brown" android:text="@string/brown"
android:textColor="@color/brown"/> android:textColor="@color/brown" />
<TextView <TextView
android:id="@+id/teal" android:id="@+id/teal"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/teal" android:text="@string/teal"
android:textColor="@color/teal"/> android:textColor="@color/teal" />
<TextView <TextView
android:id="@+id/navy" android:id="@+id/navy"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/navy" android:text="@string/navy"
android:textColor="@color/navy"/> android:textColor="@color/navy" />
<TextView <TextView
android:id="@+id/maroon" android:id="@+id/maroon"
style="@style/PopupMenuItem" style="@style/PopupMenuItem"
android:text="@string/maroon" android:text="@string/maroon"
android:textColor="@color/maroon"/> android:textColor="@color/maroon" />
<TextView <TextView
android:id="@+id/lime_green" android:id="@+id/lime_green"
style="@style/PopupMenuItem.BottomItem" style="@style/PopupMenuItem.BottomItem"
android:text="@string/lime_green" android:text="@string/lime_green"
android:textColor="@color/lime_green"/> android:textColor="@color/lime_green" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

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

@ -18,6 +18,7 @@
<color name="divider">#8B8B8B</color> <color name="divider">#8B8B8B</color>
<color name="link_color">#FF9800</color> <color name="link_color">#FF9800</color>
<color name="mention_color">#FAA61A</color> <color name="mention_color">#FAA61A</color>
<color name="error_red">#890d0d</color>
<color name="white">#FFFFFF</color> <color name="white">#FFFFFF</color>
<color name="iron">#CCCCCC</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="inbox">Inbox</string>
<string name="info">Info</string> <string name="info">Info</string>
<string name="ok">OK</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_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> <string name="user_consent_shared_preference_key">user_consent_shared_preference_key</string>
@ -133,12 +133,22 @@
<string name="upload_button">Upload</string> <string name="upload_button">Upload</string>
<!--Upload Activity--> <!--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_title_hint">Title</string>
<string name="upload_filename">Upload as (filename)</string>
<string name="upload_description_hint">Description</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_take_photo">Take photo</string>
<string name="upload_spinners_hint">Select a category</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--> <!--Upload Fields Builder Activity-->
<string name="upload_fields_builder_type_radio_buttons_title">Select type of upload</string> <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.NoActionBar" parent="BaseAppTheme.NoActionBar"/>
<style name="AppTheme.PreferenceTheme" parent="AppTheme.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> <item name="colorControlActivated">@color/accent</item>
</style> </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" /> app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory> </androidx.preference.PreferenceCategory>
<!--<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory
android:key="pref_category_uploading_key" android:key="pref_category_uploading_key"
android:title="@string/pref_category_uploading" android:title="@string/pref_category_uploading"
app:iconSpaceReserved="false"> app:iconSpaceReserved="false">
@ -38,7 +38,7 @@
android:title="@string/pref_title_uploading_app_signature_enable" android:title="@string/pref_title_uploading_app_signature_enable"
android:summary="@string/pref_summary_uploading_app_signature_enable" android:summary="@string/pref_summary_uploading_app_signature_enable"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>--> </androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory
android:key="pref_category_privacy_key" android:key="pref_category_privacy_key"

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

@ -49,7 +49,7 @@
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory> </androidx.preference.PreferenceCategory>
<!--<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory
android:key="pref_category_uploading_key" android:key="pref_category_uploading_key"
android:title="@string/pref_category_uploading" android:title="@string/pref_category_uploading"
app:iconSpaceReserved="false"> app:iconSpaceReserved="false">
@ -59,7 +59,7 @@
android:title="@string/pref_title_uploading_app_signature_enable" android:title="@string/pref_title_uploading_app_signature_enable"
android:summary="@string/pref_summary_uploading_app_signature_enable" android:summary="@string/pref_summary_uploading_app_signature_enable"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>--> </androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory
android:key="pref_category_privacy_key" 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. // Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: "com.github.ben-manes.versions"
buildscript { buildscript {
repositories { repositories {
@ -8,10 +9,11 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.0' classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.google.gms:google-services:4.2.0' classpath 'com.google.gms:google-services:4.3.2'
classpath 'io.fabric.tools:gradle:1.26.1' classpath 'io.fabric.tools:gradle:1.29.0'
classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' // Also change in app/gradle/grgit.gradle 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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