diff --git a/README.md b/README.md index 1dfb2197..0eb2123e 100644 --- a/README.md +++ b/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) diff --git a/app/build.gradle b/app/build.gradle index 4a4812ea..44028151 100644 --- a/app/build.gradle +++ b/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' diff --git a/app/gradle/grgit.gradle b/app/gradle/grgit.gradle index dc0d5e7f..da262dd7 100644 --- a/app/gradle/grgit.gradle +++ b/app/gradle/grgit.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' + classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' } } @@ -48,4 +48,4 @@ ext { getCurrentBranch = this.&getCurrentBranch getCommitHash = this.&getCommitHash isClean = this.&isClean -} \ No newline at end of file +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7620bcf7..fd302031 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + + + + + + + diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java index 77a6aebd..0963be99 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java index 29ab9b1b..a24209e5 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java index 61fc45e6..6f3cea60 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java @@ -8,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 -> { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java index 6c5e50db..2e864229 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java index b42a8caf..3a00881c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksBoardFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksBoardFragment.java index 5b8c8d01..814586ea 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksBoardFragment.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksTopicFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksTopicFragment.java index 4316db4b..e9081fbd 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksTopicFragment.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java index 985e0930..0749d367 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java @@ -61,6 +61,7 @@ public class NewTopicTask extends AsyncTask { .addFormDataPart("sc", sc) .addFormDataPart("subject", strings[1]) .addFormDataPart("topic", topic) + .addFormDataPart("icon", "xx") .build(); Request post = new Request.Builder() diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java index addccf35..95e0334c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java @@ -1,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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java index 5b6ec26e..d5ca46ad 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsAdapter.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java index af1a4cf2..b102c7f7 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java index 718c1b8d..d7ab706a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumAdapter.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java index 70e78af5..f366f295 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java index 31871069..15589d45 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java index 6c194c0e..4f4517d1 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java index 8810da1a..3818145a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java index 0c47bac5..cec14e97 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java index 38181f46..e3425c3c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java index 6054a05f..d2a4d1cb 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java index f2c17ff2..59fa6421 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java index 8ecf48bd..a0b3dafc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java index 2d4ba9bf..4df4598f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java index a884d3a6..c9637dd0 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java index a66f91ae..ec5907b6 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsFragment.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutAdapter.java index 82a3389e..9976975e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutAdapter.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java index 848baa9d..ad42a414 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java +++ b/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) { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java index 639ab3c7..d3d97663 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index dba15a45..0e885cc3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -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 { 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 { 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 containing file's extension - * @return FontAwesome character according to file's type - * @see FontAwesome - */ - @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); - } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java index 3b5b46f0..c54d661c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java @@ -38,6 +38,7 @@ public class EditTask extends AsyncTask { .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]) diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditResult.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditResult.java index a8176072..1a74caa3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditResult.java +++ b/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; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java index 6efc2c03..09206a1c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java @@ -38,28 +38,32 @@ public class PrepareForEditTask extends AsyncTaskoption[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); } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java index 13ad66d9..7ae2595c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java @@ -42,6 +42,7 @@ public class ReplyTask extends AsyncTask { .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") diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java index 974b8b20..d6fb274a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java @@ -1,65 +1,86 @@ package gr.thmmy.mthmmy.activities.upload; import android.app.Activity; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ResolveInfo; -import android.graphics.Bitmap; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; -import android.provider.MediaStore; +import android.text.Editable; +import android.text.Spannable; +import android.text.TextWatcher; +import android.text.method.LinkMovementMethod; +import android.text.style.ForegroundColorSpan; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.ProgressBar; +import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.appcompat.widget.AppCompatButton; +import androidx.appcompat.widget.AppCompatImageButton; +import androidx.core.content.FileProvider; +import androidx.preference.PreferenceManager; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import net.gotev.uploadservice.MultipartUploadRequest; -import net.gotev.uploadservice.ServerResponse; -import net.gotev.uploadservice.UploadInfo; +import net.gotev.uploadservice.UploadNotificationAction; import net.gotev.uploadservice.UploadNotificationConfig; -import net.gotev.uploadservice.UploadStatusDelegate; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; -import java.io.FileOutputStream; +import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.List; +import java.util.HashMap; import java.util.Locale; +import java.util.UUID; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.appcompat.widget.AppCompatButton; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.preference.PreferenceManager; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.model.UploadCategory; +import gr.thmmy.mthmmy.model.UploadFile; +import gr.thmmy.mthmmy.services.UploadsReceiver; import gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault; +import gr.thmmy.mthmmy.utils.FileUtils; +import gr.thmmy.mthmmy.utils.TakePhoto; import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import timber.log.Timber; import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.UPLOADING_APP_SIGNATURE_ENABLE_KEY; -import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUNDLE_UPLOAD_FIELD_BUILDER_COURSE; +import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME; +import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME; +import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_DESCRIPTION; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_FILENAME; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_TITLE; +import static gr.thmmy.mthmmy.utils.FileUtils.faIconFromFilename; public class UploadActivity extends BaseActivity { /** @@ -67,31 +88,50 @@ public class UploadActivity extends BaseActivity { */ public static final String BUNDLE_UPLOAD_CATEGORY = "UPLOAD_CATEGORY"; private static final String uploadIndexUrl = "https://www.thmmy.gr/smf/index.php?action=tpmod;dl=upload"; - private static final String uploadedFromThmmyPromptHtml = "
uploaded from mTHMMY"; + private static final String uploadedFromTHMMYPromptHtml = "
uploaded from mTHMMY"; /** * Request codes used in activities for result (AFR) calls */ - private static final int AFR_REQUEST_CODE_CHOOSE_FILE = 8; - private static final int AFR_REQUEST_CODE_CAMERA = 4; - private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74; + private static final int AFR_REQUEST_CODE_CHOOSE_FILE = 8; //Arbitrary, application specific + private static final int AFR_REQUEST_CODE_CAMERA = 4; //Arbitrary, application specific + private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74; //Arbitrary, application specific + + /** + * Request codes to gain camera and read/write permission + */ + private static final int UPLOAD_REQUEST_CAMERA_CODE = 42; //Arbitrary, application specific + private static final int UPLOAD_REQUEST_STORAGE_CODE = 12; //Arbitrary, application specific + + private static final int MAX_FILE_SIZE_SUPPORTED = 45000000; + + private HashMap uploadsCourses; private ArrayList uploadRootCategories = new ArrayList<>(); private ParseUploadPageTask parseUploadPageTask; private ArrayList bundleCategory; private String categorySelected = "-1"; private String uploaderProfileIndex = "1"; - private String uploadFilename; - private Uri fileUri; + private UploadsCourse uploadsCourse; + private String semester = ""; + + private ArrayList filesList = new ArrayList<>(); + private File photoFileCreated = null; private String fileIcon; + private AppCompatImageButton uploadFilenameInfo; + private CustomTextWatcher textWatcher; + private boolean hasModifiedFilename = false; + + private ZipTask zipTask; //UI elements private MaterialProgressBar progressBar; private LinearLayout categoriesSpinners; private AppCompatSpinnerWithoutDefault rootCategorySpinner; private EditText uploadTitle; + private EditText uploadFilename; private EditText uploadDescription; private AppCompatButton titleDescriptionBuilderButton; - private AppCompatTextView filenameHolder; + private LinearLayout filesListView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -135,71 +175,53 @@ public class UploadActivity extends BaseActivity { rootCategorySpinner.setOnItemSelectedListener(new CustomOnItemSelectedListener(uploadRootCategories)); titleDescriptionBuilderButton = findViewById(R.id.upload_title_description_builder); + titleDescriptionBuilderButton.setEnabled(false); titleDescriptionBuilderButton.setOnClickListener(view -> { - if (categorySelected.equals("-1")) { - Toast.makeText(view.getContext(), "Please choose a category first", Toast.LENGTH_SHORT).show(); - return; + if(uploadsCourse!=null && !uploadsCourse.getName().equals("") && !semester.equals("")){ + Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class); + Bundle builderExtras = new Bundle(); + builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME, uploadsCourse.getName()); + builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME, uploadsCourse.getMinifiedName()); + builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME, uploadsCourse.getGreeklishName()); + builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, semester); + intent.putExtras(builderExtras); + startActivityForResult(intent, AFR_REQUEST_CODE_FIELDS_BUILDER); } + }); - int numberOfSpinners = categoriesSpinners.getChildCount(); + uploadTitle = findViewById(R.id.upload_title); + uploadDescription = findViewById(R.id.upload_description); - if (numberOfSpinners < 3) { - Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); + uploadFilenameInfo = findViewById(R.id.upload_filename_info); + uploadFilenameInfo.setOnClickListener(view -> { + //Inflates the popup menu content + LayoutInflater layoutInflater = (LayoutInflater) view.getContext(). + getSystemService(Context.LAYOUT_INFLATER_SERVICE); + if (layoutInflater == null) { return; } - String maybeSemester = "", maybeCourse = ""; - - if (numberOfSpinners == 5) { - if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)). - getSelectedItemPosition() == -1) { - maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) - categoriesSpinners.getChildAt(numberOfSpinners - 4)).getSelectedItem(); - maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) - categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem(); - } else { - Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); - } - } else if (numberOfSpinners == 4) { - maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) - categoriesSpinners.getChildAt(numberOfSpinners - 3)).getSelectedItem(); - maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) - categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); - } else { - maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) - categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem(); - maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) - categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); - } - - if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) { - Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); - return; - } - if (maybeCourse == null) { - Toast.makeText(view.getContext(), "Please choose a course", Toast.LENGTH_SHORT).show(); - return; - } + Context wrapper = new ContextThemeWrapper(this, R.style.PopupWindow); + View popUpContent = layoutInflater.inflate(R.layout.activity_upload_filename_info_popup, null); - //Fixes course and semester - String course = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); - String semester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); + //Creates the PopupWindow + PopupWindow popUp = new PopupWindow(wrapper); + popUp.setContentView(popUpContent); + popUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT); + popUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT); + popUp.setFocusable(true); - Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class); - Bundle builderExtras = new Bundle(); - builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE, course); - builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, semester); - intent.putExtras(builderExtras); - startActivityForResult(intent, AFR_REQUEST_CODE_FIELDS_BUILDER); + ((TextView) popUpContent.findViewById(R.id.upload_filename_info_text)). + setMovementMethod(LinkMovementMethod.getInstance()); + //Displays the popup + popUp.showAsDropDown(view); }); - titleDescriptionBuilderButton.setEnabled(false); - uploadTitle = findViewById(R.id.upload_title); - uploadDescription = findViewById(R.id.upload_description); + uploadFilename = findViewById(R.id.upload_filename); + textWatcher = new CustomTextWatcher(); + uploadFilename.addTextChangedListener(textWatcher); - filenameHolder = findViewById(R.id.upload_filename); - Drawable filenameDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_attach_file_white_24dp); - filenameHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(filenameDrawable, null, null, null); + filesListView = findViewById(R.id.upload_files_list); AppCompatButton selectFileButton = findViewById(R.id.upload_select_file_button); Drawable selectStartDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_insert_drive_file_white_24dp); @@ -210,9 +232,10 @@ public class UploadActivity extends BaseActivity { "application/msword", "image/vnd.djvu", "application/gz", "application/tar.gz"}; Intent intent = new Intent(Intent.ACTION_GET_CONTENT) - //.setType("*/*") - .setType("image/jpeg") - .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); + .setType("*/*") + //.setType("image/jpeg") + .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes) + .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult(intent, AFR_REQUEST_CODE_CHOOSE_FILE); }); @@ -221,123 +244,178 @@ public class UploadActivity extends BaseActivity { Drawable takePhotoDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_photo_camera_white_24dp); takePhotoButton.setCompoundDrawablesRelativeWithIntrinsicBounds(takePhotoDrawable, null, null, null); takePhotoButton.setOnClickListener(v -> { - Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - takePhotoIntent.putExtra("return-data", true); - takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(UploadsHelper.getCacheFile(this))); - - Intent targetedIntent = new Intent(takePhotoIntent); - List resInfo = this.getPackageManager().queryIntentActivities(takePhotoIntent, 0); - for (ResolveInfo resolveInfo : resInfo) { - String packageName = resolveInfo.activityInfo.packageName; - targetedIntent.setPackage(packageName); - } - startActivityForResult(takePhotoIntent, AFR_REQUEST_CODE_CAMERA); + if (checkPerms()) + takePhoto(); + else + requestPerms(UPLOAD_REQUEST_CAMERA_CODE); }); FloatingActionButton uploadFAB = findViewById(R.id.upload_fab); + uploadFAB.setTag(true); uploadFAB.setOnClickListener(view -> { + //Attempts upload progressBar.setVisibility(View.VISIBLE); String uploadTitleText = uploadTitle.getText().toString(); - String uploadDescriptionText = uploadDescription.getText().toString(); - - if (uploadTitleText.equals("")) { - uploadTitle.setError("Required"); - } - if (fileUri == null) { - Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show(); - } - if (categorySelected.equals("-1")) { - Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); - } - - if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) { - progressBar.setVisibility(View.GONE); - return; - } - - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext()); - if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) { - uploadDescriptionText += uploadedFromThmmyPromptHtml; - } + String editTextFilename = uploadFilename.getText().toString(); + final String[] uploadDescriptionText = {uploadDescription.getText().toString()}; + + //Checks if all required fields are filled + { + boolean shouldReturn = false; + if (uploadTitleText.equals("")) { + uploadTitle.setError("Required"); + shouldReturn = true; + } + if (filesList.isEmpty()) { + Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show(); + shouldReturn = true; + } + if (categorySelected.equals("-1")) { + Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); + shouldReturn = true; + } + if (!filesList.isEmpty()) { + long totalFilesSize = 0; + for (UploadFile file : filesList) { + totalFilesSize += FileUtils.sizeFromUri(this, file.getFileUri()); + } - String tempFilePath = null; - if (uploadFilename != null) { - //File should be uploaded with a certain name. Temporarily copies the file and renames it - tempFilePath = UploadsHelper.createTempFile(this, fileUri, uploadFilename); - if (tempFilePath == null) { - //Something went wrong, abort - Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); + if (totalFilesSize > MAX_FILE_SIZE_SUPPORTED) { + Toast.makeText(view.getContext(), "Your files are too powerful for thmmy. Reduce size or split!", Toast.LENGTH_LONG).show(); + shouldReturn = true; + } + } + if (!editTextFilename.matches("(.+\\.)+.+") || + !FileUtils.getFilenameWithoutExtension(editTextFilename). + matches("[0-9a-zA-Zα-ωΑ-Ω~!@#$%^&()_+=\\-`\\[\\]{};',.]+")) { + uploadFilename.setError("Invalid filename"); + shouldReturn = true; + } + if (shouldReturn) { progressBar.setVisibility(View.GONE); return; } } - try { - new MultipartUploadRequest(view.getContext(), uploadIndexUrl) - .setUtf8Charset() - .addParameter("tp-dluploadtitle", uploadTitleText) - .addParameter("tp-dluploadcat", categorySelected) - .addParameter("tp-dluploadtext", uploadDescriptionText) - .addFileToUpload(tempFilePath == null - ? fileUri.toString() - : tempFilePath - , "tp-dluploadfile") - .addParameter("tp_dluploadicon", fileIcon) - .addParameter("tp-uploaduser", uploaderProfileIndex) - .setNotificationConfig(new UploadNotificationConfig()) - .setMaxRetries(2) - .setDelegate(new UploadStatusDelegate() { - @Override - public void onProgress(Context context, UploadInfo uploadInfo) { - } + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Upload to thmmy"); + builder.setMessage("Are you sure?"); + builder.setPositiveButton("YES, FIRE AWAY", (dialog, which) -> { + //Checks settings and possibly adds "Uploaded from mTHMMY" string to description + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext()); + if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) { + uploadDescriptionText[0] += uploadedFromTHMMYPromptHtml; + } - @Override - public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, - Exception exception) { - Toast.makeText(context, "Upload failed", Toast.LENGTH_SHORT).show(); - UploadsHelper.deleteTempFiles(); - progressBar.setVisibility(View.GONE); - } + for (UploadFile file : filesList) { + if (file.isCameraPhoto()) { + TakePhoto.galleryAddPic(this, file.getPhotoFile()); + } + } - @Override - public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { - Toast.makeText(context, "Upload completed successfully", Toast.LENGTH_SHORT).show(); - UploadsHelper.deleteTempFiles(); - BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); - - uploadTitle.setText(null); - uploadDescription.setText(null); - fileUri = null; - filenameHolder.setText(null); - filenameHolder.setVisibility(View.GONE); - progressBar.setVisibility(View.GONE); + Uri tempFileUri = null; + if (filesList.size() == 1) { + //Checks if the file needs renaming + UploadFile uploadFile = filesList.get(0); + String selectedFileFilename = FileUtils.filenameFromUri(this, uploadFile.getFileUri()); + + if (!editTextFilename.equals(selectedFileFilename)) { + //File should be uploaded with a different name + + if (checkPerms()) { + if (!uploadFile.isCameraPhoto()) { + //Temporarily copies the file to a another location and renames it + tempFileUri = UploadsHelper.createTempFile(this, storage, + uploadFile.getFileUri(), + FileUtils.getFilenameWithoutExtension(editTextFilename)); + } else { + //Renames the photo taken + String photoPath = uploadFile.getPhotoFile().getPath(); + photoPath = photoPath.substring(0, photoPath.lastIndexOf(File.separator)); + String destinationFilename = photoPath + File.separator + + FileUtils.getFilenameWithoutExtension(editTextFilename) + ".jpg"; + + if (!storage.rename(uploadFile.getPhotoFile().getAbsolutePath(), destinationFilename)) { + //Something went wrong, abort + Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); + progressBar.setVisibility(View.GONE); + return; + } + + //Points photoFile and fileUri to the new copied and renamed file + uploadFile.setPhotoFile(storage.getFile(destinationFilename)); + uploadFile.setFileUri(FileProvider.getUriForFile(this, getPackageName() + + ".provider", uploadFile.getPhotoFile())); } + } else { + requestPerms(UPLOAD_REQUEST_STORAGE_CODE); + zipTask = null; + dialog.cancel(); + return; + } + } + } else { + Uri[] filesListArray = new Uri[filesList.size()]; + for (int i = 0; i < filesList.size(); ++i) { + filesListArray[i] = filesList.get(i).getFileUri(); + } - @Override - public void onCancelled(Context context, UploadInfo uploadInfo) { - Toast.makeText(context, "Upload canceled", Toast.LENGTH_SHORT).show(); + zipTask = new ZipTask(this, editTextFilename, categorySelected, + uploadTitleText, uploadDescriptionText[0], fileIcon, + uploaderProfileIndex); - UploadsHelper.deleteTempFiles(); - progressBar.setVisibility(View.GONE); - } - }) - .startUpload(); - } catch (Exception exception) { - Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); + if (checkPerms()) { + zipTask.execute(filesListArray); + finish(); + } else { + requestPerms(UPLOAD_REQUEST_STORAGE_CODE); + dialog.cancel(); + } + + return; + } + + String uploadID = UUID.randomUUID().toString(); + if (uploadFile(this, uploadID, getConfigForUpload(this, uploadID, + editTextFilename), + categorySelected, uploadTitleText, + uploadDescriptionText[0], fileIcon, uploaderProfileIndex, + tempFileUri == null + ? filesList.get(0).getFileUri() + : tempFileUri)) { + finish(); + } else { + Toast.makeText(this, "Couldn't initiate upload.", Toast.LENGTH_SHORT).show(); + } + }); + + builder.setNegativeButton("NOPE", (dialog, which) -> { progressBar.setVisibility(View.GONE); - } + dialog.dismiss(); + }); + + AlertDialog alert = builder.create(); + alert.setOnCancelListener(dialog -> { + progressBar.setVisibility(View.GONE); + dialog.dismiss(); + }); + alert.show(); }); if (uploadRootCategories.isEmpty()) { //Parses the uploads page parseUploadPageTask = new ParseUploadPageTask(); - parseUploadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, uploadIndexUrl); + parseUploadPageTask.execute(uploadIndexUrl); } else { //Renders the already parsed data updateUIElements(); titleDescriptionBuilderButton.setEnabled(true); } + + Resources res = getResources(); + uploadsCourses = new HashMap<>(UploadsCourse + .generateUploadsCourses(res.getStringArray(R.array.string_array_uploads_courses))); } @Override @@ -355,11 +433,17 @@ public class UploadActivity extends BaseActivity { super.onResume(); } + @Override + protected void onPause() { + super.onPause(); + } + @Override protected void onDestroy() { super.onDestroy(); - if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING) + if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING) { parseUploadPageTask.cancel(true); + } } @Override @@ -369,71 +453,108 @@ public class UploadActivity extends BaseActivity { return; } - fileUri = data.getData(); - if (fileUri != null) { - String filename = UploadsHelper.filenameFromUri(this, fileUri); - filenameHolder.setText(filename); - filenameHolder.setVisibility(View.VISIBLE); - - filename = filename.toLowerCase(); - if (filename.endsWith(".jpg")) { - fileIcon = "jpg_image.gif"; - } else if (filename.endsWith(".gif")) { - fileIcon = "gif_image.gif"; - } else if (filename.endsWith(".png")) { - fileIcon = "png_image.gif"; - } else if (filename.endsWith(".html") || filename.endsWith(".htm")) { - fileIcon = "html_file.gif"; - } else if (filename.endsWith(".pdf") || filename.endsWith(".doc") || - filename.endsWith("djvu")) { - fileIcon = "text_file.gif"; - } else if (filename.endsWith(".zip") || filename.endsWith(".rar") || - filename.endsWith(".tar") || filename.endsWith(".tar.gz") || - filename.endsWith(".gz")) { - fileIcon = "archive.gif"; - } else { - fileIcon = "blank.gif"; + if (data.getClipData() != null) { + fileIcon = "archive.gif"; + textWatcher.setFileExtension(".zip"); + + if (!hasModifiedFilename) + setZipUploadFilename(); + + for (int fileIndex = 0; fileIndex < data.getClipData().getItemCount(); ++fileIndex) { + Uri newFileUri = data.getClipData().getItemAt(fileIndex).getUri(); + String filename = FileUtils.filenameFromUri(this, newFileUri); + addFileViewToList(filename); + filesList.add(new UploadFile(false, newFileUri, null)); + } + } else { + Uri newFileUri = data.getData(); + if (newFileUri != null) { + String filename = FileUtils.filenameFromUri(this, newFileUri); + + if (filesList.isEmpty()) { + textWatcher.setFileExtension(FileUtils.getFileExtension(filename)); + + if (!hasModifiedFilename) { + uploadFilename.setText(filename); + hasModifiedFilename = false; + } + + filename = filename.toLowerCase(); + if (filename.endsWith(".jpg")) { + fileIcon = "jpg_image.gif"; + } else if (filename.endsWith(".gif")) { + fileIcon = "gif_image.gif"; + } else if (filename.endsWith(".png")) { + fileIcon = "png_image.gif"; + } else if (filename.endsWith(".html") || filename.endsWith(".htm")) { + fileIcon = "html_file.gif"; + } else if (filename.endsWith(".pdf") || filename.endsWith(".doc") || + filename.endsWith("djvu")) { + fileIcon = "text_file.gif"; + } else if (filename.endsWith(".zip") || filename.endsWith(".rar") || + filename.endsWith(".tar") || filename.endsWith(".tar.gz") || + filename.endsWith(".gz")) { + fileIcon = "archive.gif"; + } else { + fileIcon = "blank.gif"; + } + } else { + fileIcon = "archive.gif"; + textWatcher.setFileExtension(".zip"); + + if (!hasModifiedFilename) + setZipUploadFilename(); + } + + addFileViewToList(filename); + filesList.add(new UploadFile(false, newFileUri, null)); } } } else if (requestCode == AFR_REQUEST_CODE_CAMERA) { if (resultCode == Activity.RESULT_CANCELED) { + //Deletes image file + storage.deleteFile(photoFileCreated.getAbsolutePath()); return; } - Bitmap bitmap; - File cacheImageFile = UploadsHelper.getCacheFile(this); + if (filesList.isEmpty()) { + textWatcher.setFileExtension(FileUtils.getFileExtension(photoFileCreated.getName())); - Uri cacheFileUri = Uri.fromFile(cacheImageFile); - fileIcon = "jpg_image.gif"; + if (!hasModifiedFilename) { + uploadFilename.setText(photoFileCreated.getName()); + hasModifiedFilename = false; + } - bitmap = UploadsHelper.getImageResized(this, cacheFileUri); - int rotation = UploadsHelper.getRotation(this, cacheFileUri); - bitmap = UploadsHelper.rotate(bitmap, rotation); + fileIcon = "jpg_image.gif"; + } else { + fileIcon = "archive.gif"; + textWatcher.setFileExtension(".zip"); - try { - FileOutputStream out = new FileOutputStream(cacheImageFile); - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); - out.flush(); - out.close(); - } catch (Exception e) { - e.printStackTrace(); + if (!hasModifiedFilename) + setZipUploadFilename(); } - String newFilename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE). - format(new Date()); - fileUri = Uri.parse(UploadsHelper.createTempFile(this, cacheFileUri, newFilename)); - - newFilename += ".jpg"; - filenameHolder.setText(newFilename); - filenameHolder.setVisibility(View.VISIBLE); - - UploadsHelper.deleteCacheFiles(this); + UploadFile newFile = new UploadFile(true, TakePhoto.processResult(this, + photoFileCreated), photoFileCreated); + addFileViewToList(FileUtils.getFilenameWithoutExtension(FileUtils. + filenameFromUri(this, newFile.getFileUri()))); + filesList.add(newFile); } else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) { if (resultCode == Activity.RESULT_CANCELED) { return; } - uploadFilename = data.getStringExtra(RESULT_FILENAME); + String previousName = uploadFilename.getText().toString(); + if (previousName.isEmpty()) { + uploadFilename.setText(data.getStringExtra(RESULT_FILENAME)); + } else { + String extractedExtension = FileUtils.getFileExtension(previousName); + String filenameWithExtension = data.getStringExtra(RESULT_FILENAME) + + (extractedExtension != null ? extractedExtension : ""); + uploadFilename.setText(filenameWithExtension); + } + hasModifiedFilename = true; + uploadTitle.setText(data.getStringExtra(RESULT_TITLE)); uploadDescription.setText(data.getStringExtra(RESULT_DESCRIPTION)); } else { @@ -441,6 +562,263 @@ public class UploadActivity extends BaseActivity { } } + @Override + public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions + , @NonNull int[] grantResults) { + switch (permsRequestCode) { + case UPLOAD_REQUEST_CAMERA_CODE: + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) + takePhoto(); + break; + case UPLOAD_REQUEST_STORAGE_CODE: + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && + zipTask != null) { + Uri[] filesListArray = new Uri[filesList.size()]; + for (int i = 0; i < filesList.size(); ++i) { + filesListArray[i] = filesList.get(i).getFileUri(); + } + + zipTask.execute(filesListArray); + finish(); + } else { + Toast.makeText(this, "Please retry uploading.", Toast.LENGTH_SHORT).show(); + } + break; + } + } + + private void setZipUploadFilename(){ + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); + String zipFilename = "mTHMMY_" + timeStamp + ".zip"; + uploadFilename.setText(zipFilename); + hasModifiedFilename = false; + } + + // Should only be called after making sure permissions are granted + private void takePhoto() { + // Create the File where the photo should go + photoFileCreated = TakePhoto.createImageFile(this); + + // Continue only if the File was successfully created + if (photoFileCreated != null) { + startActivityForResult(TakePhoto.getIntent(this, photoFileCreated), + AFR_REQUEST_CODE_CAMERA); + } + } + + private void updateUIElements() { + String[] tmpSpinnerArray = new String[uploadRootCategories.size()]; + for (int i = 0; i < uploadRootCategories.size(); ++i) { + tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle(); + } + + ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(BaseApplication.getInstance().getApplicationContext(), + R.layout.spinner_item, tmpSpinnerArray); + spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); + rootCategorySpinner.setAdapter(spinnerArrayAdapter); + + //Sets bundle selection + if (bundleCategory != null) { + int bundleSelectionIndex = -1, currentIndex = 0; + + for (UploadCategory category : uploadRootCategories) { + if (bundleCategory.get(0).contains(category.getCategoryTitle())) { + bundleSelectionIndex = currentIndex; + break; + } + ++currentIndex; + } + + if (bundleSelectionIndex != -1) { + rootCategorySpinner.setSelection(bundleSelectionIndex, true); + bundleCategory.remove(0); + } + } + } + + private void addFileViewToList(String filename) { + LayoutInflater layoutInflater = getLayoutInflater(); + LinearLayout newFileRow = (LinearLayout) layoutInflater. + inflate(R.layout.activity_upload_file_list_row, null); + + TextView itemText = newFileRow.findViewById(R.id.upload_file_item_text); + itemText.setTypeface(Typeface.createFromAsset(this.getAssets() + , "fonts/fontawesome-webfont.ttf")); + String filenameWithIcon = faIconFromFilename(this, filename) + " " + filename; + itemText.setText(filenameWithIcon); + + newFileRow.findViewById(R.id.upload_file_item_remove).setOnClickListener(view -> { + int fileIndex = filesListView.indexOfChild(newFileRow); + filesListView.removeViewAt(fileIndex); + + if (filesList.get(fileIndex).isCameraPhoto()) { + storage.deleteFile(filesList.get(fileIndex).getPhotoFile().getAbsolutePath()); + } + filesList.remove(fileIndex); + if (filesList.isEmpty()) { + filesListView.setVisibility(View.GONE); + } else if (filesList.size() == 1) { + textWatcher.setFileExtension(FileUtils.getFileExtension(FileUtils. + filenameFromUri(this, filesList.get(0).getFileUri()))); + } + }); + + filesListView.addView(newFileRow); + filesListView.setVisibility(View.VISIBLE); + } + + public static UploadNotificationConfig getConfigForUpload(Context context, String uploadID, + String filename) { + UploadNotificationConfig uploadNotificationConfig = new UploadNotificationConfig(); + uploadNotificationConfig.setIconForAllStatuses(android.R.drawable.stat_sys_upload); + uploadNotificationConfig.setTitleForAllStatuses("Uploading " + filename); + + uploadNotificationConfig.getProgress().iconResourceID = android.R.drawable.stat_sys_upload; + uploadNotificationConfig.getCompleted().iconResourceID = android.R.drawable.stat_sys_upload_done; + uploadNotificationConfig.getError().iconResourceID = android.R.drawable.stat_sys_upload_done; + uploadNotificationConfig.getError().iconColorResourceID = R.color.error_red; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + uploadNotificationConfig.getError().message = "Error during upload. Click for options"; + } + uploadNotificationConfig.getCancelled().iconColorResourceID = android.R.drawable.stat_sys_upload_done; + uploadNotificationConfig.getCancelled().autoClear = true; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + Intent combinedActionsIntent = new Intent(UploadsReceiver.ACTION_COMBINED_UPLOAD); + combinedActionsIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); + + /*combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_FILENAME, filename); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_CATEGORY, retryCategory); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_TITLE, retryTitleText); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_DESCRIPTION, retryDescription); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_ICON, retryIcon); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_UPLOADER, retryUploaderProfile); + combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_FILE_URI, retryFileUri);*/ + + uploadNotificationConfig.setClickIntentForAllStatuses(PendingIntent.getBroadcast(context, + 1, combinedActionsIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Intent retryIntent = new Intent(context, UploadsReceiver.class); + retryIntent.setAction(UploadsReceiver.ACTION_RETRY_UPLOAD); + retryIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); + + Intent cancelIntent = new Intent(context, UploadsReceiver.class); + cancelIntent.setAction(UploadsReceiver.ACTION_CANCEL_UPLOAD); + cancelIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); + + uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction( + R.drawable.ic_cancel_accent_24dp, + context.getString(R.string.upload_notification_cancel), + PendingIntent.getBroadcast(context, 0, cancelIntent, + PendingIntent.FLAG_UPDATE_CURRENT) + )); + uploadNotificationConfig.getError().actions.add(new UploadNotificationAction( + R.drawable.ic_notification, + context.getString(R.string.upload_notification_retry), + PendingIntent.getBroadcast(context, 0, retryIntent, + PendingIntent.FLAG_UPDATE_CURRENT) + )); + } + + return uploadNotificationConfig; + } + + public static boolean uploadFile(Context context, String uploadID, + UploadNotificationConfig uploadNotificationConfig, + String categorySelected, String uploadTitleText, + String uploadDescriptionText, String fileIcon, + String uploaderProfileIndex, Uri fileUri) { + try { + new MultipartUploadRequest(context, uploadID, uploadIndexUrl) + .setUtf8Charset() + .setNotificationConfig(uploadNotificationConfig) + .addParameter("tp-dluploadtitle", uploadTitleText) + .addParameter("tp-dluploadcat", categorySelected) + .addParameter("tp-dluploadtext", uploadDescriptionText) + .addFileToUpload(fileUri.toString() + , "tp-dluploadfile") + .addParameter("tp_dluploadicon", fileIcon) + .addParameter("tp-uploaduser", uploaderProfileIndex) + .setNotificationConfig(uploadNotificationConfig) + .setMaxRetries(2) + .startUpload(); + + Toast.makeText(context, "Uploading files in the background.", Toast.LENGTH_SHORT).show(); + return true; + } catch (Exception exception) { + Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); + return false; + } + } + + private class CustomTextWatcher implements TextWatcher { + String oldFilename, fileExtension; + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + //Saves an instance of the filename before changing + oldFilename = s.toString(); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { } + + @Override + public void afterTextChanged(Editable s) { + //Warns user for bad filenames + String filenameWithoutExtension = FileUtils.getFilenameWithoutExtension(s.toString()); + if (filenameWithoutExtension != null && !filenameWithoutExtension.isEmpty() && + !filenameWithoutExtension.matches("[0-9a-zA-Z~!@#$%^&()_+=\\-`\\[\\]{};',.]+")) { + uploadFilenameInfo.setImageResource(R.drawable.ic_info_outline_warning_24dp); + } else { + uploadFilenameInfo.setImageResource(R.drawable.ic_info_outline_white_24dp); + } + + if (fileExtension == null) { + hasModifiedFilename = !s.toString().isEmpty(); + return; + } + + if (!s.toString().endsWith(fileExtension)) { + //User tried to alter the extension + //Prevents the change + uploadFilename.setText(oldFilename); + return; + } + + //User has modified the filename + hasModifiedFilename = true; + if (s.toString().isEmpty() || (filesList.size() == 1 && s.toString().equals(FileUtils. + filenameFromUri(getApplicationContext(), filesList.get(0).getFileUri())))) { + //After modification the filename falls back to the original + hasModifiedFilename = false; + } + + //Adds the grey colored span to the extension + s.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.secondary_text)), + s.length() - fileExtension.length(), s.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + void setFileExtension(String extension) { + boolean oldHasModifiedFilename = hasModifiedFilename; + oldFilename = uploadFilename.getText().toString(); + fileExtension = extension; + String newFilename; + + if (!oldFilename.isEmpty()) { + newFilename = FileUtils.getFilenameWithoutExtension(oldFilename) + extension; + } else { + newFilename = extension; + } + + uploadFilename.setText(newFilename); + hasModifiedFilename = oldHasModifiedFilename; + } + } + private class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener { private ArrayList parentCategories, childCategories; @@ -463,6 +841,7 @@ public class UploadActivity extends BaseActivity { } categorySelected = parentCategories.get(position).getValue(); + setCourseAndSemester(); //Adds new sub-category spinner if (parentCategories.get(position).hasSubCategories()) { @@ -506,7 +885,72 @@ public class UploadActivity extends BaseActivity { } @Override - public void onNothingSelected(AdapterView parent) { + public void onNothingSelected(AdapterView parent) { } + + private void setCourseAndSemester(){ + uploadsCourse = null; + semester = ""; + + if (categorySelected.equals("-1")) { + titleDescriptionBuilderButton.setEnabled(false); + return; + } + + int numberOfSpinners = categoriesSpinners.getChildCount(); + + if (numberOfSpinners < 3) { + titleDescriptionBuilderButton.setEnabled(false); + return; + } + + String maybeSemester = ""; + String maybeCourse = ""; + + if (numberOfSpinners == 5) { + if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)). + getSelectedItemPosition() == -1) { + maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 4)).getSelectedItem(); + maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem(); + } + else return; + } else if (numberOfSpinners == 4) { + maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 3)).getSelectedItem(); + maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); + } else { + maybeSemester = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem(); + maybeCourse = (String) ((AppCompatSpinnerWithoutDefault) + categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); + } + + if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) { + titleDescriptionBuilderButton.setEnabled(false); + return; + } + + if (maybeCourse == null) { + titleDescriptionBuilderButton.setEnabled(false); + return; + } + + String retrievedCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); + String retrievedSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); + + UploadsCourse foundUploadsCourse = UploadsCourse.findCourse(retrievedCourse, uploadsCourses); + + if(foundUploadsCourse != null){ + uploadsCourse = foundUploadsCourse; + semester = retrievedSemester; + Timber.d("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); + titleDescriptionBuilderButton.setEnabled(true); + return; + } + + titleDescriptionBuilderButton.setEnabled(false); } } @@ -569,37 +1013,82 @@ public class UploadActivity extends BaseActivity { @Override protected void postExecution(ResultCode result) { updateUIElements(); - titleDescriptionBuilderButton.setEnabled(true); progressBar.setVisibility(ProgressBar.GONE); } } - private void updateUIElements() { - String[] tmpSpinnerArray = new String[uploadRootCategories.size()]; - for (int i = 0; i < uploadRootCategories.size(); ++i) { - tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle(); + public static class ZipTask extends AsyncTask { + // Weak references will still allow the Activity to be garbage-collected + private final WeakReference weakActivity; + final String zipFilename, categorySelected, uploadTitleText, uploadDescriptionText, + fileIcon, uploaderProfileIndex; + Uri zipFileUri; + + // Suppresses default constructor + @SuppressWarnings("unused") + private ZipTask() { + weakActivity = null; + this.zipFilename = null; + this.categorySelected = null; + this.uploadTitleText = null; + this.uploadDescriptionText = null; + this.fileIcon = null; + this.uploaderProfileIndex = null; } - ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(BaseApplication.getInstance().getApplicationContext(), - R.layout.spinner_item, tmpSpinnerArray); - spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); - rootCategorySpinner.setAdapter(spinnerArrayAdapter); + ZipTask(Activity uploadsActivity, @NonNull String zipFilename, + @NonNull String categorySelected, @NonNull String uploadTitleText, + @NonNull String uploadDescriptionText, @NonNull String fileIcon, + @NonNull String uploaderProfileIndex) { + weakActivity = new WeakReference<>(uploadsActivity); + this.zipFilename = zipFilename; + this.categorySelected = categorySelected; + this.uploadTitleText = uploadTitleText; + this.uploadDescriptionText = uploadDescriptionText; + this.fileIcon = fileIcon; + this.uploaderProfileIndex = uploaderProfileIndex; + } - //Sets bundle selection - if (bundleCategory != null) { - int bundleSelectionIndex = -1, currentIndex = 0; + @Override + protected void onPreExecute() { + assert weakActivity != null; + Toast.makeText(weakActivity.get(), "Zipping files", Toast.LENGTH_SHORT).show(); + } - for (UploadCategory category : uploadRootCategories) { - if (bundleCategory.get(0).contains(category.getCategoryTitle())) { - bundleSelectionIndex = currentIndex; - break; - } - ++currentIndex; + @Override + protected Boolean doInBackground(Uri... filesToZip) { + if (weakActivity == null || zipFilename == null) + return false; + + File zipFile = UploadsHelper.createZipFile(zipFilename); + + if (zipFile == null) + return false; + + zipFileUri = FileProvider.getUriForFile(weakActivity.get(), + weakActivity.get().getPackageName() + + ".provider", zipFile); + + UploadsHelper.zip(weakActivity.get(), filesToZip, zipFileUri); + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + if (weakActivity == null) + return; + + if (!result) { + Toast.makeText(weakActivity.get(), "Couldn't create zip!", Toast.LENGTH_SHORT).show(); + return; } - if (bundleSelectionIndex != -1) { - rootCategorySpinner.setSelection(bundleSelectionIndex, true); - bundleCategory.remove(0); + String uploadID = UUID.randomUUID().toString(); + if (!uploadFile(weakActivity.get(), uploadID, + getConfigForUpload(weakActivity.get(), uploadID, zipFilename), categorySelected, + uploadTitleText, uploadDescriptionText, fileIcon, uploaderProfileIndex, + zipFileUri)) { + Toast.makeText(weakActivity.get(), "Couldn't initiate upload.", Toast.LENGTH_SHORT).show(); } } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java index f9fd9407..ffa6f26e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java +++ b/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; - } - } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java new file mode 100644 index 00000000..8fe986c9 --- /dev/null +++ b/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 generateUploadsCourses(String[] uploadsCoursesRes){ + Map 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 uploadsCourses){ + retrievedCourse = normalizeGreekNumbers(retrievedCourse); + UploadsCourse uploadsCourse = uploadsCourses.get(retrievedCourse); + if(uploadsCourse != null) return uploadsCourse; + + String foundKey = null; + for (Map.Entry 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(); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java index e5f5301b..92b71cb0 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java @@ -1,71 +1,48 @@ package gr.thmmy.mthmmy.activities.upload; import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.media.ExifInterface; import android.net.Uri; import android.os.Environment; -import android.provider.OpenableColumns; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; + +import com.snatik.storage.Storage; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import gr.thmmy.mthmmy.utils.FileUtils; import timber.log.Timber; -class UploadsHelper { - private static final int DEFAULT_MIN_WIDTH_QUALITY = 400; - private static final String CACHE_IMAGE_NAME = "tempUploadFile.jpg"; - - @NonNull - static String filenameFromUri(Context context, Uri uri) { - String filename = null; - if (uri.getScheme().equals("content")) { - try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { - if (cursor != null && cursor.moveToFirst()) { - filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } - } - } - if (filename == null) { - filename = uri.getPath(); - int cut = filename.lastIndexOf('/'); - if (cut != -1) { - filename = filename.substring(cut + 1); - } - } - - return filename; - } +public class UploadsHelper { + private static final int BUFFER = 4096; + private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads"; @SuppressWarnings("ResultOfMethodCallIgnored") @Nullable - static String createTempFile(Context context, Uri fileUri, String newFilename) { - String oldFilename = filenameFromUri(context, fileUri); - String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); + static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) { + String oldFilename = FileUtils.filenameFromUri(context, fileUri); + String fileExtension = oldFilename.substring(oldFilename.indexOf('.')); String destinationFilename = Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + "~tmp_mThmmy_uploads" + File.separatorChar + newFilename + fileExtension; + File.separatorChar + TEMP_FILES_DIRECTORY + File.separatorChar + newFilename + fileExtension; File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + "~tmp_mThmmy_uploads"); + File.separatorChar + TEMP_FILES_DIRECTORY); - if (!tempDirectory.exists()) { - if (!tempDirectory.mkdirs()) { - Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); - Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); - return null; - } + if (!tempDirectory.exists() && !tempDirectory.mkdirs()) { + Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); + Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); + return null; } InputStream inputStream; @@ -97,103 +74,63 @@ class UploadsHelper { } } - return destinationFilename; - } + return FileProvider.getUriForFile(context, context.getPackageName() + + ".provider", storage.getFile(destinationFilename)); - static File getCacheFile(Context context) { - File imageFile = new File(context.getExternalCacheDir(), CACHE_IMAGE_NAME); - //noinspection ResultOfMethodCallIgnored - imageFile.getParentFile().mkdirs(); - return imageFile; } - @SuppressWarnings("ResultOfMethodCallIgnored") - static void deleteTempFiles() { - File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + "~tmp_mThmmy_uploads"); - - if (tempFilesDirectory.isDirectory()) { - String[] tempFilesArray = tempFilesDirectory.list(); - for (String tempFile : tempFilesArray) { - new File(tempFilesDirectory, tempFile).delete(); - } - tempFilesDirectory.delete(); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - static void deleteCacheFiles(Context context) { - File cacheFilesDirectory = context.getExternalCacheDir(); - assert cacheFilesDirectory != null; - String[] tempFilesArray = cacheFilesDirectory.list(); - for (String tempFile : tempFilesArray) { - new File(cacheFilesDirectory, tempFile).delete(); - } - } - - /** - * Resize to avoid using too much memory loading big images (e.g.: 2560*1920) - **/ - static Bitmap getImageResized(Context context, Uri selectedImage) { - Bitmap bm; - int[] sampleSizes = new int[]{5, 3, 2, 1}; - int i = 0; - do { - bm = decodeBitmap(context, selectedImage, sampleSizes[i]); - i++; - } while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length); - return bm; - } - - private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = sampleSize; + @Nullable + static File createZipFile(@NonNull String zipFilename) { + // Create a zip file name + File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + + File.separator + "mTHMMY"); - AssetFileDescriptor fileDescriptor = null; - try { - fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r"); - } catch (FileNotFoundException e) { - e.printStackTrace(); + if (!zipFolder.exists() && !zipFolder.mkdirs()) { + Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName()); + return null; } - assert fileDescriptor != null; - return BitmapFactory.decodeFileDescriptor( - fileDescriptor.getFileDescriptor(), null, options); + return new File(zipFolder, zipFilename); } - static int getRotation(Context context, Uri imageUri) { - int rotation = 0; + static void zip(Context context, Uri[] files, Uri zipFile) { try { - - context.getContentResolver().notifyChange(imageUri, null); - ExifInterface exif = new ExifInterface(imageUri.getPath()); - int orientation = exif.getAttributeInt( - ExifInterface.TAG_ORIENTATION, - ExifInterface.ORIENTATION_NORMAL); - - switch (orientation) { - case ExifInterface.ORIENTATION_ROTATE_270: - rotation = 270; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - rotation = 180; - break; - case ExifInterface.ORIENTATION_ROTATE_90: - rotation = 90; - break; + BufferedInputStream origin; + OutputStream dest = context.getContentResolver().openOutputStream(zipFile); + assert dest != null; + ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); + byte[] data = new byte[BUFFER]; + + for (Uri file : files) { + InputStream inputStream = context.getContentResolver().openInputStream(file); + assert inputStream != null; + origin = new BufferedInputStream(inputStream, BUFFER); + + ZipEntry entry = new ZipEntry(FileUtils.filenameFromUri(context, file)); + out.putNextEntry(entry); + int count; + + while ((count = origin.read(data, 0, BUFFER)) != -1) { + out.write(data, 0, count); + } + origin.close(); } + + out.close(); } catch (Exception e) { e.printStackTrace(); } - return rotation; } - static Bitmap rotate(Bitmap bm, int rotation) { - if (rotation != 0) { - Matrix matrix = new Matrix(); - matrix.postRotate(rotation); - return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + public static void deleteTempFiles(Storage storage) { + File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() + + File.separatorChar + TEMP_FILES_DIRECTORY); + + if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) { + for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) { + storage.deleteFile(tempFile.getAbsolutePath()); + } + storage.deleteDirectory(tempFilesDirectory.getAbsolutePath()); } - return bm; } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java index d8019545..0671b7e3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/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,16 +141,25 @@ public abstract class BaseActivity extends AppCompatActivity { BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class); baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton)); + + storage = new Storage(getApplicationContext()); } @Override protected void onResume() { super.onResume(); updateDrawer(); - if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown){ - isUserConsentDialogShown=true; + if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown) { + isUserConsentDialogShown = true; showUserConsentDialog(); } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + if (uploadsShowDialogReceiver == null) { + uploadsShowDialogReceiver = new UploadsShowDialogReceiver(this); + } + this.registerReceiver(uploadsShowDialogReceiver, new IntentFilter(UploadsReceiver.ACTION_COMBINED_UPLOAD)); + } } @Override @@ -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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java index ce4e4068..6ba7d3cc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java index 03622e83..c2c1a578 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java +++ b/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 { diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java index 3b41d07c..97f7d42f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java index cd57fb5e..903ff5fe 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java +++ b/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 { diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java index c3b0758b..efcb9519 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java +++ b/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 { diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java index 98277550..732149ae 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java +++ b/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 { diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java b/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java index ac622a64..1da43458 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java index 0c66ba83..7e0b7f55 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java +++ b/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. diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java b/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java new file mode 100644 index 00000000..fb5c2161 --- /dev/null +++ b/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; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java index 5631b325..79c36d4b 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java +++ b/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()); diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java new file mode 100644 index 00000000..f8b06e28 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java index 42f02375..4e91bc72 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java b/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java index b64bb57d..77293dfe 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java +++ b/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); diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java index c4674134..8f85459a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java +++ b/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 diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java index de9abfd0..f4dd5042 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java +++ b/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 { diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java index 1c8eb0b0..e1ef6f9e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java @@ -1,10 +1,17 @@ package gr.thmmy.mthmmy.utils; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.OpenableColumns; import android.webkit.MimeTypeMap; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import java.io.File; -import androidx.annotation.NonNull; +import gr.thmmy.mthmmy.R; import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; @@ -21,7 +28,94 @@ public class FileUtils { return type; } - public static boolean fileNameExists (String fileName) { + public static boolean fileNameExists(String fileName) { return fileName != null && (new File(SAVE_DIR.getAbsolutePath(), fileName)).isFile(); } + + @Nullable + public static String getFileExtension(@NonNull String filename) { + String fileExtension; + + if (!filename.contains(".")) { + return null; + } + if (filename.toLowerCase().endsWith(".tar.gz")) { + fileExtension = filename.substring(filename.length() - 7); + } else { + fileExtension = filename.substring(filename.lastIndexOf(".")); + } + + return fileExtension; + } + + public static String getFilenameWithoutExtension(String filename) { + String fileExtension = getFileExtension(filename); + + return fileExtension == null + ? filename + : filename.substring(0, filename.indexOf(fileExtension)); + } + + @NonNull + public static String filenameFromUri(Context context, Uri uri) { + String filename = null; + if (uri.getScheme().equals("content")) { + try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + } + } + } + if (filename == null) { + filename = uri.getPath(); + int cut = filename.lastIndexOf('/'); + if (cut != -1) { + filename = filename.substring(cut + 1); + } + } + + return filename; + } + + public static long sizeFromUri(Context context, @NonNull Uri uri) { + try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE)); + } + } + return -1; + } + + /** + * Returns a String with a single FontAwesome typeface character corresponding to this file's + * extension. + * + * @param filename String with filename containing file's extension + * @return FontAwesome character according to file's type + * @see FontAwesome + */ + @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); + } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java index a45e1a3f..a77502f3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java +++ b/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.BehaviorWhen a nested ScrollView is scrolled down, the view will disappear. diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java new file mode 100644 index 00000000..c5b4c490 --- /dev/null +++ b/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 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; + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java new file mode 100644 index 00000000..6c189e87 --- /dev/null +++ b/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); + } +} + diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java index f937578a..62380f80 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java +++ b/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(); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java index a038c033..5787b36d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java +++ b/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 { diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java index 25092578..c3f4dce5 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java +++ b/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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java index a7e72883..750e6a34 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/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()); } /** diff --git a/app/src/main/res/drawable/ic_attach_file_white_24dp.xml b/app/src/main/res/drawable/ic_attach_file_white_24dp.xml deleted file mode 100644 index 4834305b..00000000 --- a/app/src/main/res/drawable/ic_attach_file_white_24dp.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_cached_accent_24dp.xml b/app/src/main/res/drawable/ic_cached_accent_24dp.xml new file mode 100644 index 00000000..b4f3f7dd --- /dev/null +++ b/app/src/main/res/drawable/ic_cached_accent_24dp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_cancel_accent_24dp.xml b/app/src/main/res/drawable/ic_cancel_accent_24dp.xml new file mode 100644 index 00000000..4c1d6837 --- /dev/null +++ b/app/src/main/res/drawable/ic_cancel_accent_24dp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_info_outline_warning_24dp.xml b/app/src/main/res/drawable/ic_info_outline_warning_24dp.xml new file mode 100644 index 00000000..1c3b206c --- /dev/null +++ b/app/src/main/res/drawable/ic_info_outline_warning_24dp.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/layout/activity_board.xml b/app/src/main/res/layout/activity_board.xml index 9a51aa92..0a61658e 100644 --- a/app/src/main/res/layout/activity_board.xml +++ b/app/src/main/res/layout/activity_board.xml @@ -1,6 +1,5 @@ - + app:srcCompat="@drawable/ic_bookmark_false_accent_24dp" /> @@ -43,8 +42,7 @@ android:layout_marginTop="64dp" android:background="@color/background" android:scrollbars="none" - tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity"> - + tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" /> + app:mpb_progressStyle="horizontal" /> + app:srcCompat="@drawable/ic_add_fab" /> - - diff --git a/app/src/main/res/layout/activity_downloads.xml b/app/src/main/res/layout/activity_downloads.xml index dc92a274..97e0afbc 100644 --- a/app/src/main/res/layout/activity_downloads.xml +++ b/app/src/main/res/layout/activity_downloads.xml @@ -48,7 +48,7 @@ app:mpb_indeterminateTint="@color/accent" app:mpb_progressStyle="horizontal"/> - + app:srcCompat="@drawable/ic_file_upload_white_24dp"/> diff --git a/app/src/main/res/layout/activity_topic.xml b/app/src/main/res/layout/activity_topic.xml index aaa13e4b..b61c41cf 100644 --- a/app/src/main/res/layout/activity_topic.xml +++ b/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" /> diff --git a/app/src/main/res/layout/activity_upload.xml b/app/src/main/res/layout/activity_upload.xml index 8639fd8c..0eea2f63 100644 --- a/app/src/main/res/layout/activity_upload.xml +++ b/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"> @@ -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"> @@ -61,8 +62,8 @@ + android:layout_marginTop="6dp" + android:layout_marginBottom="6dp"> + + + + + + + + + + + android:layout_marginTop="6dp" + android:layout_marginBottom="6dp"> - - - - + + @@ -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" /> diff --git a/app/src/main/res/layout/activity_upload_fields_builder.xml b/app/src/main/res/layout/activity_upload_fields_builder.xml index 0583b601..601cdeeb 100644 --- a/app/src/main/res/layout/activity_upload_fields_builder.xml +++ b/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" diff --git a/app/src/main/res/layout/activity_upload_file_list_row.xml b/app/src/main/res/layout/activity_upload_file_list_row.xml new file mode 100644 index 00000000..08b74a4a --- /dev/null +++ b/app/src/main/res/layout/activity_upload_file_list_row.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_upload_filename_info_popup.xml b/app/src/main/res/layout/activity_upload_filename_info_popup.xml new file mode 100644 index 00000000..bb6a9704 --- /dev/null +++ b/app/src/main/res/layout/activity_upload_filename_info_popup.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_upload_progress.xml b/app/src/main/res/layout/dialog_upload_progress.xml new file mode 100644 index 00000000..18444cb4 --- /dev/null +++ b/app/src/main/res/layout/dialog_upload_progress.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/editor_view_color_picker.xml b/app/src/main/res/layout/editor_view_color_picker.xml index b71214e1..be1ce295 100644 --- a/app/src/main/res/layout/editor_view_color_picker.xml +++ b/app/src/main/res/layout/editor_view_color_picker.xml @@ -1,11 +1,10 @@ - + android:layout_height="wrap_content" + android:background="@color/card_background"> - @@ -14,84 +13,84 @@ android:id="@+id/black" style="@style/PopupMenuItem.TopItem" android:text="@string/black" - android:textColor="@color/black"/> + android:textColor="@color/black" /> + android:textColor="@color/red" /> + android:textColor="@color/yellow" /> + android:textColor="@color/pink" /> + android:textColor="@color/green" /> + android:textColor="@color/orange" /> + android:textColor="@color/purple" /> + android:textColor="@color/blue" /> + android:textColor="@color/beige" /> + android:textColor="@color/brown" /> + android:textColor="@color/teal" /> + android:textColor="@color/navy" /> + android:textColor="@color/maroon" /> + android:textColor="@color/lime_green" /> \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f2209027..6a76e274 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -18,6 +18,7 @@ #8B8B8B #FF9800 #FAA61A + #890d0d #FFFFFF #CCCCCC diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf3d9e5f..5d856f19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -132,12 +132,20 @@ Upload - Generate title and description + Generate fields Title + Upload as (filename) Description - Select file + Add files Take photo Select a category + Please follow the filename rules as\ndescribed + in this topic.\n + \nThis does not rename your local files. + Uploading + Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes + "Cancel" + "Retry" Select type of upload diff --git a/app/src/main/res/values/uploads_courses.xml b/app/src/main/res/values/uploads_courses.xml new file mode 100644 index 00000000..8f872389 --- /dev/null +++ b/app/src/main/res/values/uploads_courses.xml @@ -0,0 +1,153 @@ + + + + + Ακουστική I:Ακουστική 1:Akoustiki_I + Ακουστική II:Ακουστική 2:Akoustiki_II + Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή:Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή:Analysi_Ilektr_Kykl + Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας:ΑΣΗΕ:ASHE + Ανάλυση Χρονοσειρών:Χρονοσειρές:Xronoseires + Ανάλυση και Σχεδίαση Αλγορίθμων:Αλγόριθμοι:Algorithms + Αναγνώριση Προτύπων:Αναγνώριση Προτύπων:protipa + Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I):Αναλογικές Τηλεπ.:Anal_Tilep + Αντικειμενοστραφής Προγραμματισμός:Αντικειμενοστραφής:OOP + Αξιοπιστία Συστημάτων:Αξιοπιστία Συστημάτων:Aksiopistia_Systimaton + Αριθμητική Ανάλυση:Αριθμ. Ανάλυση:Arith_Anal + Αρχές Οικονομίας:Αρχές Οικονομίας:Arx_Oikonomias + Αρχές Παράλληλης Επεξεργασίας:Αρχές Παράλληλης Επεξεργασίας:Arxes_Parall_Epeksergasias + Αρχιτεκτονική Υπολογιστών:Αρχ. Υπολογιστών:Arx_Ypologiston + Ασαφή Συστήματα:Ασαφή:Asafi + Ασφάλεια Πληροφοριακών Συστημάτων:Ασφάλεια:Asfaleia + Ασύρματος Τηλεπικοινωνία I:Ασύρματος 1:Asyrmatos_I + Ασύρματος Τηλεπικοινωνία II:Ασύρματος 2:Asyrmatos_II + Βάσεις Δεδομένων:Βάσεις:Vaseis + Βιομηχανικά Ηλεκτρονικά:Βιομηχανικά Ηλεκτρονικά:Viomix_Ilektronika + Βιομηχανική Πληροφορική:Βιομηχανική Πληρ:Viomix_Plir + Βιοϊατρική Τεχνολογία:Βιοιατρική:Vioiatriki + Γεωηλεκτρομαγνητισμός:Γεωηλεκτρομαγνητισμός:Geoilektromagnitismos + Γραμμική Άλγεβρα:Γραμμ. Άλγεβρ.:Grammiki_Algevra + Γραφική με Υπολογιστές:Γραφική:Grafiki + Δίκτυα Τηλεπικοινωνιών:Δίκτυα Τηλέπ.:Diktya_Tilep + Δίκτυα Υπολογιστών I:Δίκτυα 1:Diktya_I + Δίκτυα Υπολογιστών II:Δίκτυα 2:Diktya_II + Διάδοση Η/Μ Κύματος II:Διάδοση 2:Diadosi_II + Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III):Διάδοση 1:Diadosi_I + Διακριτά Μαθηματικά:Διακριτά Μαθηματικά:Diakrita + Διακριτά μαθηματικά:Διακριτά Μαθηματικά:Diakrita + Διανεμημένη Παραγωγή:Διανεμημένη Παραγωγή:Dian_Paragogi + Διαφορικές Εξισώσεις:Διαφορικές:Diaforikes + Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας:ΔΣΗΕ:DSHE + Δομές Δεδομένων:Δομ. Δεδομ.:Domes_Dedomenon + Δομημένος Προγραμματισμός:Δομ. Προγραμμ.:C + Ειδικά Κεφάλαια Διαφορικών Εξισώσεων:Ειδικά Κεφάλαια Διαφορικών Εξισώσεων:Eidika_Kef_Diaf_Eksis + Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I:Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I:Eidika_Kef_HM_Pediou_I + Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας:ΕΚΣΗΕ:EKSHE + Ειδικές Αρχιτεκτονικές Υπολογιστών:Ειδικές Αρχιτεκτονικές Υπολογιστών:Eidikes_Arx_Ypolog + Ειδικές Κεραίες, Σύνθεση Κεραιών:Ειδικές Κεραίες, Σύνθεση Κεραιών:Eidikes_Keraies + Εισαγωγή στην Ενεργειακή Τεχνολογία I:ΕΕΤ 1:EET_I + Εισαγωγή στην Ενεργειακή Τεχνολογία II:ΕΕΤ2:EET_II + Εισαγωγή στην Πολιτική Οικονομία:Πολιτική Οικονομία:Polit_Oik + Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας:Εισ. Πυρηνικη Τεχν.:Intro_Pyriniki_Texn + Ενσωματωμένα Συστήματα Πραγματικού Χρόνου:Ενσωματωμένα:Enswmatwmena + Επιχειρησιακή Έρευνα:Επιχειρησιακή Έρευνα:Epixeirisiaki + Ευρυζωνικά Δίκτυα:Ευρυζωνικά:Evryzonika + Ευφυή Συστήματα Ρομπότ:Ευφυή:eufuh + Εφαρμογές Τηλεπικοινωνιακών Διατάξεων:Εφαρμογές Τηλεπ. Διατάξεων:Efarm_Tilep_Diatakseon + Εφαρμοσμένα Μαθηματικά I:Εφαρμοσμένα 1:Efarmosmena_Math_I + Εφαρμοσμένα Μαθηματικά II:Εφαρμοσμένα 2:Efarmosmena_Math_II + Εφαρμοσμένη Θερμοδυναμική:Θερμοδυναμική:Thermodynamiki + Ηλεκτρακουστική I:Ηλεκτρακουστική 1:Ilektrakoustiki_I + Ηλεκτρακουστική II:Ηλεκτρακουστική 2:Ilektrakoustiki_II + Ηλεκτρικά Κυκλώματα I:Κυκλώματα 1:Kyklomata_I + Ηλεκτρικά Κυκλώματα II:Κυκλώματα 2:Kyklomata_II + Ηλεκτρικά Κυκλώματα III:Κυκλώματα 3:Kyklomata_I + Ηλεκτρικές Μετρήσεις I:Μετρήσεις 1:Metriseis_I + Ηλεκτρικές Μετρήσεις II:Μετρήσεις 2:Metriseis_II + Ηλεκτρικές Μηχανές Α\':Μηχανές Α:Mixanes_A + Ηλεκτρικές Μηχανές Β\':Μηχανές Β:Mixanes_B + Ηλεκτρικές Μηχανές Γ\':Μηχανές Γ:Mixanes_C + Ηλεκτρική Οικονομία:Ηλεκτρική Οικονομία:Ilektr_Oikonomia + Ηλεκτρολογικά Υλικά:Ηλεκτρ. Υλικά:Ylika + Ηλεκτρομαγνητική Συμβατότητα:H/M Συμβατότητα:HM_Symvatotita + Ηλεκτρομαγνητικό Πεδίο I:Πεδίο 1:Pedio_I + Ηλεκτρομαγνητικό Πεδίο II:Πεδίο 2:Pedio_II + Ηλεκτρονικά Ισχύος I:Ισχύος 1:Isxyos_I + Ηλεκτρονικά Ισχύος II:Ισχύος 2:Isxyos_II + Ηλεκτρονικές Διατάξεις και Μετρήσεις:Ηλεκτρονικές Διατάξεις και Μετρήσεις:Ilektron_Diatakseis_Metriseis + Ηλεκτρονική I:Ηλεκτρονική 1:Ilektroniki_I + Ηλεκτρονική II:Ηλεκτρονική 2:Ilektroniki_II + Ηλεκτρονική III:Ηλεκτρονική 3:Ilektroniki_III + Ημιαγωγά Υλικά: Θεωρία-Διατάξεις:Ημιαγωγά Υλικά:Imiagoga_Ylika + Θεωρία Πιθανοτήτων και Στατιστική:Πιθανότητες:Pithanotites + Θεωρία Πληροφοριών:Θεωρία Πληρ.:Theoria_Plir + Θεωρία Σημάτων και Γραμμικών Συστημάτων:Σήματα & Συστήματα:Analog_Sima + Θεωρία Σκέδασης:Σκέδαση:Skedasi + Θεωρία Υπολογισμών και Αλγορίθμων:ΘΥΑ:THYA + Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων:Τεχνολογία Αντιδραστήρων:Texn_Antidrasthron + Κβαντική Φυσική:Κβαντική:Kvantiki + Κινητές και Δορυφορικές Επικοινωνίες:Κινητές & Δορυφορικές Επικοινωνίες:Kinites_Doryforikes_Epik + Λειτουργικά Συστήματα:Λειτουργικά:OS + Λογική Σχεδίαση:Λογική Σχεδίαση:Logiki_Sxediasi + Λογισμός I:Λογισμός 1:Logismos_I + Λογισμός II:Λογισμός 2:Logismos_II + Μετάδοση Θερμότητας:Μετάδοση Θερμ.:Metadosi_Therm + Μικροεπεξεργαστές και Περιφερειακά:Μίκρο 2:Mikro_II + Μικροκυματική Τηλεπισκόπηση:Τηλεπισκόπηση:Tilepiskopisi + Μικροκύματα I:Μικροκύματα 1:Mikrokymata_I + Μικροκύματα II:Μικροκύματα 2:Mikrokymata_II + Οπτικές Επικοινωνίες:Οπτικές Τηλεπ.:Optikes_Tilep + Οπτική I:Οπτική 1:Optiki_I + Οπτική II:Οπτική 2:Optiki_II + Οργάνωση Υπολογιστών:Οργάνωση Υπολ.:Org_Ypol + Οργάνωση και Διοίκηση Εργοστασίων:Οργάνωση και Διοίκηση Εργοστασίων:Organ_Dioik_Ergostasion + Παράλληλα και Κατανεμημένα Συστήματα:Παράλληλα:Parallila + Προγραμματιζόμενα Κυκλώματα ASIC:ASIC:ASIC + Προγραμματιστικές Τεχνικές:Προγραμματ. Τεχν.:CPP + Προηγμένες Τεχνικές Επεξεργασίας Σήματος:ΠΤΕΣ:PTES + Προσομοίωση και Μοντελοποίηση Συστημάτων:Μοντελοποίηση:Montelopoiisi + Ρομποτική:Ρομποτική:Robotiki + Σήματα και Συστήματα:Σήματα & Συστήματα:Analog_Sima + Σερβοκινητήρια Συστήματα:Σέρβο:Servo + Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας:ΣΠΗΕ:SPHE + Στοχαστικό Σήμα:Στοχ. Σήμα:Stox_Sima + Συστήματα Αυτομάτου Ελέγχου I:ΣΑΕ 1:SAE_I + Συστήματα Αυτομάτου Ελέγχου II:ΣΑΕ 2:SAE_II + Συστήματα Αυτομάτου Ελέγχου III:ΣΑΕ 3:SAE_III + Συστήματα Ηλεκτρικής Ενέργειας I:ΣΗΕ 1:SHE_I + Συστήματα Ηλεκτρικής Ενέργειας II:ΣΗΕ 2:SHE_II + Συστήματα Ηλεκτρικής Ενέργειας III:ΣΗΕ 3:SHE_III + Συστήματα Ηλεκτροκίνησης:Ηλεκτροκίνηση:Ilektrokinisi + Συστήματα Μικροϋπολογιστών:Μίκρο 1:Mikro_I + Συστήματα Πολυμέσων και Εικονική Πραγματικότητα:Πολυμέσα:Polymesa + Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα):Συσ. Υπολογιστών:Sys_Ypologiston + Σχεδίαση Συστημάτων VLSI:VLSI:VLSI + Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων:Σύνθεση:Synthesi + Σύνθεση Τηλεπικοινωνιακών Διατάξεων:Σύνθεση Τηλεπ. Διατάξεων:Synth_Tilep_Diatakseon + Τεχνικές Βελτιστοποίησης:Βελτιστοποίηση:Veltistopoiisi + Τεχνικές Κωδικοποίησης:Τεχνικές Κωδικοποίησης:Texn_Kodikopoiisis + Τεχνικές Σχεδίασης με Η/Υ:Σχέδιο:sxedio + Τεχνικές μη Καταστρεπτικών Δοκιμών:Μη Καταστρεπτικές Δοκιμές:Non_Destructive_Tests + Τεχνική Μηχανική:Τεχν. Μηχαν.:Texn_Mixan + Τεχνολογία Ήχου και Εικόνας:Τεχνολογία Ήχου και Εικόνας:Texn_Ixou_Eikonas + Τεχνολογία Ηλεκτροτεχνικών Υλικών:Ηλεκτροτεχνικά Υλικά:Ilektrotexnika_Ylika + Τεχνολογία Λογισμικού:Τεχνολογία Λογισμικού:SE + Τηλεοπτικά Συστήματα:Τηλεοπτικά:Tileoptika + Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr + Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES + Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM + Υψηλές Τάσεις I:Υψηλές 1:Ypsiles_I + Υψηλές Τάσεις II:Υψηλές 2:Ypsiles_II + Υψηλές Τάσεις III:Υψηλές 3:Ypsiles_III + Υψηλές Τάσεις 4:Υψηλές 4:Ypsiles_IV + Φυσική I:Φυσική 1:Fysiki_I + Φωτονική Τεχνολογία:Φωτονική:Fotoniki + Ψηφιακά Συστήματα I:Ψηφιακά 1:Psifiaka_I + Ψηφιακά Συστήματα II:Ψηφιακά 2:Psifiaka_II + Ψηφιακά Συστήματα III:Ψηφιακά 3:Psifiaka_III + Ψηφιακά Φίλτρα:Φίλτρα:Filtra + Ψηφιακές Τηλεπικοινωνίες I:Ψηφιακές Τηλεπ. 1:Psif_Tilep_I + Ψηφιακές Τηλεπικοινωνίες II:Ψηφιακές Τηλεπ. 2:Psif_Tilep_II + Ψηφιακή Επεξεργασία Εικόνας:ΨΕΕ:PSEE + Ψηφιακή Επεξεργασία Σήματος:ΨΕΣ:PSES + + \ No newline at end of file diff --git a/app/src/main/res/xml-v26/app_preferences_user.xml b/app/src/main/res/xml-v26/app_preferences_user.xml index fe301eb3..0bda7493 100644 --- a/app/src/main/res/xml-v26/app_preferences_user.xml +++ b/app/src/main/res/xml-v26/app_preferences_user.xml @@ -28,7 +28,7 @@ app:iconSpaceReserved="false" /> - + - +