Merge pull request #60 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2019-11-10 00:19:15 +03:00 committed by GitHub
commit 1fee52d849
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 269 additions and 126 deletions

View file

@ -1,6 +1,12 @@
Changelog
==========
Version 6.10.4 *(2019-11-05)*
----------------------------
* Improved USB device handling
* Some smaller stability and translation improvements
Version 6.10.3 *(2019-10-27)*
----------------------------

View file

@ -15,8 +15,8 @@ android {
applicationId "com.simplemobiletools.gallery.pro"
minSdkVersion 21
targetSdkVersion 28
versionCode 270
versionName "6.10.3"
versionCode 271
versionName "6.10.4"
multiDexEnabled true
setProperty("archivesBaseName", "gallery")
vectorDrawables.useSupportLibrary = true
@ -62,12 +62,12 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.18.14'
implementation 'com.simplemobiletools:commons:5.19.2'
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'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.18'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.6'
implementation 'com.google.vr:sdk-panowidget:1.180.0'
implementation 'com.google.vr:sdk-videowidget:1.180.0'

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) {
@ -891,7 +891,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
override fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>) {
val filtered = fileDirItems.filter { File(it.path).isFile && it.path.isMediaFile() } as ArrayList
val filtered = fileDirItems.filter { !getIsPathDirectory(it.path) && it.path.isMediaFile() } as ArrayList
if (filtered.isEmpty()) {
return
}

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
}
@ -945,7 +945,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun deleteConfirmed() {
val path = getCurrentMedia().getOrNull(mPos)?.path ?: return
if (File(path).isDirectory || !path.isMediaFile()) {
if (getIsPathDirectory(path) || !path.isMediaFile()) {
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()
}
}
@ -228,22 +228,57 @@ fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDelet
fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, mediumDao: MediumDao = galleryDB.MediumDao(), callback: ((wasSuccess: Boolean) -> Unit)?) {
ensureBackgroundThread {
var pathsCnt = paths.size
paths.forEach {
val file = File(it)
val internalFile = File(recycleBinPath, it)
val lastModified = file.lastModified()
try {
if (file.copyRecursively(internalFile, true)) {
mediumDao.updateDeleted("$RECYCLE_BIN$it", System.currentTimeMillis(), it)
pathsCnt--
val OTGPath = config.OTGPath
if (config.keepLastModified) {
internalFile.setLastModified(lastModified)
for (source in paths) {
if (OTGPath.isNotEmpty() && source.startsWith(OTGPath)) {
var inputStream: InputStream? = null
var out: OutputStream? = null
try {
val destination = "$recycleBinPath/$source"
val fileDocument = getSomeDocumentFile(source)
inputStream = applicationContext.contentResolver.openInputStream(fileDocument?.uri)
out = getFileOutputStreamSync(destination, source.getMimeType())
var copiedSize = 0L
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = inputStream.read(buffer)
while (bytes >= 0) {
out!!.write(buffer, 0, bytes)
copiedSize += bytes
bytes = inputStream.read(buffer)
}
out?.flush()
if (fileDocument?.getItemSize(true) == copiedSize && getDoesFilePathExist(destination)) {
mediumDao.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
pathsCnt--
}
} catch (e: Exception) {
showErrorToast(e)
return@ensureBackgroundThread
} finally {
inputStream?.close()
out?.close()
}
} else {
val file = File(source)
val internalFile = File(recycleBinPath, source)
val lastModified = file.lastModified()
try {
if (file.copyRecursively(internalFile, true)) {
mediumDao.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
pathsCnt--
if (config.keepLastModified) {
internalFile.setLastModified(lastModified)
}
}
} catch (e: Exception) {
showErrorToast(e)
return@ensureBackgroundThread
}
} catch (e: Exception) {
showErrorToast(e)
return@forEach
}
}
callback?.invoke(pathsCnt == 0)
@ -271,8 +306,19 @@ fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, mediumDa
try {
out = getFileOutputStreamSync(destination, source.getMimeType())
inputStream = getFileInputStreamSync(source)
inputStream.copyTo(out!!)
if (File(source).length() == File(destination).length()) {
var copiedSize = 0L
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = inputStream!!.read(buffer)
while (bytes >= 0) {
out!!.write(buffer, 0, bytes)
copiedSize += bytes
bytes = inputStream.read(buffer)
}
out?.flush()
if (File(source).length() == copiedSize) {
mediumDao.updateDeleted(destination.removePrefix(recycleBinPath), 0, "$RECYCLE_BIN$destination")
}
newPaths.add(destination)
@ -519,7 +565,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 {
@ -444,7 +444,7 @@ class PhotoFragment : ViewPagerFragment() {
}
}
override fun onError(e: Exception) {}
override fun onError(e: Exception?) {}
})
} catch (ignored: Exception) {
}
@ -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,

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it. -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Navegueu pels vostres records sense interrupcions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Jednoduchá Galerie Pro: Organizér a editor fotografií</string>
<string name="app_title">Jednoduchá Galerie Pro - Organizér a editor fotografií</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Billedhåndtering</string>
<string name="app_title">Simple Gallery Pro - Billedhåndtering</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -234,7 +234,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Schlichte Galerie Pro: Foto Manager &amp; Editor</string>
<string name="app_title">Schlichte Galerie Pro - Foto Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">ΑΠΛΗ ΣΥΛΛΟΓΗ Pro: Διαχείριση &amp; Επεξεργασία</string>
<string name="app_title">ΑΠΛΗ ΣΥΛΛΟΓΗ Pro - Διαχείριση &amp; Επεξεργασία</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Ξεφυλλίστε τις αναμνήσεις σας χωρίς διακοπές.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Explore sus recuerdos sin interrupciones.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -233,7 +233,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -236,7 +236,7 @@ Ezzel csak a kiválasztott mappák láthatók, mivel a kizárás és a befoglal
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Pengelola &amp; Penyunting Foto</string>
<string name="app_title">Simple Gallery Pro - Pengelola &amp; Penyunting Foto</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Pengelola &amp; Penyunting Foto</string>
<string name="app_title">Simple Gallery Pro - Pengelola &amp; Penyunting Foto</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Semplice Galleria Pro: gestore di foto &amp; editor</string>
<string name="app_title">Semplice Galleria Pro - gestore di foto &amp; editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -233,7 +233,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -234,7 +234,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Galeria Pro: Gerenciador de Imagens</string>
<string name="app_title">Simple Galeria Pro - Gerenciador de Imagens</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Editor e gestor de fotos</string>
<string name="app_title">Simple Gallery Pro - Editor e gestor de fotos</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Галерея Pro: управление изображениями</string>
<string name="app_title">Галерея Pro - управление изображениями</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Просматривайте свои воспоминания без перерывов.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Jednoduchá galéria Pro: Foto organizér a editor</string>
<string name="app_title">Jednoduchá galéria Pro - Foto organizér a editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Prehliadajte svoje spomienky bez prerušenia.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Pregledovalnik fotografij</string>
<string name="app_title">Simple Gallery Pro - Pregledovalnik fotografij</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Basit Galeri Pro: Fotoğraf Yönetici &amp; Düzenleyici</string>
<string name="app_title">Basit Galeri Pro - Fotoğraf Yönetici &amp; Düzenleyici</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: фотоменеджер і редактор</string>
<string name="app_title">Simple Gallery Pro - фотоменеджер і редактор</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -233,7 +233,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">简约图库 Pro: 图片管理 &amp; 编辑</string>
<string name="app_title">简约图库 Pro - 图片管理 &amp; 编辑</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">簡易相簿 Pro: 相片管理&amp;編輯器</string>
<string name="app_title">簡易相簿 Pro - 相片管理&amp;編輯器</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">簡易相簿 Pro: 相片管理&amp;編輯器</string>
<string name="app_title">簡易相簿 Pro - 相片管理&amp;編輯器</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">毫無阻礙地瀏覽您的回憶。</string>
<string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- App title has to have less than 50 characters. If you cannot squeeze it, just remove a part of it -->
<string name="app_title">Simple Gallery Pro: Photo Manager &amp; Editor</string>
<string name="app_title">Simple Gallery Pro - Photo Manager &amp; Editor</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description">

View file

@ -9,7 +9,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View file

@ -1 +1 @@
Simple Gallery Pro: Photo Manager & Editor
Simple Gallery Pro - Photo Manager & Editor

View file

@ -1 +1 @@
Jednoduchá Galerie Pro: Organizér a editor fotografií
Jednoduchá Galerie Pro - Organizér a editor fotografií

View file

@ -1 +1 @@
Simple Gallery Pro: Billedhåndtering
Simple Gallery Pro - Billedhåndtering

View file

@ -1 +1 @@
Schlichte Galerie Pro: Foto Manager & Editor
Schlichte Galerie Pro - Foto Manager & Editor

View file

@ -1 +1 @@
ΑΠΛΗ ΣΥΛΛΟΓΗ Pro: Διαχείριση & Επεξεργασία
ΑΠΛΗ ΣΥΛΛΟΓΗ Pro - Διαχείριση & Επεξεργασία

View file

@ -1 +1 @@
Simple Gallery Pro: Photo Manager & Editor
Simple Gallery Pro - Photo Manager & Editor

View file

@ -1 +1 @@
Simple Gallery Pro: Pengelola & Penyunting Foto
Simple Gallery Pro - Pengelola & Penyunting Foto

View file

@ -1 +1 @@
Simple Gallery Pro: Pengelola & Penyunting Foto
Simple Gallery Pro - Pengelola & Penyunting Foto

View file

@ -1 +1 @@
Semplice Galleria Pro: gestore di foto & editor
Semplice Galleria Pro - gestore di foto & editor

View file

@ -1 +1 @@
Simple Gallery Pro: Photo Manager & Editor
Simple Gallery Pro - Photo Manager & Editor

View file

@ -1 +1 @@
Simple Galeria Pro: Gerenciador de Imagens
Simple Galeria Pro - Gerenciador de Imagens

View file

@ -1 +1 @@
Simple Gallery Pro: Editor e gestor de fotos
Simple Gallery Pro - Editor e gestor de fotos

View file

@ -1 +1 @@
Галерея: управление изображениями
Галерея Pro - управление изображениями

View file

@ -1 +1 @@
Jednoduchá galéria Pro: Foto organizér a editor
Jednoduchá galéria Pro - Foto organizér a editor

View file

@ -1 +1 @@
Simple Gallery Pro: Photo Manager & Editor
Simple Gallery Pro - Photo Manager & Editor

View file

@ -1 +1 @@
Basit Galeri Pro: Fotoğraf Yönetici & Düzenleyici
Basit Galeri Pro - Fotoğraf Yönetici & Düzenleyici

View file

@ -1 +1 @@
Simple Gallery Pro: фотоменеджер і редактор
Simple Gallery Pro - фотоменеджер і редактор

View file

@ -1 +1 @@
简约图库 Pro: 图片管理 & 编辑
简约图库 Pro - 图片管理 & 编辑

View file

@ -1 +1 @@
簡易相簿 Pro: 相片管理&編輯器
簡易相簿 Pro - 相片管理&編輯器

View file

@ -1 +1 @@
簡易相簿 Pro: 相片管理&編輯器
簡易相簿 Pro - 相片管理&編輯器