From 3dcc791cba61dba5454b6dc801f70b814fde73c3 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Tue, 22 Jun 2021 16:06:14 -0400 Subject: [PATCH] convert downloadutils to kotlin --- .../awais/instagrabber/utils/BitmapUtils.kt | 4 +- .../awais/instagrabber/utils/DownloadUtils.kt | 924 +++++++++--------- .../awais/instagrabber/utils/MediaUploader.kt | 4 +- .../instagrabber/workers/DownloadWorker.kt | 12 +- 4 files changed, 460 insertions(+), 484 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt b/app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt index 4d6abced..6da17fd8 100644 --- a/app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt +++ b/app/src/main/java/awais/instagrabber/utils/BitmapUtils.kt @@ -193,9 +193,9 @@ object BitmapUtils { } @Throws(IOException::class) - fun convertToJpegAndSaveToFile(contentResolver: ContentResolver, bitmap: Bitmap, file: DocumentFile?): DocumentFile { + fun convertToJpegAndSaveToFile(contentResolver: ContentResolver, bitmap: Bitmap, file: DocumentFile?): DocumentFile? { val tempFile = file ?: DownloadUtils.getTempFile(null, "jpg") - contentResolver.openOutputStream(tempFile.uri).use { output -> + contentResolver.openOutputStream(tempFile!!.uri).use { output -> val compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output) if (!compressResult) { throw RuntimeException("Compression failed!") diff --git a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt index ae477814..a5576629 100644 --- a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt +++ b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.kt @@ -1,298 +1,248 @@ -package awais.instagrabber.utils; +package awais.instagrabber.utils -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.UriPermission; -import android.Manifest; -import android.net.Uri; -import android.util.Log; -import android.webkit.MimeTypeMap; -import android.widget.Toast; +import android.Manifest +import android.content.Context +import android.content.DialogInterface +import android.content.UriPermission +import android.net.Uri +import android.util.Log +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.core.util.Pair +import androidx.documentfile.provider.DocumentFile +import androidx.work.* +import awais.instagrabber.R +import awais.instagrabber.fragments.settings.PreferenceKeys +import awais.instagrabber.models.StoryModel +import awais.instagrabber.models.enums.MediaItemType +import awais.instagrabber.repositories.responses.Media +import awais.instagrabber.utils.TextUtils.isEmpty +import awais.instagrabber.workers.DownloadWorker +import com.google.gson.Gson +import java.io.BufferedWriter +import java.io.IOException +import java.io.OutputStreamWriter +import java.util.* +import java.util.regex.Pattern -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.core.util.Pair; -import androidx.documentfile.provider.DocumentFile; -import androidx.work.Constraints; -import androidx.work.Data; -import androidx.work.NetworkType; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import androidx.work.WorkRequest; +object DownloadUtils { + private val TAG = DownloadUtils::class.java.simpleName -import com.google.gson.Gson; - -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.regex.Pattern; - -import awais.instagrabber.R; -import awais.instagrabber.fragments.settings.PreferenceKeys; -import awais.instagrabber.models.StoryModel; -import awais.instagrabber.models.enums.MediaItemType; -import awais.instagrabber.repositories.responses.Audio; -import awais.instagrabber.repositories.responses.Media; -import awais.instagrabber.repositories.responses.User; -import awais.instagrabber.repositories.responses.MediaCandidate; -import awais.instagrabber.workers.DownloadWorker; - -import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_BARINSTA_DIR_URI; - -public final class DownloadUtils { - private static final String TAG = DownloadUtils.class.getSimpleName(); // private static final String DIR_BARINSTA = "Barinsta"; - private static final String DIR_DOWNLOADS = "Downloads"; - private static final String DIR_CAMERA = "Camera"; - private static final String DIR_EDIT = "Edit"; - private static final String DIR_RECORDINGS = "Sent Recordings"; - private static final String DIR_TEMP = "Temp"; - private static final String DIR_BACKUPS = "Backups"; - - private static DocumentFile root; - - public static final String WRITE_PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE; - public static final String[] PERMS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; - - public static void init(@NonNull final Context context, - @Nullable final String barinstaDirUri) throws ReselectDocumentTreeException { - if (TextUtils.isEmpty(barinstaDirUri)) { - throw new ReselectDocumentTreeException("folder path is null or empty"); + private const val DIR_DOWNLOADS = "Downloads" + private const val DIR_CAMERA = "Camera" + private const val DIR_EDIT = "Edit" + private const val DIR_RECORDINGS = "Sent Recordings" + private const val DIR_TEMP = "Temp" + private const val DIR_BACKUPS = "Backups" + private var root: DocumentFile? = null + const val WRITE_PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE + val PERMS = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) + @JvmStatic + @Throws(ReselectDocumentTreeException::class) + fun init( + context: Context, + barinstaDirUri: String? + ) { + if (isEmpty(barinstaDirUri)) { + throw ReselectDocumentTreeException("folder path is null or empty") } - if (!barinstaDirUri.startsWith("content://com.android.externalstorage.documents")) { + if (!barinstaDirUri!!.startsWith("content://com.android.externalstorage.documents")) { // reselect the folder in selector view - throw new ReselectDocumentTreeException(Uri.parse(barinstaDirUri)); + throw ReselectDocumentTreeException(Uri.parse(barinstaDirUri)) } - final Uri uri = Uri.parse(barinstaDirUri); - final List existingPermissions = context.getContentResolver().getPersistedUriPermissions(); + val uri = Uri.parse(barinstaDirUri) + val existingPermissions = context.contentResolver.persistedUriPermissions if (existingPermissions.isEmpty()) { // reselect the folder in selector view - throw new ReselectDocumentTreeException(uri); + throw ReselectDocumentTreeException(uri) } - final boolean anyMatch = existingPermissions.stream().anyMatch(uriPermission -> uriPermission.getUri().equals(uri)); + val anyMatch = existingPermissions.stream() + .anyMatch { uriPermission: UriPermission -> uriPermission.uri == uri } if (!anyMatch) { // reselect the folder in selector view - throw new ReselectDocumentTreeException(uri); + throw ReselectDocumentTreeException(uri) } - root = DocumentFile.fromTreeUri(context, uri); - if (root == null || !root.exists() || root.lastModified() == 0) { - root = null; - throw new ReselectDocumentTreeException(uri); + root = DocumentFile.fromTreeUri(context, uri) + if (root == null || !root!!.exists() || root!!.lastModified() == 0L) { + root = null + throw ReselectDocumentTreeException(uri) } - Utils.settingsHelper.putString(PREF_BARINSTA_DIR_URI, uri.toString()); + Utils.settingsHelper.putString(PreferenceKeys.PREF_BARINSTA_DIR_URI, uri.toString()) } - public static void destroy() { - root = null; + fun destroy() { + root = null } - @Nullable - public static DocumentFile getDownloadDir(final String... dirs) { + fun getDownloadDir(vararg dirs: String?): DocumentFile? { if (root == null) { - return null; + return null } - DocumentFile subDir = root; - if (dirs != null) { - for (final String dir : dirs) { - if (subDir == null || TextUtils.isEmpty(dir)) continue; - final DocumentFile subDirFile = subDir.findFile(dir); - final boolean exists = subDirFile != null && subDirFile.exists(); - subDir = exists ? subDirFile : subDir.createDirectory(dir); - } + var subDir = root + for (dir in dirs) { + if (subDir == null || isEmpty(dir)) continue + val subDirFile = subDir.findFile(dir!!) + val exists = subDirFile != null && subDirFile.exists() + subDir = if (exists) subDirFile else subDir.createDirectory(dir) } - return subDir; + return subDir } - @Nullable - public static DocumentFile getDownloadDir() { - // final File parent = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS); - // final File dir = new File(new File(parent, "barinsta"), "downloads"); - // if (!dir.exists()) { - // final boolean mkdirs = dir.mkdirs(); - // if (!mkdirs) { - // Log.e(TAG, "getDownloadDir: failed to create dir"); - // } - // } - // if (Utils.settingsHelper.getBoolean(FOLDER_SAVE_TO)) { - // final String customPath = Utils.settingsHelper.getString(FOLDER_PATH); - // if (!TextUtils.isEmpty(customPath)) { - // dir = new File(customPath); - // } - // } - return getDownloadDir(DIR_DOWNLOADS); + @JvmStatic + val downloadDir: DocumentFile? + get() = getDownloadDir(DIR_DOWNLOADS) + + @JvmStatic + fun getCameraDir(): DocumentFile? { + return getDownloadDir(DIR_CAMERA) } - @Nullable - public static DocumentFile getCameraDir() { - return getDownloadDir(DIR_CAMERA); + @JvmStatic + fun getImageEditDir(sessionId: String?): DocumentFile? { + return getDownloadDir(DIR_EDIT, sessionId) } - @Nullable - public static DocumentFile getImageEditDir(final String sessionId) { - return getDownloadDir(DIR_EDIT, sessionId); + fun getRecordingsDir(): DocumentFile? { + return getDownloadDir(DIR_RECORDINGS) } - @Nullable - public static DocumentFile getRecordingsDir() { - return getDownloadDir(DIR_RECORDINGS); - } - - @Nullable - public static DocumentFile getBackupsDir() { - return getDownloadDir(DIR_BACKUPS); + @JvmStatic + fun getBackupsDir(): DocumentFile? { + return getDownloadDir(DIR_BACKUPS) } // @Nullable // private static DocumentFile getDownloadDir(@NonNull final Context context, @Nullable final String username) { // return getDownloadDir(context, username, false); // } - - @Nullable - private static DocumentFile getDownloadDir(final Context context, - @Nullable final String username) { - final List userFolderPaths = getSubPathForUserFolder(username); - DocumentFile dir = root; - for (final String dirName : userFolderPaths) { - final DocumentFile file = dir.findFile(dirName); + private fun getDownloadDir( + context: Context?, + username: String? + ): DocumentFile? { + val userFolderPaths: List = getSubPathForUserFolder(username) + var dir = root + for (dirName in userFolderPaths) { + val file = dir!!.findFile(dirName) if (file != null) { - dir = file; - continue; + dir = file + continue } - dir = dir.createDirectory(dirName); - if (dir == null) break; + dir = dir.createDirectory(dirName) + if (dir == null) break } // final String joined = android.text.TextUtils.join("/", userFolderPaths); // final Uri userFolderUri = DocumentsContract.buildDocumentUriUsingTree(root.getUri(), joined); // final DocumentFile userFolder = DocumentFile.fromSingleUri(context, userFolderUri); if (context != null && (dir == null || !dir.exists())) { - Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); - return null; + Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show() + return null } - return dir; + return dir } - private static List getSubPathForUserFolder(final String username) { - final List list = new ArrayList<>(); - if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) || TextUtils.isEmpty(username)) { - list.add(DIR_DOWNLOADS); - return list; + private fun getSubPathForUserFolder(username: String?): MutableList { + val list: MutableList = ArrayList() + if (!Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_USER_FOLDER) || isEmpty( + username + ) + ) { + list.add(DIR_DOWNLOADS) + return list } - final String finalUsername = username.startsWith("@") ? username.substring(1) : username; - list.add(DIR_DOWNLOADS); - list.add(finalUsername); - return list; + val finalUsername = if (username!!.startsWith("@")) username.substring(1) else username + list.add(DIR_DOWNLOADS) + list.add(finalUsername) + return list } - private static DocumentFile getTempDir() { - DocumentFile file = root.findFile(DIR_TEMP); + private fun getTempDir(): DocumentFile? { + var file = root!!.findFile(DIR_TEMP) if (file == null) { - file = root.createDirectory(DIR_TEMP); + file = root!!.createDirectory(DIR_TEMP) } - return file; + return file } - // public static void dmDownload(@NonNull final Context context, - // @Nullable final String username, - // final String modelId, - // final String url) { - // if (url == null) return; - // if (ContextCompat.checkSelfPermission(context, PERMS[0]) == PackageManager.PERMISSION_GRANTED) { - // dmDownloadImpl(context, username, modelId, url); - // } else if (context instanceof Activity) { - // ActivityCompat.requestPermissions((Activity) context, PERMS, 8020); - // } - // } - - // private static void dmDownloadImpl(@NonNull final Context context, - // @Nullable final String username, - // final String modelId, - // final String url) { - // final DocumentFile dir = getDownloadDir(context, username); - // if (dir != null && dir.exists()) { - // download(context, url, getDownloadSavePaths(dir, modelId, url)); - // return; - // } - // Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); - // } - - private static Pair, String> getDownloadSavePaths(final List paths, - final String postId, - final String displayUrl) { - return getDownloadSavePaths(paths, postId, "", displayUrl, ""); + private fun getDownloadSavePaths( + paths: MutableList, + postId: String?, + displayUrl: String? + ): Pair, String?>? { + return getDownloadSavePaths(paths, postId, "", displayUrl, "") } - private static Pair, String> getDownloadSavePaths(final List paths, - final String postId, - final String displayUrl, - final String username) { - return getDownloadSavePaths(paths, postId, "", displayUrl, username); + private fun getDownloadSavePaths( + paths: MutableList, + postId: String?, + displayUrl: String, + username: String + ): Pair, String?>? { + return getDownloadSavePaths(paths, postId, "", displayUrl, username) } - private static Pair, String> getDownloadChildSavePaths(final List paths, - final String postId, - final int childPosition, - final String url, - final String username) { - final String sliderPostfix = "_slide_" + childPosition; - return getDownloadSavePaths(paths, postId, sliderPostfix, url, username); + private fun getDownloadChildSavePaths( + paths: MutableList, + postId: String?, + childPosition: Int, + url: String?, + username: String + ): Pair, String?>? { + val sliderPostfix = "_slide_$childPosition" + return getDownloadSavePaths(paths, postId, sliderPostfix, url, username) } - private static Pair, String> getDownloadSavePaths(final List paths, - final String postId, - final String sliderPostfix, - final String displayUrl, - final String username) { - if (paths == null) return null; - final String extension = getFileExtensionFromUrl(displayUrl); - final String usernamePrepend = TextUtils.isEmpty(username) ? "" : (username + "_"); - final String fileName = usernamePrepend + postId + sliderPostfix + extension; + private fun getDownloadSavePaths( + paths: MutableList?, + postId: String?, + sliderPostfix: String, + displayUrl: String?, + username: String + ): Pair, String?>? { + if (paths == null) return null + val extension = getFileExtensionFromUrl(displayUrl) + val usernamePrepend = if (isEmpty(username)) "" else username + "_" + val fileName = usernamePrepend + postId + sliderPostfix + extension // return new File(finalDir, fileName); // DocumentFile file = finalDir.findFile(fileName); // if (file == null) { - final String mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension.startsWith(".") ? extension.substring(1) : extension); + val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension( + if (extension.startsWith(".")) extension.substring(1) else extension + ) // file = finalDir.createFile(mimeType, fileName); // } - paths.add(fileName); - return new Pair<>(paths, mimeType); + paths.add(fileName) + return Pair(paths, mimeType) } // public static DocumentFile getTempFile() { // return getTempFile(null, null); // } - - public static DocumentFile getTempFile(final String fileName, final String extension) { - final DocumentFile dir = getTempDir(); - String name = fileName; - if (TextUtils.isEmpty(name)) { - name = UUID.randomUUID().toString(); + fun getTempFile(fileName: String?, extension: String): DocumentFile? { + val dir = getTempDir() + var name = fileName + if (isEmpty(name)) { + name = UUID.randomUUID().toString() } - String mimeType = "application/octet-stream"; - if (!TextUtils.isEmpty(extension)) { - name += "." + extension; - final String mimeType1 = Utils.mimeTypeMap.getMimeTypeFromExtension(extension); + var mimeType: String? = "application/octet-stream" + if (!isEmpty(extension)) { + name += ".$extension" + val mimeType1 = Utils.mimeTypeMap.getMimeTypeFromExtension(extension) if (mimeType1 != null) { - mimeType = mimeType1; + mimeType = mimeType1 } } - DocumentFile file = dir.findFile(name); + var file = dir!!.findFile(name!!) if (file == null) { - file = dir.createFile(mimeType, name); + file = dir.createFile(mimeType!!, name) } - return file; + return file } /** - * Copied from {@link MimeTypeMap#getFileExtensionFromUrl(String)}) - *

+ * Copied from [MimeTypeMap.getFileExtensionFromUrl]) + * + * * Returns the file extension or an empty string if there is no * extension. This method is a convenience method for obtaining the * extension of a url and has undefined results for other Strings. @@ -300,346 +250,372 @@ public final class DownloadUtils { * @param url URL * @return The file extension of the given url. */ - public static String getFileExtensionFromUrl(String url) { - if (!TextUtils.isEmpty(url)) { - int fragment = url.lastIndexOf('#'); + @JvmStatic + fun getFileExtensionFromUrl(url: String?): String { + var url = url + if (!isEmpty(url)) { + val fragment = url!!.lastIndexOf('#') if (fragment > 0) { - url = url.substring(0, fragment); + url = url.substring(0, fragment) } - - int query = url.lastIndexOf('?'); + val query = url.lastIndexOf('?') if (query > 0) { - url = url.substring(0, query); + url = url.substring(0, query) } - - int filenamePos = url.lastIndexOf('/'); - String filename = - 0 <= filenamePos ? url.substring(filenamePos + 1) : url; + val filenamePos = url.lastIndexOf('/') + val filename = if (0 <= filenamePos) url.substring(filenamePos + 1) else url // if the filename contains special characters, we don't // consider it valid for our matching purposes: if (!filename.isEmpty() && - Pattern.matches("[a-zA-Z_0-9.\\-()%]+", filename)) { - int dotPos = filename.lastIndexOf('.'); + Pattern.matches("[a-zA-Z_0-9.\\-()%]+", filename) + ) { + val dotPos = filename.lastIndexOf('.') if (0 <= dotPos) { - return filename.substring(dotPos); + return filename.substring(dotPos) } } } - - return ""; + return "" } - public static List checkDownloaded(@NonNull final Media media) { - final List checkList = new LinkedList<>(); - final User user = media.getUser(); - String username = "username"; + @JvmStatic + fun checkDownloaded(media: Media): List { + val checkList: MutableList = LinkedList() + val user = media.user + var username = "username" if (user != null) { - username = user.getUsername(); + username = user.username } - final List userFolderPaths = getSubPathForUserFolder(username); - switch (media.getMediaType()) { - case MEDIA_TYPE_IMAGE: - case MEDIA_TYPE_VIDEO: { - final String url = media.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO - ? ResponseBodyUtils.getVideoUrl(media) : ResponseBodyUtils.getImageUrl(media); - final Pair, String> file = getDownloadSavePaths(new ArrayList<>(userFolderPaths), media.getCode(), url, ""); - final boolean fileExists = file.first != null && checkPathExists(file.first); - boolean usernameFileExists = false; + val userFolderPaths: List = getSubPathForUserFolder(username) + when (media.mediaType) { + MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> { + val url = + if (media.mediaType == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl( + media + ) else ResponseBodyUtils.getImageUrl(media) + val file = getDownloadSavePaths(ArrayList(userFolderPaths), media.code, url, "") + val fileExists = file!!.first != null && checkPathExists( + file.first + ) + var usernameFileExists = false if (!fileExists) { - final Pair, String> usernameFile = getDownloadSavePaths( - new ArrayList<>(userFolderPaths), media.getCode(), url, username); - usernameFileExists = usernameFile.first != null && checkPathExists(usernameFile.first); + val usernameFile = getDownloadSavePaths( + ArrayList(userFolderPaths), media.code, url, username + ) + usernameFileExists = usernameFile!!.first != null && checkPathExists( + usernameFile.first + ) } - checkList.add(fileExists || usernameFileExists); - break; + checkList.add(fileExists || usernameFileExists) } - case MEDIA_TYPE_SLIDER: - final List sliderItems = media.getCarouselMedia(); - for (int i = 0; i < sliderItems.size(); i++) { - final Media child = sliderItems.get(i); - if (child == null) continue; - final String url = child.getMediaType() == MediaItemType.MEDIA_TYPE_VIDEO - ? ResponseBodyUtils.getVideoUrl(child) : ResponseBodyUtils.getImageUrl(child); - final Pair, String> file = getDownloadChildSavePaths( - new ArrayList<>(userFolderPaths), media.getCode(), i + 1, url, ""); - final boolean fileExists = file.first != null && checkPathExists(file.first); - boolean usernameFileExists = false; + MediaItemType.MEDIA_TYPE_SLIDER -> { + val sliderItems = media.carouselMedia + var i = 0 + while (i < sliderItems!!.size) { + val child = sliderItems[i] + val url = + if (child.mediaType == MediaItemType.MEDIA_TYPE_VIDEO) ResponseBodyUtils.getVideoUrl( + child + ) else ResponseBodyUtils.getImageUrl(child) + val file = getDownloadChildSavePaths( + ArrayList(userFolderPaths), media.code, i + 1, url, "" + ) + val fileExists = file!!.first != null && checkPathExists( + file.first + ) + var usernameFileExists = false if (!fileExists) { - final Pair, String> usernameFile = getDownloadChildSavePaths( - new ArrayList<>(userFolderPaths), media.getCode(), i + 1, url, username); - usernameFileExists = usernameFile.first != null && checkPathExists(usernameFile.first); + val usernameFile = getDownloadChildSavePaths( + ArrayList(userFolderPaths), media.code, i + 1, url, username + ) + usernameFileExists = usernameFile!!.first != null && checkPathExists( + usernameFile.first + ) } - checkList.add(fileExists || usernameFileExists); + checkList.add(fileExists || usernameFileExists) + i++ } - break; - default: - } - return checkList; - } - - private static boolean checkPathExists(@NonNull final List paths) { - if (root == null) return false; - DocumentFile dir = root; - for (final String path : paths) { - dir = dir.findFile(path); - if (dir == null || !dir.exists()) { - return false; + } + else -> { } } - return true; + return checkList } - public static void showDownloadDialog(@NonNull Context context, - @NonNull final Media feedModel, - final int childPosition) { - if (childPosition >= 0) { - final DialogInterface.OnClickListener clickListener = (dialog, which) -> { - switch (which) { - case 0: - DownloadUtils.download(context, feedModel, childPosition); - break; - case 1: - DownloadUtils.download(context, feedModel); - break; - case DialogInterface.BUTTON_NEGATIVE: - default: - dialog.dismiss(); - break; - } - }; - final String[] items = new String[]{ - context.getString(R.string.post_viewer_download_current), - context.getString(R.string.post_viewer_download_album), - }; - new AlertDialog.Builder(context) - .setTitle(R.string.post_viewer_download_dialog_title) - .setItems(items, clickListener) - .setNegativeButton(R.string.cancel, null) - .show(); - return; + private fun checkPathExists(paths: List): Boolean { + if (root == null) return false + var dir = root + for (path in paths) { + dir = dir!!.findFile(path) + if (dir == null || !dir.exists()) { + return false + } } - DownloadUtils.download(context, feedModel); + return true } - public static void download(@NonNull final Context context, - @NonNull final StoryModel storyModel) { - final DocumentFile downloadDir = getDownloadDir(context, storyModel.getUsername()); - if (downloadDir == null) return; - final String url = storyModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO - ? storyModel.getVideoUrl() - : storyModel.getStoryUrl(); - final String extension = DownloadUtils.getFileExtensionFromUrl(url); - final String baseFileName = storyModel.getStoryMediaId() + "_" - + storyModel.getTimestamp() + extension; - final String usernamePrepend = Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) - && storyModel.getUsername() != null ? storyModel.getUsername() + "_" : ""; - final String fileName = usernamePrepend + baseFileName; - DocumentFile saveFile = downloadDir.findFile(fileName); + @JvmStatic + fun showDownloadDialog( + context: Context, + feedModel: Media, + childPosition: Int + ) { + if (childPosition >= 0) { + val clickListener = + DialogInterface.OnClickListener { dialog: DialogInterface, which: Int -> + when (which) { + 0 -> download(context, feedModel, childPosition) + 1 -> download(context, feedModel) + DialogInterface.BUTTON_NEGATIVE -> dialog.dismiss() + else -> dialog.dismiss() + } + } + val items = arrayOf( + context.getString(R.string.post_viewer_download_current), + context.getString(R.string.post_viewer_download_album) + ) + AlertDialog.Builder(context) + .setTitle(R.string.post_viewer_download_dialog_title) + .setItems(items, clickListener) + .setNegativeButton(R.string.cancel, null) + .show() + return + } + download(context, feedModel) + } + + @JvmStatic + fun download( + context: Context, + storyModel: StoryModel + ) { + val downloadDir = getDownloadDir(context, storyModel.username) ?: return + val url = + if (storyModel.itemType == MediaItemType.MEDIA_TYPE_VIDEO) storyModel.videoUrl else storyModel.storyUrl + val extension = getFileExtensionFromUrl(url) + val baseFileName = (storyModel.storyMediaId + "_" + + storyModel.timestamp + extension) + val usernamePrepend = + if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) + && storyModel.username != null + ) storyModel.username + "_" else "" + val fileName = usernamePrepend + baseFileName + var saveFile = downloadDir.findFile(fileName) if (saveFile == null) { - final String mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension(extension.startsWith(".") ? extension.substring(1) : extension); - if (mimeType == null) return; - saveFile = downloadDir.createFile(mimeType, fileName); + val mimeType = Utils.mimeTypeMap.getMimeTypeFromExtension( + if (extension.startsWith(".")) extension.substring(1) else extension + ) + ?: return + saveFile = downloadDir.createFile(mimeType, fileName) } // final File saveFile = new File(downloadDir, fileName); - download(context, url, saveFile); + download(context, url, saveFile) } - public static void download(@NonNull final Context context, - @NonNull final Media feedModel) { - download(context, feedModel, -1); + @JvmOverloads + @JvmStatic + fun download( + context: Context, + feedModel: Media, + position: Int = -1 + ) { + download(context, listOf(feedModel), position) } - public static void download(@NonNull final Context context, - @NonNull final Media feedModel, - final int position) { - download(context, Collections.singletonList(feedModel), position); + @JvmStatic + fun download( + context: Context, + feedModels: List + ) { + download(context, feedModels, -1) } - public static void download(@NonNull final Context context, - @NonNull final List feedModels) { - download(context, feedModels, -1); - } - - private static void download(@NonNull final Context context, - @NonNull final List feedModels, - final int childPositionIfSingle) { - final Map map = new HashMap<>(); - for (final Media media : feedModels) { - final User mediaUser = media.getUser(); - final String username = mediaUser == null ? "" : mediaUser.getUsername(); - final List userFolderPaths = getSubPathForUserFolder(username); - // final DocumentFile downloadDir = getDownloadDir(context, mediaUser == null ? "" : mediaUser.getUsername()); - switch (media.getMediaType()) { - case MEDIA_TYPE_IMAGE: - case MEDIA_TYPE_VIDEO: { - final String url = getUrlOfType(media); - String fileName = media.getId(); - if (mediaUser != null && TextUtils.isEmpty(media.getCode())) { - fileName = mediaUser.getUsername() + "_" + fileName; + private fun download( + context: Context, + feedModels: List, + childPositionIfSingle: Int + ) { + val map: MutableMap = HashMap() + for (media in feedModels) { + val mediaUser = media.user + val username = mediaUser?.username ?: "" + val userFolderPaths = getSubPathForUserFolder(username) + when (media.mediaType) { + MediaItemType.MEDIA_TYPE_IMAGE, MediaItemType.MEDIA_TYPE_VIDEO -> { + val url = getUrlOfType(media) + var fileName = media.id + if (mediaUser != null && isEmpty(media.code)) { + fileName = mediaUser.username + "_" + fileName } - if (!TextUtils.isEmpty(media.getCode())) { - fileName = media.getCode(); + if (!isEmpty(media.code)) { + fileName = media.code if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) { - fileName = mediaUser.getUsername() + "_" + fileName; + fileName = mediaUser.username + "_" + fileName } } - final Pair, String> pair = getDownloadSavePaths(userFolderPaths, fileName, url); - final DocumentFile file = createFile(pair); - if (file == null) continue; - map.put(url, file); - break; + val pair = getDownloadSavePaths(userFolderPaths, fileName, url) + val file = createFile(pair!!) ?: continue + map[url!!] = file } - case MEDIA_TYPE_VOICE: { - final String url = getUrlOfType(media); - String fileName = media.getId(); + MediaItemType.MEDIA_TYPE_VOICE -> { + val url = getUrlOfType(media) + var fileName = media.id if (mediaUser != null) { - fileName = mediaUser.getUsername() + "_" + fileName; + fileName = mediaUser.username + "_" + fileName } - final Pair, String> pair = getDownloadSavePaths(userFolderPaths, fileName, url); - final DocumentFile file = createFile(pair); - if (file == null) continue; - map.put(url, file); - break; + val pair = getDownloadSavePaths(userFolderPaths, fileName, url) + val file = createFile(pair!!) ?: continue + map[url!!] = file } - case MEDIA_TYPE_SLIDER: - final List sliderItems = media.getCarouselMedia(); - for (int i = 0; i < sliderItems.size(); i++) { - if (childPositionIfSingle >= 0 && feedModels.size() == 1 && i != childPositionIfSingle) continue; - final Media child = sliderItems.get(i); - final String url = getUrlOfType(child); - final String usernamePrepend = Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null - ? mediaUser.getUsername() - : ""; - final Pair, String> pair = getDownloadChildSavePaths( - new ArrayList<>(userFolderPaths), media.getCode(), i + 1, url, usernamePrepend); - final DocumentFile file = createFile(pair); - if (file == null) continue; - map.put(url, file); + MediaItemType.MEDIA_TYPE_SLIDER -> { + val sliderItems = media.carouselMedia + var i = 0 + while (i < sliderItems!!.size) { + if (childPositionIfSingle >= 0 && feedModels.size == 1 && i != childPositionIfSingle) { + i++ + continue + } + val child = sliderItems[i] + val url = getUrlOfType(child) + val usernamePrepend = + if (Utils.settingsHelper.getBoolean(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) mediaUser.username else "" + val pair = getDownloadChildSavePaths( + ArrayList(userFolderPaths), media.code, i + 1, url, usernamePrepend + ) + val file = createFile(pair!!) + if (file == null) { + i++ + continue + } + map[url!!] = file + i++ } - break; - default: + } } } - if (map.isEmpty()) return; - download(context, map); + if (map.isEmpty()) return + download(context, map) } - @Nullable - private static DocumentFile createFile(@NonNull final Pair, String> pair) { - if (root == null) return null; - if (pair.first == null || pair.second == null) return null; - DocumentFile dir = root; - final List first = pair.first; - for (int i = 0; i < first.size(); i++) { - final String name = first.get(i); - final DocumentFile file = dir.findFile(name); + private fun createFile(pair: Pair, String?>): DocumentFile? { + if (root == null) return null + if (pair.first == null || pair.second == null) return null + var dir = root + val first = pair.first + for (i in first.indices) { + val name = first[i] + val file = dir!!.findFile(name) if (file != null) { - dir = file; - continue; + dir = file + continue } - dir = i == first.size() - 1 ? dir.createFile(pair.second, name) : dir.createDirectory(name); - if (dir == null) break; + dir = if (i == first.size - 1) dir.createFile( + pair.second!!, + name + ) else dir.createDirectory(name) + if (dir == null) break } - return dir; + return dir } - @Nullable - private static String getUrlOfType(@NonNull final Media media) { - switch (media.getMediaType()) { - case MEDIA_TYPE_IMAGE: { - return ResponseBodyUtils.getImageUrl(media); + private fun getUrlOfType(media: Media): String? { + when (media.mediaType) { + MediaItemType.MEDIA_TYPE_IMAGE -> { + return ResponseBodyUtils.getImageUrl(media) } - case MEDIA_TYPE_VIDEO: { - final List videoVersions = media.getVideoVersions(); - String url = null; + MediaItemType.MEDIA_TYPE_VIDEO -> { + val videoVersions = media.videoVersions + var url: String? = null if (videoVersions != null && !videoVersions.isEmpty()) { - final MediaCandidate videoVersion = videoVersions.get(0); - if (videoVersion != null) { - url = videoVersion.getUrl(); - } + url = videoVersions[0].url } - return url; + return url } - case MEDIA_TYPE_VOICE: { - final Audio audio = media.getAudio(); - String url = null; + MediaItemType.MEDIA_TYPE_VOICE -> { + val audio = media.audio + var url: String? = null if (audio != null) { - url = audio.getAudioSrc(); + url = audio.audioSrc } - return url; + return url } } - return null; + return null } - public static void download(final Context context, - final String url, - final DocumentFile filePath) { - if (context == null || url == null || filePath == null) return; - download(context, Collections.singletonMap(url, filePath)); + @JvmStatic + fun download( + context: Context?, + url: String?, + filePath: DocumentFile? + ) { + if (context == null || filePath == null) return + download(context, Collections.singletonMap(url!!, filePath)) } - private static void download(final Context context, final Map urlFilePathMap) { - if (context == null) return; - final Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build(); - final DownloadWorker.DownloadRequest request = DownloadWorker.DownloadRequest.builder() - .setUrlToFilePathMap(urlFilePathMap) - .build(); - final String requestJson = new Gson().toJson(request); - final DocumentFile tempFile = getTempFile(null, "json"); + private fun download(context: Context?, urlFilePathMap: Map) { + if (context == null) return + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + val request = DownloadWorker.DownloadRequest.builder() + .setUrlToFilePathMap(urlFilePathMap) + .build() + val requestJson = Gson().toJson(request) + val tempFile = getTempFile(null, "json") if (tempFile == null) { - Log.e(TAG, "download: temp file is null"); - return; + Log.e(TAG, "download: temp file is null") + return } - final Uri uri = tempFile.getUri(); - final ContentResolver contentResolver = context.getContentResolver(); - if (contentResolver == null) return; - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(contentResolver.openOutputStream(uri)))) { - writer.write(requestJson); - } catch (IOException e) { - Log.e(TAG, "download: Error writing request to file", e); - tempFile.delete(); - return; + val uri = tempFile.uri + val contentResolver = context.contentResolver ?: return + try { + BufferedWriter(OutputStreamWriter(contentResolver.openOutputStream(uri))).use { writer -> + writer.write( + requestJson + ) + } + } catch (e: IOException) { + Log.e(TAG, "download: Error writing request to file", e) + tempFile.delete() + return } - final WorkRequest downloadWorkRequest = new OneTimeWorkRequest.Builder(DownloadWorker.class) + val downloadWorkRequest: WorkRequest = + OneTimeWorkRequest.Builder(DownloadWorker::class.java) .setInputData( - new Data.Builder() - .putString(DownloadWorker.KEY_DOWNLOAD_REQUEST_JSON, tempFile.getUri().toString()) - .build() + Data.Builder() + .putString( + DownloadWorker.KEY_DOWNLOAD_REQUEST_JSON, + tempFile.uri.toString() + ) + .build() ) .setConstraints(constraints) .addTag("download") - .build(); + .build() WorkManager.getInstance(context) - .enqueue(downloadWorkRequest); + .enqueue(downloadWorkRequest) } - @Nullable - public static Uri getRootDirUri() { - return root != null ? root.getUri() : null; + @JvmStatic + fun getRootDirUri(): Uri? { + return if (root != null) root!!.uri else null } - public static class ReselectDocumentTreeException extends Exception { - private final Uri initialUri; + class ReselectDocumentTreeException : Exception { + val initialUri: Uri? - public ReselectDocumentTreeException() { - initialUri = null; + constructor() { + initialUri = null } - public ReselectDocumentTreeException(final String message) { - super(message); - initialUri = null; + constructor(message: String?) : super(message) { + initialUri = null } - public ReselectDocumentTreeException(final Uri initialUri) { - this.initialUri = initialUri; - } - - public Uri getInitialUri() { - return initialUri; + constructor(initialUri: Uri?) { + this.initialUri = initialUri } } -} +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/MediaUploader.kt b/app/src/main/java/awais/instagrabber/utils/MediaUploader.kt index 65fff446..e709eb1b 100644 --- a/app/src/main/java/awais/instagrabber/utils/MediaUploader.kt +++ b/app/src/main/java/awais/instagrabber/utils/MediaUploader.kt @@ -36,8 +36,8 @@ object MediaUploader { contentResolver: ContentResolver, bitmap: Bitmap, ): MediaUploadResponse = withContext(Dispatchers.IO) { - val file: DocumentFile = BitmapUtils.convertToJpegAndSaveToFile(contentResolver, bitmap, null) - val byteLength: Long = file.length() + val file: DocumentFile? = BitmapUtils.convertToJpegAndSaveToFile(contentResolver, bitmap, null) + val byteLength: Long = file!!.length() val options = createUploadPhotoOptions(byteLength) val headers = getUploadPhotoHeaders(options) val url = HOST + "/rupload_igphoto/" + options.name + "/" diff --git a/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt b/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt index 27c47c3a..d499c1b2 100644 --- a/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt +++ b/app/src/main/java/awais/instagrabber/workers/DownloadWorker.kt @@ -131,7 +131,7 @@ class DownloadWorker(context: Context, workerParams: WorkerParameters) : Corouti var totalRead = 0f try { BufferedInputStream(urlConnection.getInputStream()).use { bis -> - contentResolver.openOutputStream(outFile.uri).use { fos -> + contentResolver.openOutputStream(outFile!!.uri).use { fos -> val buffer = ByteArray(0x2000) var count: Int while (bis.read(buffer, 0, 0x2000).also { count = it } != -1) { @@ -146,11 +146,11 @@ class DownloadWorker(context: Context, workerParams: WorkerParameters) : Corouti } } } catch (e: Exception) { - Log.e(TAG, "Error while writing data from url: " + url + " to file: " + outFile.name, e) + Log.e(TAG, "Error while writing data from url: " + url + " to file: " + outFile!!.name, e) } if (isJpg) { try { - contentResolver.openInputStream(outFile.uri).use { fis -> + contentResolver.openInputStream(outFile!!.uri).use { fis -> contentResolver.openOutputStream(filePath.uri).use { fos -> val jpegIptcRewriter = JpegIptcRewriter() jpegIptcRewriter.removeIPTC(fis, fos) @@ -158,10 +158,10 @@ class DownloadWorker(context: Context, workerParams: WorkerParameters) : Corouti } } catch (e: Exception) { Log.e(TAG, "Error while removing iptc: url: " + url - + ", tempFile: " + outFile.name + + ", tempFile: " + outFile!!.name + ", finalFile: " + filePath.name, e) } - val deleted = outFile.delete() + val deleted = outFile!!.delete() if (!deleted) { Log.w(TAG, "download: tempFile not deleted!") } @@ -393,7 +393,7 @@ class DownloadWorker(context: Context, workerParams: WorkerParameters) : Corouti class Builder { private var urlToFilePathMap: MutableMap = mutableMapOf() - fun setUrlToFilePathMap(urlToFilePathMap: MutableMap): Builder { + fun setUrlToFilePathMap(urlToFilePathMap: Map): Builder { this.urlToFilePathMap = urlToFilePathMap .mapValues { it.value.uri.toString() } .toMutableMap()