Browse Source

Version 1.7.0

master v1.7.0
Ezerous 6 years ago
parent
commit
95e9353037
No known key found for this signature in database GPG Key ID: 262B2954BBA319E3
  1. 1
      README.md
  2. 35
      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. 5
      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_content/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. 7
      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. 9
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  23. 13
      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. 3
      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. 11
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  33. 51
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  34. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java
  35. 9
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditResult.java
  36. 22
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java
  37. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java
  38. 877
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java
  39. 381
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java
  40. 77
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java
  41. 179
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
  42. 188
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  43. 5
      app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
  44. 1
      app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
  45. 11
      app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java
  46. 5
      app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java
  47. 1
      app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java
  48. 1
      app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java
  49. 6
      app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java
  50. 4
      app/src/main/java/gr/thmmy/mthmmy/model/Post.java
  51. 45
      app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java
  52. 12
      app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
  53. 223
      app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java
  54. 5
      app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
  55. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java
  56. 3
      app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
  57. 1
      app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java
  58. 96
      app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java
  59. 14
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
  60. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java
  61. 173
      app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java
  62. 27
      app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java
  63. 2
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java
  64. 1
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java
  65. 1
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java
  66. 5
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
  67. 4
      app/src/main/res/drawable/ic_attach_file_white_24dp.xml
  68. 4
      app/src/main/res/drawable/ic_cached_accent_24dp.xml
  69. 4
      app/src/main/res/drawable/ic_cancel_accent_24dp.xml
  70. 4
      app/src/main/res/drawable/ic_info_outline_warning_24dp.xml
  71. 8
      app/src/main/res/layout/activity_board.xml
  72. 4
      app/src/main/res/layout/activity_downloads.xml
  73. 4
      app/src/main/res/layout/activity_topic.xml
  74. 74
      app/src/main/res/layout/activity_upload.xml
  75. 2
      app/src/main/res/layout/activity_upload_fields_builder.xml
  76. 24
      app/src/main/res/layout/activity_upload_file_list_row.xml
  77. 10
      app/src/main/res/layout/activity_upload_filename_info_popup.xml
  78. 43
      app/src/main/res/layout/dialog_upload_progress.xml
  79. 9
      app/src/main/res/layout/editor_view_color_picker.xml
  80. 1
      app/src/main/res/values/colors.xml
  81. 12
      app/src/main/res/values/strings.xml
  82. 153
      app/src/main/res/values/uploads_courses.xml
  83. 4
      app/src/main/res/xml-v26/app_preferences_user.xml
  84. 4
      app/src/main/res/xml/app_preferences_user.xml
  85. 8
      build.gradle
  86. 4
      gradle/wrapper/gradle-wrapper.properties

1
README.md

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

35
app/build.gradle

