adding some OTG file related improvements

This commit is contained in:
tibbi 2019-11-04 22:22:39 +01:00
parent b744701a70
commit 556a775848
12 changed files with 142 additions and 51 deletions

View File

@ -62,7 +62,7 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.18.14'
implementation 'com.simplemobiletools:commons:5.18.26'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'it.sephiroth.android.exif:library:1.0.1'

View File

@ -389,10 +389,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun removeTempFolder() {
if (config.tempFolderPath.isNotEmpty()) {
val newFolder = File(config.tempFolderPath)
if (newFolder.exists() && newFolder.isDirectory) {
if (getDoesFilePathExist(newFolder.absolutePath) && newFolder.isDirectory) {
if (newFolder.list()?.isEmpty() == true && newFolder.getProperSize(true) == 0L && newFolder.getFileCount(true) == 0) {
toast(String.format(getString(R.string.deleting_folder), config.tempFolderPath), Toast.LENGTH_LONG)
tryDeleteFileDirItem(newFolder.toFileDirItem(), true, true)
tryDeleteFileDirItem(newFolder.toFileDirItem(applicationContext), true, true)
}
}
config.tempFolderPath = ""
@ -408,16 +408,6 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
config.OTGPath = otgPath
config.addIncludedFolder(otgPath)
}
// OTG handling has been changed again in SDK version 28, the old method no longer works
/*if (config.OTGPath.isEmpty()) {
runOnUiThread {
ConfirmationDialog(this, getString(R.string.usb_detected), positive = R.string.ok, negative = 0) {
config.wasOTGHandled = true
showOTGPermissionDialog()
}
}
}*/
}
}
}
@ -428,8 +418,9 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
"/storage/emulated/0/Android/data/com.facebook.orca/files/stickers"
)
val OTGPath = config.OTGPath
spamFolders.forEach {
if (File(it).exists()) {
if (getDoesFilePathExist(it, OTGPath)) {
config.addExcludedFolder(it)
}
}
@ -570,7 +561,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
(it.isGif() && filter and TYPE_GIFS != 0) ||
(it.isRawFast() && filter and TYPE_RAWS != 0) ||
(it.isSvg() && filter and TYPE_SVGS != 0))
}?.mapTo(itemsToDelete) { it.toFileDirItem() }
}?.mapTo(itemsToDelete) { it.toFileDirItem(applicationContext) }
}
if (config.useRecycleBin) {
@ -590,13 +581,14 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
private fun deleteFilteredFileDirItems(fileDirItems: ArrayList<FileDirItem>, folders: ArrayList<File>) {
val OTGPath = config.OTGPath
deleteFiles(fileDirItems) {
runOnUiThread {
refreshItems()
}
ensureBackgroundThread {
folders.filter { !it.exists() }.forEach {
folders.filter { !getDoesFilePathExist(it.absolutePath, OTGPath) }.forEach {
mDirectoryDao.deleteDirPath(it.absolutePath)
}
}
@ -1115,13 +1107,14 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun checkInvalidDirectories(dirs: ArrayList<Directory>) {
val invalidDirs = ArrayList<Directory>()
val OTGPath = config.OTGPath
dirs.filter { !it.areFavorites() && !it.isRecycleBin() }.forEach {
if (!File(it.path).exists()) {
if (!getDoesFilePathExist(it.path, OTGPath)) {
invalidDirs.add(it)
} else if (it.path != config.tempFolderPath) {
val children = File(it.path).listFiles()?.asList()
val children = if (isPathOnOTG(it.path)) getOTGFolderChildrenNames(it.path) else File(it.path).list()?.asList()
val hasMediaFile = children?.any {
it?.isMediaFile() == true || (it.isDirectory && it.name.startsWith("img_", true))
it?.isMediaFile() == true || (File(it).isDirectory && it?.startsWith("img_", true) == true)
} ?: false
if (!hasMediaFile) {
@ -1151,6 +1144,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
dirs.removeAll(invalidDirs)
setupAdapter(dirs)
invalidDirs.forEach {
toast("invalid ${it.path}", Toast.LENGTH_LONG)
try {
mDirectoryDao.deleteDirPath(it.path)
} catch (ignored: Exception) {
@ -1243,9 +1237,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
oftenRepeatedPaths.removeAll(substringToRemove)
val OTGPath = config.OTGPath
oftenRepeatedPaths.forEach {
val file = File("$internalPath/$it")
if (file.exists()) {
if (getDoesFilePathExist(file.absolutePath, OTGPath)) {
config.addExcludedFolder(file.absolutePath)
}
}

View File

@ -569,7 +569,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun deleteDirectoryIfEmpty() {
val fileDirItem = FileDirItem(mPath, mPath.getFilenameFromPath(), true)
if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(true) == 0) {
if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(this, true) == 0) {
tryDeleteFileDirItem(fileDirItem, true, true)
}
}
@ -606,7 +606,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
val newMedia = it
try {
gotMedia(newMedia, false)
oldMedia.filter { !newMedia.contains(it) }.mapNotNull { it as? Medium }.filter { !File(it.path).exists() }.forEach {
oldMedia.filter { !newMedia.contains(it) }.mapNotNull { it as? Medium }.filter { !getDoesFilePathExist(it.path) }.forEach {
mMediumDao.deleteMediumPath(it.path)
}
} catch (e: Exception) {

View File

@ -75,7 +75,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
val uri = mUri.toString()
if (uri.startsWith("content:/") && uri.contains("/storage/")) {
val guessedPath = uri.substring(uri.indexOf("/storage/"))
if (File(guessedPath).exists()) {
if (getDoesFilePathExist(guessedPath)) {
val extras = intent.extras ?: Bundle()
extras.apply {
putString(REAL_FILE_PATH, guessedPath)
@ -93,7 +93,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
if (intent.extras?.containsKey(REAL_FILE_PATH) == true) {
val realPath = intent.extras!!.getString(REAL_FILE_PATH)
if (realPath != null && File(realPath).exists()) {
if (realPath != null && getDoesFilePathExist(realPath)) {
if (realPath.getFilenameFromPath().contains('.') || filename.contains('.')) {
if (isFileTypeVisible(realPath)) {
bottom_actions.beGone()
@ -125,7 +125,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
return
} else {
val path = applicationContext.getRealPathFromURI(mUri!!) ?: ""
if (path != mUri.toString() && path.isNotEmpty() && mUri!!.authority != "mms" && filename.contains('.') && File(path).exists()) {
if (path != mUri.toString() && path.isNotEmpty() && mUri!!.authority != "mms" && filename.contains('.') && getDoesFilePathExist(path)) {
if (isFileTypeVisible(path)) {
bottom_actions.beGone()
handleLockedFolderOpening(path.getParentPath()) { success ->

View File

@ -278,7 +278,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
return
}
if (!File(mPath).exists() && getPortraitPath() == "") {
if (!getDoesFilePathExist(mPath) && getPortraitPath() == "") {
finish()
return
}
@ -1066,7 +1066,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun deleteDirectoryIfEmpty() {
val fileDirItem = FileDirItem(mDirectory, mDirectory.getFilenameFromPath(), File(mDirectory).isDirectory)
if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(true) == 0) {
if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(this, true) == 0) {
tryDeleteFileDirItem(fileDirItem, true, true)
scanPathRecursively(mDirectory)
}

View File

@ -49,6 +49,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Thumbnai
private var loadImageInstantly = false
private var delayHandler = Handler(Looper.getMainLooper())
private var currentMediaHash = media.hashCode()
private val hasOTGConnected = activity.hasOTGConnected()
private var scrollHorizontally = config.scrollHorizontally
private var animateGifs = config.animateGifs
@ -492,7 +493,11 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Thumbnai
medium_check?.background?.applyColorFilter(primaryColor)
}
val path = medium.path
var path = medium.path
if (hasOTGConnected && context.isPathOnOTG(path)) {
path = path.getOTGPublicPath(context)
}
if (loadImageInstantly) {
activity.loadImage(medium.type, path, medium_thumbnail, scrollHorizontally, animateGifs, cropThumbnails, rotatedImagePaths)
} else {

View File

@ -7,7 +7,6 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.gallery.pro.R
import kotlinx.android.synthetic.main.dialog_save_as.view.*
import java.io.File
class SaveAsDialog(val activity: BaseSimpleActivity, val path: String, val appendFilename: Boolean, val callback: (savePath: String) -> Unit) {
@ -67,7 +66,7 @@ class SaveAsDialog(val activity: BaseSimpleActivity, val path: String, val appen
return@setOnClickListener
}
if (File(newPath).exists()) {
if (activity.getDoesFilePathExist(newPath)) {
val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newFilename)
ConfirmationDialog(activity, title) {
callback(newPath)

View File

@ -131,7 +131,7 @@ fun AppCompatActivity.hideSystemUI(toggleActionBarVisibility: Boolean) {
fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) {
val file = File(path, NOMEDIA)
if (file.exists()) {
if (getDoesFilePathExist(file.absolutePath)) {
callback()
return
}
@ -164,12 +164,12 @@ fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) {
fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = null) {
val file = File(path, NOMEDIA)
if (!file.exists()) {
if (!getDoesFilePathExist(file.absolutePath)) {
callback?.invoke()
return
}
tryDeleteFileDirItem(file.toFileDirItem(), false, false) {
tryDeleteFileDirItem(file.toFileDirItem(applicationContext), false, false) {
callback?.invoke()
}
}
@ -271,7 +271,7 @@ fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, mediumDa
try {
out = getFileOutputStreamSync(destination, source.getMimeType())
inputStream = getFileInputStreamSync(source)
inputStream.copyTo(out!!)
inputStream!!.copyTo(out!!)
if (File(source).length() == File(destination).length()) {
mediumDao.updateDeleted(destination.removePrefix(recycleBinPath), 0, "$RECYCLE_BIN$destination")
}
@ -519,7 +519,9 @@ fun BaseSimpleActivity.copyFile(source: String, destination: String) {
try {
out = getFileOutputStreamSync(destination, source.getMimeType())
inputStream = getFileInputStreamSync(source)
inputStream.copyTo(out!!)
inputStream!!.copyTo(out!!)
} catch (e: Exception) {
showErrorToast(e)
} finally {
inputStream?.close()
out?.close()

View File

@ -366,6 +366,7 @@ fun Context.getNoMediaFolders(callback: (folders: ArrayList<String>) -> Unit) {
val selection = "${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? AND ${MediaStore.Files.FileColumns.TITLE} LIKE ?"
val selectionArgs = arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_NONE.toString(), "%$NOMEDIA%")
val sortOrder = "${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
val OTGPath = config.OTGPath
var cursor: Cursor? = null
try {
@ -374,7 +375,7 @@ fun Context.getNoMediaFolders(callback: (folders: ArrayList<String>) -> Unit) {
do {
val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA) ?: continue
val noMediaFile = File(path)
if (noMediaFile.exists() && noMediaFile.name == NOMEDIA) {
if (getDoesFilePathExist(noMediaFile.absolutePath, OTGPath) && noMediaFile.name == NOMEDIA) {
folders.add("${noMediaFile.parent}/")
}
} while (cursor.moveToNext())
@ -661,9 +662,10 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
mediaFetcher.sortMedia(media, config.getFileSorting(pathToUse))
val grouped = mediaFetcher.groupMedia(media, pathToUse)
callback(grouped.clone() as ArrayList<ThumbnailItem>)
val OTGPath = config.OTGPath
val mediaToDelete = ArrayList<Medium>()
media.filter { !File(it.path).exists() }.forEach {
media.filter { !getDoesFilePathExist(it.path, OTGPath) }.forEach {
if (it.path.startsWith(recycleBinPath)) {
deleteDBPath(mediumDao, it.path)
} else {
@ -682,7 +684,8 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
fun Context.removeInvalidDBDirectories(dirs: ArrayList<Directory>? = null, directoryDao: DirectoryDao = galleryDB.DirectoryDao()) {
val dirsToCheck = dirs ?: directoryDao.getAll()
dirsToCheck.filter { !it.areFavorites() && !it.isRecycleBin() && !File(it.path).exists() && it.path != config.tempFolderPath }.forEach {
val OTGPath = config.OTGPath
dirsToCheck.filter { !it.areFavorites() && !it.isRecycleBin() && !getDoesFilePathExist(it.path, OTGPath) && it.path != config.tempFolderPath }.forEach {
try {
directoryDao.deleteDirPath(it.path)
} catch (ignored: Exception) {
@ -706,6 +709,10 @@ fun Context.updateDBDirectory(directory: Directory, directoryDao: DirectoryDao)
}
}
fun Context.getOTGFolderChildren(path: String) = getDocumentFile(path)?.listFiles()
fun Context.getOTGFolderChildrenNames(path: String) = getOTGFolderChildren(path)?.map { it.name }?.toMutableList()
fun Context.getFavoritePaths(): ArrayList<String> {
return try {
galleryDB.MediumDao().getFavoritePaths() as ArrayList<String>
@ -805,7 +812,7 @@ fun Context.parseFileChannel(path: String, fc: FileChannel, level: Int, start: L
fun Context.addPathToDB(path: String) {
ensureBackgroundThread {
if (!File(path).exists()) {
if (!getDoesFilePathExist(path)) {
return@ensureBackgroundThread
}
@ -833,13 +840,18 @@ fun Context.addPathToDB(path: String) {
fun Context.createDirectoryFromMedia(path: String, curMedia: ArrayList<Medium>, albumCovers: ArrayList<AlbumCover>, hiddenString: String,
includedFolders: MutableSet<String>, isSortingAscending: Boolean, getProperFileSize: Boolean): Directory {
var thumbnail = curMedia.firstOrNull { File(it.path).exists() }?.path ?: ""
val OTGPath = config.OTGPath
var thumbnail = curMedia.firstOrNull { getDoesFilePathExist(it.path, OTGPath) }?.path ?: ""
albumCovers.forEach {
if (it.path == path && File(it.tmb).exists()) {
if (it.path == path && getDoesFilePathExist(it.tmb, OTGPath)) {
thumbnail = it.tmb
}
}
if (config.OTGPath.isNotEmpty() && thumbnail.startsWith(config.OTGPath)) {
thumbnail = thumbnail.getOTGPublicPath(applicationContext)
}
val defaultMedium = Medium(0, "", "", "", 0L, 0L, 0L, 0, 0, false, 0L)
val firstItem = curMedia.firstOrNull() ?: defaultMedium
val lastItem = curMedia.lastOrNull() ?: defaultMedium

View File

@ -357,11 +357,11 @@ class PhotoFragment : ViewPagerFragment() {
private fun loadGif() {
try {
val path = mMedium.path
val source = if (path.startsWith("content://") || path.startsWith("file://")) {
InputSource.UriSource(context!!.contentResolver, Uri.parse(path))
val pathToLoad = getPathToLoad(mMedium)
val source = if (pathToLoad.startsWith("content://") || pathToLoad.startsWith("file://")) {
InputSource.UriSource(context!!.contentResolver, Uri.parse(pathToLoad))
} else {
InputSource.FileSource(path)
InputSource.FileSource(pathToLoad)
}
mView.apply {
@ -559,7 +559,7 @@ class PhotoFragment : ViewPagerFragment() {
}
}
private fun getFilePathToShow() = if (mMedium.isPortrait()) mCurrentPortraitPhotoPath else mMedium.path
private fun getFilePathToShow() = if (mMedium.isPortrait()) mCurrentPortraitPhotoPath else getPathToLoad(mMedium)
private fun openPanorama() {
Intent(context, PanoramaPhotoActivity::class.java).apply {

View File

@ -35,7 +35,7 @@ abstract class ViewPagerFragment : Fragment() {
fun getMediumExtendedDetails(medium: Medium): String {
val file = File(medium.path)
if (!file.exists()) {
if (context?.getDoesFilePathExist(file.absolutePath) == false) {
return ""
}
@ -81,6 +81,8 @@ abstract class ViewPagerFragment : Fragment() {
return details.toString().trim()
}
fun getPathToLoad(medium: Medium) = if (context!!.isPathOnOTG(medium.path)) medium.path.getOTGPublicPath(context!!) else medium.path
private fun getFileLastModified(file: File): String {
val projection = arrayOf(MediaStore.Images.Media.DATE_MODIFIED)
val uri = MediaStore.Files.getContentUri("external")

View File

@ -2,6 +2,7 @@ package com.simplemobiletools.gallery.pro.helpers
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Environment
import android.provider.BaseColumns
import android.provider.MediaStore
@ -27,8 +28,15 @@ class MediaFetcher(val context: Context) {
}
val curMedia = ArrayList<Medium>()
val newMedia = getMediaInFolder(curPath, isPickImage, isPickVideo, filterMedia, getProperDateTaken, getProperFileSize, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
if (context.isPathOnOTG(curPath)) {
if (context.hasOTGConnected()) {
val newMedia = getMediaOnOTG(curPath, isPickImage, isPickVideo, filterMedia, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
}
} else {
val newMedia = getMediaInFolder(curPath, isPickImage, isPickVideo, filterMedia, getProperDateTaken, getProperFileSize, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
}
if (sortMedia) {
sortMedia(curMedia, context.config.getFileSorting(curPath))
@ -39,12 +47,13 @@ class MediaFetcher(val context: Context) {
fun getFoldersToScan(): ArrayList<String> {
return try {
val OTGPath = context.config.OTGPath
val folders = getLatestFileFolders()
folders.addAll(arrayListOf(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString(),
"${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera",
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()
).filter { File(it).exists() })
).filter { context.getDoesFilePathExist(it, OTGPath) })
val filterMedia = context.config.filterMedia
val uri = MediaStore.Files.getContentUri("external")
@ -167,7 +176,8 @@ class MediaFetcher(val context: Context) {
val foldersToIgnore = arrayListOf("/storage/emulated/legacy")
val config = context.config
val includedFolders = config.includedFolders
var foldersToScan = config.everShownFolders.filter { it == FAVORITES || it == RECYCLE_BIN || File(it).exists() }.toMutableList() as ArrayList
val OTGPath = config.OTGPath
var foldersToScan = config.everShownFolders.filter { it == FAVORITES || it == RECYCLE_BIN || context.getDoesFilePathExist(it, OTGPath) }.toMutableList() as ArrayList
cursor.use {
if (cursor.moveToFirst()) {
@ -327,6 +337,72 @@ class MediaFetcher(val context: Context) {
return media
}
private fun getMediaOnOTG(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int, favoritePaths: ArrayList<String>,
getVideoDurations: Boolean): ArrayList<Medium> {
val media = ArrayList<Medium>()
val files = context.getDocumentFile(folder)?.listFiles() ?: return media
val checkFileExistence = context.config.fileLoadingPriority == PRIORITY_VALIDITY
val showHidden = context.config.shouldShowHidden
val OTGPath = context.config.OTGPath
for (file in files) {
if (shouldStop) {
break
}
val filename = file.name ?: continue
val isImage = filename.isImageFast()
val isVideo = if (isImage) false else filename.isVideoFast()
val isGif = if (isImage || isVideo) false else filename.isGif()
val isRaw = if (isImage || isVideo || isGif) false else filename.isRawFast()
val isSvg = if (isImage || isVideo || isGif || isRaw) false else filename.isSvg()
if (!isImage && !isVideo && !isGif && !isRaw && !isSvg)
continue
if (isVideo && (isPickImage || filterMedia and TYPE_VIDEOS == 0))
continue
if (isImage && (isPickVideo || filterMedia and TYPE_IMAGES == 0))
continue
if (isGif && filterMedia and TYPE_GIFS == 0)
continue
if (isRaw && filterMedia and TYPE_RAWS == 0)
continue
if (isSvg && filterMedia and TYPE_SVGS == 0)
continue
if (!showHidden && filename.startsWith('.'))
continue
val size = file.length()
if (size <= 0L || (checkFileExistence && !context.getDoesFilePathExist(file.uri.toString(), OTGPath)))
continue
val dateTaken = file.lastModified()
val dateModified = file.lastModified()
val type = when {
isVideo -> TYPE_VIDEOS
isGif -> TYPE_GIFS
isRaw -> TYPE_RAWS
isSvg -> TYPE_SVGS
else -> TYPE_IMAGES
}
val path = Uri.decode(file.uri.toString().replaceFirst("${context.config.OTGTreeUri}/document/${context.config.OTGPartition}%3A", "${context.config.OTGPath}/"))
val videoDuration = if (getVideoDurations) path.getVideoDuration() else 0
val isFavorite = favoritePaths.contains(path)
val medium = Medium(null, filename, path, folder, dateModified, dateTaken, size, type, videoDuration, isFavorite, 0L)
media.add(medium)
}
return media
}
private fun getFolderDateTakens(folder: String): HashMap<String, Long> {
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,