diff --git a/build.gradle b/build.gradle index d21e6e10f..28cbc963c 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { propMinSdkVersion = 16 propTargetSdkVersion = propCompileSdkVersion propVersionCode = 1 - propVersionName = '3.11.47' + propVersionName = '3.11.48' kotlin_version = '1.2.21' support_libs = '27.0.2' } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/activities/BaseSimpleActivity.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/activities/BaseSimpleActivity.kt index 2bac70a08..1c8919893 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/activities/BaseSimpleActivity.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/activities/BaseSimpleActivity.kt @@ -183,8 +183,7 @@ open class BaseSimpleActivity : AppCompatActivity() { return } - val destinationFolder = File(destination) - if (!destinationFolder.exists() && getSomeDocumentFile(destination) == null) { + if (!doesFilePathExist(destination)) { toast(R.string.invalid_destination) return } @@ -203,6 +202,7 @@ open class BaseSimpleActivity : AppCompatActivity() { val updatedFiles = ArrayList(fileDirItems.size * 2) updatedFiles.addAll(fileDirItems) try { + val destinationFolder = File(destination) for (oldFileDirItem in fileDirItems) { val newFile = File(destinationFolder, oldFileDirItem.name) if (!newFile.exists() && File(oldFileDirItem.path).renameTo(newFile)) { @@ -244,15 +244,15 @@ open class BaseSimpleActivity : AppCompatActivity() { } val file = files[index] - val newFile = File(destinationFileDirItem.path, file.name) - if (newFile.exists()) { - FileConflictDialog(this, newFile) { resolution, applyForAll -> + val newFileDirItem = FileDirItem("${destinationFileDirItem.path}/${file.name}", file.name, file.isDirectory) + if (doesFilePathExist(newFileDirItem.path)) { + FileConflictDialog(this, newFileDirItem) { resolution, applyForAll -> if (applyForAll) { conflictResolutions.clear() conflictResolutions[""] = resolution checkConflict(files, destinationFileDirItem, files.size, conflictResolutions, callback) } else { - conflictResolutions[newFile.absolutePath] = resolution + conflictResolutions[newFileDirItem.path] = resolution checkConflict(files, destinationFileDirItem, index + 1, conflictResolutions, callback) } } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/asynctasks/CopyMoveTask.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/asynctasks/CopyMoveTask.kt index fae963493..6ea861805 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/asynctasks/CopyMoveTask.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/asynctasks/CopyMoveTask.kt @@ -232,9 +232,9 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal while (bytes >= 0) { out!!.write(buffer, 0, bytes) copiedSize += bytes + mCurrentProgress += bytes bytes = inputStream.read(buffer) } - mCurrentProgress += copiedSize if (source.size == copiedSize) { mTransferredFiles.add(source) diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/CreateNewFolderDialog.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/CreateNewFolderDialog.kt index 0747cf6f7..71b01a80c 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/CreateNewFolderDialog.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/CreateNewFolderDialog.kt @@ -12,7 +12,7 @@ import java.io.File class CreateNewFolderDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) { init { val view = activity.layoutInflater.inflate(R.layout.dialog_create_new_folder, null) - view.folder_path.text = activity.humanizePath(path).trimEnd('/') + "/" + view.folder_path.text = "${activity.humanizePath(path)}/" AlertDialog.Builder(activity) .setPositiveButton(R.string.ok, null) @@ -45,9 +45,12 @@ class CreateNewFolderDialog(val activity: BaseSimpleActivity, val path: String, when { activity.needsStupidWritePermissions(path) -> activity.handleSAFDialog(path) { try { - val documentFile = activity.getDocumentFile(path) - documentFile?.createDirectory(path.getFilenameFromPath()) - sendSuccess(alertDialog, path) + val documentFile = activity.getDocumentFile(path.getParentPath()) + if (documentFile?.createDirectory(path.getFilenameFromPath()) != null) { + sendSuccess(alertDialog, path) + } else { + activity.toast(R.string.unknown_error_occurred) + } } catch (e: SecurityException) { activity.showErrorToast(e) } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FileConflictDialog.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FileConflictDialog.kt index 409a7087b..7240cdb39 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FileConflictDialog.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FileConflictDialog.kt @@ -11,18 +11,18 @@ import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.helpers.CONFLICT_MERGE import com.simplemobiletools.commons.helpers.CONFLICT_OVERWRITE import com.simplemobiletools.commons.helpers.CONFLICT_SKIP +import com.simplemobiletools.commons.models.FileDirItem import kotlinx.android.synthetic.main.dialog_file_conflict.view.* -import java.io.File -class FileConflictDialog(val activity: Activity, val file: File, val callback: (resolution: Int, applyForAll: Boolean) -> Unit) { +class FileConflictDialog(val activity: Activity, val fileDirItem: FileDirItem, val callback: (resolution: Int, applyForAll: Boolean) -> Unit) { val view = activity.layoutInflater.inflate(R.layout.dialog_file_conflict, null)!! init { view.apply { - val stringBase = if (file.isDirectory) R.string.folder_already_exists else R.string.file_already_exists - conflict_dialog_title.text = String.format(activity.getString(stringBase), file.name) + val stringBase = if (fileDirItem.isDirectory) R.string.folder_already_exists else R.string.file_already_exists + conflict_dialog_title.text = String.format(activity.getString(stringBase), fileDirItem.name) conflict_dialog_apply_to_all.isChecked = activity.baseConfig.lastConflictApplyToAll - conflict_dialog_radio_merge.beVisibleIf(file.isDirectory) + conflict_dialog_radio_merge.beVisibleIf(fileDirItem.isDirectory) val resolutionButton = when (activity.baseConfig.lastConflictResolution) { CONFLICT_OVERWRITE -> conflict_dialog_radio_overwrite diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FilePickerDialog.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FilePickerDialog.kt index e53ff8019..5717f32a2 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FilePickerDialog.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FilePickerDialog.kt @@ -94,7 +94,7 @@ class FilePickerDialog(val activity: BaseSimpleActivity, private fun createNewFolder() { CreateNewFolderDialog(activity, currPath) { - callback(it.trimEnd('/')) + callback(it) mDialog.dismiss() } } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/PropertiesDialog.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/PropertiesDialog.kt index 771cac8f3..b850ac8cf 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/PropertiesDialog.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/PropertiesDialog.kt @@ -6,15 +6,16 @@ import android.media.ExifInterface import android.provider.MediaStore import android.support.v7.app.AlertDialog import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.TextView import com.simplemobiletools.commons.R import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.sumByInt import com.simplemobiletools.commons.helpers.sumByLong +import com.simplemobiletools.commons.models.FileDirItem import kotlinx.android.synthetic.main.dialog_properties.view.* import kotlinx.android.synthetic.main.property_item.view.* -import java.io.File import java.io.FileNotFoundException import java.util.* @@ -31,28 +32,33 @@ class PropertiesDialog() { * @param countHiddenItems toggle determining if we will count hidden files themselves and their sizes (reasonable only at directory properties) */ constructor(activity: Activity, path: String, countHiddenItems: Boolean = false) : this() { + if (!activity.doesFilePathExist(path)) { + activity.toast(String.format(activity.getString(R.string.source_file_doesnt_exist), path)) + return + } + mInflater = LayoutInflater.from(activity) mResources = activity.resources val view = mInflater.inflate(R.layout.dialog_properties, null) mPropertyView = view.properties_holder - val file = File(path) - addProperty(R.string.name, file.name) - addProperty(R.string.path, file.parent) + val fileDirItem = FileDirItem(path, path.getFilenameFromPath(), activity.getIsPathDirectory(path)) + addProperty(R.string.name, fileDirItem.name) + addProperty(R.string.path, fileDirItem.getParentPath()) addProperty(R.string.size, "…", R.id.properties_size) Thread { - val fileCount = file.getFileCount(countHiddenItems) - val size = file.getProperSize(countHiddenItems).formatSize() + val fileCount = fileDirItem.getProperFileCount(activity, countHiddenItems) + val size = fileDirItem.getProperSize(activity, countHiddenItems).formatSize() activity.runOnUiThread { view.findViewById(R.id.properties_size).property_value.text = size - if (file.isDirectory) { + if (fileDirItem.isDirectory) { view.findViewById(R.id.properties_file_count).property_value.text = fileCount.toString() } } - if (!file.isDirectory) { + if (!fileDirItem.isDirectory) { val projection = arrayOf(MediaStore.Images.Media.DATE_MODIFIED) val uri = MediaStore.Files.getContentUri("external") val selection = "${MediaStore.MediaColumns.DATA} = ?" @@ -60,37 +66,39 @@ class PropertiesDialog() { val cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null) cursor?.use { if (cursor.moveToFirst()) { - val dateModified = cursor.getIntValue(MediaStore.Images.Media.DATE_MODIFIED) - activity.runOnUiThread { - view.findViewById(R.id.properties_last_modified).property_value.text = (dateModified * 1000L).formatDate() - } + val dateModified = cursor.getLongValue(MediaStore.Images.Media.DATE_MODIFIED) + updateLastModified(activity, view, dateModified) + } else { + updateLastModified(activity, view, fileDirItem.getLastModified(activity)) } } } }.start() when { - file.isDirectory -> { - addProperty(R.string.direct_children_count, getDirectChildrenCount(file, countHiddenItems)) + fileDirItem.isDirectory -> { + addProperty(R.string.direct_children_count, fileDirItem.getDirectChildrenCount(activity, countHiddenItems).toString()) addProperty(R.string.files_count, "…", R.id.properties_file_count) } - file.isImageSlow() -> addProperty(R.string.resolution, file.getResolution().formatAsResolution()) - file.isAudioSlow() -> { - file.getDuration().let { addProperty(R.string.duration, it) } - file.getSongTitle()?.let { addProperty(R.string.song_title, it) } - file.getArtist()?.let { addProperty(R.string.artist, it) } - file.getAlbum()?.let { addProperty(R.string.album, it) } + fileDirItem.path.isImageSlow() -> { + fileDirItem.getResolution()?.let { addProperty(R.string.resolution, it.formatAsResolution()) } } - file.isVideoSlow() -> { - file.getDuration().let { addProperty(R.string.duration, it) } - addProperty(R.string.resolution, file.getResolution().formatAsResolution()) - file.getArtist()?.let { addProperty(R.string.artist, it) } - file.getAlbum()?.let { addProperty(R.string.album, it) } + fileDirItem.path.isAudioSlow() -> { + fileDirItem.getDuration()?.let { addProperty(R.string.duration, it) } + fileDirItem.getSongTitle()?.let { addProperty(R.string.song_title, it) } + fileDirItem.getArtist()?.let { addProperty(R.string.artist, it) } + fileDirItem.getAlbum()?.let { addProperty(R.string.album, it) } + } + fileDirItem.path.isVideoSlow() -> { + fileDirItem.getDuration()?.let { addProperty(R.string.duration, it) } + fileDirItem.getResolution()?.let { addProperty(R.string.resolution, it.formatAsResolution()) } + fileDirItem.getArtist()?.let { addProperty(R.string.artist, it) } + fileDirItem.getAlbum()?.let { addProperty(R.string.album, it) } } } - if (file.isDirectory) { - addProperty(R.string.last_modified, file.lastModified().formatDate()) + if (fileDirItem.isDirectory) { + addProperty(R.string.last_modified, fileDirItem.getLastModified(activity).formatDate()) } else { addProperty(R.string.last_modified, "…", R.id.properties_last_modified) try { @@ -108,6 +116,12 @@ class PropertiesDialog() { } } + private fun updateLastModified(activity: Activity, view: View, timestamp: Long) { + activity.runOnUiThread { + view.findViewById(R.id.properties_last_modified).property_value.text = timestamp.formatDate() + } + } + /** * A File Properties dialog constructor with an optional parameter, usable at multiple items selected * @@ -121,22 +135,25 @@ class PropertiesDialog() { val view = mInflater.inflate(R.layout.dialog_properties, null) mPropertyView = view.properties_holder - val files = ArrayList(paths.size) - paths.forEach { files.add(File(it)) } + val fileDirItems = ArrayList(paths.size) + paths.forEach { + val fileDirItem = FileDirItem(it, it.getFilenameFromPath(), activity.getIsPathDirectory(it)) + fileDirItems.add(fileDirItem) + } - val isSameParent = isSameParent(files) + val isSameParent = isSameParent(fileDirItems) addProperty(R.string.items_selected, paths.size.toString()) if (isSameParent) { - addProperty(R.string.path, files[0].parent) + addProperty(R.string.path, fileDirItems[0].getParentPath()) } addProperty(R.string.size, "…", R.id.properties_size) addProperty(R.string.files_count, "…", R.id.properties_file_count) Thread { - val fileCount = files.sumByInt { it.getFileCount(countHiddenItems) } - val size = files.sumByLong { it.getProperSize(countHiddenItems) }.formatSize() + val fileCount = fileDirItems.sumByInt { it.getProperFileCount(activity, countHiddenItems) } + val size = fileDirItems.sumByLong { it.getProperSize(activity, countHiddenItems) }.formatSize() activity.runOnUiThread { view.findViewById(R.id.properties_size).property_value.text = size view.findViewById(R.id.properties_file_count).property_value.text = fileCount.toString() @@ -168,10 +185,10 @@ class PropertiesDialog() { } } - private fun isSameParent(files: List): Boolean { - var parent = files[0].parent - for (file in files) { - val curParent = file.parent + private fun isSameParent(fileDirItems: List): Boolean { + var parent = fileDirItems[0].getParentPath() + for (file in fileDirItems) { + val curParent = file.getParentPath() if (curParent != parent) { return false } @@ -181,14 +198,6 @@ class PropertiesDialog() { return true } - private fun getDirectChildrenCount(file: File, countHiddenItems: Boolean): String { - return if (file.listFiles() == null) { - "0" - } else { - file.listFiles().filter { it != null && (!it.isHidden || (it.isHidden && countHiddenItems)) }.size.toString() - } - } - private fun addProperty(labelId: Int, value: String?, viewId: Int = 0) { if (value == null) return diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/RenameItemDialog.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/RenameItemDialog.kt index 91960697c..eb2142ce0 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/RenameItemDialog.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/RenameItemDialog.kt @@ -6,18 +6,16 @@ import com.simplemobiletools.commons.R import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.* import kotlinx.android.synthetic.main.dialog_rename_item.view.* -import java.io.File import java.util.* class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val callback: (newPath: String) -> Unit) { init { - val file = File(path) - val fullName = file.name + val fullName = path.getFilenameFromPath() val dotAt = fullName.lastIndexOf(".") var name = fullName val view = activity.layoutInflater.inflate(R.layout.dialog_rename_item, null).apply { - if (dotAt > 0 && !file.isDirectory) { + if (dotAt > 0 && !activity.getIsPathDirectory(path)) { name = fullName.substring(0, dotAt) val extension = fullName.substring(dotAt + 1) rename_item_extension.setText(extension) @@ -27,7 +25,7 @@ class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val c } rename_item_name.setText(name) - rename_item_path.text = activity.humanizePath(file.parent ?: "") + "/" + rename_item_path.text = activity.humanizePath(path.getParentPath()) } AlertDialog.Builder(activity) @@ -50,22 +48,29 @@ class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val c return@setOnClickListener } - val updatedFiles = ArrayList() - updatedFiles.add(file) + val updatedPaths = ArrayList() + updatedPaths.add(path) if (!newExtension.isEmpty()) { newName += ".$newExtension" } - val newFile = File(file.parent, newName) - if (newFile.exists()) { + if (!activity.doesFilePathExist(path)) { + activity.toast(String.format(activity.getString(R.string.source_file_doesnt_exist), path)) + return@setOnClickListener + } + + val newPath = "${path.getParentPath()}/$newName" + if (activity.doesFilePathExist(newPath)) { activity.toast(R.string.name_taken) return@setOnClickListener } - updatedFiles.add(newFile) - activity.renameFile(path, "${file.parent}/$newName") { + updatedPaths.add(newPath) + activity.renameFile(path, newPath) { if (it) { - sendSuccess(updatedFiles) + activity.scanPaths(updatedPaths) { + callback(newPath) + } dismiss() } else { activity.toast(R.string.unknown_error_occurred) @@ -75,30 +80,4 @@ class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val c } } } - - private fun sendSuccess(updatedFiles: ArrayList) { - activity.apply { - if (updatedFiles[1].isDirectory) { - val files = updatedFiles[1].listFiles() - if (files != null) { - if (files.isEmpty()) { - scanPath(updatedFiles[1].absolutePath) { - callback(updatedFiles[1].absolutePath.trimEnd('/')) - } - } else { - for (file in files) { - scanPath(file.absolutePath) { - callback(updatedFiles[1].absolutePath.trimEnd('/')) - } - break - } - } - } - } else { - scanFiles(updatedFiles) { - callback(updatedFiles[1].absolutePath.trimEnd('/')) - } - } - } - } } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Activity.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Activity.kt index 25e231f0c..84da7211b 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Activity.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Activity.kt @@ -452,7 +452,7 @@ fun BaseSimpleActivity.renameFile(oldPath: String, newPath: String, callback: (( } try { - val uri = DocumentsContract.renameDocument(applicationContext.contentResolver, document.uri, newPath) + val uri = DocumentsContract.renameDocument(applicationContext.contentResolver, document.uri, newPath.getFilenameFromPath()) if (document.uri != uri) { updateInMediaStore(oldPath, newPath) scanPaths(arrayListOf(oldPath, newPath)) { diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Context-storage.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Context-storage.kt index 96ac40969..0b3e0f57e 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Context-storage.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/Context-storage.kt @@ -98,7 +98,7 @@ fun Context.humanizePath(path: String): String { val basePath = path.getBasePath(this) return when (basePath) { "/" -> "${getHumanReadablePath(basePath)}$path" - OTG_PATH -> path.replaceFirst(basePath, "${getHumanReadablePath(basePath)}/") + OTG_PATH -> path.replaceFirst(basePath, getHumanReadablePath(basePath)).replaceFirst("otg://", OTG_PATH).trimEnd('/') else -> path.replaceFirst(basePath, getHumanReadablePath(basePath)) } } @@ -357,6 +357,14 @@ fun Context.trySAFFileDelete(fileDirItem: FileDirItem, allowDeleteFolder: Boolea fun Context.doesFilePathExist(path: String) = if (isPathOnOTG(path)) getFastDocumentFile(path)?.exists() ?: false else File(path).exists() +fun Context.getIsPathDirectory(path: String): Boolean { + return if (isPathOnOTG(path)) { + getFastDocumentFile(path)?.isDirectory ?: false + } else { + File(path).isDirectory + } +} + // avoid these being set as SD card paths private val physicalPaths = arrayListOf( "/storage/sdcard1", // Motorola Xoom diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/DocumentFile.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/DocumentFile.kt index a02a7d8eb..16e85fd03 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/DocumentFile.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/DocumentFile.kt @@ -27,3 +27,29 @@ private fun getDirectorySize(dir: DocumentFile, countHiddenItems: Boolean): Long } return size } + +fun DocumentFile.getFileCount(countHiddenItems: Boolean): Int { + return if (isDirectory) { + getDirectoryFileCount(this, countHiddenItems) + } else { + 1 + } +} + +private fun getDirectoryFileCount(dir: DocumentFile, countHiddenItems: Boolean): Int { + var count = 0 + if (dir.exists()) { + val files = dir.listFiles() + if (files != null) { + for (i in files.indices) { + val file = files[i] + if (file.isDirectory) { + count += getDirectoryFileCount(file, countHiddenItems) + } else if (!file.name.startsWith(".") || countHiddenItems) { + count++ + } + } + } + } + return count +} diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/File.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/File.kt index eb47f9f36..01d404315 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/File.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/File.kt @@ -2,9 +2,6 @@ package com.simplemobiletools.commons.extensions import android.content.Context import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.Point -import android.media.MediaMetadataRetriever import com.simplemobiletools.commons.models.FileDirItem import java.io.File @@ -20,80 +17,6 @@ fun File.isAudioSlow() = absolutePath.isAudioFast() || getMimeType().startsWith( fun File.getMimeType() = absolutePath.getMimeTypeFromPath() -fun File.getDuration() = getDurationSeconds().getFormattedDuration() - -fun File.getDurationSeconds(): Int { - return try { - val retriever = MediaMetadataRetriever() - retriever.setDataSource(absolutePath) - val time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) - val timeInMs = java.lang.Long.parseLong(time) - (timeInMs / 1000).toInt() - } catch (e: Exception) { - 0 - } -} - -fun File.getArtist(): String? { - return try { - val retriever = MediaMetadataRetriever() - retriever.setDataSource(absolutePath) - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST) - } catch (ignored: Exception) { - null - } -} - -fun File.getAlbum(): String? { - return try { - val retriever = MediaMetadataRetriever() - retriever.setDataSource(absolutePath) - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) - } catch (ignored: Exception) { - null - } -} - -fun File.getSongTitle(): String? { - return try { - val retriever = MediaMetadataRetriever() - retriever.setDataSource(absolutePath) - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) - } catch (ignored: Exception) { - null - } -} - -fun File.getResolution(): Point { - return if (isImageFast() || isImageSlow()) { - getImageResolution() - } else if (isVideoFast() || isVideoSlow()) { - getVideoResolution() - } else { - return Point(0, 0) - } -} - -fun File.getVideoResolution(): Point { - try { - val retriever = MediaMetadataRetriever() - retriever.setDataSource(absolutePath) - val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt() - val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt() - return Point(width, height) - } catch (ignored: Exception) { - - } - return Point(0, 0) -} - -fun File.getImageResolution(): Point { - val options = BitmapFactory.Options() - options.inJustDecodeBounds = true - BitmapFactory.decodeFile(absolutePath, options) - return Point(options.outWidth, options.outHeight) -} - fun File.getCompressionFormat() = when (extension.toLowerCase()) { "png" -> Bitmap.CompressFormat.PNG "webp" -> Bitmap.CompressFormat.WEBP @@ -102,7 +25,7 @@ fun File.getCompressionFormat() = when (extension.toLowerCase()) { fun File.getProperSize(countHiddenItems: Boolean): Long { return if (isDirectory) { - getDirectorySize(File(path), countHiddenItems) + getDirectorySize(this, countHiddenItems) } else { length() } @@ -127,7 +50,7 @@ private fun getDirectorySize(dir: File, countHiddenItems: Boolean): Long { fun File.getFileCount(countHiddenItems: Boolean): Int { return if (isDirectory) { - getDirectoryFileCount(File(path), countHiddenItems) + getDirectoryFileCount(this, countHiddenItems) } else { 1 } @@ -151,4 +74,4 @@ private fun getDirectoryFileCount(dir: File, countHiddenItems: Boolean): Int { return count } -fun File.toFileDirItem(context: Context) = FileDirItem(absolutePath, name, absolutePath.getIsDirectory(context), 0, 0L) +fun File.toFileDirItem(context: Context) = FileDirItem(absolutePath, name, context.getIsPathDirectory(absolutePath), 0, 0L) diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt index 89edc4bd1..53f4e626e 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/extensions/String.kt @@ -1,9 +1,11 @@ package com.simplemobiletools.commons.extensions import android.content.Context +import android.graphics.BitmapFactory +import android.graphics.Point import android.media.ExifInterface +import android.media.MediaMetadataRetriever import com.simplemobiletools.commons.helpers.OTG_PATH -import java.io.File import java.text.SimpleDateFormat import java.util.* @@ -126,11 +128,83 @@ fun String.getGenericMimeType(): String { fun String.getParentPath() = substring(0, length - getFilenameFromPath().length) -fun String.getIsDirectory(context: Context): Boolean { - return if (context.isPathOnOTG(this)) { - context.getFastDocumentFile(this)?.isDirectory ?: false +fun String.getDuration() = getFileDurationSeconds()?.getFormattedDuration() + +fun String.getFileDurationSeconds(): Int? { + return try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(this) + val time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) + val timeInMs = java.lang.Long.parseLong(time) + (timeInMs / 1000).toInt() + } catch (e: Exception) { + null + } +} + +fun String.getFileArtist(): String? { + return try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(this) + retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST) + } catch (ignored: Exception) { + null + } +} + +fun String.getFileAlbum(): String? { + return try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(this) + retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) + } catch (ignored: Exception) { + null + } +} + +fun String.getFileSongTitle(): String? { + return try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(this) + retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) + } catch (ignored: Exception) { + null + } +} + +fun String.getResolution(): Point? { + return if (isImageFast() || isImageSlow()) { + getImageResolution() + } else if (isVideoFast() || isVideoSlow()) { + getVideoResolution() } else { - File(this).isDirectory + return null + } +} + +fun String.getVideoResolution(): Point? { + try { + val retriever = MediaMetadataRetriever() + retriever.setDataSource(this) + val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt() + val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt() + return Point(width, height) + } catch (ignored: Exception) { + + } + return null +} + +fun String.getImageResolution(): Point? { + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(this, options) + val width = options.outWidth + val height = options.outHeight + return if (width > 0 && height > 0) { + Point(options.outWidth, options.outHeight) + } else { + null } } diff --git a/commons/src/main/kotlin/com/simplemobiletools/commons/models/FileDirItem.kt b/commons/src/main/kotlin/com/simplemobiletools/commons/models/FileDirItem.kt index 66680c661..b63b88e5e 100644 --- a/commons/src/main/kotlin/com/simplemobiletools/commons/models/FileDirItem.kt +++ b/commons/src/main/kotlin/com/simplemobiletools/commons/models/FileDirItem.kt @@ -63,5 +63,45 @@ data class FileDirItem(val path: String, val name: String = "", var isDirectory: } } + fun getProperFileCount(context: Context, countHidden: Boolean): Int { + return if (context.isPathOnOTG(path)) { + context.getDocumentFile(path)?.getFileCount(countHidden) ?: 0 + } else { + File(path).getFileCount(countHidden) + } + } + + fun getDirectChildrenCount(context: Context, countHiddenItems: Boolean): Int { + return if (context.isPathOnOTG(path)) { + context.getDocumentFile(path)?.listFiles()?.filter { if (countHiddenItems) true else !it.name.startsWith(".") }?.size ?: 0 + } else { + File(path).listFiles().filter { if (countHiddenItems) true else it.isHidden }.size + } + } + + fun getLastModified(context: Context): Long { + return if (context.isPathOnOTG(path)) { + context.getFastDocumentFile(path)?.lastModified() ?: 0L + } else { + File(path).lastModified() + } + } + fun getParentPath() = path.getParentPath() + + fun getDuration() = path.getDuration() + + fun getFileDurationSeconds() = path.getFileDurationSeconds() + + fun getArtist() = path.getFileArtist() + + fun getAlbum() = path.getFileAlbum() + + fun getSongTitle() = path.getFileSongTitle() + + fun getResolution() = path.getResolution() + + fun getVideoResolution() = path.getVideoResolution() + + fun getImageResolution() = path.getImageResolution() } diff --git a/commons/src/main/res/values-ca/strings.xml b/commons/src/main/res/values-ca/strings.xml index 6ff414dd3..49de0ac69 100644 --- a/commons/src/main/res/values-ca/strings.xml +++ b/commons/src/main/res/values-ca/strings.xml @@ -43,6 +43,7 @@ El nom de l\'arxiu no pot ser buit El nom de l\'arxiu conté caràcters no vàlids La extensió no pot estar buida + Source file %s doesn\'t exist Copiar diff --git a/commons/src/main/res/values-cs/strings.xml b/commons/src/main/res/values-cs/strings.xml index f3f351ed9..09f4fdc0f 100644 --- a/commons/src/main/res/values-cs/strings.xml +++ b/commons/src/main/res/values-cs/strings.xml @@ -43,6 +43,7 @@ Název souboru nemůže být prázdný Název souboru obsahuje neplatné znaky Přípona nemůže být prázdná + Source file %s doesn\'t exist Kopírovat diff --git a/commons/src/main/res/values-da/strings.xml b/commons/src/main/res/values-da/strings.xml index 01dcc4d8f..ecad0c5a0 100644 --- a/commons/src/main/res/values-da/strings.xml +++ b/commons/src/main/res/values-da/strings.xml @@ -43,6 +43,7 @@ Filen skal have et navn Filnavnet indeholder ugyldige tegn Filtypen kan ikke stå tom + Source file %s doesn\'t exist Kopier diff --git a/commons/src/main/res/values-de/strings.xml b/commons/src/main/res/values-de/strings.xml index 6608d4d1c..43b36e025 100644 --- a/commons/src/main/res/values-de/strings.xml +++ b/commons/src/main/res/values-de/strings.xml @@ -43,6 +43,7 @@ Dateiname darf nicht leer sein Dateiname enthält ungültige Zeichen Dateiendung darf nicht leer sein + Source file %s doesn\'t exist Kopieren diff --git a/commons/src/main/res/values-es/strings.xml b/commons/src/main/res/values-es/strings.xml index 71f1fde06..bebe4f7ae 100644 --- a/commons/src/main/res/values-es/strings.xml +++ b/commons/src/main/res/values-es/strings.xml @@ -43,6 +43,7 @@ El nombre de archivo no puede estar vacío El nombre archivo contiene caracteres no válidos La extensión no puede estar vacía + Source file %s doesn\'t exist Copiar diff --git a/commons/src/main/res/values-fi/strings.xml b/commons/src/main/res/values-fi/strings.xml index 6a95fb4fe..a4d4313f3 100644 --- a/commons/src/main/res/values-fi/strings.xml +++ b/commons/src/main/res/values-fi/strings.xml @@ -43,6 +43,7 @@ Filename cannot be empty Filename contains invalid characters Extension cannot be empty + Source file %s doesn\'t exist Copy diff --git a/commons/src/main/res/values-fr/strings.xml b/commons/src/main/res/values-fr/strings.xml index cef0c8d05..3f1557a1e 100644 --- a/commons/src/main/res/values-fr/strings.xml +++ b/commons/src/main/res/values-fr/strings.xml @@ -43,6 +43,7 @@ Le nom de fichier ne peut pas rester vide Le nom de fichier contient des caractères invalides L\'extension ne peut pas rester vide + Source file %s doesn\'t exist Copier diff --git a/commons/src/main/res/values-hi-rIN/strings.xml b/commons/src/main/res/values-hi-rIN/strings.xml index e508e2828..4b881a9bd 100644 --- a/commons/src/main/res/values-hi-rIN/strings.xml +++ b/commons/src/main/res/values-hi-rIN/strings.xml @@ -43,6 +43,7 @@ Filename cannot be empty Filename contains invalid characters Extension cannot be empty + Source file %s doesn\'t exist Copy diff --git a/commons/src/main/res/values-hr/strings.xml b/commons/src/main/res/values-hr/strings.xml index 1031d0303..295fc0011 100644 --- a/commons/src/main/res/values-hr/strings.xml +++ b/commons/src/main/res/values-hr/strings.xml @@ -43,6 +43,7 @@ Naziv datoteke ne smije biti prazan Naziv datoteke sadrži ne podržane znakove Ekstenzija ne smije biti prazna + Source file %s doesn\'t exist Kopiraj diff --git a/commons/src/main/res/values-hu/strings.xml b/commons/src/main/res/values-hu/strings.xml index 95fcdd79e..32fa46463 100644 --- a/commons/src/main/res/values-hu/strings.xml +++ b/commons/src/main/res/values-hu/strings.xml @@ -43,6 +43,7 @@ Filename cannot be empty Filename contains invalid characters Extension cannot be empty + Source file %s doesn\'t exist Copy diff --git a/commons/src/main/res/values-id/strings.xml b/commons/src/main/res/values-id/strings.xml index 55509a542..29627e993 100644 --- a/commons/src/main/res/values-id/strings.xml +++ b/commons/src/main/res/values-id/strings.xml @@ -43,6 +43,7 @@ Nama File Tidak Boleh Kosong Nama mengandung Karakter Tidak Di Dukung Extension Tidak Boleh Kosong + Source file %s doesn\'t exist Salin diff --git a/commons/src/main/res/values-it/strings.xml b/commons/src/main/res/values-it/strings.xml index d99e49140..9427eb4f9 100644 --- a/commons/src/main/res/values-it/strings.xml +++ b/commons/src/main/res/values-it/strings.xml @@ -43,6 +43,7 @@ Il nome del file non deve essere vuoto Il nome contiene caratteri non validi L\'estensione non può essere vuota + Source file %s doesn\'t exist Copia diff --git a/commons/src/main/res/values-ja/strings.xml b/commons/src/main/res/values-ja/strings.xml index 23139cbe3..50f2e6e27 100644 --- a/commons/src/main/res/values-ja/strings.xml +++ b/commons/src/main/res/values-ja/strings.xml @@ -43,6 +43,7 @@ ファイル名は空にできません ファイル名に無効な文字が含まれています 拡張子は省略できません + Source file %s doesn\'t exist コピー diff --git a/commons/src/main/res/values-ko-rKR/strings.xml b/commons/src/main/res/values-ko-rKR/strings.xml index 9c54abe6f..70a80d141 100644 --- a/commons/src/main/res/values-ko-rKR/strings.xml +++ b/commons/src/main/res/values-ko-rKR/strings.xml @@ -43,6 +43,7 @@ 파일명이 비어있으면 안됨 파일명에 사용할 수 없는 문자가 포함됨 확장자가 비어있으면 안됨 + Source file %s doesn\'t exist 복사 diff --git a/commons/src/main/res/values-lt/strings.xml b/commons/src/main/res/values-lt/strings.xml index b54445ce0..019fa12f1 100644 --- a/commons/src/main/res/values-lt/strings.xml +++ b/commons/src/main/res/values-lt/strings.xml @@ -43,6 +43,7 @@ Failo pavadinimas negali bûti paliktas tusèias Failo varde yra negalimø simboliø Papildinys negali bûti paliktas tuðèias + Source file %s doesn\'t exist Kopijuoti diff --git a/commons/src/main/res/values-nb/strings.xml b/commons/src/main/res/values-nb/strings.xml index fe535a74f..88e97a84c 100644 --- a/commons/src/main/res/values-nb/strings.xml +++ b/commons/src/main/res/values-nb/strings.xml @@ -43,6 +43,7 @@ Filename cannot be empty Filename contains invalid characters Extension cannot be empty + Source file %s doesn\'t exist Kopier diff --git a/commons/src/main/res/values-nl/strings.xml b/commons/src/main/res/values-nl/strings.xml index 4abbc8c66..e5a434133 100644 --- a/commons/src/main/res/values-nl/strings.xml +++ b/commons/src/main/res/values-nl/strings.xml @@ -43,6 +43,7 @@ Bestandsnaam mag niet leeg zijn Bestandsnaam bevat ongeldige tekens Extensie mag niet leeg zijn + Source file %s doesn\'t exist Kopiëren diff --git a/commons/src/main/res/values-no/strings.xml b/commons/src/main/res/values-no/strings.xml index 466b9142a..38edb8707 100644 --- a/commons/src/main/res/values-no/strings.xml +++ b/commons/src/main/res/values-no/strings.xml @@ -43,6 +43,7 @@ Filename cannot be empty Filename contains invalid characters Extension cannot be empty + Source file %s doesn\'t exist Kopier diff --git a/commons/src/main/res/values-pl/strings.xml b/commons/src/main/res/values-pl/strings.xml index 19cd918bb..8fa2824bc 100644 --- a/commons/src/main/res/values-pl/strings.xml +++ b/commons/src/main/res/values-pl/strings.xml @@ -43,6 +43,7 @@ Nazwa pliku nie może być pusta Nazwa pliku zawiera niedozwolone znaki Rozszerzenie pliku nie może być puste + Source file %s doesn\'t exist Kopiuj diff --git a/commons/src/main/res/values-pt-rBR/strings.xml b/commons/src/main/res/values-pt-rBR/strings.xml index 7bb745399..a326c66c5 100644 --- a/commons/src/main/res/values-pt-rBR/strings.xml +++ b/commons/src/main/res/values-pt-rBR/strings.xml @@ -43,6 +43,7 @@ O nome do arquivo não pode ficar em branco O nome do arquivo contém caracteres inválidos A extensão não pode ficar em branco + Source file %s doesn\'t exist Copiar diff --git a/commons/src/main/res/values-pt/strings.xml b/commons/src/main/res/values-pt/strings.xml index 8e9e98932..762be94c1 100644 --- a/commons/src/main/res/values-pt/strings.xml +++ b/commons/src/main/res/values-pt/strings.xml @@ -43,6 +43,7 @@ O nome do ficheiro não pode estar vazio O nome do ficheiro contém caracteres inválidos A extensão não pode estar vazia + Source file %s doesn\'t exist Copiar diff --git a/commons/src/main/res/values-ru/strings.xml b/commons/src/main/res/values-ru/strings.xml index d605f18c4..43c29350e 100644 --- a/commons/src/main/res/values-ru/strings.xml +++ b/commons/src/main/res/values-ru/strings.xml @@ -43,6 +43,7 @@ Имя файла не может быть пустым Имя файла содержит недопустимые символы Расширение не может быть пустым + Source file %s doesn\'t exist Копировать diff --git a/commons/src/main/res/values-sk/strings.xml b/commons/src/main/res/values-sk/strings.xml index 198e88965..43689e074 100644 --- a/commons/src/main/res/values-sk/strings.xml +++ b/commons/src/main/res/values-sk/strings.xml @@ -43,6 +43,7 @@ Názov súboru nemôže byť prázdny Názov súboru obsahuje neplatné znaky Prípona nesmie byť prázdna + Zdrojový súbor %s neexistuje Kopírovať diff --git a/commons/src/main/res/values-sv/strings.xml b/commons/src/main/res/values-sv/strings.xml index d92fece97..24353b614 100644 --- a/commons/src/main/res/values-sv/strings.xml +++ b/commons/src/main/res/values-sv/strings.xml @@ -43,6 +43,7 @@ Du måste ange ett filnamn Filnamnet innehåller ogiltiga tecken Filändelsen får inte vara tom + Source file %s doesn\'t exist Kopiera diff --git a/commons/src/main/res/values-tr/strings.xml b/commons/src/main/res/values-tr/strings.xml index 7e271b172..721dff987 100644 --- a/commons/src/main/res/values-tr/strings.xml +++ b/commons/src/main/res/values-tr/strings.xml @@ -43,6 +43,7 @@ Dosya adı boş olamaz Dosya adı geçersiz karakterler içeriyor Uzantı boş olamaz + Source file %s doesn\'t exist Kopyala diff --git a/commons/src/main/res/values-zh-rCN/strings.xml b/commons/src/main/res/values-zh-rCN/strings.xml index b460b402e..25113ef85 100644 --- a/commons/src/main/res/values-zh-rCN/strings.xml +++ b/commons/src/main/res/values-zh-rCN/strings.xml @@ -43,6 +43,7 @@ 文件名不能为空 文件名包含非法字符 扩展名不能为空 + Source file %s doesn\'t exist 复制 diff --git a/commons/src/main/res/values-zh-rTW/strings.xml b/commons/src/main/res/values-zh-rTW/strings.xml index 26bf20f65..050cd82d1 100644 --- a/commons/src/main/res/values-zh-rTW/strings.xml +++ b/commons/src/main/res/values-zh-rTW/strings.xml @@ -43,6 +43,7 @@ 檔名不得空白 檔名包含無效字符 副檔名不得空白 + Source file %s doesn\'t exist 複製 diff --git a/commons/src/main/res/values/strings.xml b/commons/src/main/res/values/strings.xml index 67ee1d334..e49c9d931 100644 --- a/commons/src/main/res/values/strings.xml +++ b/commons/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ Filename cannot be empty Filename contains invalid characters Extension cannot be empty + Source file %s doesn\'t exist Copy