@ -13,8 +13,8 @@ android {
applicationId "gr.thmmy.mthmmy"
minSdkVersion 19
targetSdkVersion 28
versionCode 17
versionName "1.6.2"
versionCode 18
versionName "1.7.0"
archivesBaseName = "mTHMMY-v$versionName"
buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\""
buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\""
@ -44,7 +44,7 @@ android {
tasks.whenTaskAdded { task ->
if (task.name.contains("assembleRelease")) {
task.getDependsOn().add({
def inputFile = new File("app/google-services.json")
def inputFile = new File("google-services.json")
def json = new JsonSlurper().parseText(inputFile.text)
if (json.project_info.project_id != "mthmmy-release-3aef0")
throw new GradleException('Please supply the correct google-services.json for release (or manually change the id above)!')
@ -61,24 +61,26 @@ tasks.whenTaskAdded { task ->
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.preference:preference:1.1.0-alpha02'
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'androidx.preference:preference:1.1.0-beta01'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.exifinterface:exifinterface:1.1.0-alpha01'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.google.firebase:firebase-core:17.0.0'
implementation 'com.google.firebase:firebase-messaging:19.0.1'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.snatik:storage:2.1.0'
implementation 'com.squareup.okhttp3:okhttp:3.14.2'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation 'org.jsoup:jsoup:1.10.3' //TODO: Warning: upgrading from 1.10.3 will break stuff!
implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
implementation 'com.github.franmontiel:PersistentCookieJar:1.0.1'
implementation 'com.github.PhilJay:MPAndroidChart:3.0.3'
implementation 'com.mikepenz:materialdrawer:6.1.1'
implementation 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar'
@ -86,10 +88,11 @@ dependencies {
implementation 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'//TODO: deprecated!
implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2'
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'ru.noties:markwon:2.0.0'
implementation 'net.gotev:uploadservice:3.4.2'
implementation 'net.gotev:uploadservice-okhttp:3.4.2'
implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.4' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler
implementation 'ru.noties:markwon:2.0.2'
implementation 'net.gotev:uploadservice:3.5.2'
implementation 'net.gotev:uploadservice-okhttp:3.5.2'
implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5'
//Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler
}
apply plugin: 'com.google.gms.google-services'

2
app/gradle/grgit.gradle

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

10
app/src/main/AndroidManifest.xml

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

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

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

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

@ -14,11 +14,12 @@ import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.appbar.AppBarLayout;
import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.appbar.AppBarLayout;
import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,11 +1,21 @@
package gr.thmmy.mthmmy.activities.downloads;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
@ -13,10 +23,8 @@ import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Objects;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Download;
@ -29,6 +37,8 @@ import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
import static gr.thmmy.mthmmy.activities.upload.UploadActivity.BUNDLE_UPLOAD_CATEGORY;
public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener {
/**
* The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle.
@ -47,7 +57,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
private MaterialProgressBar progressBar;
private RecyclerView recyclerView;
private DownloadsAdapter downloadsAdapter;
//private FloatingActionButton uploadFAB;
private FloatingActionButton uploadFAB;
private ParseDownloadPageTask parseDownloadPageTask;
private int numberOfPages = -1;
@ -113,37 +123,37 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
}
});
// uploadFAB = findViewById(R.id.upload_fab);
// uploadFAB.setEnabled(false);
// uploadFAB.hide();
uploadFAB = findViewById(R.id.upload_fab);
uploadFAB.setEnabled(false);
uploadFAB.hide();
parseDownloadPageTask = new ParseDownloadPageTask();
parseDownloadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, downloadsUrl);
}
// @Override
// public boolean onCreateOptionsMenu(Menu menu) {
// // Inflates the menu; this adds items to the action bar if it is present.
// getMenuInflater().inflate(R.menu.downloads_menu, menu);
// super.onCreateOptionsMenu(menu);
// return true;
// }
//
// @Override
// public boolean onOptionsItemSelected(MenuItem item) {
// // Handle presses on the action bar items
// switch (item.getItemId()) {
// case R.id.menu_upload:
// Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class);
// Bundle extras = new Bundle();
// extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav);
// intent.putExtras(extras);
// startActivity(intent);
// return true;
// default:
// return super.onOptionsItemSelected(item);
// }
// }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflates the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.downloads_menu, menu);
super.onCreateOptionsMenu(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.menu_upload:
Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav);
intent.putExtras(extras);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onLoadMore() {
@ -198,7 +208,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
@Override
protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
//if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
}
@Override
@ -296,7 +306,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
toolbar.setTitle(downloadsTitle);
++pagesLoaded;
//if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
progressBar.setVisibility(ProgressBar.INVISIBLE);
downloadsAdapter.notifyDataSetChanged();
isLoadingMore = false;

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

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

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

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

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

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

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

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

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

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

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

@ -9,6 +9,10 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
@ -17,9 +21,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;

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

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

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

@ -10,6 +10,11 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
@ -18,10 +23,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary;

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

@ -18,6 +18,13 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.squareup.picasso.Picasso;
@ -31,12 +38,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.profile.latestPosts.LatestPostsFragment;
import gr.thmmy.mthmmy.activities.profile.stats.StatsFragment;

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

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

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

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

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

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

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

@ -13,6 +13,8 @@ import android.webkit.WebView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -21,7 +23,6 @@ import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.Objects;
import androidx.fragment.app.Fragment;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;

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

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

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

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

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

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

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

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

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

@ -28,16 +28,17 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.res.ResourcesCompat;
import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask;

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large

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

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

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

@ -0,0 +1,77 @@
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))&& (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 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();
}
}

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

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

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

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

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

@ -10,6 +10,9 @@ import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
@ -33,8 +36,6 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.session.SessionManager;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,223 @@
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.base.BaseApplication;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
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);
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) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP &&
uploadInfo.getUploadId().equals(dialogUploadID) &&
uploadProgressDialog != null) {
Button alertDialogNeutral = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
alertDialogNeutral.setText("Resume on background");
alertDialogNeutral.setOnClickListener(v -> uploadProgressDialog.dismiss());
Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
alertDialogNegative.setText("Cancel");
alertDialogNegative.setOnClickListener(v -> {
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) {
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("Cancel");
alertDialogNegative.setOnClickListener(v -> {
NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(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("Upload failed.");
}
if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) {
uploadProgressDialog.dismiss();
}
}
} else {
NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(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(), "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;
}
Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show();
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null);
UploadsHelper.deleteTempFiles(storage);
}
@Override
public void onCancelled(Context context, UploadInfo uploadInfo) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
uploadProgressDialog = null;
dialogUploadID = null;
}
Toast.makeText(context.getApplicationContext(), "Upload canceled", Toast.LENGTH_SHORT).show();
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
/*NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(uploadInfo.getNotificationID());
}*/
UploadsHelper.deleteTempFiles(storage);
}
public static void setDialogDisplay(AlertDialog uploadProgressDialog, String dialogUploadID,
Intent multipartUploadRetryIntent) {
UploadsReceiver.uploadProgressDialog = uploadProgressDialog;
UploadsReceiver.dialogUploadID = dialogUploadID;
//UploadsReceiver.multipartUploadRetryIntent = multipartUploadRetryIntent;
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -235,7 +235,7 @@ public class ParseHelpers {
stringBuilder.append(Character.toString((char) i));
}
Timber.i("Email deobfuscated.");
Timber.d("Email deobfuscated.");
return stringBuilder.toString();
}

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

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

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

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

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

@ -8,9 +8,10 @@ import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList;
import androidx.lifecycle.MutableLiveData;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask;
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
@ -219,7 +220,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
PrepareForEditResult editResult = prepareForEditResult.getValue();
Timber.i("Editing post");
new EditTask(editTaskCallbacks, position).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editResult.getCommitEditUrl(), message,
editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic());
editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic(), editResult.getIcon());
}
/**

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>

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

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
@ -43,8 +42,7 @@
android:layout_marginTop="64dp"
android:background="@color/background"
android:scrollbars="none"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity">
</androidx.recyclerview.widget.RecyclerView>
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar"
@ -67,5 +65,3 @@
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_add_fab" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="@color/card_background">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">

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

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

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

@ -132,12 +132,20 @@
<string name="upload_button">Upload</string>
<!--Upload Activity-->
<string name="upload_title_description_builder">Generate title and description</string>
<string name="upload_title_description_builder">Generate fields</string>
<string name="upload_title_hint">Title</string>
<string name="upload_filename">Upload as (filename)</string>
<string name="upload_description_hint">Description</string>
<string name="upload_select_file">Select file</string>
<string name="upload_select_file">Add files</string>
<string name="upload_take_photo">Take photo</string>
<string name="upload_spinners_hint">Select a category</string>
<string name="upload_filename_info">Please follow the filename rules as\ndescribed
in&#160;<a href="https://www.thmmy.gr/smf/index.php?topic=34294.0">this topic</a>.\n
\nThis does not rename your local files.</string>
<string name="upload_progress_dialog_title">Uploading</string>
<string name="upload_progress_dialog_bytes_uploaded">Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes</string>
<string name="upload_notification_cancel">"Cancel"</string>
<string name="upload_notification_retry">"Retry"</string>
<!--Upload Fields Builder Activity-->
<string name="upload_fields_builder_type_radio_buttons_title">Select type of upload</string>

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

@ -0,0 +1,153 @@
<?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>Διαφορικές Εξισώσεις:Διαφορικές: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>Ηλεκτρικές Μηχανές Α\':Μηχανές Α: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>Στοχαστικό Σήμα:Στοχ. Σήμα:Stox_Sima</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>Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr</item>
<item>Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES</item>
<item>Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM</item>
<item>Υψηλές Τάσεις I:Υψηλές 1:Ypsiles_I</item>
<item>Υψηλές Τάσεις II:Υψηλές 2:Ypsiles_II</item>
<item>Υψηλές Τάσεις III:Υψηλές 3:Ypsiles_III</item>
<item>Υψηλές Τάσεις 4:Υψηλές 4:Ypsiles_IV</item>
<item>Φυσική I:Φυσική 1:Fysiki_I</item>
<item>Φωτονική Τεχνολογία:Φωτονική:Fotoniki</item>
<item>Ψηφιακά Συστήματα I:Ψηφιακά 1:Psifiaka_I</item>
<item>Ψηφιακά Συστήματα II:Ψηφιακά 2:Psifiaka_II</item>
<item>Ψηφιακά Συστήματα III:Ψηφιακά 3:Psifiaka_III</item>
<item>Ψηφιακά Φίλτρα:Φίλτρα:Filtra</item>
<item>Ψηφιακές Τηλεπικοινωνίες I:Ψηφιακές Τηλεπ. 1:Psif_Tilep_I</item>
<item>Ψηφιακές Τηλεπικοινωνίες II:Ψηφιακές Τηλεπ. 2:Psif_Tilep_II</item>
<item>Ψηφιακή Επεξεργασία Εικόνας:ΨΕΕ:PSEE</item>
<item>Ψηφιακή Επεξεργασία Σήματος:ΨΕΣ:PSES</item>
</string-array>
</resources>

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

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

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

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

8
build.gradle

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

4
gradle/wrapper/gradle-wrapper.properties

@ -1,6 +1,6 @@
#Fri Sep 28 13:21:54 EEST 2018
#Sat Jun 15 18:56:13 EEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

Loading…
Cancel
Save