From 344d425363a1056353aff279b07466f2031e9311 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 12 Jan 2019 14:27:56 +0200 Subject: [PATCH 01/37] Up version --- app/build.gradle | 4 ++-- app/src/main/res/values/styles.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 12853e57..4a4812ea 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 16 - versionName "1.6.1" + versionCode 17 + versionName "1.6.2" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 9d79d36d..2c0687b1 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -31,7 +31,7 @@ From e51f809e97bd1862aa3ce3fe8a12719c3e38bc2e Mon Sep 17 00:00:00 2001 From: Apostolof Date: Sat, 12 Jan 2019 17:55:54 +0200 Subject: [PATCH 02/37] Fix signature emojis --- .../activities/profile/ProfileActivity.java | 3 +- .../profile/summary/SummaryFragment.java | 2 +- .../mthmmy/utils/parsing/ParseHelpers.java | 153 ++++++++++++++++-- 3 files changed, 146 insertions(+), 12 deletions(-) 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 247365c8..38181f46 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 @@ -57,6 +57,7 @@ import timber.log.Timber; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL; +import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.emojiTagToHtml; /** * Activity for user profile. When creating an Intent of this activity you need to bundle a String @@ -273,7 +274,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment { //Finds personal text Element tmpEl = profilePage.select("td.windowbg:nth-child(2)").first(); if (tmpEl != null) { - personalText = tmpEl.text().trim(); + personalText = emojiTagToHtml(tmpEl.text().trim()); } else { //Should never get here! //Something is wrong. 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 12e93a60..2d4ba9bf 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 @@ -144,7 +144,7 @@ public class SummaryFragment extends Fragment { || summaryRow.text().contains("Κατάσταση")) continue; else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) { //This needs special handling since it may have css - pHtml = ParseHelpers.youtubeEmbeddedFix(summaryRow); + pHtml = ParseHelpers.emojiTagToHtml(ParseHelpers.youtubeEmbeddedFix(summaryRow)); //Add stuff to make it work in WebView //style.css pHtml = ("\n" + 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 976ff387..df79d093 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 @@ -7,6 +7,7 @@ import org.jsoup.nodes.TextNode; import org.jsoup.select.Elements; import java.util.ArrayList; +import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -194,7 +195,7 @@ public class ParseHelpers { * @param html html to parse * @return a document with deobfuscated emails */ - public static Document parse(String html){ + public static Document parse(String html) { Document document = Jsoup.parse(html); deobfuscateElements(document.select("span.__cf_email__,a.__cf_email__"), true); return document; @@ -203,19 +204,18 @@ public class ParseHelpers { /** * Use this method instead of parse() if you are targeting specific elements */ - public static void deobfuscateElements(Elements elements, boolean found){ - if(!found) + public static void deobfuscateElements(Elements elements, boolean found) { + if (!found) elements = elements.select("span.__cf_email__,a.__cf_email__"); for (Element obfuscatedElement : elements) { String deobfuscatedEmail = deobfuscateEmail(obfuscatedElement.attr("data-cfemail")); - if(obfuscatedElement.is("span")){ + if (obfuscatedElement.is("span")) { Element parent = obfuscatedElement.parent(); - if (parent.is("a")&&parent.attr("href").contains("email-protection")) - parent.attr("href", "mailto:"+deobfuscatedEmail); - } - else if (obfuscatedElement.attr("href").contains("email-protection")) - obfuscatedElement.attr("href", "mailto:"+deobfuscatedEmail); + if (parent.is("a") && parent.attr("href").contains("email-protection")) + parent.attr("href", "mailto:" + deobfuscatedEmail); + } else if (obfuscatedElement.attr("href").contains("email-protection")) + obfuscatedElement.attr("href", "mailto:" + deobfuscatedEmail); obfuscatedElement.replaceWith(new TextNode(deobfuscatedEmail, "")); } @@ -226,7 +226,7 @@ public class ParseHelpers { * @param obfuscatedEmail CloudFlare-obfuscated email * @return deobfuscated email */ - private static String deobfuscateEmail(String obfuscatedEmail){ + private static String deobfuscateEmail(String obfuscatedEmail) { //Deobfuscate final StringBuilder stringBuilder = new StringBuilder(); final int r = Integer.parseInt(obfuscatedEmail.substring(0, 2), 16); @@ -238,4 +238,137 @@ public class ParseHelpers { Timber.i("Email deobfuscated."); return stringBuilder.toString(); } + + public static String emojiTagToHtml(String emojiTagedString) { + HashMap tagToHtmlMap = new HashMap<>(); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":)"), Pattern.MULTILINE), "\"Smiley\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(";)"), Pattern.MULTILINE), "\"Wink\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":D"), Pattern.MULTILINE), "\"Cheesy\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(";D"), Pattern.MULTILINE), "\"Grin\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(">:("), Pattern.MULTILINE), "\"Angry\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":("), Pattern.MULTILINE), "\"Sad\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":o"), Pattern.MULTILINE), "\"Shocked\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("8))"), Pattern.MULTILINE), "\"Cool\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":???:"), Pattern.MULTILINE), "\"Huh\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":P"), Pattern.MULTILINE), "\"Tongue\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-["), Pattern.MULTILINE), "\"Embarrassed\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-X"), Pattern.MULTILINE), "\"Lips"); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-\\"), Pattern.MULTILINE), "\"Undecided\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-*"), Pattern.MULTILINE), "\"Kiss\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":'("), Pattern.MULTILINE), "\"Cry\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("<3"), Pattern.MULTILINE), "\"heart\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^locked^"), Pattern.MULTILINE), "\"kleidaria\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rollover^"), Pattern.MULTILINE), "\"roll_over\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^redface^"), Pattern.MULTILINE), "\"redface\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^confused^"), Pattern.MULTILINE), "\"confused\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^innocent^"), Pattern.MULTILINE), "\"innocent\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sleep^"), Pattern.MULTILINE), "\"sleep\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sealed^"), Pattern.MULTILINE), "\"lips_sealed\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^cool^"), Pattern.MULTILINE), "\"cool\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^crazy^"), Pattern.MULTILINE), "\"crazy\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^mad^"), Pattern.MULTILINE), "\"mad\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^wav^"), Pattern.MULTILINE), "\"wav\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^binkybaby^"), Pattern.MULTILINE), "\"BinkyBaby\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^Police^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^dontknow^"), Pattern.MULTILINE), "\"DontKnow\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(":angry4:"), Pattern.MULTILINE), "\"angry4\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^angryhot^"), Pattern.MULTILINE), "\"angryAndHot\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^angry^"), Pattern.MULTILINE), "\"angry\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^fouska^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^nysta^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sfinaki^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^banghead^"), Pattern.MULTILINE), "\"bang_head\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^crybaby^"), Pattern.MULTILINE), "\"CryBaby\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^hello^"), Pattern.MULTILINE), "\"Hello\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^jerk^"), Pattern.MULTILINE), "\"jerk\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^nono^"), Pattern.MULTILINE), "\"NoNo\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^notworthy^"), Pattern.MULTILINE), "\"NotWorthy\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^off-topic^"), Pattern.MULTILINE), "\"Off-topic\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^puke^"), Pattern.MULTILINE), "\"Puke\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shout^"), Pattern.MULTILINE), "\"Shout\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^slurp^"), Pattern.MULTILINE), "\"Slurp\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^superconfused^"), Pattern.MULTILINE), "\"SuperConfused\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^superinnocent^"), Pattern.MULTILINE), "\"SuperInnocent\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^cellPhone^"), Pattern.MULTILINE), "\"CellPhone\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^idiot^"), Pattern.MULTILINE), "\"Idiot\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^knuppel^"), Pattern.MULTILINE), "\"Knuppel\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^tickedOff^"), Pattern.MULTILINE), "\"TickedOff\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^peace^"), Pattern.MULTILINE), "\"Peace\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^suspicious^"), Pattern.MULTILINE), "\"Suspicious\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^caffine^"), Pattern.MULTILINE), "\"Caffine\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^argue^"), Pattern.MULTILINE), "\"argue\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^banned2^"), Pattern.MULTILINE), "\"banned2\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^banned^"), Pattern.MULTILINE), "\"banned\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bath^"), Pattern.MULTILINE), "\"bath\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^beg^"), Pattern.MULTILINE), "\"beg\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bluescreen^"), Pattern.MULTILINE), "\"bluescreen\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^boil^"), Pattern.MULTILINE), "\"boil\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bye^"), Pattern.MULTILINE), "\"bye\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^callmerip^"), Pattern.MULTILINE), "\"callmerip\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^carnaval^"), Pattern.MULTILINE), "\"carnaval\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^clap^"), Pattern.MULTILINE), "\"clap\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^coffepot^"), Pattern.MULTILINE), "\"coffepot\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^crap^"), Pattern.MULTILINE), "\"crap\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^curses^"), Pattern.MULTILINE), "\"curses\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^funny^"), Pattern.MULTILINE), "\"funny\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^guitar^"), Pattern.MULTILINE), "\"guitar\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^kissy^"), Pattern.MULTILINE), "\"kissy\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^band^"), Pattern.MULTILINE), "\"band\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^ivres^"), Pattern.MULTILINE), "\"ivres\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^kaloe^"), Pattern.MULTILINE), "\"kaloe\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^kremala^"), Pattern.MULTILINE), "\"kremala\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^moon^"), Pattern.MULTILINE), "\"moon\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^mopping^"), Pattern.MULTILINE), "\"mopping\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^mountza^"), Pattern.MULTILINE), "\"mountza\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^pcsleep^"), Pattern.MULTILINE), "\"pcsleep\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^pinokio^"), Pattern.MULTILINE), "\"pinokio\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^poke^"), Pattern.MULTILINE), "\"poke\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^seestars^"), Pattern.MULTILINE), "\"seestars\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^sfyri^"), Pattern.MULTILINE), "\"sfyri\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^spam^"), Pattern.MULTILINE), "\"spam\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^super^"), Pattern.MULTILINE), "\"super\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^tafos^"), Pattern.MULTILINE), "\"tafos\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^tomato^"), Pattern.MULTILINE), "\"tomato\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^ytold^"), Pattern.MULTILINE), "\"ytold\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^beer^"), Pattern.MULTILINE), "\"beer\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^yue^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^eatpaper^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^fritz^"), Pattern.MULTILINE), "\"ο"); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^wade^"), Pattern.MULTILINE), "\"o"); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^lypi^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^aytoxeir^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^victory^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^filarakia^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^hat^"), Pattern.MULTILINE), "\"bonjour\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^miss^"), Pattern.MULTILINE), "\"bonjour2\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rolfmao^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^lock^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^que^"), Pattern.MULTILINE), "\"question\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shifty^"), Pattern.MULTILINE), "\"shifty\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shy^"), Pattern.MULTILINE), "\"shy\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^music_listen^"), Pattern.MULTILINE), "\"music_listenning\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^bagface^"), Pattern.MULTILINE), "\"bag_face\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rotate^"), Pattern.MULTILINE), "\"rotation\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^love^"), Pattern.MULTILINE), "\"love\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^speech^"), Pattern.MULTILINE), "\"speech\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^facepalm^"), Pattern.MULTILINE), "\"\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^shocked^"), Pattern.MULTILINE), "\"shocked\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^ex_shocked^"), Pattern.MULTILINE), "\"extremely_shocked\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^smurf^"), Pattern.MULTILINE), "\"smurf\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^monster^"), Pattern.MULTILINE), "\"monster\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^pig^"), Pattern.MULTILINE), "\"pig\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("^lol^"), Pattern.MULTILINE), "\"lol\""); + + //Needs priority over the rest tags + final Pattern pattern = Pattern.compile(Pattern.quote("::)"), Pattern.MULTILINE); + Matcher matcher = pattern.matcher(emojiTagedString); + emojiTagedString = matcher.replaceAll("\"Roll"); + + for (Pattern patternKey : tagToHtmlMap.keySet()) { + matcher = patternKey.matcher(emojiTagedString); + emojiTagedString = matcher.replaceAll(tagToHtmlMap.get(patternKey)); + } + + return emojiTagedString; + } } From cd5fa4bdd903afc54a585cc838f7b56e0144038b Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 12 Jan 2019 19:47:53 +0200 Subject: [PATCH 03/37] Tiny signature emoji fix --- .../main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 df79d093..f937578a 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 @@ -245,7 +245,7 @@ public class ParseHelpers { tagToHtmlMap.put(Pattern.compile(Pattern.quote(";)"), Pattern.MULTILINE), "\"Wink\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote(":D"), Pattern.MULTILINE), "\"Cheesy\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote(";D"), Pattern.MULTILINE), "\"Grin\""); - tagToHtmlMap.put(Pattern.compile(Pattern.quote(">:("), Pattern.MULTILINE), "\"Angry\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote(">:("), Pattern.MULTILINE), "\"Angry\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote(":("), Pattern.MULTILINE), "\"Sad\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote(":o"), Pattern.MULTILINE), "\"Shocked\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote("8))"), Pattern.MULTILINE), "\"Cool\""); @@ -256,7 +256,7 @@ public class ParseHelpers { tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-\\"), Pattern.MULTILINE), "\"Undecided\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote(":-*"), Pattern.MULTILINE), "\"Kiss\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote(":'("), Pattern.MULTILINE), "\"Cry\""); - tagToHtmlMap.put(Pattern.compile(Pattern.quote("<3"), Pattern.MULTILINE), "\"heart\""); + tagToHtmlMap.put(Pattern.compile(Pattern.quote("<3"), Pattern.MULTILINE), "\"heart\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote("^locked^"), Pattern.MULTILINE), "\"kleidaria\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote("^rollover^"), Pattern.MULTILINE), "\"roll_over\""); tagToHtmlMap.put(Pattern.compile(Pattern.quote("^redface^"), Pattern.MULTILINE), "\"redface\""); From 6fffe4777c468fe38feb9113c91a0090fc59bf5a Mon Sep 17 00:00:00 2001 From: Ezerous Date: Thu, 23 May 2019 15:26:15 +0300 Subject: [PATCH 04/37] Add last commit badge --- README.md | 1 + 1 file changed, 1 insertion(+) 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) From 33a10747eb300dc9240453a1054fc0ca6fffcd24 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sun, 13 Jan 2019 12:08:40 +0200 Subject: [PATCH 05/37] Up version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4a4812ea..c2b07d75 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.6.3" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" From 4709796b8afd7cd98426e2f3abd7ab63a0064766 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Sun, 16 Jun 2019 12:56:55 +0300 Subject: [PATCH 06/37] Update uploads to androidx, comment out retry stuff, Fix FAB disappearing --- app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 8 + .../activities/board/BoardActivity.java | 1 + .../downloads/DownloadsActivity.java | 68 +- .../mthmmy/activities/topic/TopicAdapter.java | 36 +- .../activities/upload/UploadActivity.java | 791 ++++++++++++++---- .../upload/UploadFieldsBuilderActivity.java | 342 ++++---- .../activities/upload/UploadsHelper.java | 182 ++-- .../gr/thmmy/mthmmy/base/BaseActivity.java | 192 ++++- .../gr/thmmy/mthmmy/model/UploadFile.java | 44 + .../mthmmy/services/UploadsReceiver.java | 223 +++++ .../java/gr/thmmy/mthmmy/utils/FileUtils.java | 97 ++- .../mthmmy/utils/ScrollAwareFABBehavior.java | 18 +- .../java/gr/thmmy/mthmmy/utils/TakePhoto.java | 174 ++++ .../drawable/ic_attach_file_white_24dp.xml | 4 - .../res/drawable/ic_cached_accent_24dp.xml | 4 + .../res/drawable/ic_cancel_accent_24dp.xml | 4 + .../drawable/ic_info_outline_warning_24dp.xml | 4 + app/src/main/res/layout/activity_board.xml | 15 +- .../main/res/layout/activity_downloads.xml | 4 +- app/src/main/res/layout/activity_topic.xml | 4 +- app/src/main/res/layout/activity_upload.xml | 72 +- .../layout/activity_upload_fields_builder.xml | 2 +- .../layout/activity_upload_file_list_row.xml | 24 + .../activity_upload_filename_info_popup.xml | 10 + .../res/layout/dialog_upload_progress.xml | 43 + .../res/layout/editor_view_color_picker.xml | 37 +- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 12 +- app/src/main/res/xml/app_preferences_user.xml | 4 +- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 32 files changed, 1799 insertions(+), 630 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java delete mode 100644 app/src/main/res/drawable/ic_attach_file_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_cached_accent_24dp.xml create mode 100644 app/src/main/res/drawable/ic_cancel_accent_24dp.xml create mode 100644 app/src/main/res/drawable/ic_info_outline_warning_24dp.xml create mode 100644 app/src/main/res/layout/activity_upload_file_list_row.xml create mode 100644 app/src/main/res/layout/activity_upload_filename_info_popup.xml create mode 100644 app/src/main/res/layout/dialog_upload_progress.xml diff --git a/app/build.gradle b/app/build.gradle index c2b07d75..337120bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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)!') @@ -73,6 +73,7 @@ dependencies { 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.snatik:storage:2.1.0' implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7620bcf7..1e8bf257 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -160,6 +160,14 @@ + + + + + + { 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..e4c3676c 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,8 +1,12 @@ 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; @@ -16,7 +20,11 @@ import java.util.Objects; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + 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/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index dba15a45..5472a742 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 @@ -87,6 +87,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 +393,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 +1036,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/upload/UploadActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java index 974b8b20..20fea00f 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,54 +1,68 @@ 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.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 androidx.annotation.NonNull; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import androidx.core.content.FileProvider; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.preference.PreferenceManager; +import androidx.appcompat.widget.AppCompatButton; +import androidx.appcompat.widget.AppCompatImageButton; +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 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.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; @@ -60,6 +74,7 @@ import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUND 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 { /** @@ -71,27 +86,40 @@ public class UploadActivity extends BaseActivity { /** * 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 code to gain read/write permission + */ + private static final int UPLOAD_REQUEST_CODE = 42; //Arbitrary, application specific + + private static final int MAX_FILE_SIZE_SUPPORTED = 45000000; + //private UploadsReceiver uploadsReceiver = new UploadsReceiver(); 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 ArrayList filesList = new ArrayList<>(); + private File photoFileCreated = null; private String fileIcon; + private AppCompatImageButton uploadFilenameInfo; + private CustomTextWatcher textWatcher; + private boolean hasModifiedFilename = false; //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) { @@ -197,9 +225,36 @@ public class UploadActivity extends BaseActivity { uploadTitle = findViewById(R.id.upload_title); uploadDescription = findViewById(R.id.upload_description); - filenameHolder = findViewById(R.id.upload_filename); - Drawable filenameDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_attach_file_white_24dp); - filenameHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(filenameDrawable, null, null, null); + 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; + } + + Context wrapper = new ContextThemeWrapper(this, R.style.PopupWindow); + View popUpContent = layoutInflater.inflate(R.layout.activity_upload_filename_info_popup, null); + + //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); + + ((TextView) popUpContent.findViewById(R.id.upload_filename_info_text)). + setMovementMethod(LinkMovementMethod.getInstance()); + //Displays the popup + popUp.showAsDropDown(view); + }); + + uploadFilename = findViewById(R.id.upload_filename); + textWatcher = new CustomTextWatcher(); + uploadFilename.addTextChangedListener(textWatcher); + + 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); @@ -212,7 +267,8 @@ public class UploadActivity extends BaseActivity { Intent intent = new Intent(Intent.ACTION_GET_CONTENT) //.setType("*/*") .setType("image/jpeg") - .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); + .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes) + .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult(intent, AFR_REQUEST_CODE_CHOOSE_FILE); }); @@ -221,118 +277,153 @@ 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_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); + 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 (!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; } - @Override - public void onCancelled(Context context, UploadInfo uploadInfo) { - Toast.makeText(context, "Upload canceled", Toast.LENGTH_SHORT).show(); + //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 { + Uri[] filesListArray = new Uri[filesList.size()]; + for (int i = 0; i < filesList.size(); ++i) { + filesListArray[i] = filesList.get(i).getFileUri(); + } + + new ZipTask(this, editTextFilename, categorySelected, + uploadTitleText, uploadDescriptionText[0], fileIcon, + uploaderProfileIndex).execute(filesListArray); + finish(); + return; + } - UploadsHelper.deleteTempFiles(); - progressBar.setVisibility(View.GONE); - } - }) - .startUpload(); - } catch (Exception exception) { - Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); + 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(); @@ -355,11 +446,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 +466,119 @@ 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) { + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); + String zipFilename = "mThmmy_" + timeStamp + ".zip"; + uploadFilename.setText(zipFilename); + hasModifiedFilename = false; + } + + 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) { + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); + String zipFilename = "mThmmy_" + timeStamp + ".zip"; + uploadFilename.setText(zipFilename); + hasModifiedFilename = false; + } + } + + 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"; - - bitmap = UploadsHelper.getImageResized(this, cacheFileUri); - int rotation = UploadsHelper.getRotation(this, cacheFileUri); - bitmap = UploadsHelper.rotate(bitmap, rotation); + if (!hasModifiedFilename) { + uploadFilename.setText(photoFileCreated.getName()); + hasModifiedFilename = false; + } - try { - FileOutputStream out = new FileOutputStream(cacheImageFile); - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); - out.flush(); - out.close(); - } catch (Exception e) { - e.printStackTrace(); + fileIcon = "jpg_image.gif"; + } else { + fileIcon = "archive.gif"; + textWatcher.setFileExtension(".zip"); + + if (!hasModifiedFilename) { + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); + String zipFilename = "mThmmy_" + timeStamp + ".zip"; + uploadFilename.setText(zipFilename); + hasModifiedFilename = false; + } } - 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 filenameWithExtension = data.getStringExtra(RESULT_FILENAME) + + FileUtils.getFileExtension(previousName); + uploadFilename.setText(filenameWithExtension); + } + hasModifiedFilename = true; + uploadTitle.setText(data.getStringExtra(RESULT_TITLE)); uploadDescription.setText(data.getStringExtra(RESULT_DESCRIPTION)); } else { @@ -441,6 +586,243 @@ public class UploadActivity extends BaseActivity { } } + @Override + public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions + , @NonNull int[] grantResults) { + switch (permsRequestCode) { + case UPLOAD_REQUEST_CODE: + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) + takePhoto(); + break; + } + } + + // 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; @@ -574,32 +956,79 @@ public class UploadActivity extends BaseActivity { } } - 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 (bundleSelectionIndex != -1) { - rootCategorySpinner.setSelection(bundleSelectionIndex, true); - bundleCategory.remove(0); + 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; + } + + 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..e89fda41 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 @@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.activities.upload; import android.app.Activity; import android.content.Intent; import android.os.Bundle; +import androidx.annotation.Nullable; import android.text.Editable; import android.text.TextWatcher; import android.view.View; @@ -12,14 +13,14 @@ import android.widget.RadioGroup; import android.widget.Toast; import java.util.Calendar; +import java.util.regex.Matcher; +import java.util.regex.Pattern; -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 { +public class UploadFieldsBuilderActivity extends BaseActivity { static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE"; static final String BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER"; @@ -28,6 +29,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity { static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION"; private String course, semester; + private boolean isValidYear; private LinearLayout semesterChooserLinear; private RadioGroup typeRadio, semesterRadio; @@ -38,18 +40,17 @@ 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; + isValidYear = inputYear <= currentYear && inputYear > 1980; } else { - isValid = false; + isValidYear = false; } - if (!isValid) { + if (!isValidYear) { year.setError("Please enter a valid year"); } else { year.setError(null); @@ -86,7 +87,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 +95,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 +125,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; } @@ -212,304 +216,324 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity { return getGreeklishOrMinifiedCourseName(false); } + private String normalizeLatinNumbers(String stringWithLatinNumbers) { + String greekLatinOne = "Ι", englishLatinOne = "I"; + String normalisedString; + + //Separates the latin number suffix from the course name + final String regex = "(.+)\\ ([IΙ]+)"; + final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE); + final Matcher matcher = pattern.matcher(stringWithLatinNumbers); + + if (matcher.matches() && matcher.groupCount() == 2) { + normalisedString = matcher.group(1) + " " + matcher.group(2).replaceAll(greekLatinOne, englishLatinOne); + } else { + normalisedString = stringWithLatinNumbers; + } + + return normalisedString; + } @Nullable private String getGreeklishOrMinifiedCourseName(boolean greeklish) { - if (course.contains("Ψηφιακή Επεξεργασία Σήματος")) { + String normalisedCourse = normalizeLatinNumbers(course); + + if (normalisedCourse.contains(("Ψηφιακή Επεξεργασία Σήματος"))) { return greeklish ? "PSES" : "ΨΕΣ"; - } else if (course.contains("Ψηφιακή Επεξεργασία Εικόνας")) { + } else if (normalisedCourse.contains(("Ψηφιακή Επεξεργασία Εικόνας"))) { return greeklish ? "psee" : "ΨΕΕ"; - } else if (course.contains("Ψηφιακές Τηλεπικοινωνίες ΙΙ")) { + } else if (normalisedCourse.contains(("Ψηφιακές Τηλεπικοινωνίες II"))) { return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2"; - } else if (course.contains("Ψηφιακές Τηλεπικοινωνίες Ι")) { + } else if (normalisedCourse.contains(("Ψηφιακές Τηλεπικοινωνίες I"))) { return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1"; - } else if (course.contains("Ψηφιακά Φίλτρα")) { + } else if (normalisedCourse.contains(("Ψηφιακά Φίλτρα"))) { return greeklish ? "filtra" : "Φίλτρα"; - } else if (course.contains("Ψηφιακά Συστήματα ΙΙΙ")) { + } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα III"))) { return greeklish ? "pshfiaka_III" : "Ψηφιακά 3"; - } else if (course.contains("Ψηφιακά Συστήματα ΙΙ")) { + } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα II"))) { return greeklish ? "pshfiaka_II" : "Ψηφιακά 2"; - } else if (course.contains("Ψηφιακά Συστήματα Ι")) { + } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα I"))) { return greeklish ? "pshfiaka_I" : "Ψηφιακά 1"; - } else if (course.contains("Φωτονική Τεχνολογία")) { + } else if (normalisedCourse.contains(("Φωτονική Τεχνολογία"))) { return greeklish ? "fwtonikh" : "Φωτονική"; - } else if (course.contains("Φυσική Ι")) { + } else if (normalisedCourse.contains(("Φυσική I"))) { return greeklish ? "fysikh_I" : "Φυσική 1"; - } else if (course.contains("Υψηλές Τάσεις ΙΙΙ")) { + } else if (normalisedCourse.contains(("Υψηλές Τάσεις III"))) { return greeklish ? "ypshles_III" : "Υψηλές 3"; - } else if (course.contains("Υψηλές Τάσεις ΙΙ")) { + } else if (normalisedCourse.contains(("Υψηλές Τάσεις II"))) { return greeklish ? "ypshles_II" : "Υψηλές 2"; - } else if (course.contains("Υψηλές Τάσεις Ι")) { + } else if (normalisedCourse.contains(("Υψηλές Τάσεις I"))) { return greeklish ? "ypshles_I" : "Υψηλές 1"; - } else if (course.contains("Υψηλές Τάσεις 4")) { + } else if (normalisedCourse.contains(("Υψηλές Τάσεις 4"))) { return greeklish ? "ypshles_IV" : "Υψηλές 4"; - } else if (course.contains("Υπολογιστικός Ηλεκτρομαγνητισμός")) { + } else if (normalisedCourse.contains(("Υπολογιστικός Ηλεκτρομαγνητισμός"))) { return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ"; - } else if (course.contains("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα")) { + } else if (normalisedCourse.contains(("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα"))) { return greeklish ? "ymes" : "ΥΜΕΣ"; - } else if (course.contains("Τηλεπικοινωνιακή Ηλεκτρονική")) { + } else if (normalisedCourse.contains(("Τηλεπικοινωνιακή Ηλεκτρονική"))) { return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ."; - } else if (course.contains("Τηλεοπτικά Συστήματα")) { + } else if (normalisedCourse.contains(("Τηλεοπτικά Συστήματα"))) { return greeklish ? "tileoptika" : "Τηλεοπτικά"; - } else if (course.contains("Τεχνολογία Λογισμικού")) { + } else if (normalisedCourse.contains(("Τεχνολογία Λογισμικού"))) { return greeklish ? "SE" : "Τεχνολογία Λογισμικού"; - } else if (course.contains("Τεχνολογία Ηλεκτροτεχνικών Υλικών")) { + } else if (normalisedCourse.contains(("Τεχνολογία Ηλεκτροτεχνικών Υλικών"))) { return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά"; - } else if (course.contains("Τεχνολογία Ήχου και Εικόνας")) { + } else if (normalisedCourse.contains(("Τεχνολογία Ήχου και Εικόνας"))) { return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας"; - } else if (course.contains("Τεχνική Μηχανική")) { + } else if (normalisedCourse.contains(("Τεχνική Μηχανική"))) { return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν."; - } else if (course.contains("Τεχνικές μη Καταστρεπτικών Δοκιμών")) { + } else if (normalisedCourse.contains(("Τεχνικές μη Καταστρεπτικών Δοκιμών"))) { return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές"; - } else if (course.contains("Τεχνικές Σχεδίασης με Η/Υ")) { + } else if (normalisedCourse.contains(("Τεχνικές Σχεδίασης με Η/Υ"))) { return greeklish ? "sxedio" : "Σχέδιο"; - } else if (course.contains("Τεχνικές Κωδικοποίησης")) { + } else if (normalisedCourse.contains(("Τεχνικές Κωδικοποίησης"))) { return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης"; - } else if (course.contains("Τεχνικές Βελτιστοποίησης")) { + } else if (normalisedCourse.contains(("Τεχνικές Βελτιστοποίησης"))) { return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση"; - } else if (course.contains("Σύνθεση Τηλεπικοινωνιακών Διατάξεων")) { + } else if (normalisedCourse.contains(("Σύνθεση Τηλεπικοινωνιακών Διατάξεων"))) { return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων"; - } else if (course.contains("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων")) { + } else if (normalisedCourse.contains(("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων"))) { return greeklish ? "synthesh" : "Σύνθεση"; - } else if (course.contains("Σχεδίαση Συστημάτων VLSI")) { + } else if (normalisedCourse.contains(("Σχεδίαση Συστημάτων VLSI"))) { return greeklish ? "VLSI" : "VLSI"; - } else if (course.contains("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)")) { + } else if (normalisedCourse.contains(("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)"))) { return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών"; - } else if (course.contains("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα")) { + } else if (normalisedCourse.contains(("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα"))) { return greeklish ? "polymesa" : "Πολυμέσα"; - } else if (course.contains("Συστήματα Μικροϋπολογιστών")) { + } else if (normalisedCourse.contains(("Συστήματα Μικροϋπολογιστών"))) { return greeklish ? "mikro_I" : "Μίκρο 1"; - } else if (course.contains("Συστήματα Ηλεκτροκίνησης")) { + } else if (normalisedCourse.contains(("Συστήματα Ηλεκτροκίνησης"))) { return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση"; - } else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙΙ")) { + } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας III"))) { return greeklish ? "SHE_III" : "ΣΗΕ 3"; - } else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙ")) { + } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας II"))) { return greeklish ? "SHE_II" : "ΣΗΕ 2"; - } else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας Ι")) { + } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας I"))) { return greeklish ? "SHE_I" : "ΣΗΕ 1"; - } else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙI")) { + } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου III"))) { return greeklish ? "SAE_III" : "ΣΑΕ 3"; - } else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙ")) { + } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου II"))) { return greeklish ? "SAE_II" : "ΣΑΕ 2"; - } else if (course.contains("Συστήματα Αυτομάτου Ελέγχου Ι")) { + } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου I"))) { return greeklish ? "SAE_1" : "ΣΑΕ 1"; - } else if (course.contains("Στοχαστικό Σήμα")) { + } else if (normalisedCourse.contains(("Στοχαστικό Σήμα"))) { return greeklish ? "stox_shma" : "Στοχ. Σήμα"; - } else if (course.contains("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας")) { + } else if (normalisedCourse.contains(("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας"))) { return greeklish ? "SPHE" : "ΣΠΗΕ"; - } else if (course.contains("Σερβοκινητήρια Συστήματα")) { + } else if (normalisedCourse.contains(("Σερβοκινητήρια Συστήματα"))) { return greeklish ? "servo" : "Σέρβο"; - } else if (course.contains("Σήματα και Συστήματα")) { + } else if (normalisedCourse.contains(("Σήματα και Συστήματα"))) { return greeklish ? "analog_shma" : "Σύματα & Συστήματα"; - } else if (course.contains("Ρομποτική")) { + } else if (normalisedCourse.contains(("Ρομποτική"))) { return greeklish ? "rompotikh" : "Ρομποτική"; - } else if (course.contains("Προσομοίωση και Μοντελοποίηση Συστημάτων")) { + } else if (normalisedCourse.contains(("Προσομοίωση και Μοντελοποίηση Συστημάτων"))) { return greeklish ? "montelopoihsh" : "Μοντελοποίηση"; - } else if (course.contains("Προηγμένες Τεχνικές Επεξεργασίας Σήματος")) { + } else if (normalisedCourse.contains(("Προηγμένες Τεχνικές Επεξεργασίας Σήματος"))) { return greeklish ? "ptes" : "ΠΤΕΣ"; - } else if (course.contains("Προγραμματιστικές Τεχνικές")) { + } else if (normalisedCourse.contains(("Προγραμματιστικές Τεχνικές"))) { return greeklish ? "cpp" : "Προγραμματ. Τεχν."; - } else if (course.contains("Προγραμματιζόμενα Κυκλώματα ASIC")) { + } else if (normalisedCourse.contains(("Προγραμματιζόμενα Κυκλώματα ASIC"))) { return greeklish ? "asic" : "ASIC"; - } else if (course.contains("Παράλληλα και Κατανεμημένα Συστήματα")) { + } else if (normalisedCourse.contains(("Παράλληλα και Κατανεμημένα Συστήματα"))) { return greeklish ? "parallhla" : "Παράλληλα"; - } else if (course.contains("Οργάνωση και Διοίκηση Εργοστασίων")) { + } else if (normalisedCourse.contains(("Οργάνωση και Διοίκηση Εργοστασίων"))) { return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων"; - } else if (course.contains("Οργάνωση Υπολογιστών")) { + } else if (normalisedCourse.contains(("Οργάνωση Υπολογιστών"))) { return greeklish ? "org_ypol" : "Οργάνωση Υπολ."; - } else if (course.contains("Οπτική ΙΙ")) { + } else if (normalisedCourse.contains(("Οπτική II"))) { return greeklish ? "optikh_II" : "Οπτική 2"; - } else if (course.contains("Οπτική Ι")) { + } else if (normalisedCourse.contains(("Οπτική I"))) { return greeklish ? "optikh_I" : "Οπτική 1"; - } else if (course.contains("Οπτικές Επικοινωνίες")) { + } else if (normalisedCourse.contains(("Οπτικές Επικοινωνίες"))) { return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ."; - } else if (course.contains("Μικροκύματα II")) { + } else if (normalisedCourse.contains(("Μικροκύματα II"))) { return greeklish ? "mikrokymata_II" : "Μικροκύματα 2"; - } else if (course.contains("Μικροκύματα I")) { + } else if (normalisedCourse.contains(("Μικροκύματα I"))) { return greeklish ? "mikrokymata_I" : "Μικροκύματα 1"; - } else if (course.contains("Μικροκυματική Τηλεπισκόπηση")) { + } else if (normalisedCourse.contains(("Μικροκυματική Τηλεπισκόπηση"))) { return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση"; - } else if (course.contains("Μικροεπεξεργαστές και Περιφερειακά")) { + } else if (normalisedCourse.contains(("Μικροεπεξεργαστές και Περιφερειακά"))) { return greeklish ? "mikro_II" : "Μίκρο 2"; - } else if (course.contains("Μετάδοση Θερμότητας")) { + } else if (normalisedCourse.contains(("Μετάδοση Θερμότητας"))) { return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ."; - } else if (course.contains("Λογισμός ΙΙ")) { + } else if (normalisedCourse.contains(("Λογισμός II"))) { return greeklish ? "logismos_II" : "Λογισμός 2"; - } else if (course.contains("Λογισμός Ι")) { + } else if (normalisedCourse.contains(("Λογισμός I"))) { return greeklish ? "logismos_I" : "Λογισμός 1"; - } else if (course.contains("Λογική Σχεδίαση")) { + } else if (normalisedCourse.contains(("Λογική Σχεδίαση"))) { return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση"; - } else if (course.contains("Λειτουργικά Συστήματα")) { + } else if (normalisedCourse.contains(("Λειτουργικά Συστήματα"))) { return greeklish ? "OS" : "Λειτουργικά"; - } else if (course.contains("Κινητές και Δορυφορικές Επικοινωνίες")) { + } else if (normalisedCourse.contains(("Κινητές και Δορυφορικές Επικοινωνίες"))) { return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες"; - } else if (course.contains("Κβαντική Φυσική")) { + } else if (normalisedCourse.contains(("Κβαντική Φυσική"))) { return greeklish ? "kvantikh" : "Κβαντική"; - } else if (course.contains("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων")) { + } else if (normalisedCourse.contains(("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων"))) { return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων"; - } else if (course.contains("Θεωρία Υπολογισμών και Αλγορίθμων")) { + } else if (normalisedCourse.contains(("Θεωρία Υπολογισμών και Αλγορίθμων"))) { return greeklish ? "thya" : "ΘΥΑ"; - } else if (course.contains("Θεωρία Σκέδασης")) { + } else if (normalisedCourse.contains(("Θεωρία Σκέδασης"))) { return greeklish ? "skedash" : "Σκέδαση"; - } else if (course.contains("Θεωρία Σημάτων και Γραμμικών Συστημάτων")) { + } else if (normalisedCourse.contains(("Θεωρία Σημάτων και Γραμμικών Συστημάτων"))) { return greeklish ? "analog_shma" : "Σύματα & Συστήματα"; - } else if (course.contains("Θεωρία Πληροφοριών")) { + } else if (normalisedCourse.contains(("Θεωρία Πληροφοριών"))) { return greeklish ? "theoria_plir" : "Θεωρία Πληρ."; - } else if (course.contains("Θεωρία Πιθανοτήτων και Στατιστική")) { + } else if (normalisedCourse.contains(("Θεωρία Πιθανοτήτων και Στατιστική"))) { return greeklish ? "pithanothtes" : "Πιθανότητες"; - } else if (course.contains("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις")) { + } else if (normalisedCourse.contains(("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις"))) { return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά"; - } else if (course.contains("Ηλεκτρονική ΙΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρονική III"))) { return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3"; - } else if (course.contains("Ηλεκτρονική ΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρονική II"))) { return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2"; - } else if (course.contains("Ηλεκτρονική Ι")) { + } else if (normalisedCourse.contains(("Ηλεκτρονική I"))) { return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1"; - } else if (course.contains("Ηλεκτρονικές Διατάξεις και Μετρήσεις")) { + } else if (normalisedCourse.contains(("Ηλεκτρονικές Διατάξεις και Μετρήσεις"))) { return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις"; - } else if (course.contains("Ηλεκτρονικά Ισχύος ΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρονικά Iσχύος II"))) { return greeklish ? "isxyos_II" : "Ισχύος 2"; - } else if (course.contains("Ηλεκτρονικά Ισχύος Ι")) { + } else if (normalisedCourse.contains(("Ηλεκτρονικά Iσχύος I"))) { return greeklish ? "isxyos_I" : "Ισχύος 1"; - } else if (course.contains("Ηλεκτρομαγνητικό Πεδίο ΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο II"))) { return greeklish ? "pedio_II" : "Πεδίο 2"; - } else if (course.contains("Ηλεκτρομαγνητικό Πεδίο Ι")) { + } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο I"))) { return greeklish ? "pedio_I" : "Πεδίο 1"; - } else if (course.contains("Ηλεκτρομαγνητική Συμβατότητα")) { + } else if (normalisedCourse.contains(("Ηλεκτρομαγνητική Συμβατότητα"))) { return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα"; - } else if (course.contains("Ηλεκτρολογικά Υλικά")) { + } else if (normalisedCourse.contains(("Ηλεκτρολογικά Υλικά"))) { return greeklish ? "ylika" : "Ηλεκτρ. Υλικά"; - } else if (course.contains("Ηλεκτρική Οικονομία")) { + } else if (normalisedCourse.contains(("Ηλεκτρική Οικονομία"))) { return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία"; - } else if (course.contains("Ηλεκτρικές Μηχανές Γ'")) { + } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Γ'"))) { return greeklish ? "mhxanes_C" : "Μηχανές Γ"; - } else if (course.contains("Ηλεκτρικές Μηχανές Β'")) { + } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Β'"))) { return greeklish ? "mhxanes_B" : "Μηχανές Β"; - } else if (course.contains("Ηλεκτρικές Μηχανές Α'")) { + } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Α'"))) { return greeklish ? "mhxanes_A" : "Μηχανές Α"; - } else if (course.contains("Ηλεκτρικές Μετρήσεις ΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρικές Μετρήσεις II"))) { return greeklish ? "metrhseis_II" : "Μετρήσεις 2"; - } else if (course.contains("Ηλεκτρικές Μετρήσεις Ι")) { + } else if (normalisedCourse.contains(("Ηλεκτρικές Μετρήσεις I"))) { return greeklish ? "metrhseis_1" : "Μετρήσεις 1"; - } else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα III"))) { return greeklish ? "kyklwmata_I" : "Κυκλώματα 3"; - } else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα II"))) { return greeklish ? "kyklwmata_II" : "Κυκλώματα 2"; - } else if (course.contains("Ηλεκτρικά Κυκλώματα Ι")) { + } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα I"))) { return greeklish ? "kyklwmata_I" : "Κυκλώματα 1"; - } else if (course.contains("Ηλεκτρακουστική ΙΙ")) { + } else if (normalisedCourse.contains(("Ηλεκτρακουστική II"))) { return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2"; - } else if (course.contains("Ηλεκτρακουστική Ι")) { + } else if (normalisedCourse.contains(("Ηλεκτρακουστική I"))) { return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1"; - } else if (course.contains("Εφαρμοσμένη Θερμοδυναμική")) { + } else if (normalisedCourse.contains(("Εφαρμοσμένη Θερμοδυναμική"))) { return greeklish ? "thermodynamikh" : "Θερμοδυναμική"; - } else if (course.contains("Εφαρμοσμένα Μαθηματικά ΙΙ")) { + } else if (normalisedCourse.contains(("Εφαρμοσμένα Μαθηματικά II"))) { return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2"; - } else if (course.contains("Εφαρμοσμένα Μαθηματικά Ι")) { + } else if (normalisedCourse.contains(("Εφαρμοσμένα Μαθηματικά I"))) { return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1"; - } else if (course.contains("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων")) { + } else if (normalisedCourse.contains(("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων"))) { return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων"; - } else if (course.contains("Ευφυή Συστήματα Ρομπότ")) { + } else if (normalisedCourse.contains(("Ευφυή Συστήματα Ρομπότ"))) { return greeklish ? "eufuh" : "Ευφυή"; - } else if (course.contains("Ευρυζωνικά Δίκτυα")) { + } else if (normalisedCourse.contains(("Ευρυζωνικά Δίκτυα"))) { return greeklish ? "eyryzwnika" : "Ευρυζωνικά"; - } else if (course.contains("Επιχειρησιακή Έρευνα")) { + } else if (normalisedCourse.contains(("Επιχειρησιακή Έρευνα"))) { return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα"; - } else if (course.contains("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου")) { + } else if (normalisedCourse.contains(("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου"))) { return greeklish ? "enswmatwmena" : "Ενσωματωμένα"; - } else if (course.contains("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας")) { + } else if (normalisedCourse.contains(("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας"))) { return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν."; - } else if (course.contains("Εισαγωγή στην Πολιτική Οικονομία")) { + } else if (normalisedCourse.contains(("Εισαγωγή στην Πολιτική Οικονομία"))) { return greeklish ? "polit_oik" : "Πολιτική Οικονομία"; - } else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία ΙΙ")) { + } else if (normalisedCourse.contains(("Εισαγωγή στην Ενεργειακή Τεχνολογία II"))) { return greeklish ? "EET_2" : "ΕΕΤ2"; - } else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία Ι")) { + } else if (normalisedCourse.contains(("Εισαγωγή στην Ενεργειακή Τεχνολογία I"))) { return greeklish ? "EET_I" : "ΕΕΤ 1"; - } else if (course.contains("Ειδικές Κεραίες, Σύνθεση Κεραιών")) { + } else if (normalisedCourse.contains(("Ειδικές Κεραίες, Σύνθεση Κεραιών"))) { return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών"; - } else if (course.contains("Ειδικές Αρχιτεκτονικές Υπολογιστών")) { + } else if (normalisedCourse.contains(("Ειδικές Αρχιτεκτονικές Υπολογιστών"))) { return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών"; - } else if (course.contains("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας")) { + } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας"))) { return greeklish ? "ekshe" : "ΕΚΣΗΕ"; - } else if (course.contains("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι")) { - return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι"; - } else if (course.contains("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων")) { + } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I"))) { + return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I"; + } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων"))) { return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων"; - } else if (course.contains("Δομημένος Προγραμματισμός")) { + } else if (normalisedCourse.contains(("Δομημένος Προγραμματισμός"))) { return greeklish ? "C" : "Δομ. Προγραμμ."; - } else if (course.contains("Δομές Δεδομένων")) { + } else if (normalisedCourse.contains(("Δομές Δεδομένων"))) { return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ."; - } else if (course.contains("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας")) { + } else if (normalisedCourse.contains(("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας"))) { return greeklish ? "dshe" : "ΔΣΗΕ"; - } else if (course.contains("Διαφορικές Εξισώσεις")) { + } else if (normalisedCourse.contains(("Διαφορικές Εξισώσεις"))) { return greeklish ? "diaforikes" : "Διαφορικές"; - } else if (course.contains("Διανεμημένη Παραγωγή")) { + } else if (normalisedCourse.contains(("Διανεμημένη Παραγωγή"))) { return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή"; - } else if (course.contains("Διακριτά μαθηματικά")) { + } else if (normalisedCourse.contains(("Διακριτά μαθηματικά"))) { return greeklish ? "diakrita" : "Διακριτά Μαθηματικά"; - } else if (course.contains("Διακριτά Μαθηματικά")) { + } else if (normalisedCourse.contains(("Διακριτά Μαθηματικά"))) { return greeklish ? "diakrita" : "Διακριτά Μαθηματικά"; - } else if (course.contains("Διάδοση Ηλεκτρομαγνητικού Κύματος Ι (πρώην Πεδίο ΙΙΙ)")) { + } else if (normalisedCourse.contains(("Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III)"))) { return greeklish ? "diadosi_1" : "Διάδοση 1"; - } else if (course.contains("Διάδοση Η/Μ Κύματος ΙΙ")) { + } else if (normalisedCourse.contains(("Διάδοση Η/Μ Κύματος II"))) { return greeklish ? "diadosi_II" : "Διάδοση 2"; - } else if (course.contains("Δίκτυα Υπολογιστών ΙΙ")) { + } else if (normalisedCourse.contains(("Δίκτυα Υπολογιστών II"))) { return greeklish ? "diktya_II" : "Δίκτυα 2"; - } else if (course.contains("Δίκτυα Υπολογιστών Ι")) { + } else if (normalisedCourse.contains(("Δίκτυα Υπολογιστών I"))) { return greeklish ? "diktya_I" : "Δίκτυα 1"; - } else if (course.contains("Δίκτυα Τηλεπικοινωνιών")) { + } else if (normalisedCourse.contains(("Δίκτυα Τηλεπικοινωνιών"))) { return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ."; - } else if (course.contains("Γραφική με Υπολογιστές")) { + } else if (normalisedCourse.contains(("Γραφική με Υπολογιστές"))) { return greeklish ? "grafikh" : "Γραφική"; - } else if (course.contains("Γραμμική Άλγεβρα")) { + } else if (normalisedCourse.contains(("Γραμμική Άλγεβρα"))) { return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ."; - } else if (course.contains("Γεωηλεκτρομαγνητισμός")) { + } else if (normalisedCourse.contains(("Γεωηλεκτρομαγνητισμός"))) { return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός"; - } else if (course.contains("Βιοϊατρική Τεχνολογία")) { + } else if (normalisedCourse.contains(("Βιοϊατρική Τεχνολογία"))) { return greeklish ? "vioiatriki" : "Βιοιατρική"; - } else if (course.contains("Βιομηχανική Πληροφορική")) { + } else if (normalisedCourse.contains(("Βιομηχανική Πληροφορική"))) { return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ"; - } else if (course.contains("Βιομηχανικά Ηλεκτρονικά")) { + } else if (normalisedCourse.contains(("Βιομηχανικά Ηλεκτρονικά"))) { return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά"; - } else if (course.contains("Βάσεις Δεδομένων")) { + } else if (normalisedCourse.contains(("Βάσεις Δεδομένων"))) { return greeklish ? "vaseis" : "Βάσεις"; - } else if (course.contains("Ασύρματος Τηλεπικοινωνία ΙΙ")) { + } else if (normalisedCourse.contains(("Ασύρματος Τηλεπικοινωνία II"))) { return greeklish ? "asyrmatos_II" : "Ασύρματος 2"; - } else if (course.contains("Ασύρματος Τηλεπικοινωνία Ι")) { + } else if (normalisedCourse.contains(("Ασύρματος Τηλεπικοινωνία I"))) { return greeklish ? "asyrmatos_I" : "Ασύρματος 1"; - } else if (course.contains("Ασφάλεια Πληροφοριακών Συστημάτων")) { + } else if (normalisedCourse.contains(("Ασφάλεια Πληροφοριακών Συστημάτων"))) { return greeklish ? "asfaleia" : "Ασφάλεια"; - } else if (course.contains("Ασαφή Συστήματα")) { + } else if (normalisedCourse.contains(("Ασαφή Συστήματα"))) { return greeklish ? "asafh" : "Ασαφή"; - } else if (course.contains("Αρχιτεκτονική Υπολογιστών")) { + } else if (normalisedCourse.contains(("Αρχιτεκτονική Υπολογιστών"))) { return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών"; - } else if (course.contains("Αρχές Παράλληλης Επεξεργασίας")) { + } else if (normalisedCourse.contains(("Αρχές Παράλληλης Επεξεργασίας"))) { return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας"; - } else if (course.contains("Αρχές Οικονομίας")) { + } else if (normalisedCourse.contains(("Αρχές Οικονομίας"))) { return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας"; - } else if (course.contains("Αριθμητική Ανάλυση")) { + } else if (normalisedCourse.contains(("Αριθμητική Ανάλυση"))) { return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση"; - } else if (course.contains("Αξιοπιστία Συστημάτων")) { + } else if (normalisedCourse.contains(("Αξιοπιστία Συστημάτων"))) { return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων"; - } else if (course.contains("Αντικειμενοστραφής Προγραμματισμός")) { + } else if (normalisedCourse.contains(("Αντικειμενοστραφής Προγραμματισμός"))) { return greeklish ? "OOP" : "Αντικειμενοστραφής"; - } else if (course.contains("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα Ι)")) { + } else if (normalisedCourse.contains(("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I)"))) { return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ."; - } else if (course.contains("Αναγνώριση Προτύπων")) { + } else if (normalisedCourse.contains(("Αναγνώριση Προτύπων"))) { return greeklish ? "protipa" : "Αναγνώριση Προτύπων"; - } else if (course.contains("Ανάλυση και Σχεδίαση Αλγορίθμων")) { + } else if (normalisedCourse.contains(("Ανάλυση και Σχεδίαση Αλγορίθμων"))) { return greeklish ? "algorithms" : "Αλγόριθμοι"; - } else if (course.contains("Ανάλυση Χρονοσειρών")) { + } else if (normalisedCourse.contains(("Ανάλυση Χρονοσειρών"))) { return greeklish ? "xronoseires" : "Χρονοσειρές"; - } else if (course.contains("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας")) { + } else if (normalisedCourse.contains(("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας"))) { return greeklish ? "ASHE" : "ΑΣΗΕ"; - } else if (course.contains("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή")) { + } else if (normalisedCourse.contains(("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή"))) { return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή"; - } else if (course.contains("Ακουστική ΙΙ")) { + } else if (normalisedCourse.contains(("Ακουστική II"))) { return greeklish ? "akoystikh_II" : "Ακουστική 2"; - } else if (course.contains("Ακουστική Ι")) { + } else if (normalisedCourse.contains(("Ακουστική I"))) { return greeklish ? "akoystikh_I" : "Ακουστική 1"; } else { + Timber.wtf("Unrecognised course came in the upload fields generator! Course string = %s", course); return null; } } 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..1bb45c84 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,64 +1,44 @@ 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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; + +import android.util.Log; import android.widget.Toast; +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 final static 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); + 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()) { @@ -97,103 +77,65 @@ 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(); + @Nullable + public static File createZipFile(@NonNull String zipFilename) { + // Create a zip file name + File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + + File.separator + "mThmmy"); + + if (!zipFolder.exists()) { + if (!zipFolder.mkdirs()) { + Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName()); + return null; } - tempFilesDirectory.delete(); - } - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - static void deleteCacheFiles(Context context) { - File cacheFilesDirectory = context.getExternalCacheDir(); - assert cacheFilesDirectory != null; - String[] tempFilesArray = cacheFilesDirectory.list(); - for (String tempFile : tempFilesArray) { - new File(cacheFilesDirectory, tempFile).delete(); - } - } - - /** - * Resize to avoid using too much memory loading big images (e.g.: 2560*1920) - **/ - static Bitmap getImageResized(Context context, Uri selectedImage) { - Bitmap bm; - int[] sampleSizes = new int[]{5, 3, 2, 1}; - int i = 0; - do { - bm = decodeBitmap(context, selectedImage, sampleSizes[i]); - i++; - } while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length); - return bm; - } - - private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = sampleSize; - - AssetFileDescriptor fileDescriptor = null; - try { - fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r"); - } catch (FileNotFoundException e) { - e.printStackTrace(); } - 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; + public 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/model/UploadFile.java b/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java new file mode 100644 index 00000000..c1bdb539 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java @@ -0,0 +1,44 @@ +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/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java new file mode 100644 index 00000000..c7c56e86 --- /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 androidx.appcompat.app.AlertDialog; + +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +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/utils/FileUtils.java b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java index 1c8eb0b0..7beffeb6 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,16 @@ package gr.thmmy.mthmmy.utils; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.OpenableColumns; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.webkit.MimeTypeMap; import java.io.File; -import androidx.annotation.NonNull; +import gr.thmmy.mthmmy.R; import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; @@ -21,7 +27,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..06a355f2 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java @@ -2,15 +2,16 @@ package gr.thmmy.mthmmy.utils; import android.content.Context; import android.util.AttributeSet; +import android.util.Log; 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. @@ -37,7 +38,9 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior 0 || (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed > 50)) { + Log.d("THISSSS", "GOT_HIDE"); child.hide(new FloatingActionButton.OnVisibilityChangedListener() { @Override public void onHidden(FloatingActionButton fab) { @@ -47,7 +50,14 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior 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()) { + if (!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/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..5ac81b3a 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,8 @@ android:layout_marginTop="64dp" android:background="@color/background" android:scrollbars="none" - tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity"> - + app:layout_behavior="@string/appbar_scrolling_view_behavior" + 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..fb985454 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/xml/app_preferences_user.xml b/app/src/main/res/xml/app_preferences_user.xml index 2599df44..0ea00d34 100644 --- a/app/src/main/res/xml/app_preferences_user.xml +++ b/app/src/main/res/xml/app_preferences_user.xml @@ -49,7 +49,7 @@ app:iconSpaceReserved="false" /> - + Date: Mon, 17 Jun 2019 09:49:05 +0300 Subject: [PATCH 07/37] Fix post icons getting messed up --- .../create_content/NewTopicTask.java | 1 + .../activities/topic/tasks/EditTask.java | 1 + .../topic/tasks/PrepareForEditResult.java | 9 ++++++-- .../topic/tasks/PrepareForEditTask.java | 22 +++++++++++-------- .../activities/topic/tasks/ReplyTask.java | 1 + .../mthmmy/viewmodel/TopicViewModel.java | 2 +- 6 files changed, 24 insertions(+), 12 deletions(-) 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/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/viewmodel/TopicViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java index a7e72883..c15e46db 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -219,7 +219,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()); } /** From bc9fbcf56c73ae8d30a2de2a870707d0e6349ac6 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Mon, 17 Jun 2019 14:54:45 +0300 Subject: [PATCH 08/37] Fix permissions issue for Android API >28 --- app/src/main/AndroidManifest.xml | 1 + .../activities/upload/UploadActivity.java | 55 ++++++++++++++----- .../mthmmy/utils/ScrollAwareFABBehavior.java | 3 - 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1e8bf257..3f63279b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + 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(); + } + break; } } 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 06a355f2..44c0d4f3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java @@ -38,9 +38,7 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior 0 || (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed > 50)) { - Log.d("THISSSS", "GOT_HIDE"); child.hide(new FloatingActionButton.OnVisibilityChangedListener() { @Override public void onHidden(FloatingActionButton fab) { @@ -50,7 +48,6 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior Date: Tue, 18 Jun 2019 17:58:51 +0300 Subject: [PATCH 09/37] Fix multiple file choosing, Fix options lost on orientation change --- app/src/main/AndroidManifest.xml | 1 + .../gr/thmmy/mthmmy/activities/upload/UploadActivity.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3f63279b..fd302031 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,7 @@ Date: Wed, 19 Jun 2019 15:48:58 +0300 Subject: [PATCH 10/37] Add firebase event for shouts --- .../gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java | 2 ++ 1 file changed, 2 insertions(+) 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..35d86faf 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 @@ -17,6 +17,7 @@ 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 +134,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) { From b81b2fb0eb5fc58cf13b7fc510629619756aee46 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Wed, 19 Jun 2019 16:01:49 +0300 Subject: [PATCH 11/37] Up version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 337120bc..3fe9335d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 18 - versionName "1.6.3" + versionCode 19 + versionName "1.7.0" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" From a28ea2874268c4340be6c6f01394f432c517c72e Mon Sep 17 00:00:00 2001 From: Apostolof Date: Wed, 19 Jun 2019 18:50:26 +0300 Subject: [PATCH 12/37] Various fixes --- .../activities/upload/UploadActivity.java | 59 +++++++++++-------- .../upload/UploadFieldsBuilderActivity.java | 4 +- app/src/main/res/layout/activity_board.xml | 1 - .../main/res/xml-v26/app_preferences_user.xml | 4 +- 4 files changed, 39 insertions(+), 29 deletions(-) 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 33a280f2..67f612e3 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 @@ -360,29 +360,37 @@ public class UploadActivity extends BaseActivity { if (!editTextFilename.equals(selectedFileFilename)) { //File should be uploaded with a different name - 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())); + 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 { @@ -586,8 +594,9 @@ public class UploadActivity extends BaseActivity { if (previousName.isEmpty()) { uploadFilename.setText(data.getStringExtra(RESULT_FILENAME)); } else { + String extractedExtension = FileUtils.getFileExtension(previousName); String filenameWithExtension = data.getStringExtra(RESULT_FILENAME) + - FileUtils.getFileExtension(previousName); + (extractedExtension != null ? extractedExtension : ""); uploadFilename.setText(filenameWithExtension); } hasModifiedFilename = true; @@ -609,7 +618,7 @@ public class UploadActivity extends BaseActivity { break; case UPLOAD_REQUEST_STORAGE_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && - zipTask != null) { + zipTask != null) { Uri[] filesListArray = new Uri[filesList.size()]; for (int i = 0; i < filesList.size(); ++i) { filesListArray[i] = filesList.get(i).getFileUri(); @@ -617,6 +626,8 @@ public class UploadActivity extends BaseActivity { zipTask.execute(filesListArray); finish(); + } else { + Toast.makeText(this, "Please retry uploading.", Toast.LENGTH_SHORT).show(); } break; } 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 e89fda41..ebf221f6 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 @@ -390,9 +390,9 @@ public class UploadFieldsBuilderActivity extends BaseActivity { return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1"; } else if (normalisedCourse.contains(("Ηλεκτρονικές Διατάξεις και Μετρήσεις"))) { return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις"; - } else if (normalisedCourse.contains(("Ηλεκτρονικά Iσχύος II"))) { + } else if (normalisedCourse.contains(("Ηλεκτρονικά Ισχύος II"))) { return greeklish ? "isxyos_II" : "Ισχύος 2"; - } else if (normalisedCourse.contains(("Ηλεκτρονικά Iσχύος I"))) { + } else if (normalisedCourse.contains(("Ηλεκτρονικά Ισχύος I"))) { return greeklish ? "isxyos_I" : "Ισχύος 1"; } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο II"))) { return greeklish ? "pedio_II" : "Πεδίο 2"; diff --git a/app/src/main/res/layout/activity_board.xml b/app/src/main/res/layout/activity_board.xml index 5ac81b3a..0a61658e 100644 --- a/app/src/main/res/layout/activity_board.xml +++ b/app/src/main/res/layout/activity_board.xml @@ -42,7 +42,6 @@ android:layout_marginTop="64dp" android:background="@color/background" android:scrollbars="none" - app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" /> - + Date: Thu, 20 Jun 2019 15:31:25 +0300 Subject: [PATCH 13/37] Up libs --- app/build.gradle | 8 ++++---- build.gradle | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3fe9335d..56b9112b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,7 +62,7 @@ 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.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' @@ -70,9 +70,9 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 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.google.firebase:firebase-core:17.0.0' + implementation 'com.google.firebase:firebase-messaging:19.0.0' + implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.snatik:storage:2.1.0' implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.squareup.picasso:picasso:2.5.2' diff --git a/build.gradle b/build.gradle index 81176d71..677adbde 100644 --- a/build.gradle +++ b/build.gradle @@ -10,8 +10,8 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.4.1' classpath 'com.google.gms:google-services:4.2.0' - classpath 'io.fabric.tools:gradle:1.26.1' - classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' // Also change in app/gradle/grgit.gradle + classpath 'io.fabric.tools:gradle:1.29.0' + classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle } } From 81054f07689bd4fa84ff0e9e463c257fe16adfe0 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Fri, 21 Jun 2019 19:28:39 +0300 Subject: [PATCH 14/37] Uploads improvements (moved courses in resources, disabled Generate Button when needed) --- .../mthmmy/activities/AboutActivity.java | 5 +- .../mthmmy/activities/LoginActivity.java | 5 +- .../activities/board/BoardActivity.java | 9 +- .../mthmmy/activities/board/BoardAdapter.java | 3 +- .../bookmarks/BookmarksActivity.java | 11 +- .../bookmarks/BookmarksBoardFragment.java | 5 +- .../bookmarks/BookmarksTopicFragment.java | 5 +- .../downloads/DownloadsActivity.java | 12 +- .../downloads/DownloadsAdapter.java | 3 +- .../mthmmy/activities/main/MainActivity.java | 11 +- .../activities/main/forum/ForumAdapter.java | 5 +- .../activities/main/forum/ForumFragment.java | 7 +- .../activities/main/recent/RecentAdapter.java | 5 +- .../main/recent/RecentFragment.java | 7 +- .../activities/main/unread/UnreadAdapter.java | 5 +- .../main/unread/UnreadFragment.java | 9 +- .../activities/profile/ProfileActivity.java | 13 +- .../latestPosts/LatestPostsAdapter.java | 3 +- .../latestPosts/LatestPostsFragment.java | 7 +- .../profile/stats/StatsFragment.java | 3 +- .../profile/summary/SummaryFragment.java | 3 +- .../activities/settings/SettingsActivity.java | 1 + .../activities/settings/SettingsFragment.java | 5 +- .../activities/shoutbox/ShoutAdapter.java | 1 + .../activities/shoutbox/ShoutboxFragment.java | 1 + .../activities/topic/TopicActivity.java | 11 +- .../mthmmy/activities/topic/TopicAdapter.java | 15 +- .../activities/upload/UploadActivity.java | 170 ++++---- .../upload/UploadFieldsBuilderActivity.java | 383 ++---------------- .../activities/upload/UploadsCourse.java | 66 +++ .../activities/upload/UploadsHelper.java | 5 +- .../gr/thmmy/mthmmy/base/BaseApplication.java | 5 +- .../gr/thmmy/mthmmy/base/BaseFragment.java | 1 + .../thmmy/mthmmy/editorview/EditorView.java | 11 +- .../mthmmy/editorview/EmojiKeyboard.java | 5 +- .../editorview/EmojiKeyboardAdapter.java | 1 + .../editorview/FormatButtonsAdapter.java | 1 + .../java/gr/thmmy/mthmmy/model/Bookmark.java | 6 +- .../main/java/gr/thmmy/mthmmy/model/Post.java | 4 +- .../gr/thmmy/mthmmy/model/UploadFile.java | 1 + .../mthmmy/services/NotificationService.java | 7 +- .../mthmmy/services/UploadsReceiver.java | 4 +- .../thmmy/mthmmy/session/SessionManager.java | 5 +- .../utils/AppCompatSpinnerWithoutDefault.java | 4 +- .../utils/CustomLinearLayoutManager.java | 1 + .../java/gr/thmmy/mthmmy/utils/FileUtils.java | 3 +- .../mthmmy/utils/ScrollAwareFABBehavior.java | 1 - .../utils/ScrollAwareLinearBehavior.java | 4 +- .../java/gr/thmmy/mthmmy/utils/TakePhoto.java | 3 +- .../mthmmy/utils/ToggledBackgroundButton.java | 27 ++ .../mthmmy/utils/parsing/ParseHelpers.java | 2 +- .../thmmy/mthmmy/viewmodel/BaseViewModel.java | 1 + .../mthmmy/viewmodel/ShoutboxViewModel.java | 1 + .../mthmmy/viewmodel/TopicViewModel.java | 3 +- app/src/main/res/layout/activity_upload.xml | 2 +- app/src/main/res/values/uploads_courses.xml | 153 +++++++ 56 files changed, 516 insertions(+), 534 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java create mode 100644 app/src/main/res/values/uploads_courses.xml 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 0effdaa3..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; 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/downloads/DownloadsActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java index e4c3676c..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 @@ -10,6 +10,12 @@ 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; @@ -17,12 +23,6 @@ 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 com.google.android.material.floatingactionbutton.FloatingActionButton; - import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.upload.UploadActivity; import gr.thmmy.mthmmy.base.BaseActivity; 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 35d86faf..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,6 +16,7 @@ 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; 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 5472a742..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; 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 67f612e3..31f51b8e 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 @@ -6,6 +6,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -53,6 +54,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.Locale; import java.util.UUID; @@ -71,7 +73,9 @@ 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; @@ -84,7 +88,7 @@ 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 */ @@ -100,12 +104,15 @@ public class UploadActivity extends BaseActivity { private static final int MAX_FILE_SIZE_SUPPORTED = 45000000; - //private UploadsReceiver uploadsReceiver = new UploadsReceiver(); + private HashMap uploadsCourses; + private ArrayList uploadRootCategories = new ArrayList<>(); private ParseUploadPageTask parseUploadPageTask; private ArrayList bundleCategory; private String categorySelected = "-1"; private String uploaderProfileIndex = "1"; + private UploadsCourse uploadsCourse; + private String semester = ""; private ArrayList filesList = new ArrayList<>(); private File photoFileCreated = null; @@ -168,64 +175,19 @@ 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; - } - - int numberOfSpinners = categoriesSpinners.getChildCount(); - - if (numberOfSpinners < 3) { - Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show(); - 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(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); } - if (maybeCourse == null) { - Toast.makeText(view.getContext(), "Please choose a course", Toast.LENGTH_SHORT).show(); - return; - } - - //Fixes course and semester - String course = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); - String semester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); - - 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); }); - titleDescriptionBuilderButton.setEnabled(false); uploadTitle = findViewById(R.id.upload_title); uploadDescription = findViewById(R.id.upload_description); @@ -343,7 +305,7 @@ public class UploadActivity extends BaseActivity { //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; + uploadDescriptionText[0] += uploadedFromTHMMYPromptHtml; } for (UploadFile file : filesList) { @@ -450,6 +412,10 @@ public class UploadActivity extends BaseActivity { updateUIElements(); titleDescriptionBuilderButton.setEnabled(true); } + + Resources res = getResources(); + uploadsCourses = new HashMap<>(UploadsCourse + .generateUploadsCourses(res.getStringArray(R.array.string_array_uploads_courses))); } @Override @@ -493,7 +459,7 @@ public class UploadActivity extends BaseActivity { if (!hasModifiedFilename) { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); - String zipFilename = "mThmmy_" + timeStamp + ".zip"; + String zipFilename = "mTHMMY_" + timeStamp + ".zip"; uploadFilename.setText(zipFilename); hasModifiedFilename = false; } @@ -542,7 +508,7 @@ public class UploadActivity extends BaseActivity { if (!hasModifiedFilename) { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); - String zipFilename = "mThmmy_" + timeStamp + ".zip"; + String zipFilename = "mTHMMY_" + timeStamp + ".zip"; uploadFilename.setText(zipFilename); hasModifiedFilename = false; } @@ -574,7 +540,7 @@ public class UploadActivity extends BaseActivity { if (!hasModifiedFilename) { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); - String zipFilename = "mThmmy_" + timeStamp + ".zip"; + String zipFilename = "mTHMMY_" + timeStamp + ".zip"; uploadFilename.setText(zipFilename); hasModifiedFilename = false; } @@ -881,6 +847,7 @@ public class UploadActivity extends BaseActivity { } categorySelected = parentCategories.get(position).getValue(); + setCourseAndSemester(); //Adds new sub-category spinner if (parentCategories.get(position).hasSubCategories()) { @@ -924,7 +891,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.i("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); + titleDescriptionBuilderButton.setEnabled(true); + return; + } + + titleDescriptionBuilderButton.setEnabled(false); } } @@ -987,7 +1019,6 @@ public class UploadActivity extends BaseActivity { @Override protected void postExecution(ResultCode result) { updateUIElements(); - titleDescriptionBuilderButton.setEnabled(true); progressBar.setVisibility(ProgressBar.GONE); } } @@ -1032,14 +1063,14 @@ public class UploadActivity extends BaseActivity { @Override protected Boolean doInBackground(Uri... filesToZip) { - if (weakActivity == null || zipFilename == null) { + if (weakActivity == null || zipFilename == null) return false; - } + File zipFile = UploadsHelper.createZipFile(zipFilename); - if (zipFile == null) { + if (zipFile == null) return false; - } + zipFileUri = FileProvider.getUriForFile(weakActivity.get(), weakActivity.get().getPackageName() + ".provider", zipFile); @@ -1050,9 +1081,8 @@ public class UploadActivity extends BaseActivity { @Override protected void onPostExecute(Boolean result) { - if (weakActivity == null) { + if (weakActivity == null) return; - } if (!result) { Toast.makeText(weakActivity.get(), "Couldn't create zip!", 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 ebf221f6..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 @@ -3,7 +3,6 @@ package gr.thmmy.mthmmy.activities.upload; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import androidx.annotation.Nullable; import android.text.Editable; import android.text.TextWatcher; import android.view.View; @@ -12,23 +11,26 @@ import android.widget.LinearLayout; import android.widget.RadioGroup; import android.widget.Toast; +import androidx.annotation.Nullable; + import java.util.Calendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; import timber.log.Timber; public class UploadFieldsBuilderActivity extends BaseActivity { - static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE"; + + static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME"; + static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME"; + static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME"; static final String BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER"; static final String 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; @@ -46,25 +48,20 @@ public class UploadFieldsBuilderActivity extends BaseActivity { int inputYear = Integer.parseInt(working); isValidYear = inputYear <= currentYear && inputYear > 1980; - } else { + } else isValidYear = false; - } - if (!isValidYear) { + 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 @@ -74,9 +71,11 @@ public class UploadFieldsBuilderActivity extends BaseActivity { 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()); @@ -143,11 +142,11 @@ public class UploadFieldsBuilderActivity extends BaseActivity { 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; } @@ -157,11 +156,11 @@ public class UploadFieldsBuilderActivity extends BaseActivity { 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; } @@ -170,11 +169,11 @@ public class UploadFieldsBuilderActivity extends BaseActivity { 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; } @@ -205,336 +204,4 @@ public class UploadFieldsBuilderActivity extends BaseActivity { return null; } } - - @Nullable - private String getGreeklishCourseName() { - return getGreeklishOrMinifiedCourseName(true); - } - - @Nullable - private String getMinifiedCourseName() { - return getGreeklishOrMinifiedCourseName(false); - } - - private String normalizeLatinNumbers(String stringWithLatinNumbers) { - String greekLatinOne = "Ι", englishLatinOne = "I"; - String normalisedString; - - //Separates the latin number suffix from the course name - final String regex = "(.+)\\ ([IΙ]+)"; - final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE); - final Matcher matcher = pattern.matcher(stringWithLatinNumbers); - - if (matcher.matches() && matcher.groupCount() == 2) { - normalisedString = matcher.group(1) + " " + matcher.group(2).replaceAll(greekLatinOne, englishLatinOne); - } else { - normalisedString = stringWithLatinNumbers; - } - - return normalisedString; - } - - @Nullable - private String getGreeklishOrMinifiedCourseName(boolean greeklish) { - String normalisedCourse = normalizeLatinNumbers(course); - - if (normalisedCourse.contains(("Ψηφιακή Επεξεργασία Σήματος"))) { - return greeklish ? "PSES" : "ΨΕΣ"; - } else if (normalisedCourse.contains(("Ψηφιακή Επεξεργασία Εικόνας"))) { - return greeklish ? "psee" : "ΨΕΕ"; - } else if (normalisedCourse.contains(("Ψηφιακές Τηλεπικοινωνίες II"))) { - return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2"; - } else if (normalisedCourse.contains(("Ψηφιακές Τηλεπικοινωνίες I"))) { - return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1"; - } else if (normalisedCourse.contains(("Ψηφιακά Φίλτρα"))) { - return greeklish ? "filtra" : "Φίλτρα"; - } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα III"))) { - return greeklish ? "pshfiaka_III" : "Ψηφιακά 3"; - } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα II"))) { - return greeklish ? "pshfiaka_II" : "Ψηφιακά 2"; - } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα I"))) { - return greeklish ? "pshfiaka_I" : "Ψηφιακά 1"; - } else if (normalisedCourse.contains(("Φωτονική Τεχνολογία"))) { - return greeklish ? "fwtonikh" : "Φωτονική"; - } else if (normalisedCourse.contains(("Φυσική I"))) { - return greeklish ? "fysikh_I" : "Φυσική 1"; - } else if (normalisedCourse.contains(("Υψηλές Τάσεις III"))) { - return greeklish ? "ypshles_III" : "Υψηλές 3"; - } else if (normalisedCourse.contains(("Υψηλές Τάσεις II"))) { - return greeklish ? "ypshles_II" : "Υψηλές 2"; - } else if (normalisedCourse.contains(("Υψηλές Τάσεις I"))) { - return greeklish ? "ypshles_I" : "Υψηλές 1"; - } else if (normalisedCourse.contains(("Υψηλές Τάσεις 4"))) { - return greeklish ? "ypshles_IV" : "Υψηλές 4"; - } else if (normalisedCourse.contains(("Υπολογιστικός Ηλεκτρομαγνητισμός"))) { - return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ"; - } else if (normalisedCourse.contains(("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα"))) { - return greeklish ? "ymes" : "ΥΜΕΣ"; - } else if (normalisedCourse.contains(("Τηλεπικοινωνιακή Ηλεκτρονική"))) { - return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ."; - } else if (normalisedCourse.contains(("Τηλεοπτικά Συστήματα"))) { - return greeklish ? "tileoptika" : "Τηλεοπτικά"; - } else if (normalisedCourse.contains(("Τεχνολογία Λογισμικού"))) { - return greeklish ? "SE" : "Τεχνολογία Λογισμικού"; - } else if (normalisedCourse.contains(("Τεχνολογία Ηλεκτροτεχνικών Υλικών"))) { - return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά"; - } else if (normalisedCourse.contains(("Τεχνολογία Ήχου και Εικόνας"))) { - return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας"; - } else if (normalisedCourse.contains(("Τεχνική Μηχανική"))) { - return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν."; - } else if (normalisedCourse.contains(("Τεχνικές μη Καταστρεπτικών Δοκιμών"))) { - return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές"; - } else if (normalisedCourse.contains(("Τεχνικές Σχεδίασης με Η/Υ"))) { - return greeklish ? "sxedio" : "Σχέδιο"; - } else if (normalisedCourse.contains(("Τεχνικές Κωδικοποίησης"))) { - return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης"; - } else if (normalisedCourse.contains(("Τεχνικές Βελτιστοποίησης"))) { - return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση"; - } else if (normalisedCourse.contains(("Σύνθεση Τηλεπικοινωνιακών Διατάξεων"))) { - return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων"; - } else if (normalisedCourse.contains(("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων"))) { - return greeklish ? "synthesh" : "Σύνθεση"; - } else if (normalisedCourse.contains(("Σχεδίαση Συστημάτων VLSI"))) { - return greeklish ? "VLSI" : "VLSI"; - } else if (normalisedCourse.contains(("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)"))) { - return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών"; - } else if (normalisedCourse.contains(("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα"))) { - return greeklish ? "polymesa" : "Πολυμέσα"; - } else if (normalisedCourse.contains(("Συστήματα Μικροϋπολογιστών"))) { - return greeklish ? "mikro_I" : "Μίκρο 1"; - } else if (normalisedCourse.contains(("Συστήματα Ηλεκτροκίνησης"))) { - return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση"; - } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας III"))) { - return greeklish ? "SHE_III" : "ΣΗΕ 3"; - } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας II"))) { - return greeklish ? "SHE_II" : "ΣΗΕ 2"; - } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας I"))) { - return greeklish ? "SHE_I" : "ΣΗΕ 1"; - } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου III"))) { - return greeklish ? "SAE_III" : "ΣΑΕ 3"; - } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου II"))) { - return greeklish ? "SAE_II" : "ΣΑΕ 2"; - } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου I"))) { - return greeklish ? "SAE_1" : "ΣΑΕ 1"; - } else if (normalisedCourse.contains(("Στοχαστικό Σήμα"))) { - return greeklish ? "stox_shma" : "Στοχ. Σήμα"; - } else if (normalisedCourse.contains(("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας"))) { - return greeklish ? "SPHE" : "ΣΠΗΕ"; - } else if (normalisedCourse.contains(("Σερβοκινητήρια Συστήματα"))) { - return greeklish ? "servo" : "Σέρβο"; - } else if (normalisedCourse.contains(("Σήματα και Συστήματα"))) { - return greeklish ? "analog_shma" : "Σύματα & Συστήματα"; - } else if (normalisedCourse.contains(("Ρομποτική"))) { - return greeklish ? "rompotikh" : "Ρομποτική"; - } else if (normalisedCourse.contains(("Προσομοίωση και Μοντελοποίηση Συστημάτων"))) { - return greeklish ? "montelopoihsh" : "Μοντελοποίηση"; - } else if (normalisedCourse.contains(("Προηγμένες Τεχνικές Επεξεργασίας Σήματος"))) { - return greeklish ? "ptes" : "ΠΤΕΣ"; - } else if (normalisedCourse.contains(("Προγραμματιστικές Τεχνικές"))) { - return greeklish ? "cpp" : "Προγραμματ. Τεχν."; - } else if (normalisedCourse.contains(("Προγραμματιζόμενα Κυκλώματα ASIC"))) { - return greeklish ? "asic" : "ASIC"; - } else if (normalisedCourse.contains(("Παράλληλα και Κατανεμημένα Συστήματα"))) { - return greeklish ? "parallhla" : "Παράλληλα"; - } else if (normalisedCourse.contains(("Οργάνωση και Διοίκηση Εργοστασίων"))) { - return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων"; - } else if (normalisedCourse.contains(("Οργάνωση Υπολογιστών"))) { - return greeklish ? "org_ypol" : "Οργάνωση Υπολ."; - } else if (normalisedCourse.contains(("Οπτική II"))) { - return greeklish ? "optikh_II" : "Οπτική 2"; - } else if (normalisedCourse.contains(("Οπτική I"))) { - return greeklish ? "optikh_I" : "Οπτική 1"; - } else if (normalisedCourse.contains(("Οπτικές Επικοινωνίες"))) { - return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ."; - } else if (normalisedCourse.contains(("Μικροκύματα II"))) { - return greeklish ? "mikrokymata_II" : "Μικροκύματα 2"; - } else if (normalisedCourse.contains(("Μικροκύματα I"))) { - return greeklish ? "mikrokymata_I" : "Μικροκύματα 1"; - } else if (normalisedCourse.contains(("Μικροκυματική Τηλεπισκόπηση"))) { - return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση"; - } else if (normalisedCourse.contains(("Μικροεπεξεργαστές και Περιφερειακά"))) { - return greeklish ? "mikro_II" : "Μίκρο 2"; - } else if (normalisedCourse.contains(("Μετάδοση Θερμότητας"))) { - return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ."; - } else if (normalisedCourse.contains(("Λογισμός II"))) { - return greeklish ? "logismos_II" : "Λογισμός 2"; - } else if (normalisedCourse.contains(("Λογισμός I"))) { - return greeklish ? "logismos_I" : "Λογισμός 1"; - } else if (normalisedCourse.contains(("Λογική Σχεδίαση"))) { - return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση"; - } else if (normalisedCourse.contains(("Λειτουργικά Συστήματα"))) { - return greeklish ? "OS" : "Λειτουργικά"; - } else if (normalisedCourse.contains(("Κινητές και Δορυφορικές Επικοινωνίες"))) { - return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες"; - } else if (normalisedCourse.contains(("Κβαντική Φυσική"))) { - return greeklish ? "kvantikh" : "Κβαντική"; - } else if (normalisedCourse.contains(("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων"))) { - return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων"; - } else if (normalisedCourse.contains(("Θεωρία Υπολογισμών και Αλγορίθμων"))) { - return greeklish ? "thya" : "ΘΥΑ"; - } else if (normalisedCourse.contains(("Θεωρία Σκέδασης"))) { - return greeklish ? "skedash" : "Σκέδαση"; - } else if (normalisedCourse.contains(("Θεωρία Σημάτων και Γραμμικών Συστημάτων"))) { - return greeklish ? "analog_shma" : "Σύματα & Συστήματα"; - } else if (normalisedCourse.contains(("Θεωρία Πληροφοριών"))) { - return greeklish ? "theoria_plir" : "Θεωρία Πληρ."; - } else if (normalisedCourse.contains(("Θεωρία Πιθανοτήτων και Στατιστική"))) { - return greeklish ? "pithanothtes" : "Πιθανότητες"; - } else if (normalisedCourse.contains(("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις"))) { - return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά"; - } else if (normalisedCourse.contains(("Ηλεκτρονική III"))) { - return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3"; - } else if (normalisedCourse.contains(("Ηλεκτρονική II"))) { - return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2"; - } else if (normalisedCourse.contains(("Ηλεκτρονική I"))) { - return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1"; - } else if (normalisedCourse.contains(("Ηλεκτρονικές Διατάξεις και Μετρήσεις"))) { - return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις"; - } else if (normalisedCourse.contains(("Ηλεκτρονικά Ισχύος II"))) { - return greeklish ? "isxyos_II" : "Ισχύος 2"; - } else if (normalisedCourse.contains(("Ηλεκτρονικά Ισχύος I"))) { - return greeklish ? "isxyos_I" : "Ισχύος 1"; - } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο II"))) { - return greeklish ? "pedio_II" : "Πεδίο 2"; - } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο I"))) { - return greeklish ? "pedio_I" : "Πεδίο 1"; - } else if (normalisedCourse.contains(("Ηλεκτρομαγνητική Συμβατότητα"))) { - return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα"; - } else if (normalisedCourse.contains(("Ηλεκτρολογικά Υλικά"))) { - return greeklish ? "ylika" : "Ηλεκτρ. Υλικά"; - } else if (normalisedCourse.contains(("Ηλεκτρική Οικονομία"))) { - return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία"; - } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Γ'"))) { - return greeklish ? "mhxanes_C" : "Μηχανές Γ"; - } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Β'"))) { - return greeklish ? "mhxanes_B" : "Μηχανές Β"; - } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Α'"))) { - return greeklish ? "mhxanes_A" : "Μηχανές Α"; - } else if (normalisedCourse.contains(("Ηλεκτρικές Μετρήσεις II"))) { - return greeklish ? "metrhseis_II" : "Μετρήσεις 2"; - } else if (normalisedCourse.contains(("Ηλεκτρικές Μετρήσεις I"))) { - return greeklish ? "metrhseis_1" : "Μετρήσεις 1"; - } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα III"))) { - return greeklish ? "kyklwmata_I" : "Κυκλώματα 3"; - } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα II"))) { - return greeklish ? "kyklwmata_II" : "Κυκλώματα 2"; - } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα I"))) { - return greeklish ? "kyklwmata_I" : "Κυκλώματα 1"; - } else if (normalisedCourse.contains(("Ηλεκτρακουστική II"))) { - return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2"; - } else if (normalisedCourse.contains(("Ηλεκτρακουστική I"))) { - return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1"; - } else if (normalisedCourse.contains(("Εφαρμοσμένη Θερμοδυναμική"))) { - return greeklish ? "thermodynamikh" : "Θερμοδυναμική"; - } else if (normalisedCourse.contains(("Εφαρμοσμένα Μαθηματικά II"))) { - return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2"; - } else if (normalisedCourse.contains(("Εφαρμοσμένα Μαθηματικά I"))) { - return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1"; - } else if (normalisedCourse.contains(("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων"))) { - return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων"; - } else if (normalisedCourse.contains(("Ευφυή Συστήματα Ρομπότ"))) { - return greeklish ? "eufuh" : "Ευφυή"; - } else if (normalisedCourse.contains(("Ευρυζωνικά Δίκτυα"))) { - return greeklish ? "eyryzwnika" : "Ευρυζωνικά"; - } else if (normalisedCourse.contains(("Επιχειρησιακή Έρευνα"))) { - return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα"; - } else if (normalisedCourse.contains(("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου"))) { - return greeklish ? "enswmatwmena" : "Ενσωματωμένα"; - } else if (normalisedCourse.contains(("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας"))) { - return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν."; - } else if (normalisedCourse.contains(("Εισαγωγή στην Πολιτική Οικονομία"))) { - return greeklish ? "polit_oik" : "Πολιτική Οικονομία"; - } else if (normalisedCourse.contains(("Εισαγωγή στην Ενεργειακή Τεχνολογία II"))) { - return greeklish ? "EET_2" : "ΕΕΤ2"; - } else if (normalisedCourse.contains(("Εισαγωγή στην Ενεργειακή Τεχνολογία I"))) { - return greeklish ? "EET_I" : "ΕΕΤ 1"; - } else if (normalisedCourse.contains(("Ειδικές Κεραίες, Σύνθεση Κεραιών"))) { - return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών"; - } else if (normalisedCourse.contains(("Ειδικές Αρχιτεκτονικές Υπολογιστών"))) { - return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών"; - } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας"))) { - return greeklish ? "ekshe" : "ΕΚΣΗΕ"; - } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I"))) { - return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I"; - } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων"))) { - return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων"; - } else if (normalisedCourse.contains(("Δομημένος Προγραμματισμός"))) { - return greeklish ? "C" : "Δομ. Προγραμμ."; - } else if (normalisedCourse.contains(("Δομές Δεδομένων"))) { - return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ."; - } else if (normalisedCourse.contains(("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας"))) { - return greeklish ? "dshe" : "ΔΣΗΕ"; - } else if (normalisedCourse.contains(("Διαφορικές Εξισώσεις"))) { - return greeklish ? "diaforikes" : "Διαφορικές"; - } else if (normalisedCourse.contains(("Διανεμημένη Παραγωγή"))) { - return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή"; - } else if (normalisedCourse.contains(("Διακριτά μαθηματικά"))) { - return greeklish ? "diakrita" : "Διακριτά Μαθηματικά"; - } else if (normalisedCourse.contains(("Διακριτά Μαθηματικά"))) { - return greeklish ? "diakrita" : "Διακριτά Μαθηματικά"; - } else if (normalisedCourse.contains(("Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III)"))) { - return greeklish ? "diadosi_1" : "Διάδοση 1"; - } else if (normalisedCourse.contains(("Διάδοση Η/Μ Κύματος II"))) { - return greeklish ? "diadosi_II" : "Διάδοση 2"; - } else if (normalisedCourse.contains(("Δίκτυα Υπολογιστών II"))) { - return greeklish ? "diktya_II" : "Δίκτυα 2"; - } else if (normalisedCourse.contains(("Δίκτυα Υπολογιστών I"))) { - return greeklish ? "diktya_I" : "Δίκτυα 1"; - } else if (normalisedCourse.contains(("Δίκτυα Τηλεπικοινωνιών"))) { - return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ."; - } else if (normalisedCourse.contains(("Γραφική με Υπολογιστές"))) { - return greeklish ? "grafikh" : "Γραφική"; - } else if (normalisedCourse.contains(("Γραμμική Άλγεβρα"))) { - return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ."; - } else if (normalisedCourse.contains(("Γεωηλεκτρομαγνητισμός"))) { - return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός"; - } else if (normalisedCourse.contains(("Βιοϊατρική Τεχνολογία"))) { - return greeklish ? "vioiatriki" : "Βιοιατρική"; - } else if (normalisedCourse.contains(("Βιομηχανική Πληροφορική"))) { - return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ"; - } else if (normalisedCourse.contains(("Βιομηχανικά Ηλεκτρονικά"))) { - return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά"; - } else if (normalisedCourse.contains(("Βάσεις Δεδομένων"))) { - return greeklish ? "vaseis" : "Βάσεις"; - } else if (normalisedCourse.contains(("Ασύρματος Τηλεπικοινωνία II"))) { - return greeklish ? "asyrmatos_II" : "Ασύρματος 2"; - } else if (normalisedCourse.contains(("Ασύρματος Τηλεπικοινωνία I"))) { - return greeklish ? "asyrmatos_I" : "Ασύρματος 1"; - } else if (normalisedCourse.contains(("Ασφάλεια Πληροφοριακών Συστημάτων"))) { - return greeklish ? "asfaleia" : "Ασφάλεια"; - } else if (normalisedCourse.contains(("Ασαφή Συστήματα"))) { - return greeklish ? "asafh" : "Ασαφή"; - } else if (normalisedCourse.contains(("Αρχιτεκτονική Υπολογιστών"))) { - return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών"; - } else if (normalisedCourse.contains(("Αρχές Παράλληλης Επεξεργασίας"))) { - return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας"; - } else if (normalisedCourse.contains(("Αρχές Οικονομίας"))) { - return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας"; - } else if (normalisedCourse.contains(("Αριθμητική Ανάλυση"))) { - return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση"; - } else if (normalisedCourse.contains(("Αξιοπιστία Συστημάτων"))) { - return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων"; - } else if (normalisedCourse.contains(("Αντικειμενοστραφής Προγραμματισμός"))) { - return greeklish ? "OOP" : "Αντικειμενοστραφής"; - } else if (normalisedCourse.contains(("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I)"))) { - return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ."; - } else if (normalisedCourse.contains(("Αναγνώριση Προτύπων"))) { - return greeklish ? "protipa" : "Αναγνώριση Προτύπων"; - } else if (normalisedCourse.contains(("Ανάλυση και Σχεδίαση Αλγορίθμων"))) { - return greeklish ? "algorithms" : "Αλγόριθμοι"; - } else if (normalisedCourse.contains(("Ανάλυση Χρονοσειρών"))) { - return greeklish ? "xronoseires" : "Χρονοσειρές"; - } else if (normalisedCourse.contains(("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας"))) { - return greeklish ? "ASHE" : "ΑΣΗΕ"; - } else if (normalisedCourse.contains(("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή"))) { - return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή"; - } else if (normalisedCourse.contains(("Ακουστική II"))) { - return greeklish ? "akoystikh_II" : "Ακουστική 2"; - } else if (normalisedCourse.contains(("Ακουστική I"))) { - return greeklish ? "akoystikh_I" : "Ακουστική 1"; - } else { - Timber.wtf("Unrecognised course came in the upload fields generator! Course string = %s", course); - 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..0fbedce8 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java @@ -0,0 +1,66 @@ +package gr.thmmy.mthmmy.activities.upload; + +import java.util.HashMap; +import java.util.Map; + +import timber.log.Timber; + +public class UploadsCourse { + private String name; + private String minifiedName; + private String greeklishName; + + public 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); + Timber.w("AAAAAAAA %s",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); + //TODO: report to Firebase for a new Course + } + + return uploadsCourses.get(foundKey); + } + + private static String normalizeGreekNumbers(String stringWithGreekNumbers) { + return stringWithGreekNumbers.replaceAll("Ι", "I"); + } +} 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 1bb45c84..5bb385c2 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 @@ -3,13 +3,12 @@ package gr.thmmy.mthmmy.activities.upload; import android.content.Context; import android.net.Uri; import android.os.Environment; +import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.FileProvider; -import android.util.Log; -import android.widget.Toast; - import com.snatik.storage.Storage; import java.io.BufferedInputStream; 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 index c1bdb539..fb5c2161 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java @@ -1,6 +1,7 @@ package gr.thmmy.mthmmy.model; import android.net.Uri; + import androidx.annotation.Nullable; import java.io.File; 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..41ff3d95 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; diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index c7c56e86..f8b06e28 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -5,14 +5,14 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; -import androidx.appcompat.app.AlertDialog; - 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; 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/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 7beffeb6..e1ef6f9e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java @@ -4,9 +4,10 @@ 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 android.webkit.MimeTypeMap; import java.io.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 44c0d4f3..a77502f3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java @@ -2,7 +2,6 @@ package gr.thmmy.mthmmy.utils; import android.content.Context; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java index 91e2305c..cc877deb 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java @@ -6,13 +6,13 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewPropertyAnimator; -import com.google.android.material.snackbar.Snackbar; - import androidx.annotation.NonNull; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.ViewCompat; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; +import com.google.android.material.snackbar.Snackbar; + /** * Extends LinearLayout's behavior. Used for bottom navigation bar. *

When 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 index b2ef977f..1ac9c6a8 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java @@ -14,10 +14,11 @@ import android.media.ExifInterface; 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 android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; 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 c15e46db..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; diff --git a/app/src/main/res/layout/activity_upload.xml b/app/src/main/res/layout/activity_upload.xml index fb985454..0eea2f63 100644 --- a/app/src/main/res/layout/activity_upload.xml +++ b/app/src/main/res/layout/activity_upload.xml @@ -118,7 +118,7 @@ android:inputType="textMultiLine" /> - + + + + Ακουστική I,Ακουστική 1,akoystikh_I + Ακουστική II,Ακουστική 2,akoystikh_II + Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή,Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή,analysh_hlektr_kykl + Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας,ΑΣΗΕ,ASHE + Ανάλυση Χρονοσειρών,Χρονοσειρές,xronoseires + Ανάλυση και Σχεδίαση Αλγορίθμων,Αλγόριθμοι,algorithms + Αναγνώριση Προτύπων,Αναγνώριση Προτύπων,protipa + Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I),Αναλογικές Τηλεπ.,anal_thlep + Αντικειμενοστραφής Προγραμματισμός,Αντικειμενοστραφής,OOP + Αξιοπιστία Συστημάτων,Αξιοπιστία Συστημάτων,aksiopistia_systhmatwn + Αριθμητική Ανάλυση,Αριθμ. Ανάλυση,arith_anal + Αρχές Οικονομίας,Αρχές Οικονομίας,arx_oikonomias + Αρχές Παράλληλης Επεξεργασίας,Αρχές Παράλληλης Επεξεργασίας,arxes_parall_epeksergasias + Αρχιτεκτονική Υπολογιστών,Αρχ. Υπολογιστών,arx_ypologistwn + Ασαφή Συστήματα,Ασαφή,asafh + Ασφάλεια Πληροφοριακών Συστημάτων,Ασφάλεια,asfaleia + Ασύρματος Τηλεπικοινωνία I,Ασύρματος 1,asyrmatos_I + Ασύρματος Τηλεπικοινωνία II,Ασύρματος 2,asyrmatos_II + Βάσεις Δεδομένων,Βάσεις,vaseis + Βιομηχανικά Ηλεκτρονικά,Βιομηχανικά Ηλεκτρονικά,bhomix_hlektronika + Βιομηχανική Πληροφορική,Βιομηχανική Πληρ,viomix_plir + Βιοϊατρική Τεχνολογία,Βιοιατρική,vioiatriki + Γεωηλεκτρομαγνητισμός,Γεωηλεκτρομαγνητισμός,geohlektromagnitismos + Γραμμική Άλγεβρα,Γραμμ. Άλγεβρ.,grammikh_algebra + Γραφική με Υπολογιστές,Γραφική,grafikh + Δίκτυα Τηλεπικοινωνιών,Δίκτυα Τηλέπ.,diktya_thlep + Δίκτυα Υπολογιστών I,Δίκτυα 1,diktya_I + Δίκτυα Υπολογιστών II,Δίκτυα 2,diktya_II + Διάδοση Η/Μ Κύματος II,Διάδοση 2,diadosi_II + Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III),Διάδοση 1,diadosi_I + Διακριτά Μαθηματικά,Διακριτά Μαθηματικά,diakrita + Διακριτά μαθηματικά,Διακριτά Μαθηματικά,diakrita + Διανεμημένη Παραγωγή,Διανεμημένη Παραγωγή,dian_paragwgh + Διαφορικές Εξισώσεις,Διαφορικές,diaforikes + Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας,ΔΣΗΕ,dshe + Δομές Δεδομένων,Δομ. Δεδομ.,dom_dedomenwn + Δομημένος Προγραμματισμός,Δομ. Προγραμμ.,C + Ειδικά Κεφάλαια Διαφορικών Εξισώσεων,Ειδικά Κεφάλαια Διαφορικών Εξισώσεων,eidika_kef_diaf_eksis + Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I,Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I,eidika_kef_HM_pedioy_I + Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας,ΕΚΣΗΕ,ekshe + Ειδικές Αρχιτεκτονικές Υπολογιστών,Ειδικές Αρχιτεκτονικές Υπολογιστών,eidikes_arx_ypolog + Ειδικές Κεραίες,eidikes_keraies, Σύνθεση Κεραιών + Εισαγωγή στην Ενεργειακή Τεχνολογία I,ΕΕΤ 1,EET_I + Εισαγωγή στην Ενεργειακή Τεχνολογία II,ΕΕΤ2,EET_II + Εισαγωγή στην Πολιτική Οικονομία,Πολιτική Οικονομία,polit_oik + Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας,Εισ. Πυρηνικη Τεχν.,Intro_Purhnikh_Texn + Ενσωματωμένα Συστήματα Πραγματικού Χρόνου,Ενσωματωμένα,enswmatwmena + Επιχειρησιακή Έρευνα,Επιχειρησιακή Έρευνα,epixeirisiaki + Ευρυζωνικά Δίκτυα,Ευρυζωνικά,eyryzwnika + Ευφυή Συστήματα Ρομπότ,Ευφυή,eufuh + Εφαρμογές Τηλεπικοινωνιακών Διατάξεων,Εφαρμογές Τηλεπ. Διατάξεων,efarm_thlep_diataksewn + Εφαρμοσμένα Μαθηματικά I,Εφαρμοσμένα 1,efarmosmena_math_I + Εφαρμοσμένα Μαθηματικά II,Εφαρμοσμένα 2,efarmosmena_math_II + Εφαρμοσμένη Θερμοδυναμική,Θερμοδυναμική,thermodynamikh + Ηλεκτρακουστική I,Ηλεκτροακουστική 1,hlektroakoystikh_I + Ηλεκτρακουστική II,Ηλεκτροακουστική 2,hlektroakoystikh_II + Ηλεκτρικά Κυκλώματα I,Κυκλώματα 1,kyklwmata_I + Ηλεκτρικά Κυκλώματα II,Κυκλώματα 2,kyklwmata_II + Ηλεκτρικά Κυκλώματα III,Κυκλώματα 3,kyklwmata_I + Ηλεκτρικές Μετρήσεις I,Μετρήσεις 1,metrhseis_I + Ηλεκτρικές Μετρήσεις II,Μετρήσεις 2,metrhseis_II + Ηλεκτρικές Μηχανές Α\',Μηχανές Α,mhxanes_A + Ηλεκτρικές Μηχανές Β\',Μηχανές Β,mhxanes_B + Ηλεκτρικές Μηχανές Γ\',Μηχανές Γ,mhxanes_C + Ηλεκτρική Οικονομία,Ηλεκτρική Οικονομία,hlektr_oikonomia + Ηλεκτρολογικά Υλικά,Ηλεκτρ. Υλικά,ylika + Ηλεκτρομαγνητική Συμβατότητα,H/M Συμβατότητα,HM_symvatothta + Ηλεκτρομαγνητικό Πεδίο I,Πεδίο 1,pedio_I + Ηλεκτρομαγνητικό Πεδίο II,Πεδίο 2,pedio_II + Ηλεκτρονικά Iσχύος I,Iσχύος 1,isxyos_I + Ηλεκτρονικά Iσχύος II,Iσχύος 2,isxyos_II + Ηλεκτρονικές Διατάξεις και Μετρήσεις,Ηλεκτρονικές Διατάξεις και Μετρήσεις,hlektron_diatakseis_metrhseis + Ηλεκτρονική I,Ηλεκτρονική 1,hlektronikh_I + Ηλεκτρονική II,Ηλεκτρονική 2,hlektronikh_II + Ηλεκτρονική III,Ηλεκτρονική 3,hlektronikh_III + Ημιαγωγά Υλικά: Θεωρία-Διατάξεις,Ημιαγωγά Υλικά,Hmiagwga_Ylika + Θεωρία Πιθανοτήτων και Στατιστική,Πιθανότητες,pithanothtes + Θεωρία Πληροφοριών,Θεωρία Πληρ.,theoria_plir + Θεωρία Σημάτων και Γραμμικών Συστημάτων,Σύματα & Συστήματα,analog_shma + Θεωρία Σκέδασης,Σκέδαση,skedash + Θεωρία Υπολογισμών και Αλγορίθμων,ΘΥΑ,thya + Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων,Τεχνολογία Αντιδραστήρων,texn_antidrasthrwn + Κβαντική Φυσική,Κβαντική,kvantikh + Κινητές και Δορυφορικές Επικοινωνίες,Κινητές & Δορυφορικές Επικοινωνίες,kinhtes_doryforikes_epik + Λειτουργικά Συστήματα,Λειτουργικά,OS + Λογική Σχεδίαση,Λογική Σχεδίαση,logiki_sxediash + Λογισμός I,Λογισμός 1,logismos_I + Λογισμός II,Λογισμός 2,logismos_II + Μετάδοση Θερμότητας,Μετάδοση Θερμ.,metadosi_therm + Μικροεπεξεργαστές και Περιφερειακά,Μίκρο 2,mikro_II + Μικροκυματική Τηλεπισκόπηση,Τηλεπισκόπηση,thlepiskophsh + Μικροκύματα I,Μικροκύματα 1,mikrokymata_I + Μικροκύματα II,Μικροκύματα 2,mikrokymata_II + Οπτικές Επικοινωνίες,Οπτικές Τηλεπ.,optikes_thlep + Οπτική I,Οπτική 1,optikh_I + Οπτική II,Οπτική 2,optikh_II + Οργάνωση Υπολογιστών,Οργάνωση Υπολ.,org_ypol + Οργάνωση και Διοίκηση Εργοστασίων,Οργάνωση και Διοίκηση Εργοστασίων,organ_dioik_ergostasiwn + Παράλληλα και Κατανεμημένα Συστήματα,Παράλληλα,parallhla + Προγραμματιζόμενα Κυκλώματα ASIC,ASIC,asic + Προγραμματιστικές Τεχνικές,Προγραμματ. Τεχν.,cpp + Προηγμένες Τεχνικές Επεξεργασίας Σήματος,ΠΤΕΣ,ptes + Προσομοίωση και Μοντελοποίηση Συστημάτων,Μοντελοποίηση,montelopoihsh + Ρομποτική,Ρομποτική,rompotikh + Σήματα και Συστήματα,Σύματα & Συστήματα,analog_shma + Σερβοκινητήρια Συστήματα,Σέρβο,servo + Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας,ΣΠΗΕ,SPHE + Στοχαστικό Σήμα,Στοχ. Σήμα,stox_shma + Συστήματα Αυτομάτου Ελέγχου I,ΣΑΕ 1,SAE_I + Συστήματα Αυτομάτου Ελέγχου II,ΣΑΕ 2,SAE_II + Συστήματα Αυτομάτου Ελέγχου III,ΣΑΕ 3,SAE_III + Συστήματα Ηλεκτρικής Ενέργειας I,ΣΗΕ 1,SHE_I + Συστήματα Ηλεκτρικής Ενέργειας II,ΣΗΕ 2,SHE_II + Συστήματα Ηλεκτρικής Ενέργειας III,ΣΗΕ 3,SHE_III + Συστήματα Ηλεκτροκίνησης,Ηλεκτροκίνηση,hlektrokinhsh + Συστήματα Μικροϋπολογιστών,Μίκρο 1,mikro_I + Συστήματα Πολυμέσων και Εικονική Πραγματικότητα,Πολυμέσα,polymesa + Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα),Συσ. Υπολογιστών,sys_ypologistwn + Σχεδίαση Συστημάτων VLSI,VLSI,VLSI + Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων,Σύνθεση,synthesh + Σύνθεση Τηλεπικοινωνιακών Διατάξεων,Σύνθεση Τηλεπ. Διατάξεων,synth_thlep_diataksewn + Τεχνικές Βελτιστοποίησης,Βελτιστοποίηση,veltistopoihsh + Τεχνικές Κωδικοποίησης,Τεχνικές Κωδικοποίησης,texn_kwdikopoihshs + Τεχνικές Σχεδίασης με Η/Υ,Σχέδιο,sxedio + Τεχνικές μη Καταστρεπτικών Δοκιμών,Μη Καταστρεπτικές Δοκιμές,non_destructive_tests + Τεχνική Μηχανική,Τεχν. Μηχαν.,texn_mhxan + Τεχνολογία Ήχου και Εικόνας,Τεχνολογία Ήχου και Εικόνας,texn_hxoy_eikonas + Τεχνολογία Ηλεκτροτεχνικών Υλικών,Ηλεκτροτεχνικά Υλικά,Hlektrotexnika_Ylika + Τεχνολογία Λογισμικού,Τεχνολογία Λογισμικού,SE + Τηλεοπτικά Συστήματα,Τηλεοπτικά,tileoptika + Τηλεπικοινωνιακή Ηλεκτρονική,Τηλεπ. Ηλεκτρ.,tilep_ilektr + Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα,ΥΜΕΣ,ymes + Υπολογιστικός Ηλεκτρομαγνητισμός,Υπολογιστικός Η/Μ,ypologistikos_HM + Υψηλές Τάσεις 4,Υψηλές 4,ypshles_IV + Υψηλές Τάσεις I,Υψηλές 1,ypshles_I + Υψηλές Τάσεις II,Υψηλές 2,ypshles_II + Υψηλές Τάσεις III,Υψηλές 3,ypshles_III + Φυσική I,Φυσική 1,fysikh_I + Φωτονική Τεχνολογία,Φωτονική,fwtonikh + Ψηφιακά Συστήματα I,Ψηφιακά 1,pshfiaka_I + Ψηφιακά Συστήματα II,Ψηφιακά 2,pshfiaka_II + Ψηφιακά Συστήματα III,Ψηφιακά 3,pshfiaka_III + Ψηφιακά Φίλτρα,Φίλτρα,filtra + Ψηφιακές Τηλεπικοινωνίες I,Ψηφιακές Τηλεπ. 1,pshf_thlep_I + Ψηφιακές Τηλεπικοινωνίες II,Ψηφιακές Τηλεπ. 2,pshf_thlep_II + Ψηφιακή Επεξεργασία Εικόνας,ΨΕΕ,PSEE + Ψηφιακή Επεξεργασία Σήματος,ΨΕΣ,PSES + + \ No newline at end of file From 6a76d7e399a8e0fdf86c70576b1374628cb960f8 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 22 Jun 2019 12:48:09 +0300 Subject: [PATCH 15/37] Uploads improvements & fixes --- .../activities/upload/UploadActivity.java | 36 ++++++++----------- .../activities/upload/UploadsCourse.java | 21 ++++++++--- .../activities/upload/UploadsHelper.java | 28 +++++++-------- .../mthmmy/utils/CrashReportingTree.java | 3 +- .../java/gr/thmmy/mthmmy/utils/TakePhoto.java | 6 ++-- 5 files changed, 47 insertions(+), 47 deletions(-) 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 31f51b8e..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 @@ -457,12 +457,8 @@ public class UploadActivity extends BaseActivity { fileIcon = "archive.gif"; textWatcher.setFileExtension(".zip"); - if (!hasModifiedFilename) { - String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); - String zipFilename = "mTHMMY_" + timeStamp + ".zip"; - uploadFilename.setText(zipFilename); - hasModifiedFilename = false; - } + if (!hasModifiedFilename) + setZipUploadFilename(); for (int fileIndex = 0; fileIndex < data.getClipData().getItemCount(); ++fileIndex) { Uri newFileUri = data.getClipData().getItemAt(fileIndex).getUri(); @@ -506,12 +502,8 @@ public class UploadActivity extends BaseActivity { fileIcon = "archive.gif"; textWatcher.setFileExtension(".zip"); - if (!hasModifiedFilename) { - String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); - String zipFilename = "mTHMMY_" + timeStamp + ".zip"; - uploadFilename.setText(zipFilename); - hasModifiedFilename = false; - } + if (!hasModifiedFilename) + setZipUploadFilename(); } addFileViewToList(filename); @@ -538,12 +530,8 @@ public class UploadActivity extends BaseActivity { fileIcon = "archive.gif"; textWatcher.setFileExtension(".zip"); - if (!hasModifiedFilename) { - String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date()); - String zipFilename = "mTHMMY_" + timeStamp + ".zip"; - uploadFilename.setText(zipFilename); - hasModifiedFilename = false; - } + if (!hasModifiedFilename) + setZipUploadFilename(); } UploadFile newFile = new UploadFile(true, TakePhoto.processResult(this, @@ -599,6 +587,13 @@ public class UploadActivity extends BaseActivity { } } + 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 @@ -768,8 +763,7 @@ public class UploadActivity extends BaseActivity { } @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } + public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { @@ -951,7 +945,7 @@ public class UploadActivity extends BaseActivity { if(foundUploadsCourse != null){ uploadsCourse = foundUploadsCourse; semester = retrievedSemester; - Timber.i("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); + Timber.d("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); titleDescriptionBuilderButton.setEnabled(true); return; } 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 index 0fbedce8..6b4af081 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java @@ -1,16 +1,21 @@ 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; -public class UploadsCourse { +class UploadsCourse { private String name; private String minifiedName; private String greeklishName; - public UploadsCourse(String fullName, String minifiedName, String greeklishName) { + private UploadsCourse(String fullName, String minifiedName, String greeklishName) { this.name = fullName; this.minifiedName = minifiedName; this.greeklishName = greeklishName; @@ -41,7 +46,6 @@ public class UploadsCourse { static UploadsCourse findCourse(String retrievedCourse, Map uploadsCourses){ retrievedCourse = normalizeGreekNumbers(retrievedCourse); - Timber.w("AAAAAAAA %s",retrievedCourse); UploadsCourse uploadsCourse = uploadsCourses.get(retrievedCourse); if(uploadsCourse != null) return uploadsCourse; @@ -54,13 +58,20 @@ public class UploadsCourse { if(foundKey==null){ Timber.w("Couldn't find course that matches %s", retrievedCourse); - //TODO: report to Firebase for a new Course + 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) { - return stringWithGreekNumbers.replaceAll("Ι", "I"); + 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 5bb385c2..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 @@ -25,26 +25,24 @@ import gr.thmmy.mthmmy.utils.FileUtils; import timber.log.Timber; public class UploadsHelper { - private final static int BUFFER = 4096; - private static final String TEMP_FILES_DIRECTORY = "~tmp_mThmmy_uploads"; + private static final int BUFFER = 4096; + private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads"; @SuppressWarnings("ResultOfMethodCallIgnored") @Nullable static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) { String oldFilename = FileUtils.filenameFromUri(context, fileUri); - String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); + String fileExtension = oldFilename.substring(oldFilename.indexOf('.')); String destinationFilename = Environment.getExternalStorageDirectory().getPath() + File.separatorChar + TEMP_FILES_DIRECTORY + File.separatorChar + newFilename + fileExtension; File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + 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; @@ -82,28 +80,26 @@ public class UploadsHelper { } @Nullable - public static File createZipFile(@NonNull String zipFilename) { + static File createZipFile(@NonNull String zipFilename) { // Create a zip file name File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + - File.separator + "mThmmy"); + File.separator + "mTHMMY"); - if (!zipFolder.exists()) { - if (!zipFolder.mkdirs()) { + if (!zipFolder.exists() && !zipFolder.mkdirs()) { Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName()); return null; - } } return new File(zipFolder, zipFilename); } - public static void zip(Context context, Uri[] files, Uri zipFile) { + static void zip(Context context, Uri[] files, Uri zipFile) { try { BufferedInputStream origin; OutputStream dest = context.getContentResolver().openOutputStream(zipFile); assert dest != null; ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); - byte data[] = new byte[BUFFER]; + byte[] data = new byte[BUFFER]; for (Uri file : files) { InputStream inputStream = context.getContentResolver().openInputStream(file); 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/TakePhoto.java b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java index 1ac9c6a8..f1d7d990 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java @@ -32,7 +32,7 @@ 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"; + private static final String IMAGE_CONTENT_DESCRIPTION = "mTHMMY uploads image"; @Nullable public static Intent getIntent(Context context, @NonNull File photoFile) { @@ -81,10 +81,10 @@ public class TakePhoto { 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"; + String imageFileName = "mTHMMY_" + timeStamp + ".jpg"; File imageFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + - File.separator + "mThmmy"); + File.separator + "mTHMMY"); if (!imageFolder.exists()) { if (!imageFolder.mkdirs()) { From 8b06c9727b75748249f766ccb29482bc874ce7a9 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 22 Jun 2019 13:22:31 +0300 Subject: [PATCH 16/37] Updated uploads courses XML --- .../activities/upload/UploadsCourse.java | 4 +- app/src/main/res/values/uploads_courses.xml | 296 +++++++++--------- 2 files changed, 150 insertions(+), 150 deletions(-) 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 index 6b4af081..8fe986c9 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java @@ -36,7 +36,7 @@ class UploadsCourse { static Map generateUploadsCourses(String[] uploadsCoursesRes){ Map uploadsCourses = new HashMap<>(); for(String uploadsCourseStr:uploadsCoursesRes) { - String[] split = uploadsCourseStr.split(","); + String[] split = uploadsCourseStr.split(":"); UploadsCourse uploadsCourse = new UploadsCourse(split[0], split[1], split[2]); uploadsCourses.put(uploadsCourse.getName(),uploadsCourse); } @@ -52,7 +52,7 @@ class UploadsCourse { String foundKey = null; for (Map.Entry entry : uploadsCourses.entrySet()) { String key = entry.getKey(); - if (key.contains(retrievedCourse)&& (foundKey==null || key.length()>foundKey.length())) + if ((key.contains(retrievedCourse))&& (foundKey==null || key.length()>foundKey.length())) foundKey = key; } diff --git a/app/src/main/res/values/uploads_courses.xml b/app/src/main/res/values/uploads_courses.xml index 68e82be2..8f872389 100644 --- a/app/src/main/res/values/uploads_courses.xml +++ b/app/src/main/res/values/uploads_courses.xml @@ -1,153 +1,153 @@ - + - Ακουστική I,Ακουστική 1,akoystikh_I - Ακουστική II,Ακουστική 2,akoystikh_II - Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή,Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή,analysh_hlektr_kykl - Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας,ΑΣΗΕ,ASHE - Ανάλυση Χρονοσειρών,Χρονοσειρές,xronoseires - Ανάλυση και Σχεδίαση Αλγορίθμων,Αλγόριθμοι,algorithms - Αναγνώριση Προτύπων,Αναγνώριση Προτύπων,protipa - Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I),Αναλογικές Τηλεπ.,anal_thlep - Αντικειμενοστραφής Προγραμματισμός,Αντικειμενοστραφής,OOP - Αξιοπιστία Συστημάτων,Αξιοπιστία Συστημάτων,aksiopistia_systhmatwn - Αριθμητική Ανάλυση,Αριθμ. Ανάλυση,arith_anal - Αρχές Οικονομίας,Αρχές Οικονομίας,arx_oikonomias - Αρχές Παράλληλης Επεξεργασίας,Αρχές Παράλληλης Επεξεργασίας,arxes_parall_epeksergasias - Αρχιτεκτονική Υπολογιστών,Αρχ. Υπολογιστών,arx_ypologistwn - Ασαφή Συστήματα,Ασαφή,asafh - Ασφάλεια Πληροφοριακών Συστημάτων,Ασφάλεια,asfaleia - Ασύρματος Τηλεπικοινωνία I,Ασύρματος 1,asyrmatos_I - Ασύρματος Τηλεπικοινωνία II,Ασύρματος 2,asyrmatos_II - Βάσεις Δεδομένων,Βάσεις,vaseis - Βιομηχανικά Ηλεκτρονικά,Βιομηχανικά Ηλεκτρονικά,bhomix_hlektronika - Βιομηχανική Πληροφορική,Βιομηχανική Πληρ,viomix_plir - Βιοϊατρική Τεχνολογία,Βιοιατρική,vioiatriki - Γεωηλεκτρομαγνητισμός,Γεωηλεκτρομαγνητισμός,geohlektromagnitismos - Γραμμική Άλγεβρα,Γραμμ. Άλγεβρ.,grammikh_algebra - Γραφική με Υπολογιστές,Γραφική,grafikh - Δίκτυα Τηλεπικοινωνιών,Δίκτυα Τηλέπ.,diktya_thlep - Δίκτυα Υπολογιστών I,Δίκτυα 1,diktya_I - Δίκτυα Υπολογιστών II,Δίκτυα 2,diktya_II - Διάδοση Η/Μ Κύματος II,Διάδοση 2,diadosi_II - Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III),Διάδοση 1,diadosi_I - Διακριτά Μαθηματικά,Διακριτά Μαθηματικά,diakrita - Διακριτά μαθηματικά,Διακριτά Μαθηματικά,diakrita - Διανεμημένη Παραγωγή,Διανεμημένη Παραγωγή,dian_paragwgh - Διαφορικές Εξισώσεις,Διαφορικές,diaforikes - Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας,ΔΣΗΕ,dshe - Δομές Δεδομένων,Δομ. Δεδομ.,dom_dedomenwn - Δομημένος Προγραμματισμός,Δομ. Προγραμμ.,C - Ειδικά Κεφάλαια Διαφορικών Εξισώσεων,Ειδικά Κεφάλαια Διαφορικών Εξισώσεων,eidika_kef_diaf_eksis - Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I,Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I,eidika_kef_HM_pedioy_I - Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας,ΕΚΣΗΕ,ekshe - Ειδικές Αρχιτεκτονικές Υπολογιστών,Ειδικές Αρχιτεκτονικές Υπολογιστών,eidikes_arx_ypolog - Ειδικές Κεραίες,eidikes_keraies, Σύνθεση Κεραιών - Εισαγωγή στην Ενεργειακή Τεχνολογία I,ΕΕΤ 1,EET_I - Εισαγωγή στην Ενεργειακή Τεχνολογία II,ΕΕΤ2,EET_II - Εισαγωγή στην Πολιτική Οικονομία,Πολιτική Οικονομία,polit_oik - Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας,Εισ. Πυρηνικη Τεχν.,Intro_Purhnikh_Texn - Ενσωματωμένα Συστήματα Πραγματικού Χρόνου,Ενσωματωμένα,enswmatwmena - Επιχειρησιακή Έρευνα,Επιχειρησιακή Έρευνα,epixeirisiaki - Ευρυζωνικά Δίκτυα,Ευρυζωνικά,eyryzwnika - Ευφυή Συστήματα Ρομπότ,Ευφυή,eufuh - Εφαρμογές Τηλεπικοινωνιακών Διατάξεων,Εφαρμογές Τηλεπ. Διατάξεων,efarm_thlep_diataksewn - Εφαρμοσμένα Μαθηματικά I,Εφαρμοσμένα 1,efarmosmena_math_I - Εφαρμοσμένα Μαθηματικά II,Εφαρμοσμένα 2,efarmosmena_math_II - Εφαρμοσμένη Θερμοδυναμική,Θερμοδυναμική,thermodynamikh - Ηλεκτρακουστική I,Ηλεκτροακουστική 1,hlektroakoystikh_I - Ηλεκτρακουστική II,Ηλεκτροακουστική 2,hlektroakoystikh_II - Ηλεκτρικά Κυκλώματα I,Κυκλώματα 1,kyklwmata_I - Ηλεκτρικά Κυκλώματα II,Κυκλώματα 2,kyklwmata_II - Ηλεκτρικά Κυκλώματα III,Κυκλώματα 3,kyklwmata_I - Ηλεκτρικές Μετρήσεις I,Μετρήσεις 1,metrhseis_I - Ηλεκτρικές Μετρήσεις II,Μετρήσεις 2,metrhseis_II - Ηλεκτρικές Μηχανές Α\',Μηχανές Α,mhxanes_A - Ηλεκτρικές Μηχανές Β\',Μηχανές Β,mhxanes_B - Ηλεκτρικές Μηχανές Γ\',Μηχανές Γ,mhxanes_C - Ηλεκτρική Οικονομία,Ηλεκτρική Οικονομία,hlektr_oikonomia - Ηλεκτρολογικά Υλικά,Ηλεκτρ. Υλικά,ylika - Ηλεκτρομαγνητική Συμβατότητα,H/M Συμβατότητα,HM_symvatothta - Ηλεκτρομαγνητικό Πεδίο I,Πεδίο 1,pedio_I - Ηλεκτρομαγνητικό Πεδίο II,Πεδίο 2,pedio_II - Ηλεκτρονικά Iσχύος I,Iσχύος 1,isxyos_I - Ηλεκτρονικά Iσχύος II,Iσχύος 2,isxyos_II - Ηλεκτρονικές Διατάξεις και Μετρήσεις,Ηλεκτρονικές Διατάξεις και Μετρήσεις,hlektron_diatakseis_metrhseis - Ηλεκτρονική I,Ηλεκτρονική 1,hlektronikh_I - Ηλεκτρονική II,Ηλεκτρονική 2,hlektronikh_II - Ηλεκτρονική III,Ηλεκτρονική 3,hlektronikh_III - Ημιαγωγά Υλικά: Θεωρία-Διατάξεις,Ημιαγωγά Υλικά,Hmiagwga_Ylika - Θεωρία Πιθανοτήτων και Στατιστική,Πιθανότητες,pithanothtes - Θεωρία Πληροφοριών,Θεωρία Πληρ.,theoria_plir - Θεωρία Σημάτων και Γραμμικών Συστημάτων,Σύματα & Συστήματα,analog_shma - Θεωρία Σκέδασης,Σκέδαση,skedash - Θεωρία Υπολογισμών και Αλγορίθμων,ΘΥΑ,thya - Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων,Τεχνολογία Αντιδραστήρων,texn_antidrasthrwn - Κβαντική Φυσική,Κβαντική,kvantikh - Κινητές και Δορυφορικές Επικοινωνίες,Κινητές & Δορυφορικές Επικοινωνίες,kinhtes_doryforikes_epik - Λειτουργικά Συστήματα,Λειτουργικά,OS - Λογική Σχεδίαση,Λογική Σχεδίαση,logiki_sxediash - Λογισμός I,Λογισμός 1,logismos_I - Λογισμός II,Λογισμός 2,logismos_II - Μετάδοση Θερμότητας,Μετάδοση Θερμ.,metadosi_therm - Μικροεπεξεργαστές και Περιφερειακά,Μίκρο 2,mikro_II - Μικροκυματική Τηλεπισκόπηση,Τηλεπισκόπηση,thlepiskophsh - Μικροκύματα I,Μικροκύματα 1,mikrokymata_I - Μικροκύματα II,Μικροκύματα 2,mikrokymata_II - Οπτικές Επικοινωνίες,Οπτικές Τηλεπ.,optikes_thlep - Οπτική I,Οπτική 1,optikh_I - Οπτική II,Οπτική 2,optikh_II - Οργάνωση Υπολογιστών,Οργάνωση Υπολ.,org_ypol - Οργάνωση και Διοίκηση Εργοστασίων,Οργάνωση και Διοίκηση Εργοστασίων,organ_dioik_ergostasiwn - Παράλληλα και Κατανεμημένα Συστήματα,Παράλληλα,parallhla - Προγραμματιζόμενα Κυκλώματα ASIC,ASIC,asic - Προγραμματιστικές Τεχνικές,Προγραμματ. Τεχν.,cpp - Προηγμένες Τεχνικές Επεξεργασίας Σήματος,ΠΤΕΣ,ptes - Προσομοίωση και Μοντελοποίηση Συστημάτων,Μοντελοποίηση,montelopoihsh - Ρομποτική,Ρομποτική,rompotikh - Σήματα και Συστήματα,Σύματα & Συστήματα,analog_shma - Σερβοκινητήρια Συστήματα,Σέρβο,servo - Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας,ΣΠΗΕ,SPHE - Στοχαστικό Σήμα,Στοχ. Σήμα,stox_shma - Συστήματα Αυτομάτου Ελέγχου I,ΣΑΕ 1,SAE_I - Συστήματα Αυτομάτου Ελέγχου II,ΣΑΕ 2,SAE_II - Συστήματα Αυτομάτου Ελέγχου III,ΣΑΕ 3,SAE_III - Συστήματα Ηλεκτρικής Ενέργειας I,ΣΗΕ 1,SHE_I - Συστήματα Ηλεκτρικής Ενέργειας II,ΣΗΕ 2,SHE_II - Συστήματα Ηλεκτρικής Ενέργειας III,ΣΗΕ 3,SHE_III - Συστήματα Ηλεκτροκίνησης,Ηλεκτροκίνηση,hlektrokinhsh - Συστήματα Μικροϋπολογιστών,Μίκρο 1,mikro_I - Συστήματα Πολυμέσων και Εικονική Πραγματικότητα,Πολυμέσα,polymesa - Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα),Συσ. Υπολογιστών,sys_ypologistwn - Σχεδίαση Συστημάτων VLSI,VLSI,VLSI - Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων,Σύνθεση,synthesh - Σύνθεση Τηλεπικοινωνιακών Διατάξεων,Σύνθεση Τηλεπ. Διατάξεων,synth_thlep_diataksewn - Τεχνικές Βελτιστοποίησης,Βελτιστοποίηση,veltistopoihsh - Τεχνικές Κωδικοποίησης,Τεχνικές Κωδικοποίησης,texn_kwdikopoihshs - Τεχνικές Σχεδίασης με Η/Υ,Σχέδιο,sxedio - Τεχνικές μη Καταστρεπτικών Δοκιμών,Μη Καταστρεπτικές Δοκιμές,non_destructive_tests - Τεχνική Μηχανική,Τεχν. Μηχαν.,texn_mhxan - Τεχνολογία Ήχου και Εικόνας,Τεχνολογία Ήχου και Εικόνας,texn_hxoy_eikonas - Τεχνολογία Ηλεκτροτεχνικών Υλικών,Ηλεκτροτεχνικά Υλικά,Hlektrotexnika_Ylika - Τεχνολογία Λογισμικού,Τεχνολογία Λογισμικού,SE - Τηλεοπτικά Συστήματα,Τηλεοπτικά,tileoptika - Τηλεπικοινωνιακή Ηλεκτρονική,Τηλεπ. Ηλεκτρ.,tilep_ilektr - Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα,ΥΜΕΣ,ymes - Υπολογιστικός Ηλεκτρομαγνητισμός,Υπολογιστικός Η/Μ,ypologistikos_HM - Υψηλές Τάσεις 4,Υψηλές 4,ypshles_IV - Υψηλές Τάσεις I,Υψηλές 1,ypshles_I - Υψηλές Τάσεις II,Υψηλές 2,ypshles_II - Υψηλές Τάσεις III,Υψηλές 3,ypshles_III - Φυσική I,Φυσική 1,fysikh_I - Φωτονική Τεχνολογία,Φωτονική,fwtonikh - Ψηφιακά Συστήματα I,Ψηφιακά 1,pshfiaka_I - Ψηφιακά Συστήματα II,Ψηφιακά 2,pshfiaka_II - Ψηφιακά Συστήματα III,Ψηφιακά 3,pshfiaka_III - Ψηφιακά Φίλτρα,Φίλτρα,filtra - Ψηφιακές Τηλεπικοινωνίες I,Ψηφιακές Τηλεπ. 1,pshf_thlep_I - Ψηφιακές Τηλεπικοινωνίες II,Ψηφιακές Τηλεπ. 2,pshf_thlep_II - Ψηφιακή Επεξεργασία Εικόνας,ΨΕΕ,PSEE - Ψηφιακή Επεξεργασία Σήματος,ΨΕΣ,PSES + Ακουστική 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 From 165acddce1e3a1f5ea542735c6d35e7627bd5c2d Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 22 Jun 2019 14:02:42 +0300 Subject: [PATCH 17/37] Grgit up version fix --- app/gradle/grgit.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 +} From f894090e21e59ad212239da42dceebaabc64fede Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 22 Jun 2019 14:08:33 +0300 Subject: [PATCH 18/37] Added gradle-versions-plugin --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 677adbde..108b8fa6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: "com.github.ben-manes.versions" buildscript { repositories { @@ -12,6 +13,7 @@ buildscript { classpath 'com.google.gms:google-services:4.2.0' classpath 'io.fabric.tools:gradle:1.29.0' classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle + classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0" } } From 225585b7323a692d5b38fbe2bb021f321d79be54 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 22 Jun 2019 14:21:37 +0300 Subject: [PATCH 19/37] Up libs --- app/build.gradle | 12 ++++++------ app/src/main/assets/apache_libraries.html | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 56b9112b..390fcb78 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -71,10 +71,10 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.firebase:firebase-core:17.0.0' - implementation 'com.google.firebase:firebase-messaging:19.0.0' + implementation 'com.google.firebase:firebase-messaging:19.0.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.snatik:storage:2.1.0' - implementation 'com.squareup.okhttp3:okhttp:3.12.0' + 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! @@ -87,10 +87,10 @@ dependencies { implementation 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'//TODO: deprecated! implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2' implementation 'com.jakewharton.timber:timber:4.7.1' - implementation 'ru.noties:markwon:2.0.0' - implementation 'net.gotev:uploadservice:3.4.2' - implementation 'net.gotev:uploadservice-okhttp:3.4.2' - implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.4' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler + implementation 'ru.noties:markwon:2.0.2' + implementation 'net.gotev:uploadservice:3.5.2' + implementation 'net.gotev:uploadservice-okhttp:3.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/src/main/assets/apache_libraries.html b/app/src/main/assets/apache_libraries.html index b0e33882..911a8e50 100644 --- a/app/src/main/assets/apache_libraries.html +++ b/app/src/main/assets/apache_libraries.html @@ -39,7 +39,7 @@

From 5a5c9a7cfe0e8d2adb0f1aa74c92f5e81131207c Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 22 Jun 2019 17:53:17 +0300 Subject: [PATCH 20/37] More up libs --- app/build.gradle | 12 +++++++----- .../thmmy/mthmmy/services/NotificationService.java | 5 ++--- .../main/java/gr/thmmy/mthmmy/utils/TakePhoto.java | 12 +++++------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 390fcb78..fa5831b8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,14 +61,15 @@ tasks.whenTaskAdded { task -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.0.2' + 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:17.0.0' implementation 'com.google.firebase:firebase-messaging:19.0.1' @@ -78,8 +79,8 @@ dependencies { 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' @@ -90,7 +91,8 @@ dependencies { 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 + 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/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java index 41ff3d95..79c36d4b 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java @@ -261,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/utils/TakePhoto.java b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java index f1d7d990..c5b4c490 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java @@ -10,7 +10,6 @@ import android.content.res.AssetFileDescriptor; 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.MediaStore; @@ -19,6 +18,7 @@ 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; @@ -86,12 +86,10 @@ public class TakePhoto { File imageFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "mTHMMY"); - if (!imageFolder.exists()) { - if (!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; - } + 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); From 6d88b32b0ae669cdcb98cc7118f09292bf4888a4 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Fri, 28 Jun 2019 21:41:50 +0300 Subject: [PATCH 21/37] Version code fix --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index fa5831b8..44028151 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 19 + versionCode 18 versionName "1.7.0" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" From 36eb640a456d35c79da136df64141801a7ebd766 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Sat, 29 Jun 2019 10:00:36 +0300 Subject: [PATCH 22/37] Revert libs upgrade --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 44028151..9f6c919e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,7 +75,7 @@ dependencies { 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.okhttp3:okhttp:3.12.0' //TODO: Warning: okhttp has dropped support for Android v.19 since okhttp 3.13! implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' implementation 'org.jsoup:jsoup:1.10.3' //TODO: Warning: upgrading from 1.10.3 will break stuff! @@ -90,7 +90,7 @@ dependencies { implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'ru.noties:markwon:2.0.2' implementation 'net.gotev:uploadservice:3.5.2' - implementation 'net.gotev:uploadservice-okhttp:3.5.2' + implementation 'net.gotev:uploadservice-okhttp:3.4.2' //TODO: Warning: v.3.5 depends on okhttp 3.13! implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler } From f65407d31965dc9baa99aa448915e672ad553095 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sun, 30 Jun 2019 13:03:54 +0300 Subject: [PATCH 23/37] ParseHelpers fix --- .../gr/thmmy/mthmmy/utils/CrashReporter.java | 22 ++++++------ .../mthmmy/utils/parsing/ParseHelpers.java | 34 +++++++++---------- build.gradle | 2 +- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java index b9907e93..67ba0395 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java @@ -17,8 +17,8 @@ public class CrashReporter { public static void reportForumInfo(Document document) { ParseHelpers.Theme theme = ParseHelpers.parseTheme(document); ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document); - String themeKey = "forum theme", themeValue = null; - String languageKey = "forum language", languageValue = null; + String themeKey = "forum theme", themeValue; + String languageKey = "forum language", languageValue; switch (theme) { case SCRIBBLES2: themeValue = "Scribbles2"; @@ -32,19 +32,17 @@ public class CrashReporter { case HELIOS_MULTI: themeValue = "Helios_Multi"; break; - case THEME_UNKNOWN: + default: themeValue = "Unknown theme"; - break; - } - switch (language) { - case GREEK: - languageValue = "Greek"; - break; - case ENGLISH: - languageValue = "English"; - break; } + if (language == ParseHelpers.Language.GREEK) + languageValue = "Greek"; + else if (language == ParseHelpers.Language.ENGLISH) + languageValue = "English"; + else + languageValue = "Unknown"; + Crashlytics.setString(themeKey, themeValue); Crashlytics.setString(languageKey, languageValue); Crashlytics.setBool("isLoggedIn", BaseApplication.getInstance().getSessionManager().isLoggedIn()); 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 62380f80..206d4c3b 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 @@ -23,7 +23,7 @@ public class ParseHelpers { /** * An enum describing a forum page's language by defining the types:
    *
  • {@link #PAGE_INCOMPLETE}
  • - *
  • {@link #UNDEFINED_LANGUAGE}
  • + *
  • {@link #UNKNOWN_LANGUAGE}
  • *
  • {@link #ENGLISH}
  • *
  • {@link #GREEK}
  • *
@@ -42,9 +42,9 @@ public class ParseHelpers { */ PAGE_INCOMPLETE, /** - * Page language is not (yet) supported. + * Page language could not be determined/ not (yet) supported. */ - UNDEFINED_LANGUAGE; + UNKNOWN_LANGUAGE; /** * Returns one of the supported forum languages. @@ -57,13 +57,12 @@ public class ParseHelpers { Element welcoming = page.select("h3").first(); if (welcoming == null) { Element welcomingGuest = page.select("div[id=myuser]").first(); - if (welcomingGuest != null) { - if (welcomingGuest.text().contains("Welcome")) return ENGLISH; - } + if (welcomingGuest != null && welcomingGuest.text().contains("Welcome")) + return ENGLISH; return PAGE_INCOMPLETE; } else if (welcoming.text().contains("Καλώς ορίσατε")) return GREEK; else if (welcoming.text().contains("Hey")) return ENGLISH; - else return UNDEFINED_LANGUAGE; + else return UNKNOWN_LANGUAGE; } } @@ -77,16 +76,17 @@ public class ParseHelpers { public static Theme parseTheme(Document page) { Element stylesheet = page.select("link[rel=stylesheet]").first(); - if (stylesheet.attr("href").contains("scribbles2")) - return Theme.SCRIBBLES2; - else if (stylesheet.attr("href").contains("helios_multi")) - return Theme.HELIOS_MULTI; - else if (stylesheet.attr("href").contains("smfone")) - return Theme.SMFONE_BLUE; - else if (stylesheet.attr("href").contains("default")) - return Theme.SMF_DEFAULT; - else - return Theme.THEME_UNKNOWN; + if(stylesheet!=null){ + if (stylesheet.attr("href").contains("scribbles2")) + return Theme.SCRIBBLES2; + else if (stylesheet.attr("href").contains("helios_multi")) + return Theme.HELIOS_MULTI; + else if (stylesheet.attr("href").contains("smfone")) + return Theme.SMFONE_BLUE; + else if (stylesheet.attr("href").contains("default")) + return Theme.SMF_DEFAULT; + } + return Theme.THEME_UNKNOWN; } /** diff --git a/build.gradle b/build.gradle index 108b8fa6..65830711 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.4.1' - classpath 'com.google.gms:google-services:4.2.0' + classpath 'com.google.gms:google-services:4.3.0' classpath 'io.fabric.tools:gradle:1.29.0' classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0" From e12e7e3507b54d339cd448b7f8006c263369da84 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sun, 30 Jun 2019 19:56:14 +0300 Subject: [PATCH 24/37] Up version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9f6c919e..1219d9fa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 18 - versionName "1.7.0" + versionCode 19 + versionName "1.7.1" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" From c22fe9faef4e246b8a9525a2f5624b9a5d5b707c Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 1 Jul 2019 12:29:42 +0300 Subject: [PATCH 25/37] Ensure correct google-services.json is supplied vol.2 --- app/build.gradle | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 1219d9fa..c26690b9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,20 +41,30 @@ android { } } +def firebaseReleaseProjectId = "mthmmy-release-3aef0" + tasks.whenTaskAdded { task -> if (task.name.contains("assembleRelease")) { task.getDependsOn().add({ - 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)!') + def googleServicesFile = new File("app/src/release/google-services.json") + if(googleServicesFile.exists()){ + def json = new JsonSlurper().parseText(googleServicesFile.text) + if (json.project_info.project_id != firebaseReleaseProjectId) + throw new GradleException('Please supply the correct google-services.json for release in app/src/release/ directory!') + } + else + throw new GradleException('Please add the release google-services.json in app/src/release/ directory!') }) } else if (task.name.contains("assembleDebug")) { task.getDependsOn().add({ - def inputFile = new File("app/google-services.json") - def json = new JsonSlurper().parseText(inputFile.text) - if (json.project_info.project_id == "mthmmy-release-3aef0") - throw new GradleException('Please replace the release google-services.json with a debug one!') + def googleServicesFile = new File("app/src/debug/google-services.json") + if(googleServicesFile.exists()){ + def json = new JsonSlurper().parseText(googleServicesFile.text) + if (json.project_info.project_id == firebaseReleaseProjectId) + throw new GradleException('Please replace google-services.json in app/src/debug/ with a debug one!') + } + else + throw new GradleException('Please add a debug google-services.json in app/src/debug/ directory!') }) } } From 81513ed3e71bc3fbfc9c59ca7f2439e7f3e161c2 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 1 Jul 2019 12:30:26 +0300 Subject: [PATCH 26/37] Up version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c26690b9..c3aa2e1e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 19 - versionName "1.7.1" + versionCode 20 + versionName "1.7.2" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" From ef7d88d84bc1c3efec4e391ff1384c26fd911eeb Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 1 Jul 2019 14:47:59 +0300 Subject: [PATCH 27/37] AboutActivity improvements, up version --- app/build.gradle | 4 +- .../mthmmy/activities/AboutActivity.java | 85 +++++++++++++------ .../gr/thmmy/mthmmy/base/BaseApplication.java | 10 ++- 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c3aa2e1e..b47ae972 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 20 - versionName "1.7.2" + versionCode 21 + versionName "1.7.3" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" 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 0963be99..49e9dc8c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java @@ -11,8 +11,10 @@ import android.view.LayoutInflater; import android.view.View; import android.webkit.WebView; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.ScrollView; import android.widget.TextView; +import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.coordinatorlayout.widget.CoordinatorLayout; @@ -23,6 +25,7 @@ import com.google.android.material.appbar.AppBarLayout; import gr.thmmy.mthmmy.BuildConfig; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; +import gr.thmmy.mthmmy.base.BaseApplication; public class AboutActivity extends BaseActivity { private static final int TIME_INTERVAL = 1000; @@ -32,8 +35,9 @@ public class AboutActivity extends BaseActivity { private AppBarLayout appBar; private CoordinatorLayout coordinatorLayout; + private ScrollView mainContent; private AlertDialog alertDialog; - private FrameLayout trollGif; + private FrameLayout easterEggImage; @Override protected void onCreate(Bundle savedInstanceState) { @@ -70,41 +74,41 @@ public class AboutActivity extends BaseActivity { createDrawer(); drawer.setSelection(ABOUT_ID); - final ScrollView mainContent = findViewById(R.id.scrollview); - trollGif = findViewById(R.id.trollPicFrame); + mainContent = findViewById(R.id.scrollview); + easterEggImage = findViewById(R.id.trollPicFrame); + + // Set Easter egg on logo image + ImageView logoImageView = findViewById(R.id.logoView); + logoImageView.setOnClickListener(view -> { + if (mVersionLastPressedTime + TIME_INTERVAL > System.currentTimeMillis()) { + if (mVersionPressedCounter == TIMES_TO_PRESS) + showEasterEgg(); + mVersionLastPressedTime = System.currentTimeMillis(); + ++mVersionPressedCounter; + } else { + mVersionLastPressedTime = System.currentTimeMillis(); + mVersionPressedCounter = 0; + } + }); - TextView tv = findViewById(R.id.version); - if (tv != null) { + TextView versionTextView = findViewById(R.id.version); + if (versionTextView != null) { if (BuildConfig.DEBUG) - tv.setText(getString(R.string.version, versionName + versionInfo)); + versionTextView.setText(getString(R.string.version, versionName + versionInfo)); else - tv.setText(getString(R.string.version, versionName)); - + versionTextView.setText(getString(R.string.version, versionName)); - if(BuildConfig.DEBUG && gitExists){ - tv.setOnClickListener(view -> { + if(gitExists){ + versionTextView.setOnClickListener(view -> { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/ThmmyNoLife/mTHMMY/commit/" + BuildConfig.COMMIT_HASH)); startActivity(intent); }); } - else{ // Easter Egg - tv.setOnClickListener(view -> { - if (mVersionLastPressedTime + TIME_INTERVAL > System.currentTimeMillis()) { - if (mVersionPressedCounter == TIMES_TO_PRESS) { - appBar.setVisibility(View.INVISIBLE); - mainContent.setVisibility(View.INVISIBLE); - trollGif.setVisibility(View.VISIBLE); - drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - } - mVersionLastPressedTime = System.currentTimeMillis(); - ++mVersionPressedCounter; - } else { - mVersionLastPressedTime = System.currentTimeMillis(); - mVersionPressedCounter = 0; - } - }); - } + + versionTextView.setOnLongClickListener(view -> { + Toast.makeText(getApplicationContext(), BaseApplication.getFirebaseProjectId(), Toast.LENGTH_SHORT).show(); + return true; + }); } TextView privacyPolicy = findViewById(R.id.privacy_policy_header); @@ -122,6 +126,14 @@ public class AboutActivity extends BaseActivity { super.onResume(); } + @Override + public void onBackPressed() { + if(easterEggImage.getVisibility()==View.INVISIBLE) + super.onBackPressed(); + else + hideEasterEgg(); + } + public void displayApacheLibraries(View v) { LayoutInflater inflater = LayoutInflater.from(this); WebView webView = (WebView) inflater.inflate(R.layout.dialog_licenses, coordinatorLayout, false); @@ -152,4 +164,21 @@ public class AboutActivity extends BaseActivity { alertDialog.getWindow().setLayout(width, height); } + private void showEasterEgg(){ + if(getResources().getConfiguration().orientation==ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){ + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + appBar.setVisibility(View.INVISIBLE); + mainContent.setVisibility(View.INVISIBLE); + easterEggImage.setVisibility(View.VISIBLE); + drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + } + } + + private void hideEasterEgg(){ + appBar.setVisibility(View.VISIBLE); + mainContent.setVisibility(View.VISIBLE); + easterEggImage.setVisibility(View.INVISIBLE); + drawer.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + } } 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 6ba7d3cc..ba0e6a0f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java @@ -18,6 +18,7 @@ import com.crashlytics.android.core.CrashlyticsCore; import com.franmontiel.persistentcookiejar.PersistentCookieJar; import com.franmontiel.persistentcookiejar.cache.SetCookieCache; import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; +import com.google.firebase.FirebaseApp; import com.google.firebase.analytics.FirebaseAnalytics; import com.itkacher.okhttpprofiler.OkHttpProfilerInterceptor; import com.jakewharton.picasso.OkHttp3Downloader; @@ -51,7 +52,8 @@ import timber.log.Timber; public class BaseApplication extends Application { private static BaseApplication baseApplication; //BaseApplication singleton - //Firebase Analytics + //Firebase + private static String firebaseProjectId; private FirebaseAnalytics firebaseAnalytics; //Client & SessionManager @@ -63,7 +65,6 @@ public class BaseApplication extends Application { //Display Metrics private static float dpWidth; - public static BaseApplication getInstance() { return baseApplication; } @@ -87,6 +88,7 @@ public class BaseApplication extends Application { else Timber.i("Starting app with Crashlytics disabled."); + firebaseProjectId = FirebaseApp.getInstance().getOptions().getProjectId(); firebaseAnalytics = FirebaseAnalytics.getInstance(this); boolean enableAnalytics = settingsSharedPrefs.getBoolean(getString(R.string.pref_privacy_analytics_enable_key), false); firebaseAnalytics.setAnalyticsCollectionEnabled(enableAnalytics); @@ -216,4 +218,8 @@ public class BaseApplication extends Application { } else Timber.i("Crashlytics were already initialized for this app session."); } + + public static String getFirebaseProjectId(){ + return firebaseProjectId; + } } From ba729cb2181e097a024f808e46bbcd24c56d0508 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Thu, 4 Jul 2019 10:51:02 +0300 Subject: [PATCH 28/37] Fix for unsupported courses --- .../gr/thmmy/mthmmy/activities/upload/UploadsCourse.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 index 8fe986c9..1a2f55ae 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java @@ -59,8 +59,9 @@ class UploadsCourse { 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); + bundle.putString("course_name", retrievedCourse); + BaseApplication.getInstance().logFirebaseAnalyticsEvent("unsupported_uploads_course", bundle); + return null; } return uploadsCourses.get(foundKey); From 35b15e7fb15715aa2eb46f38df84330ea350783e Mon Sep 17 00:00:00 2001 From: Ezerous Date: Thu, 4 Jul 2019 10:56:43 +0300 Subject: [PATCH 29/37] Fix for unsupported courses --- .../activities/upload/UploadActivity.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 d6fb274a..dcf9b90e 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 @@ -940,14 +940,16 @@ public class UploadActivity extends BaseActivity { 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; + if(!retrievedCourse.isEmpty()){ + 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); From d91dfcf477c623c97251601ce6f42690b892563e Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sat, 6 Jul 2019 15:37:14 +0300 Subject: [PATCH 30/37] Uploads improvements --- .../activities/upload/UploadActivity.java | 44 ++++++------------- .../activities/upload/UploadsCourse.java | 5 ++- app/src/main/res/values/uploads_courses.xml | 4 ++ 3 files changed, 21 insertions(+), 32 deletions(-) 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 dcf9b90e..6d0a2d9b 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 @@ -130,7 +130,7 @@ public class UploadActivity extends BaseActivity { private EditText uploadTitle; private EditText uploadFilename; private EditText uploadDescription; - private AppCompatButton titleDescriptionBuilderButton; + private AppCompatButton generateFieldsButton; private LinearLayout filesListView; @Override @@ -174,9 +174,9 @@ public class UploadActivity extends BaseActivity { rootCategorySpinner = findViewById(R.id.upload_spinner_category_root); rootCategorySpinner.setOnItemSelectedListener(new CustomOnItemSelectedListener(uploadRootCategories)); - titleDescriptionBuilderButton = findViewById(R.id.upload_title_description_builder); - titleDescriptionBuilderButton.setEnabled(false); - titleDescriptionBuilderButton.setOnClickListener(view -> { + generateFieldsButton = findViewById(R.id.upload_title_description_builder); + generateFieldsButton.setEnabled(false); + generateFieldsButton.setOnClickListener(view -> { if(uploadsCourse!=null && !uploadsCourse.getName().equals("") && !semester.equals("")){ Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class); Bundle builderExtras = new Bundle(); @@ -410,7 +410,7 @@ public class UploadActivity extends BaseActivity { } else { //Renders the already parsed data updateUIElements(); - titleDescriptionBuilderButton.setEnabled(true); + generateFieldsButton.setEnabled(true); } Resources res = getResources(); @@ -841,7 +841,7 @@ public class UploadActivity extends BaseActivity { } categorySelected = parentCategories.get(position).getValue(); - setCourseAndSemester(); + generateFieldsButton.setEnabled(false); //Adds new sub-category spinner if (parentCategories.get(position).hasSubCategories()) { @@ -882,6 +882,8 @@ public class UploadActivity extends BaseActivity { } } } + else + setCourseAndSemester(); } @Override @@ -890,21 +892,13 @@ public class UploadActivity extends BaseActivity { private void setCourseAndSemester(){ uploadsCourse = null; semester = ""; - - if (categorySelected.equals("-1")) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } + if (categorySelected.equals("-1")) return; int numberOfSpinners = categoriesSpinners.getChildCount(); - if (numberOfSpinners < 3) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } + if (numberOfSpinners < 3) return; - String maybeSemester = ""; - String maybeCourse = ""; + String maybeSemester, maybeCourse; if (numberOfSpinners == 5) { if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)). @@ -927,15 +921,8 @@ public class UploadActivity extends BaseActivity { categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); } - if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } - - if (maybeCourse == null) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } + if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) return; + if (maybeCourse == null) return; String retrievedCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); String retrievedSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); @@ -947,12 +934,9 @@ public class UploadActivity extends BaseActivity { uploadsCourse = foundUploadsCourse; semester = retrievedSemester; Timber.d("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); - titleDescriptionBuilderButton.setEnabled(true); - return; + generateFieldsButton.setEnabled(true); } } - - titleDescriptionBuilderButton.setEnabled(false); } } 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 index 1a2f55ae..12f0475f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java @@ -52,8 +52,9 @@ class 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 ((key.contains(retrievedCourse) || retrievedCourse.contains(key)) + && (foundKey==null || key.length()>foundKey.length())) + foundKey = key; } if(foundKey==null){ diff --git a/app/src/main/res/values/uploads_courses.xml b/app/src/main/res/values/uploads_courses.xml index 8f872389..6fb45b37 100644 --- a/app/src/main/res/values/uploads_courses.xml +++ b/app/src/main/res/values/uploads_courses.xml @@ -35,6 +35,7 @@ Διακριτά Μαθηματικά:Διακριτά Μαθηματικά:Diakrita Διακριτά μαθηματικά:Διακριτά Μαθηματικά:Diakrita Διανεμημένη Παραγωγή:Διανεμημένη Παραγωγή:Dian_Paragogi + Διατάξεις Υψηλών Συχνοτήτων:ΔΥΣ:DYS Διαφορικές Εξισώσεις:Διαφορικές:Diaforikes Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας:ΔΣΗΕ:DSHE Δομές Δεδομένων:Δομ. Δεδομ.:Domes_Dedomenon @@ -63,6 +64,7 @@ Ηλεκτρικά Κυκλώματα III:Κυκλώματα 3:Kyklomata_I Ηλεκτρικές Μετρήσεις I:Μετρήσεις 1:Metriseis_I Ηλεκτρικές Μετρήσεις II:Μετρήσεις 2:Metriseis_II + Ηλεκτρικές Μηχανές I:Μηχανές I:Mixanes_I Ηλεκτρικές Μηχανές Α\':Μηχανές Α:Mixanes_A Ηλεκτρικές Μηχανές Β\':Μηχανές Β:Mixanes_B Ηλεκτρικές Μηχανές Γ\':Μηχανές Γ:Mixanes_C @@ -132,6 +134,8 @@ Τεχνολογία Ηλεκτροτεχνικών Υλικών:Ηλεκτροτεχνικά Υλικά:Ilektrotexnika_Ylika Τεχνολογία Λογισμικού:Τεχνολογία Λογισμικού:SE Τηλεοπτικά Συστήματα:Τηλεοπτικά:Tileoptika + Τηλεπικοινωνιακά Συστήματα I:Τηλεπικοινωνιακά I:Tilepikoinoniaka_I + Τηλεπικοινωνιακά Συστήματα II:Τηλεπικοινωνιακά II:Tilepikoinoniaka_II Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM From 9a61ffead17eb04d4b68286647228840e47deb18 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Sun, 7 Jul 2019 20:11:40 +0300 Subject: [PATCH 31/37] Uploads improvements (2) --- .../gr/thmmy/mthmmy/activities/upload/UploadActivity.java | 8 +++++--- .../activities/upload/UploadFieldsBuilderActivity.java | 6 +++--- app/src/main/res/values/uploads_courses.xml | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) 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 6d0a2d9b..0b1195d2 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 @@ -924,15 +924,17 @@ public class UploadActivity extends BaseActivity { if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) return; if (maybeCourse == null) return; - String retrievedCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); - String retrievedSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); + String retrievedCourse = maybeCourse.replaceAll("-", "") + .replaceAll("\\((πρώην|πρωην).*\\)","") + .replace("(ΝΠΣ)", "") + .trim(); if(!retrievedCourse.isEmpty()){ UploadsCourse foundUploadsCourse = UploadsCourse.findCourse(retrievedCourse, uploadsCourses); if(foundUploadsCourse != null){ uploadsCourse = foundUploadsCourse; - semester = retrievedSemester; + semester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); Timber.d("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); generateFieldsButton.setEnabled(true); } 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 ffa6f26e..f7eb6e0d 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 @@ -156,11 +156,11 @@ public class UploadFieldsBuilderActivity extends BaseActivity { private String buildTitle() { switch (typeRadio.getCheckedRadioButtonId()) { case R.id.upload_fields_builder_radio_button_exams: - return courseMinifiedName + " - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString(); + return "[" + courseMinifiedName + "] - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString(); case R.id.upload_fields_builder_radio_button_exam_solutions: - return courseMinifiedName + " - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString(); + return "[" + courseMinifiedName + "] - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString(); case R.id.upload_fields_builder_radio_button_notes: - return courseMinifiedName + " - " + "Σημειώσεις παραδόσεων " + year.getText().toString(); + return "[" + courseMinifiedName + "] - " + "Σημειώσεις παραδόσεων " + year.getText().toString(); default: return null; } diff --git a/app/src/main/res/values/uploads_courses.xml b/app/src/main/res/values/uploads_courses.xml index 6fb45b37..51781f41 100644 --- a/app/src/main/res/values/uploads_courses.xml +++ b/app/src/main/res/values/uploads_courses.xml @@ -111,7 +111,8 @@ Σήματα και Συστήματα:Σήματα & Συστήματα:Analog_Sima Σερβοκινητήρια Συστήματα:Σέρβο:Servo Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας:ΣΠΗΕ:SPHE - Στοχαστικό Σήμα:Στοχ. Σήμα:Stox_Sima + Στοχαστικά Σήματα και Διαδικασίες:Στοχαστικό:Stochastic + Στοχαστικό Σήμα:Στοχαστικό:Stochastic Συστήματα Αυτομάτου Ελέγχου I:ΣΑΕ 1:SAE_I Συστήματα Αυτομάτου Ελέγχου II:ΣΑΕ 2:SAE_II Συστήματα Αυτομάτου Ελέγχου III:ΣΑΕ 3:SAE_III From e35688cdf15ca01182e76cb2d1d1514c5428f799 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 16 Sep 2019 17:59:51 +0300 Subject: [PATCH 32/37] Uploads fix --- app/build.gradle | 4 +- .../activities/upload/UploadActivity.java | 3 +- .../multipart/MultipartUploadRequest.java | 99 +++++++++++ .../upload/multipart/MultipartUploadTask.java | 160 ++++++++++++++++++ 4 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java diff --git a/app/build.gradle b/app/build.gradle index b47ae972..08113599 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 28 - versionCode 21 - versionName "1.7.3" + versionCode 22 + versionName "1.7.4" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" 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 0b1195d2..98c23db9 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 @@ -40,7 +40,6 @@ import androidx.preference.PreferenceManager; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import net.gotev.uploadservice.MultipartUploadRequest; import net.gotev.uploadservice.UploadNotificationAction; import net.gotev.uploadservice.UploadNotificationConfig; @@ -67,6 +66,7 @@ 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.activities.upload.multipart.MultipartUploadRequest; import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; @@ -740,6 +740,7 @@ public class UploadActivity extends BaseActivity { .addFileToUpload(fileUri.toString() , "tp-dluploadfile") .addParameter("tp_dluploadicon", fileIcon) + .addParameter("tp_dluploadpic", "") .addParameter("tp-uploaduser", uploaderProfileIndex) .setNotificationConfig(uploadNotificationConfig) .setMaxRetries(2) diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java new file mode 100644 index 00000000..f6126523 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java @@ -0,0 +1,99 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +import android.content.Context; +import android.content.Intent; + +import net.gotev.uploadservice.HttpUploadRequest; +import net.gotev.uploadservice.Logger; +import net.gotev.uploadservice.UploadFile; +import net.gotev.uploadservice.UploadTask; + +import java.io.FileNotFoundException; +import java.net.MalformedURLException; + +/** + From MultipartUploadRequest gotev/android-upload-service in order to use the local custom + MultipartUploadTask. + */ +public class MultipartUploadRequest extends HttpUploadRequest { + + private static final String LOG_TAG = MultipartUploadRequest.class.getSimpleName(); + private boolean isUtf8Charset = false; + + + public MultipartUploadRequest(final Context context, final String uploadId, final String serverUrl) + throws IllegalArgumentException, MalformedURLException { + super(context, uploadId, serverUrl); + } + + public MultipartUploadRequest(final Context context, final String serverUrl) + throws MalformedURLException, IllegalArgumentException { + this(context, null, serverUrl); + } + + @Override + protected void initializeIntent(Intent intent) { + super.initializeIntent(intent); + intent.putExtra(MultipartUploadTask.PARAM_UTF8_CHARSET, isUtf8Charset); + } + + @Override + protected Class getTaskClass() { + return MultipartUploadTask.class; + } + + public MultipartUploadRequest addFileToUpload(String filePath, + String parameterName, + String fileName, String contentType) + throws FileNotFoundException, IllegalArgumentException { + + UploadFile file = new UploadFile(filePath); + filePath = file.getPath(); + + if (parameterName == null || "".equals(parameterName)) { + throw new IllegalArgumentException("Please specify parameterName value for file: " + + filePath); + } + + file.setProperty(MultipartUploadTask.PROPERTY_PARAM_NAME, parameterName); + + if (contentType == null || contentType.isEmpty()) { + contentType = file.getContentType(context); + Logger.debug(LOG_TAG, "Auto-detected MIME type for " + filePath + + " is: " + contentType); + } else { + Logger.debug(LOG_TAG, "Content Type set for " + filePath + + " is: " + contentType); + } + + file.setProperty(MultipartUploadTask.PROPERTY_CONTENT_TYPE, contentType); + + if (fileName == null || "".equals(fileName)) { + fileName = file.getName(context); + Logger.debug(LOG_TAG, "Using original file name: " + fileName); + } else { + Logger.debug(LOG_TAG, "Using custom file name: " + fileName); + } + + file.setProperty(MultipartUploadTask.PROPERTY_REMOTE_FILE_NAME, fileName); + + params.files.add(file); + return this; + } + + public MultipartUploadRequest addFileToUpload(final String path, final String parameterName, + final String fileName) + throws FileNotFoundException, IllegalArgumentException { + return addFileToUpload(path, parameterName, fileName, null); + } + + public MultipartUploadRequest addFileToUpload(final String path, final String parameterName) + throws FileNotFoundException, IllegalArgumentException { + return addFileToUpload(path, parameterName, null, null); + } + + public MultipartUploadRequest setUtf8Charset() { + isUtf8Charset = true; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java new file mode 100644 index 00000000..be9bb0f6 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java @@ -0,0 +1,160 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +import android.content.Intent; + +import net.gotev.uploadservice.HttpUploadTask; +import net.gotev.uploadservice.NameValue; +import net.gotev.uploadservice.UploadFile; +import net.gotev.uploadservice.UploadService; +import net.gotev.uploadservice.http.BodyWriter; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + Extended MultipartUploadTask from gotev/android-upload-service to include a fix for the parameter + tp_dluploadpic. Also changed Connection to keep-alive. + */ +public class MultipartUploadTask extends HttpUploadTask { + + static final String PARAM_UTF8_CHARSET = "multipartUtf8Charset"; + + private static final String BOUNDARY_SIGNATURE = "-------AndroidUploadService"; + private static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final String NEW_LINE = "\r\n"; + private static final String TWO_HYPHENS = "--"; + + // properties associated to each file + static final String PROPERTY_REMOTE_FILE_NAME = "httpRemoteFileName"; + static final String PROPERTY_CONTENT_TYPE = "httpContentType"; + static final String PROPERTY_PARAM_NAME = "httpParamName"; + + private byte[] boundaryBytes; + private byte[] trailerBytes; + private Charset charset; + + @Override + protected void init(UploadService service, Intent intent) throws IOException { + super.init(service, intent); + + String boundary = BOUNDARY_SIGNATURE + System.nanoTime(); + boundaryBytes = (TWO_HYPHENS + boundary + NEW_LINE).getBytes(US_ASCII); + trailerBytes = (TWO_HYPHENS + boundary + TWO_HYPHENS + NEW_LINE).getBytes(US_ASCII); + charset = intent.getBooleanExtra(PARAM_UTF8_CHARSET, false) ? + Charset.forName("UTF-8") : US_ASCII; + + httpParams.addHeader("Connection", "Keep-Alive"); + httpParams.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary); + } + + @Override + protected long getBodyLength() throws UnsupportedEncodingException { + return (getRequestParametersLength() + getFilesLength() + trailerBytes.length); + } + + @Override + public void onBodyReady(BodyWriter bodyWriter) throws IOException { + //reset uploaded bytes when the body is ready to be written + //because sometimes this gets invoked when network changes + uploadedBytes = 0; + writeRequestParameters(bodyWriter); + writeFiles(bodyWriter); + bodyWriter.write(trailerBytes); + uploadedBytes += trailerBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + } + + private long getFilesLength() throws UnsupportedEncodingException { + long total = 0; + + for (UploadFile file : params.files) { + total += getTotalMultipartBytes(file); + } + + return total; + } + + private long getRequestParametersLength() throws UnsupportedEncodingException { + long parametersBytes = 0; + + if (!httpParams.getRequestParameters().isEmpty()) { + for (final NameValue parameter : httpParams.getRequestParameters()) { + // the bytes needed for every parameter are the sum of the boundary bytes + // and the bytes occupied by the parameter + parametersBytes += boundaryBytes.length + getMultipartBytes(parameter).length; + } + } + + return parametersBytes; + } + + private byte[] getMultipartBytes(NameValue parameter) throws UnsupportedEncodingException { + if(parameter.getName().equals("tp_dluploadpic")){ + String header = "Content-Disposition: form-data; name=\"" + + parameter.getName() + "\"; filename=\"\"" + NEW_LINE + + "Content-Type: application/octet-stream" + NEW_LINE + NEW_LINE; + return header.getBytes(charset); + } + else + return ("Content-Disposition: form-data; name=\"" + parameter.getName() + "\"" + + NEW_LINE + NEW_LINE + parameter.getValue() + NEW_LINE).getBytes(charset); + } + + private byte[] getMultipartHeader(UploadFile file) + throws UnsupportedEncodingException { + String header = "Content-Disposition: form-data; name=\"" + + file.getProperty(PROPERTY_PARAM_NAME) + "\"; filename=\"" + + file.getProperty(PROPERTY_REMOTE_FILE_NAME) + "\"" + NEW_LINE + + "Content-Type: " + file.getProperty(PROPERTY_CONTENT_TYPE) + + NEW_LINE + NEW_LINE; + + return header.getBytes(charset); + } + + private long getTotalMultipartBytes(UploadFile file) + throws UnsupportedEncodingException { + return boundaryBytes.length + getMultipartHeader(file).length + file.length(service) + + NEW_LINE.getBytes(charset).length; + } + + private void writeRequestParameters(BodyWriter bodyWriter) throws IOException { + if (!httpParams.getRequestParameters().isEmpty()) { + for (final NameValue parameter : httpParams.getRequestParameters()) { + bodyWriter.write(boundaryBytes); + byte[] formItemBytes = getMultipartBytes(parameter); + bodyWriter.write(formItemBytes); + + uploadedBytes += boundaryBytes.length + formItemBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + } + } + } + + private void writeFiles(BodyWriter bodyWriter) throws IOException { + for (UploadFile file : params.files) { + if (!shouldContinue) + break; + + bodyWriter.write(boundaryBytes); + byte[] headerBytes = getMultipartHeader(file); + bodyWriter.write(headerBytes); + + uploadedBytes += boundaryBytes.length + headerBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + + bodyWriter.writeStream(file.getStream(service), this); + + byte[] newLineBytes = NEW_LINE.getBytes(charset); + bodyWriter.write(newLineBytes); + uploadedBytes += newLineBytes.length; + } + } + + @Override + protected void onSuccessfulUpload() { + addAllFilesToSuccessfullyUploadedFiles(); + } + +} + From d49651fdcc3acffab8fbe8c42f177e54c56d01a9 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 16 Sep 2019 20:01:24 +0300 Subject: [PATCH 33/37] Improved uploads error handling --- .../mthmmy/activities/upload/UploadActivity.java | 2 +- .../multipart/MultipartUploadException.java | 8 ++++++++ .../thmmy/mthmmy/services/UploadsReceiver.java | 16 +++++++++++++--- .../mthmmy/utils/parsing/ParseException.java | 5 ++--- 4 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java 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 98c23db9..347acdc7 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 @@ -58,6 +58,7 @@ import java.util.Locale; import java.util.UUID; import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.activities.upload.multipart.MultipartUploadRequest; import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.model.UploadCategory; @@ -66,7 +67,6 @@ 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.activities.upload.multipart.MultipartUploadRequest; import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java new file mode 100644 index 00000000..0b8cdc29 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java @@ -0,0 +1,8 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +public class MultipartUploadException extends RuntimeException { + public MultipartUploadException(String message) { + super(message); + } + +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index f8b06e28..015dc91d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -22,8 +22,10 @@ import net.gotev.uploadservice.UploadServiceBroadcastReceiver; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.upload.UploadsHelper; +import gr.thmmy.mthmmy.activities.upload.multipart.MultipartUploadException; import gr.thmmy.mthmmy.base.BaseApplication; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import timber.log.Timber; public class UploadsReceiver extends UploadServiceBroadcastReceiver { public static final String UPLOAD_ID_KEY = "UPLOAD_ID_KEY"; @@ -153,7 +155,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text); dialogProgressBar.setVisibility(View.GONE); - dialogProgressText.setText("Upload failed."); + dialogProgressText.setText("Upload failed"); } if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) { @@ -185,12 +187,20 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { dialogUploadID = null; } - Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); + String response = serverResponse.getBodyAsString(); + if(response.contains("Η προσθήκη του αρχείου ήταν επιτυχημένη.")||response.contains("The upload was successful.")){ + Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); + BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); + } + else { + Timber.e(new MultipartUploadException(response)); + onError(context,uploadInfo,serverResponse,new MultipartUploadException(response)); + } + if (storage == null) { storage = new Storage(context.getApplicationContext()); } - BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); UploadsHelper.deleteTempFiles(storage); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java index 942e789b..5fc06664 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java @@ -1,13 +1,12 @@ package gr.thmmy.mthmmy.utils.parsing; /** - * ParseException is to be used for errors while parsing. + * Use ParseException for errors while parsing. */ public class ParseException extends RuntimeException { public ParseException() {} - public ParseException(String message) - { + public ParseException(String message) { super(message); } From 0137f5b651e7908fcc2e9fa8726b77628f2933c2 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 16 Sep 2019 20:31:17 +0300 Subject: [PATCH 34/37] Minor uploads cleanup --- .../activities/upload/UploadActivity.java | 4 ++-- .../thmmy/mthmmy/services/UploadsReceiver.java | 17 +++++++++-------- app/src/main/res/values/strings.xml | 7 +++++-- 3 files changed, 16 insertions(+), 12 deletions(-) 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 347acdc7..dd728db4 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 @@ -710,13 +710,13 @@ public class UploadActivity extends BaseActivity { uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction( R.drawable.ic_cancel_accent_24dp, - context.getString(R.string.upload_notification_cancel), + context.getString(R.string.upload_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), + context.getString(R.string.upload_retry), PendingIntent.getBroadcast(context, 0, retryIntent, PendingIntent.FLAG_UPDATE_CURRENT) )); diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index 015dc91d..6c978dda 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -90,11 +90,11 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { uploadInfo.getUploadId().equals(dialogUploadID) && uploadProgressDialog != null) { Button alertDialogNeutral = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEUTRAL); - alertDialogNeutral.setText("Resume on background"); + alertDialogNeutral.setText(R.string.upload_resume_in_background); alertDialogNeutral.setOnClickListener(v -> uploadProgressDialog.dismiss()); Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - alertDialogNegative.setText("Cancel"); + alertDialogNegative.setText(R.string.upload_cancel); alertDialogNegative.setOnClickListener(v -> { UploadService.stopUpload(dialogUploadID); uploadProgressDialog.dismiss(); @@ -137,7 +137,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { });*/ Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - alertDialogNegative.setText("Cancel"); + alertDialogNegative.setText(R.string.upload_cancel); alertDialogNegative.setOnClickListener(v -> { NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). getSystemService(Context.NOTIFICATION_SERVICE); @@ -155,7 +155,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text); dialogProgressBar.setVisibility(View.GONE); - dialogProgressText.setText("Upload failed"); + dialogProgressText.setText(R.string.upload_failed); } if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) { @@ -174,7 +174,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { context.sendBroadcast(combinedActionsIntent); } - Toast.makeText(context.getApplicationContext(), "Upload failed", Toast.LENGTH_SHORT).show(); + Toast.makeText(context.getApplicationContext(), R.string.upload_failed, Toast.LENGTH_SHORT).show(); if (storage == null) { storage = new Storage(context.getApplicationContext()); } @@ -193,8 +193,9 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); } else { - Timber.e(new MultipartUploadException(response)); - onError(context,uploadInfo,serverResponse,new MultipartUploadException(response)); + MultipartUploadException multipartUploadException = new MultipartUploadException(response); + Timber.e(multipartUploadException); + onError(context,uploadInfo,serverResponse,multipartUploadException); } if (storage == null) { @@ -211,7 +212,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { dialogUploadID = null; } - Toast.makeText(context.getApplicationContext(), "Upload canceled", Toast.LENGTH_SHORT).show(); + Toast.makeText(context.getApplicationContext(), R.string.upload_canceled, Toast.LENGTH_SHORT).show(); if (storage == null) { storage = new Storage(context.getApplicationContext()); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d856f19..2a32d992 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -144,8 +144,11 @@ \nThis does not rename your local files.
Uploading Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes - "Cancel" - "Retry" + "Upload failed" + "Upload canceled" + "Resume in background" + "Cancel" + "Retry" Select type of upload From 29c4caa7af680223cc00d2702af628fe7e78b721 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Tue, 17 Sep 2019 12:50:24 +0300 Subject: [PATCH 35/37] Up dependencies --- app/build.gradle | 16 ++++++++-------- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 08113599..a4f3e951 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,13 +6,14 @@ apply plugin: 'com.android.application' apply plugin: 'io.fabric' android { - compileSdkVersion 28 + compileSdkVersion 29 + buildToolsVersion = '29.0.2' defaultConfig { vectorDrawables.useSupportLibrary = true applicationId "gr.thmmy.mthmmy" minSdkVersion 19 - targetSdkVersion 28 + targetSdkVersion 29 versionCode 22 versionName "1.7.4" archivesBaseName = "mTHMMY-v$versionName" @@ -71,15 +72,15 @@ tasks.whenTaskAdded { task -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.1.0-beta01' - implementation 'androidx.preference:preference:1.1.0-beta01' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha04' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.exifinterface:exifinterface:1.1.0-alpha01' + implementation 'androidx.exifinterface:exifinterface:1.1.0-beta01' implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.firebase:firebase-core:17.0.0' implementation 'com.google.firebase:firebase-messaging:19.0.1' @@ -101,8 +102,7 @@ dependencies { implementation 'ru.noties:markwon:2.0.2' implementation 'net.gotev:uploadservice:3.5.2' implementation 'net.gotev:uploadservice-okhttp:3.4.2' //TODO: Warning: v.3.5 depends on okhttp 3.13! - implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5' -//Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler + 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/build.gradle b/build.gradle index 65830711..70b74ad6 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' - classpath 'com.google.gms:google-services:4.3.0' + classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.google.gms:google-services:4.3.2' classpath 'io.fabric.tools:gradle:1.29.0' classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e43564c..7076a01e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Jun 15 18:56:13 EEST 2019 +#Tue Sep 17 12:32:34 EEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip From 2e0235de6140b77dd108f10440c358fc11ced0cb Mon Sep 17 00:00:00 2001 From: Ezerous Date: Tue, 17 Sep 2019 13:43:22 +0300 Subject: [PATCH 36/37] Fixes for fatal crashes --- .../main/recent/RecentFragment.java | 5 +-- .../main/unread/UnreadFragment.java | 7 ++-- .../mthmmy/activities/topic/Posting.java | 35 ++++++++++--------- .../activities/topic/TopicActivity.java | 4 +-- ...ForReply.java => PrepareForReplyTask.java} | 16 +++++---- .../mthmmy/viewmodel/TopicViewModel.java | 12 +++---- 6 files changed, 44 insertions(+), 35 deletions(-) rename app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/{PrepareForReply.java => PrepareForReplyTask.java} (83%) 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 4f4517d1..6e14b79b 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 @@ -22,6 +22,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.session.SessionManager; @@ -146,9 +147,9 @@ public class RecentFragment extends BaseFragment { topicSummaries.addAll(fetchedRecent); recentAdapter.notifyDataSetChanged(); } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { - Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(getContext(), "Unexpected error," + + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Unexpected error," + " please contact the developers with the details", Toast.LENGTH_LONG).show(); } 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 cec14e97..1db4131e 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 @@ -143,11 +143,12 @@ public class UnreadFragment extends BaseFragment { @Override public void onDestroy() { super.onDestroy(); - if (unreadTask.isRunning()) + if (unreadTask!=null && unreadTask.isRunning()) unreadTask.cancel(true); - if (markReadTask.isRunning()) + if (markReadTask!=null && markReadTask.isRunning()) markReadTask.cancel(true); - topicSummaries.clear(); + if(topicSummaries!=null) + topicSummaries.clear(); } public interface UnreadFragmentInteractionListener extends FragmentInteractionListener { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java index 88c2f8a3..6fb1cc3f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java @@ -2,6 +2,7 @@ package gr.thmmy.mthmmy.activities.topic; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import java.io.IOException; @@ -60,22 +61,24 @@ public class Posting { String finalUrl = response.request().url().toString(); if (finalUrl.contains("action=post")) { Document postErrorPage = Jsoup.parse(response.body().string()); - String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first() - .toString().split("
"); - for (int i = 0; i < errors.length; ++i) { //TODO test - Timber.d(String.valueOf(i)); - Timber.d(errors[i]); - } - for (String error : errors) { - if (error.contains("Your session timed out while posting") || - error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή")) - return REPLY_STATUS.SESSION_ENDED; - if (error.contains("No subject was filled in") - || error.contains("Δεν δόθηκε τίτλος")) - return REPLY_STATUS.NO_SUBJECT; - if (error.contains("The message body was left empty") - || error.contains("Δεν δόθηκε κείμενο για το μήνυμα")) - return REPLY_STATUS.EMPTY_BODY; + Element errorsElement = postErrorPage.select("tr[id=errors] div[id=error_list]").first(); + if(errorsElement!=null){ + String[] errors = errorsElement.toString().split("
"); + for (int i = 0; i < errors.length; ++i) { //TODO test + Timber.d(String.valueOf(i)); + Timber.d(errors[i]); + } + for (String error : errors) { + if (error.contains("Your session timed out while posting") || + error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή")) + return REPLY_STATUS.SESSION_ENDED; + if (error.contains("No subject was filled in") + || error.contains("Δεν δόθηκε τίτλος")) + return REPLY_STATUS.NO_SUBJECT; + if (error.contains("The message body was left empty") + || error.contains("Δεν δόθηκε κείμενο για το μήνυμα")) + return REPLY_STATUS.EMPTY_BODY; + } } return REPLY_STATUS.NEW_REPLY_WHILE_POSTING; } 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 d3d97663..e0e52b3e 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 @@ -42,7 +42,7 @@ import java.util.ArrayList; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; -import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply; +import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask; import gr.thmmy.mthmmy.base.BaseActivity; @@ -618,7 +618,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } } }); - viewModel.setPrepareForReplyCallbacks(new PrepareForReply.PrepareForReplyCallbacks() { + viewModel.setPrepareForReplyCallbacks(new PrepareForReplyTask.PrepareForReplyCallbacks() { @Override public void onPrepareForReplyStarted() { progressBar.setVisibility(ProgressBar.VISIBLE); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java similarity index 83% rename from app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java rename to app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java index bf5e12e0..36024a7e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java @@ -15,13 +15,13 @@ import okhttp3.Request; import okhttp3.Response; import timber.log.Timber; -public class PrepareForReply extends AsyncTask { +public class PrepareForReplyTask extends AsyncTask { private PrepareForReplyCallbacks listener; private OnPrepareForReplyFinished finishListener; private String replyPageUrl; - public PrepareForReply(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, - String replyPageUrl) { + public PrepareForReplyTask(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, + String replyPageUrl) { this.listener = listener; this.finishListener = finishListener; this.replyPageUrl = replyPageUrl; @@ -49,12 +49,16 @@ public class PrepareForReply extends AsyncTask voteTaskFinishedListener; private ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener; @@ -177,7 +177,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa stopLoading(); setPageIndicatorIndex(pageCount, true); Timber.i("Preparing for reply"); - currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this, + currentPrepareForReplyTask = new PrepareForReplyTask(prepareForReplyCallbacks, this, replyPageUrl.getValue()); currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0])); } @@ -424,7 +424,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa this.editTaskCallbacks = editTaskCallbacks; } - public void setPrepareForReplyCallbacks(PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks) { + public void setPrepareForReplyCallbacks(PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks) { this.prepareForReplyCallbacks = prepareForReplyCallbacks; } From a6a3280aa80366ce443e3988ea8186c4e34ded21 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Tue, 17 Sep 2019 18:58:16 +0300 Subject: [PATCH 37/37] Uploads minor tweaks --- .../activities/upload/UploadActivity.java | 20 ++++----- .../gr/thmmy/mthmmy/base/BaseActivity.java | 10 ++--- .../mthmmy/services/UploadsReceiver.java | 41 +++++++++---------- .../mthmmy/viewmodel/TopicViewModel.java | 2 +- app/src/main/res/values/strings.xml | 5 +-- 5 files changed, 38 insertions(+), 40 deletions(-) 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 dd728db4..0d9135a4 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 @@ -372,7 +372,6 @@ public class UploadActivity extends BaseActivity { requestPerms(UPLOAD_REQUEST_STORAGE_CODE); dialog.cancel(); } - return; } @@ -667,7 +666,7 @@ public class UploadActivity extends BaseActivity { filesListView.setVisibility(View.VISIBLE); } - public static UploadNotificationConfig getConfigForUpload(Context context, String uploadID, + public UploadNotificationConfig getConfigForUpload(Context context, String uploadID, String filename) { UploadNotificationConfig uploadNotificationConfig = new UploadNotificationConfig(); uploadNotificationConfig.setIconForAllStatuses(android.R.drawable.stat_sys_upload); @@ -699,7 +698,7 @@ public class UploadActivity extends BaseActivity { uploadNotificationConfig.setClickIntentForAllStatuses(PendingIntent.getBroadcast(context, 1, combinedActionsIntent, PendingIntent.FLAG_UPDATE_CURRENT)); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + else { Intent retryIntent = new Intent(context, UploadsReceiver.class); retryIntent.setAction(UploadsReceiver.ACTION_RETRY_UPLOAD); retryIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); @@ -710,7 +709,7 @@ public class UploadActivity extends BaseActivity { uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction( R.drawable.ic_cancel_accent_24dp, - context.getString(R.string.upload_cancel), + context.getString(R.string.cancel), PendingIntent.getBroadcast(context, 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT) )); @@ -731,7 +730,7 @@ public class UploadActivity extends BaseActivity { String uploadDescriptionText, String fileIcon, String uploaderProfileIndex, Uri fileUri) { try { - new MultipartUploadRequest(context, uploadID, uploadIndexUrl) + MultipartUploadRequest multipartUploadRequest = new MultipartUploadRequest(context, uploadID, uploadIndexUrl) .setUtf8Charset() .setNotificationConfig(uploadNotificationConfig) .addParameter("tp-dluploadtitle", uploadTitleText) @@ -743,10 +742,11 @@ public class UploadActivity extends BaseActivity { .addParameter("tp_dluploadpic", "") .addParameter("tp-uploaduser", uploaderProfileIndex) .setNotificationConfig(uploadNotificationConfig) - .setMaxRetries(2) - .startUpload(); - - Toast.makeText(context, "Uploading files in the background.", Toast.LENGTH_SHORT).show(); + .setMaxRetries(2); + Timber.d("Uploading a file with properties: \nTitle: %s\nCategory: %s\nDescription: %s\nIcon: %s\nUploader: %s", + uploadTitleText, categorySelected, uploadDescriptionText, fileIcon, uploaderProfileIndex); + multipartUploadRequest.startUpload(); + Toast.makeText(context, "Uploading file(s) in the background...", Toast.LENGTH_SHORT).show(); return true; } catch (Exception exception) { Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); @@ -1006,7 +1006,7 @@ public class UploadActivity extends BaseActivity { } } - public static class ZipTask extends AsyncTask { + public 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, 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 0671b7e3..a4ea57dd 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -921,22 +921,22 @@ public abstract class BaseActivity extends AppCompatActivity { progressDialogBuilder.setView(progressDialogLayout); uploadsProgressDialog = progressDialogBuilder.create(); - if (!UploadService.getTaskList().contains("" + dialogUploadID)) { + if (!UploadService.getTaskList().contains(dialogUploadID)) { //Upload probably failed at this point - uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Retry", (progressDialog, progressWhich) -> { + uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "", (progressDialog, progressWhich) -> { /*LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context.getApplicationContext()); localBroadcastManager.sendBroadcast(multipartUploadRetryIntent);*/ - uploadsProgressDialog.dismiss(); + //uploadsProgressDialog.dismiss(); //context.sendBroadcast(retryIntent); }); - uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Cancel", (progressDialog, progressWhich) -> { + uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), (progressDialog, progressWhich) -> { uploadsProgressDialog.dismiss(); }); TextView dialogProgressText = progressDialogLayout.findViewById(R.id.dialog_upload_progress_text); dialogProgressBar.setVisibility(View.GONE); - dialogProgressText.setText("Upload failed."); + dialogProgressText.setText(getString(R.string.upload_failed)); uploadsProgressDialog.show(); } else { diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index 6c978dda..1406eb57 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -59,6 +59,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { switch (intentAction) { case ACTION_CANCEL_UPLOAD: String uploadID = intentBundle.getString(UPLOAD_ID_KEY); + Timber.d("Received ACTION_CANCEL_UPLOAD (id: %s)", uploadID); UploadService.stopUpload(uploadID); break; /*case ACTION_RETRY_UPLOAD: @@ -86,6 +87,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { @Override public void onProgress(Context context, UploadInfo uploadInfo) { + Timber.i("Upload in progress (id: %s)",uploadInfo.getUploadId()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && uploadInfo.getUploadId().equals(dialogUploadID) && uploadProgressDialog != null) { @@ -94,8 +96,9 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { alertDialogNeutral.setOnClickListener(v -> uploadProgressDialog.dismiss()); Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - alertDialogNegative.setText(R.string.upload_cancel); + alertDialogNegative.setText(R.string.cancel); alertDialogNegative.setOnClickListener(v -> { + Timber.d("Cancelling upload (id: %s)", dialogUploadID); UploadService.stopUpload(dialogUploadID); uploadProgressDialog.dismiss(); }); @@ -124,6 +127,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { @Override public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, Exception exception) { + Timber.i("Error while uploading (id: %s)",uploadInfo.getUploadId()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && uploadInfo.getUploadId().equals(dialogUploadID) && uploadProgressDialog != null) { @@ -137,13 +141,9 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { });*/ Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - alertDialogNegative.setText(R.string.upload_cancel); + alertDialogNegative.setText(R.string.cancel); alertDialogNegative.setOnClickListener(v -> { - NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). - getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) { - notificationManager.cancel(uploadInfo.getNotificationID()); - } + cancelNotification(context, uploadInfo.getNotificationID()); UploadsHelper.deleteTempFiles(storage); uploadProgressDialog.dismiss(); }); @@ -163,12 +163,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { } } } else { - NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). - getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) { - notificationManager.cancel(uploadInfo.getNotificationID()); - } - + cancelNotification(context, uploadInfo.getNotificationID()); Intent combinedActionsIntent = new Intent(UploadsReceiver.ACTION_COMBINED_UPLOAD); combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadInfo.getUploadId()); context.sendBroadcast(combinedActionsIntent); @@ -189,6 +184,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { String response = serverResponse.getBodyAsString(); if(response.contains("Η προσθήκη του αρχείου ήταν επιτυχημένη.")||response.contains("The upload was successful.")){ + Timber.i("Upload completed successfully (id: %s)",uploadInfo.getUploadId()); Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); } @@ -207,21 +203,17 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { @Override public void onCancelled(Context context, UploadInfo uploadInfo) { + Timber.i("Upload cancelled (id: %s)", uploadInfo.getUploadId()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { uploadProgressDialog = null; dialogUploadID = null; } - Toast.makeText(context.getApplicationContext(), R.string.upload_canceled, Toast.LENGTH_SHORT).show(); - if (storage == null) { + Toast.makeText(context.getApplicationContext(), R.string.upload_cancelled, 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()); - }*/ + //cancelNotification(context, uploadInfo.getNotificationID()); UploadsHelper.deleteTempFiles(storage); } @@ -231,4 +223,11 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { UploadsReceiver.dialogUploadID = dialogUploadID; //UploadsReceiver.multipartUploadRetryIntent = multipartUploadRetryIntent; } + + private void cancelNotification(Context context, int notificationId){ + NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). + getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) + notificationManager.cancel(notificationId); + } } \ No newline at end of file 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 6c23115e..bac3952a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -17,8 +17,8 @@ import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask; import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; -import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyResult; +import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.RemoveVoteTask; import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.SubmitVoteTask; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2a32d992..fabd3e8a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,7 +13,7 @@ Bookmarks Info OK - Cancel + "Cancel" "To use mTHMMY you have to agree to our Privacy Policy by choosing one of the options below. Choose \"Yes, I want to help\", if you consent to the collection of anonymized data that will help us improve the app. Otherwise, choose \"Nope, leave me alone\". You can change your preferences any time through the app's Settings. user_consent_shared_preference_key @@ -145,9 +145,8 @@ Uploading Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes "Upload failed" - "Upload canceled" + "Upload canceled" "Resume in background" - "Cancel" "Retry"