From 265b0705bd666a3a50ac95bec7fd1688a34a5356 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Fri, 22 Jun 2018 11:33:48 +0300 Subject: [PATCH] Improved downloading (overhauled) --- app/build.gradle | 5 +- app/src/main/AndroidManifest.xml | 13 - .../activities/board/BoardActivity.java | 4 +- .../downloads/DownloadsActivity.java | 127 ++++++---- .../downloads/DownloadsAdapter.java | 4 +- .../activities/main/forum/ForumFragment.java | 4 +- .../main/recent/RecentFragment.java | 4 +- .../main/unread/UnreadFragment.java | 4 +- .../mthmmy/activities/topic/TopicAdapter.java | 2 +- .../gr/thmmy/mthmmy/base/BaseActivity.java | 73 +++++- .../java/gr/thmmy/mthmmy/model/Download.java | 9 + .../java/gr/thmmy/mthmmy/model/ThmmyPage.java | 2 - .../thmmy/mthmmy/services/DownloadHelper.java | 73 ++++++ .../services/downloads/DownloadsReceiver.java | 88 ------- .../services/downloads/DownloadsService.java | 225 ------------------ .../thmmy/mthmmy/session/SessionManager.java | 10 + .../java/gr/thmmy/mthmmy/utils/FileUtils.java | 26 ++ .../thmmy/mthmmy/utils/parsing/ParseTask.java | 7 +- .../res/layout/download_prompt_dialog.xml | 48 ++++ app/src/main/res/values/strings.xml | 11 +- app/src/main/res/xml/provider_paths.xml | 8 +- build.gradle | 1 - 22 files changed, 338 insertions(+), 410 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/services/DownloadHelper.java delete mode 100644 app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsReceiver.java delete mode 100644 app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsService.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java create mode 100644 app/src/main/res/layout/download_prompt_dialog.xml diff --git a/app/build.gradle b/app/build.gradle index d8a01d0d..3b463434 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,6 @@ apply plugin: 'io.fabric' android { compileSdkVersion 27 - buildToolsVersion "27.0.3" defaultConfig { vectorDrawables.useSupportLibrary = true @@ -35,9 +34,9 @@ dependencies { implementation 'com.android.support:support-v4:27.1.1' implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.android.support:recyclerview-v7:27.1.1' - implementation 'com.google.firebase:firebase-core:16.0.0' + implementation 'com.google.firebase:firebase-core:16.0.1' implementation 'com.google.firebase:firebase-messaging:17.0.0' - implementation 'com.crashlytics.sdk.android:crashlytics:2.9.3' + implementation 'com.crashlytics.sdk.android:crashlytics:2.9.4' implementation 'com.squareup.okhttp3:okhttp:3.10.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 bca9ca45..c9bfa2af 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -116,9 +116,6 @@ android:resource="@xml/provider_paths" /> - @@ -126,16 +123,6 @@ - - - - - - - \ No newline at end of file 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 c6620218..251c7c2b 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 @@ -25,8 +25,8 @@ import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.Topic; -import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseException; +import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import timber.log.Timber; @@ -298,7 +298,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo } @Override - protected void postParsing(ResultCode result) { + protected void postExecution(ResultCode result) { //TODO if (result == ResultCode.SUCCESS)... if (boardTitle == null || Objects.equals(boardTitle, "") || !Objects.equals(boardTitle, parsedTitle)) { 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 6d899d23..93d92f1a 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 @@ -20,11 +20,15 @@ import java.util.Objects; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.base.BaseActivity; +import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.model.Download; import gr.thmmy.mthmmy.model.ThmmyPage; -import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseException; +import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; import timber.log.Timber; public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener { @@ -158,13 +162,16 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter. /** * An {@link ParseTask} that handles asynchronous fetching of a downloads page and parsing it's - * data. {@link ParseTask#postParsing(ResultCode) postParsing} method calls {@link RecyclerView#swapAdapter} + * data. {@link ParseTask#postExecution(ResultCode) postExecution} method calls {@link RecyclerView#swapAdapter} * to build graphics. *

*

Calling TopicTask's {@link ParseTask#execute execute} method needs to have profile's url * as String parameter!

*/ private class ParseDownloadPageTask extends ParseTask { + private Download.DownloadItemType type; + private Download download; + @Override protected void onPreExecute() { if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE); @@ -173,69 +180,89 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter. @Override protected void parse(Document downloadPage) throws ParseException { - if (downloadsTitle == null || Objects.equals(downloadsTitle, "")) - downloadsTitle = downloadPage.select("div.nav>b>a.nav").last().text(); + try{ + if (downloadsTitle == null || Objects.equals(downloadsTitle, "")) + downloadsTitle = downloadPage.select("div.nav>b>a.nav").last().text(); - //Removes loading item - if (isLoadingMore) { - if (parsedDownloads.size() > 0) parsedDownloads.remove(parsedDownloads.size() - 1); - } - - Download.DownloadItemType type; - if (ThmmyPage.resolvePageCategory(Uri.parse(url)).is(ThmmyPage. - PageCategory.DOWNLOADS_CATEGORY)) - type = Download.DownloadItemType.DOWNLOADS_CATEGORY; - else type = Download.DownloadItemType.DOWNLOADS_FILE; - - Elements pages = downloadPage.select("a.navPages"); - if (pages != null) { - for (Element page : pages) { - int pageNumber = Integer.parseInt(page.text()); - if (pageNumber > numberOfPages) numberOfPages = pageNumber; + //Removes loading item + if (isLoadingMore) { + if (parsedDownloads.size() > 0) parsedDownloads.remove(parsedDownloads.size() - 1); } - } else numberOfPages = 1; - - Elements rows = downloadPage.select("table.tborder>tbody>tr"); - if (type == Download.DownloadItemType.DOWNLOADS_CATEGORY) { - Elements navigationLinks = downloadPage.select("div.nav>b"); - for (Element row : rows) { - if (row.select("td").size() == 1) continue; - - String url = row.select("b>a").first().attr("href"), - title = row.select("b>a").first().text(), - subtitle = row.select("div.smalltext:not(:has(a))").text(); - if (!row.select("td").last().hasClass("windowbg2")) { - if (navigationLinks.size() < 4) { - - parsedDownloads.add(new Download(type, url, title, subtitle, null, - true, null)); + + if (ThmmyPage.resolvePageCategory(Uri.parse(url)).is(ThmmyPage.PageCategory.DOWNLOADS_CATEGORY)) + type = Download.DownloadItemType.DOWNLOADS_CATEGORY; + else + type = Download.DownloadItemType.DOWNLOADS_FILE; + + Elements pages = downloadPage.select("a.navPages"); + if (pages != null) { + for (Element page : pages) { + int pageNumber = Integer.parseInt(page.text()); + if (pageNumber > numberOfPages) numberOfPages = pageNumber; + } + } else numberOfPages = 1; + + Elements rows = downloadPage.select("table.tborder>tbody>tr"); + if (type == Download.DownloadItemType.DOWNLOADS_CATEGORY) { + Elements navigationLinks = downloadPage.select("div.nav>b"); + for (Element row : rows) { + if (row.select("td").size() == 1) continue; + + String url = row.select("b>a").first().attr("href"), + title = row.select("b>a").first().text(), + subtitle = row.select("div.smalltext:not(:has(a))").text(); + if (!row.select("td").last().hasClass("windowbg2")) { + if (navigationLinks.size() < 4) { + + parsedDownloads.add(new Download(type, url, title, subtitle, null, + true, null)); + } else { + String stats = row.text(); + stats = stats.replace(title, "").replace(subtitle, "").trim(); + parsedDownloads.add(new Download(type, url, title, subtitle, stats, + false, null)); + } } else { String stats = row.text(); stats = stats.replace(title, "").replace(subtitle, "").trim(); parsedDownloads.add(new Download(type, url, title, subtitle, stats, false, null)); } - } else { - String stats = row.text(); - stats = stats.replace(title, "").replace(subtitle, "").trim(); - parsedDownloads.add(new Download(type, url, title, subtitle, stats, - false, null)); } + } else { + download = new Download(type, + rows.select("b>a").first().attr("href"), + rows.select("b>a").first().text(), + rows.select("div.smalltext:not(:has(a))").text(), + rows.select("span:not(:has(a))").first().text(), + false, + rows.select("span:has(a)").first().text()); + parsedDownloads.add(download); } - } else { - parsedDownloads.add(new Download(type, - rows.select("b>a").first().attr("href"), - rows.select("b>a").first().text(), - rows.select("div.smalltext:not(:has(a))").text(), - rows.select("span:not(:has(a))").first().text(), - false, - rows.select("span:has(a)").first().text())); + }catch(Exception e){ + throw new ParseException("Parsing failed (DownloadsActivity)"); } } + @Override + protected void postParsing() { + if (type == Download.DownloadItemType.DOWNLOADS_FILE) { + OkHttpClient client = BaseApplication.getInstance().getClient(); + String fileName = null; + try { + Response response = client.newCall(new Request.Builder().url(download.getUrl()).build()).execute(); + String contentDisposition = response.headers("Content-Disposition").toString(); //check if link provides an attachment + if (contentDisposition.contains("attachment")) + fileName = contentDisposition.split("\"")[1]; + download.setFileName(fileName); + } catch (Exception e) { + Timber.e(e, "Couldn't extract fileName."); + } + } + } @Override - protected void postParsing(ResultCode result) { + protected void postExecution(ResultCode result) { if (downloadsTitle != null && !downloadsTitle.equals("") && !downloadsTitle.equals("Αρχεία για λήψη") && toolbar.getTitle() != downloadsTitle) 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 7a7bf17f..5395b447 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 @@ -127,8 +127,8 @@ class DownloadsAdapter extends RecyclerView.Adapter { @Override public void onClick(View view) { try { - ((BaseActivity) context).launchDownloadService(new ThmmyFile( - new URL(download.getUrl()), null, null)); + ((BaseActivity) context).downloadFile(new ThmmyFile( + new URL(download.getUrl()), download.getFileName(), null)); } catch (MalformedURLException e) { e.printStackTrace(); } 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 02da2734..3b6366b4 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 @@ -27,8 +27,8 @@ import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Category; import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.utils.CustomRecyclerView; -import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseException; +import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import okhttp3.HttpUrl; import okhttp3.Request; @@ -215,7 +215,7 @@ public class ForumFragment extends BaseFragment { } @Override - protected void postParsing(ParseTask.ResultCode result) { + protected void postExecution(ParseTask.ResultCode result) { if (result == ResultCode.SUCCESS) forumAdapter.notifyParentDataSetChanged(false); 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 5dab8c1e..aee35e37 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 @@ -24,8 +24,8 @@ import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.utils.CustomRecyclerView; -import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseException; +import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import timber.log.Timber; @@ -190,7 +190,7 @@ public class RecentFragment extends BaseFragment { } @Override - protected void postParsing(ParseTask.ResultCode result) { + protected void postExecution(ParseTask.ResultCode result) { if (result == ResultCode.SUCCESS) { topicSummaries.clear(); 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 7b42fb20..98be97e2 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 @@ -25,8 +25,8 @@ import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.utils.CustomRecyclerView; -import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseException; +import gr.thmmy.mthmmy.utils.parsing.ParseTask; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import okhttp3.Request; import timber.log.Timber; @@ -204,7 +204,7 @@ public class UnreadFragment extends BaseFragment { } @Override - protected void postParsing(ParseTask.ResultCode result) { + protected void postExecution(ParseTask.ResultCode result) { if (result == ResultCode.SUCCESS) unreadAdapter.notifyDataSetChanged(); 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 40bb41f1..54560b58 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 @@ -238,7 +238,7 @@ class TopicAdapter extends RecyclerView.Adapter { attached.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - ((BaseActivity) context).launchDownloadService(attachedFile); + ((BaseActivity) context).downloadFile(attachedFile); } }); 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 833f7028..d5cb13ec 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -7,16 +7,21 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.design.widget.BottomSheetDialog; import android.support.v4.content.ContextCompat; +import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; +import android.widget.Button; import android.widget.ImageButton; +import android.widget.TextView; import android.widget.Toast; import com.google.firebase.messaging.FirebaseMessaging; @@ -32,6 +37,7 @@ import com.mikepenz.materialdrawer.model.ProfileDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IProfile; +import java.io.File; import java.util.ArrayList; import gr.thmmy.mthmmy.R; @@ -43,9 +49,11 @@ import gr.thmmy.mthmmy.activities.main.MainActivity; import gr.thmmy.mthmmy.activities.profile.ProfileActivity; import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.ThmmyFile; -import gr.thmmy.mthmmy.services.downloads.DownloadsService; +import gr.thmmy.mthmmy.services.DownloadHelper; import gr.thmmy.mthmmy.session.SessionManager; +import gr.thmmy.mthmmy.utils.FileUtils; import okhttp3.OkHttpClient; +import timber.log.Timber; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWNLOADS_TITLE; @@ -53,6 +61,8 @@ import static gr.thmmy.mthmmy.activities.downloads.DownloadsActivity.BUNDLE_DOWN import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL; import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL; import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME; +import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; +import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType; public abstract class BaseActivity extends AppCompatActivity { // Client & Cookies @@ -600,7 +610,7 @@ public abstract class BaseActivity extends AppCompatActivity { , @NonNull int[] grantResults) { switch (permsRequestCode) { case PERMISSIONS_REQUEST_CODE: - launchDownloadService(); + downloadFile(); break; } } @@ -609,9 +619,9 @@ public abstract class BaseActivity extends AppCompatActivity { //----------------------------------DOWNLOAD---------------------- private ThmmyFile tempThmmyFile; - public void launchDownloadService(ThmmyFile thmmyFile) { + public void downloadFile(ThmmyFile thmmyFile) { if (checkPerms()) - DownloadsService.startActionDownload(this, thmmyFile.getFileUrl().toString()); + prepareDownload(thmmyFile); else { tempThmmyFile = thmmyFile; requestPerms(); @@ -619,15 +629,64 @@ public abstract class BaseActivity extends AppCompatActivity { } //Uses temp file - called after permission grant - private void launchDownloadService() { + private void downloadFile() { if (checkPerms()) - DownloadsService.startActionDownload(this, tempThmmyFile.getFileUrl().toString()); + prepareDownload(tempThmmyFile); + } + + private void prepareDownload(ThmmyFile thmmyFile){ + String fileName = thmmyFile.getFilename(); + if(FileUtils.fileNameExists(fileName)) + openDownloadPrompt(thmmyFile); + else + DownloadHelper.enqueueDownload(thmmyFile); + } + + private void openDownloadPrompt(final ThmmyFile thmmyFile) { + View view = getLayoutInflater().inflate(R.layout.download_prompt_dialog, null); + final BottomSheetDialog dialog = new BottomSheetDialog(this); + dialog.setContentView(view); + TextView downloadPromptTextView = view.findViewById(R.id.downloadPromptTextView); + downloadPromptTextView.setText(getString(R.string.downloadPromptText,thmmyFile.getFilename())); + Button cancelButton = view.findViewById(R.id.cancel); + Button openButton = view.findViewById(R.id.open); + Button downloadButton = view.findViewById(R.id.download); + cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dialog.dismiss(); + } + }); + openButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dialog.dismiss(); + try{ + String fileName = thmmyFile.getFilename(); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_GRANT_READ_URI_PERMISSION); + Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", new File(SAVE_DIR, fileName)); + intent.setDataAndType(fileUri, getMimeType(fileName)); + BaseActivity.this.startActivity(intent); + }catch (Exception e){ + Timber.e(e,"Couldn't open downloaded file..."); + Toast.makeText(BaseActivity.this, "Couldn't open file...", Toast.LENGTH_SHORT).show(); + } + } + }); + downloadButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dialog.dismiss(); + DownloadHelper.enqueueDownload(thmmyFile); + } + }); + dialog.show(); } //----------------------------------MISC---------------------- protected void setMainActivity(MainActivity mainActivity) { this.mainActivity = mainActivity; } - } diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Download.java b/app/src/main/java/gr/thmmy/mthmmy/model/Download.java index 5216fea4..045129b8 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/Download.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/Download.java @@ -6,6 +6,7 @@ public class Download { private final String url, title, subTitle, statNumbers, extraInfo; private final boolean hasSubCategory; private final DownloadItemType type; + private String fileName; public Download() { type = null; @@ -55,4 +56,12 @@ public class Download { public boolean hasSubCategory() { return hasSubCategory; } + + public String getFileName(){ + return fileName; + } + + public void setFileName(String fileName){ + this.fileName = fileName; + } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java index aff0a38a..bc451fd1 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java +++ b/app/src/main/java/gr/thmmy/mthmmy/model/ThmmyPage.java @@ -3,8 +3,6 @@ package gr.thmmy.mthmmy.model; import android.net.Uri; import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import timber.log.Timber; diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/DownloadHelper.java b/app/src/main/java/gr/thmmy/mthmmy/services/DownloadHelper.java new file mode 100644 index 00000000..1e1a5c31 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/services/DownloadHelper.java @@ -0,0 +1,73 @@ +package gr.thmmy.mthmmy.services; + +import android.app.DownloadManager; +import android.content.Context; +import android.net.Uri; +import android.os.Environment; +import android.widget.Toast; + +import java.io.File; + +import gr.thmmy.mthmmy.base.BaseApplication; +import gr.thmmy.mthmmy.model.ThmmyFile; +import okhttp3.Cookie; +import timber.log.Timber; + +import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType; + +/** + * Not an actual service, but simply a helper class that adds a download to the queue of Android's + * DownloadManager system service. + */ +public class DownloadHelper { + public static final File SAVE_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); + + public static void enqueueDownload(ThmmyFile thmmyFile){ + Context applicationContext = BaseApplication.getInstance().getApplicationContext(); + Toast.makeText(applicationContext, "Download started!", Toast.LENGTH_SHORT).show(); + + try { + String fileName = renameFileIfExists(thmmyFile.getFilename()); + Uri downloadURI = Uri.parse(thmmyFile.getFileUrl().toString()); + + DownloadManager downloadManager = (DownloadManager)applicationContext.getSystemService(Context.DOWNLOAD_SERVICE); + DownloadManager.Request request = new DownloadManager.Request(downloadURI); + + Cookie thmmyCookie = BaseApplication.getInstance().getSessionManager().getThmmyCookie(); + request.addRequestHeader("Cookie", thmmyCookie.name() + "=" + thmmyCookie.value()); + request.setTitle(fileName); + request.setMimeType(getMimeType(fileName)); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + request.setDestinationInExternalPublicDir(SAVE_DIR.getName(), fileName); + request.allowScanningByMediaScanner(); + + downloadManager.enqueue(request); + } catch (Exception e) { + Toast.makeText(applicationContext, "Download failed...", Toast.LENGTH_SHORT).show(); + Timber.e(e, "Exception while enqueuing download."); + } + } + + private static String renameFileIfExists(String originalFileName) { + final String dirPath = SAVE_DIR.getAbsolutePath(); + File file = new File(dirPath, originalFileName); + + String nameFormat; + String[] tokens = originalFileName.split("\\.(?=[^.]+$)"); + + if (tokens.length != 2) { + Timber.w("Couldn't get file extension..."); + nameFormat = originalFileName + "(%d)"; + } else + nameFormat = tokens[0] + "-%d." + tokens[1]; + + for (int i = 1; ; i++) { + if (!file.isFile()) + break; + + file = new File(dirPath, String.format(nameFormat, i)); + } + + return file.getName(); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsReceiver.java deleted file mode 100644 index 535372a4..00000000 --- a/app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsReceiver.java +++ /dev/null @@ -1,88 +0,0 @@ -package gr.thmmy.mthmmy.services.downloads; - -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.support.v4.app.NotificationCompat; -import android.webkit.MimeTypeMap; - -import java.io.File; - -import timber.log.Timber; - -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.ACTION_DOWNLOAD; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.COMPLETED; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.EXTRA_DOWNLOAD_ID; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.EXTRA_DOWNLOAD_STATE; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.EXTRA_FILE_NAME; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.EXTRA_NOTIFICATION_TEXT; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.EXTRA_NOTIFICATION_TICKER; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.EXTRA_NOTIFICATION_TITLE; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.SAVE_DIR; -import static gr.thmmy.mthmmy.services.downloads.DownloadsService.STARTED; - -public class DownloadsReceiver extends BroadcastReceiver { - private static final String NOTIFICATION_TAG = "DOWNLOADS"; - private static final String DOWNLOADS_CHANNEL_ID = "Downloads"; - private static final String DOWNLOADS_CHANNEL_NAME = "Downloads"; - - public DownloadsReceiver() {} - - @Override - public void onReceive(Context context, Intent intent) { - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, DOWNLOADS_CHANNEL_ID); - NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - - if (intent.getAction().equals(ACTION_DOWNLOAD)) { - Bundle extras = intent.getExtras(); - int id = extras.getInt(EXTRA_DOWNLOAD_ID); - String state = extras.getString(EXTRA_DOWNLOAD_STATE, "NONE"); - String title = extras.getString(EXTRA_NOTIFICATION_TITLE); - String text = extras.getString(EXTRA_NOTIFICATION_TEXT); - String ticker = extras.getString(EXTRA_NOTIFICATION_TICKER); - - notificationBuilder.setContentTitle(title) - .setContentText(text) - .setTicker(ticker) - .setAutoCancel(true); - - if (state.equals(STARTED)) - notificationBuilder.setOngoing(true) - .setSmallIcon(android.R.drawable.stat_sys_download); - else if (state.equals(COMPLETED)) { - String fileName = extras.getString(EXTRA_FILE_NAME, "NONE"); - - File file = new File(SAVE_DIR, fileName); - if (file.exists()) { - String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension( - MimeTypeMap.getFileExtensionFromUrl(file.getAbsolutePath())); - - - Intent chooserIntent = new Intent(Intent.ACTION_VIEW); - chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - chooserIntent.setDataAndType(Uri.fromFile(file), type); - Intent chooser = Intent.createChooser(chooserIntent, "Open With..."); - - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, chooser, PendingIntent.FLAG_CANCEL_CURRENT); - notificationBuilder.setContentIntent(pendingIntent) - .setSmallIcon(android.R.drawable.stat_sys_download_done); - - } else - Timber.w("File doesn't exist."); - } - - // Since Android Oreo notification channel is needed. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - notificationManager.createNotificationChannel(new NotificationChannel(DOWNLOADS_CHANNEL_ID, DOWNLOADS_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH)); - - notificationManager.notify(NOTIFICATION_TAG, id, notificationBuilder.build()); - } - } - -} diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsService.java b/app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsService.java deleted file mode 100644 index 9692f5ea..00000000 --- a/app/src/main/java/gr/thmmy/mthmmy/services/downloads/DownloadsService.java +++ /dev/null @@ -1,225 +0,0 @@ -package gr.thmmy.mthmmy.services.downloads; - -import android.app.DownloadManager; -import android.app.IntentService; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Environment; -import android.support.annotation.NonNull; -import android.webkit.MimeTypeMap; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; - -import gr.thmmy.mthmmy.base.BaseApplication; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okio.BufferedSink; -import okio.Okio; -import timber.log.Timber; - -/** - * An {@link IntentService} subclass for handling asynchronous task requests in - * a service on a separate handler thread. - */ -public class DownloadsService extends IntentService { - private static final String TAG = "DownloadsService"; - private static int sDownloadId = 0; - - private DownloadsReceiver receiver; - - public static final String SAVE_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mthmmy"; - - public static final String ACTION_DOWNLOAD = "gr.thmmy.mthmmy.services.action.DOWNLOAD"; - public static final String EXTRA_DOWNLOAD_URL = "gr.thmmy.mthmmy.services.extra.DOWNLOAD_URL"; - - public static final String EXTRA_DOWNLOAD_ID = "gr.thmmy.mthmmy.services.extra.DOWNLOAD_ID"; - public static final String EXTRA_DOWNLOAD_STATE = "gr.thmmy.mthmmy.services.extra.DOWNLOAD_STATE"; - public static final String EXTRA_FILE_NAME = "gr.thmmy.mthmmy.services.extra.FILE_NAME"; - public static final String EXTRA_NOTIFICATION_TITLE = "gr.thmmy.mthmmy.services.extra.NOTIFICATION_TITLE"; - public static final String EXTRA_NOTIFICATION_TEXT = "gr.thmmy.mthmmy.services.extra.NOTIFICATION_TEXT"; - public static final String EXTRA_NOTIFICATION_TICKER = "gr.thmmy.mthmmy.services.extra.NOTIFICATION_TICKER"; - - public static final String STARTED = "Started"; - public static final String COMPLETED = "Completed"; - public static final String FAILED = "Failed"; - - - public DownloadsService() { - super("DownloadsService"); - } - - @Override - public void onCreate() { - super.onCreate(); - final IntentFilter filter = new IntentFilter(DownloadsService.ACTION_DOWNLOAD); - receiver = new DownloadsReceiver(); - registerReceiver(receiver, filter); - - } - - @Override - public void onDestroy() { - super.onDestroy(); - this.unregisterReceiver(receiver); - } - - /** - * Starts this service to perform action Download with the given parameters. If - * the service is already performing a task this action will be queued. - * - * @see IntentService - */ - public static void startActionDownload(Context context, String downloadUrl) { - Intent intent = new Intent(context, DownloadsService.class); - intent.setAction(ACTION_DOWNLOAD); - intent.putExtra(EXTRA_DOWNLOAD_URL, downloadUrl); - context.startService(intent); - } - - @Override - protected void onHandleIntent(Intent intent) { - if (intent != null) { - final String action = intent.getAction(); - if (ACTION_DOWNLOAD.equals(action)) { - final String downloadLink = intent.getStringExtra(EXTRA_DOWNLOAD_URL); - handleActionDownload(downloadLink); - } - } - } - - /** - * Handle action Foo in the provided background thread with the provided - * parameters. - */ - private void handleActionDownload(String downloadLink) { - OkHttpClient client = BaseApplication.getInstance().getClient(); - BufferedSink sink = null; - String fileName = "file"; - - int downloadId = sDownloadId; - sDownloadId++; - - try { - Request request = new Request.Builder().url(downloadLink).build(); - Response response = client.newCall(request).execute(); - - String contentDisposition = response.headers("Content-Disposition").toString(); //check if link provides an attachment - if (contentDisposition.contains("attachment")) { - fileName = contentDisposition.split("\"")[1]; - - File dirPath = new File(SAVE_DIR); - if (!dirPath.isDirectory()) { - if (dirPath.mkdirs()) - Timber.i("mTHMMY's directory created successfully!"); - else - Timber.e("Couldn't create mTHMMY's directory..."); - } - - - String nameFormat; - String[] tokens = fileName.split("\\.(?=[^\\.]+$)"); - - if (tokens.length != 2) { - Timber.w("Couldn't get file extension..."); - nameFormat = fileName + "(%d)"; - } else - nameFormat = tokens[0] + "(%d)." + tokens[1]; - - - File file = new File(dirPath, fileName); - - for (int i = 1; ; i++) { - if (!file.exists()) - break; - - file = new File(dirPath, String.format(nameFormat, i)); - } - - fileName = file.getName(); - - Timber.v("Started saving file %s", fileName); - sendNotification(downloadId, STARTED, fileName); - - sink = Okio.buffer(Okio.sink(file)); - sink.writeAll(response.body().source()); - sink.flush(); - Timber.i("Download OK!"); - sendNotification(downloadId, COMPLETED, fileName); - - // Register download - DownloadManager mManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); - long length = file.length(); - mManager.addCompletedDownload(fileName, fileName, false, getMimeType(file), SAVE_DIR +File.separator+ fileName, length, false); - - } else - Timber.e("No attachment in response!"); - } catch (FileNotFoundException e) { - Timber.i("Download failed..."); - Timber.e(e, "FileNotFound"); - sendNotification(downloadId, FAILED, fileName); - } catch (IOException e) { - Timber.i("Download failed..."); - Timber.e(e, "IOException"); - sendNotification(downloadId, FAILED, fileName); - } finally { - if (sink != null) { - try { - sink.close(); - } catch (IOException e) { - // Ignore - Significant errors should already have been reported - } - } - } - } - - private void sendNotification(int downloadId, String type, @NonNull String fileName) { - Intent intent = new Intent(ACTION_DOWNLOAD); - switch (type) { - case STARTED: { - intent.putExtra(EXTRA_NOTIFICATION_TITLE, "\"" + fileName + "\""); - intent.putExtra(EXTRA_NOTIFICATION_TEXT, "Download Started"); - intent.putExtra(EXTRA_NOTIFICATION_TICKER, "Downloading..."); - break; - } - case COMPLETED: { - intent.putExtra(EXTRA_NOTIFICATION_TITLE, "\"" + fileName + "\""); - intent.putExtra(EXTRA_NOTIFICATION_TEXT, "Download Completed"); - intent.putExtra(EXTRA_NOTIFICATION_TICKER, "Download Completed"); - break; - } - case FAILED: { - intent.putExtra(EXTRA_NOTIFICATION_TITLE, "\"" + fileName + "\""); - intent.putExtra(EXTRA_NOTIFICATION_TEXT, "Download Failed"); - intent.putExtra(EXTRA_NOTIFICATION_TICKER, "Download Failed"); - break; - } - default: { - Timber.e("Invalid notification case!"); - return; - } - } - intent.putExtra(EXTRA_DOWNLOAD_ID, downloadId); - intent.putExtra(EXTRA_DOWNLOAD_STATE, type); - intent.putExtra(EXTRA_FILE_NAME, fileName); - sendBroadcast(intent); - - } - - @NonNull - static String getMimeType(@NonNull File file) { - String type = null; - final String url = file.toString(); - final String extension = MimeTypeMap.getFileExtensionFromUrl(url); - if (extension != null) - type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); - if (type == null) - type = "*/*"; - - return type; - } - -} 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 94f8a284..9168ebf3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java +++ b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java @@ -254,6 +254,16 @@ public class SessionManager { return sharedPrefs.getString(AVATAR_LINK, AVATAR_LINK); } + public Cookie getThmmyCookie() { + List cookieList = cookieJar.loadForRequest(indexUrl); + for(Cookie cookie: cookieList) + { + if(cookie.name().equals("THMMYgrC00ki3")) + return cookie; + } + return null; + } + public boolean hasAvatar() { return sharedPrefs.getBoolean(HAS_AVATAR, false); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java new file mode 100644 index 00000000..9f2d4757 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java @@ -0,0 +1,26 @@ +package gr.thmmy.mthmmy.utils; + +import android.support.annotation.NonNull; +import android.webkit.MimeTypeMap; + +import java.io.File; + +import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; + +public class FileUtils { + @NonNull + public static String getMimeType(@NonNull String fileName) { + String type = null; + final String extension = MimeTypeMap.getFileExtensionFromUrl(fileName); + if (extension != null) + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); + if (type == null) + type = "*/*"; + + return type; + } + + public static boolean fileNameExists (String fileName) { + return fileName != null && (new File(SAVE_DIR.getAbsolutePath(), fileName)).isFile(); + } +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseTask.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseTask.java index 16b21583..4f335025 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseTask.java @@ -26,7 +26,9 @@ public abstract class ParseTask extends AsyncTask + + + + + + + +