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 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)* Version 6.10.3 *(2019-10-27)*
---------------------------- ----------------------------

View file

@ -15,8 +15,8 @@ android {
applicationId "com.simplemobiletools.gallery.pro" applicationId "com.simplemobiletools.gallery.pro"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode 270 versionCode 271
versionName "6.10.3" versionName "6.10.4"
multiDexEnabled true multiDexEnabled true
setProperty("archivesBaseName", "gallery") setProperty("archivesBaseName", "gallery")
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -62,12 +62,12 @@ android {
} }
dependencies { 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 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'it.sephiroth.android.exif:library:1.0.1' implementation 'it.sephiroth.android.exif:library:1.0.1'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.18' 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.android.exoplayer:exoplayer-core:2.9.6'
implementation 'com.google.vr:sdk-panowidget:1.180.0' implementation 'com.google.vr:sdk-panowidget:1.180.0'
implementation 'com.google.vr:sdk-videowidget: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() { private fun removeTempFolder() {
if (config.tempFolderPath.isNotEmpty()) { if (config.tempFolderPath.isNotEmpty()) {
val newFolder = File(config.tempFolderPath) 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) { 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) 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 = "" config.tempFolderPath = ""
@ -408,16 +408,6 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
config.OTGPath = otgPath config.OTGPath = otgPath
config.addIncludedFolder(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" "/storage/emulated/0/Android/data/com.facebook.orca/files/stickers"
) )
val OTGPath = config.OTGPath
spamFolders.forEach { spamFolders.forEach {
if (File(it).exists()) { if (getDoesFilePathExist(it, OTGPath)) {
config.addExcludedFolder(it) config.addExcludedFolder(it)
} }
} }
@ -570,7 +561,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
(it.isGif() && filter and TYPE_GIFS != 0) || (it.isGif() && filter and TYPE_GIFS != 0) ||
(it.isRawFast() && filter and TYPE_RAWS != 0) || (it.isRawFast() && filter and TYPE_RAWS != 0) ||
(it.isSvg() && filter and TYPE_SVGS != 0)) (it.isSvg() && filter and TYPE_SVGS != 0))
}?.mapTo(itemsToDelete) { it.toFileDirItem() } }?.mapTo(itemsToDelete) { it.toFileDirItem(applicationContext) }
} }
if (config.useRecycleBin) { if (config.useRecycleBin) {
@ -590,13 +581,14 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
} }
private fun deleteFilteredFileDirItems(fileDirItems: ArrayList<FileDirItem>, folders: ArrayList<File>) { private fun deleteFilteredFileDirItems(fileDirItems: ArrayList<FileDirItem>, folders: ArrayList<File>) {
val OTGPath = config.OTGPath
deleteFiles(fileDirItems) { deleteFiles(fileDirItems) {
runOnUiThread { runOnUiThread {
refreshItems() refreshItems()
} }
ensureBackgroundThread { ensureBackgroundThread {
folders.filter { !it.exists() }.forEach { folders.filter { !getDoesFilePathExist(it.absolutePath, OTGPath) }.forEach {
mDirectoryDao.deleteDirPath(it.absolutePath) mDirectoryDao.deleteDirPath(it.absolutePath)
} }
} }
@ -1115,13 +1107,14 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun checkInvalidDirectories(dirs: ArrayList<Directory>) { private fun checkInvalidDirectories(dirs: ArrayList<Directory>) {
val invalidDirs = ArrayList<Directory>() val invalidDirs = ArrayList<Directory>()
val OTGPath = config.OTGPath
dirs.filter { !it.areFavorites() && !it.isRecycleBin() }.forEach { dirs.filter { !it.areFavorites() && !it.isRecycleBin() }.forEach {
if (!File(it.path).exists()) { if (!getDoesFilePathExist(it.path, OTGPath)) {
invalidDirs.add(it) invalidDirs.add(it)
} else if (it.path != config.tempFolderPath) { } 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 { 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 } ?: false
if (!hasMediaFile) { if (!hasMediaFile) {
@ -1151,6 +1144,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
dirs.removeAll(invalidDirs) dirs.removeAll(invalidDirs)
setupAdapter(dirs) setupAdapter(dirs)
invalidDirs.forEach { invalidDirs.forEach {
toast("invalid ${it.path}", Toast.LENGTH_LONG)
try { try {
mDirectoryDao.deleteDirPath(it.path) mDirectoryDao.deleteDirPath(it.path)
} catch (ignored: Exception) { } catch (ignored: Exception) {
@ -1243,9 +1237,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
} }
oftenRepeatedPaths.removeAll(substringToRemove) oftenRepeatedPaths.removeAll(substringToRemove)
val OTGPath = config.OTGPath
oftenRepeatedPaths.forEach { oftenRepeatedPaths.forEach {
val file = File("$internalPath/$it") val file = File("$internalPath/$it")
if (file.exists()) { if (getDoesFilePathExist(file.absolutePath, OTGPath)) {
config.addExcludedFolder(file.absolutePath) config.addExcludedFolder(file.absolutePath)
} }
} }

View file

@ -569,7 +569,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun deleteDirectoryIfEmpty() { private fun deleteDirectoryIfEmpty() {
val fileDirItem = FileDirItem(mPath, mPath.getFilenameFromPath(), true) 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) tryDeleteFileDirItem(fileDirItem, true, true)
} }
} }
@ -606,7 +606,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
val newMedia = it val newMedia = it
try { try {
gotMedia(newMedia, false) 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) mMediumDao.deleteMediumPath(it.path)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -891,7 +891,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
} }
override fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>) { 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()) { if (filtered.isEmpty()) {
return return
} }

View file

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

View file

@ -278,7 +278,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
return return
} }
if (!File(mPath).exists() && getPortraitPath() == "") { if (!getDoesFilePathExist(mPath) && getPortraitPath() == "") {
finish() finish()
return return
} }
@ -945,7 +945,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun deleteConfirmed() { private fun deleteConfirmed() {
val path = getCurrentMedia().getOrNull(mPos)?.path ?: return val path = getCurrentMedia().getOrNull(mPos)?.path ?: return
if (File(path).isDirectory || !path.isMediaFile()) { if (getIsPathDirectory(path) || !path.isMediaFile()) {
return return
} }
@ -1066,7 +1066,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun deleteDirectoryIfEmpty() { private fun deleteDirectoryIfEmpty() {
val fileDirItem = FileDirItem(mDirectory, mDirectory.getFilenameFromPath(), File(mDirectory).isDirectory) 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) tryDeleteFileDirItem(fileDirItem, true, true)
scanPathRecursively(mDirectory) scanPathRecursively(mDirectory)
} }

View file

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

View file

@ -7,7 +7,6 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.gallery.pro.R import com.simplemobiletools.gallery.pro.R
import kotlinx.android.synthetic.main.dialog_save_as.view.* 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) { 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 return@setOnClickListener
} }
if (File(newPath).exists()) { if (activity.getDoesFilePathExist(newPath)) {
val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newFilename) val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newFilename)
ConfirmationDialog(activity, title) { ConfirmationDialog(activity, title) {
callback(newPath) callback(newPath)

View file

@ -131,7 +131,7 @@ fun AppCompatActivity.hideSystemUI(toggleActionBarVisibility: Boolean) {
fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) { fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) {
val file = File(path, NOMEDIA) val file = File(path, NOMEDIA)
if (file.exists()) { if (getDoesFilePathExist(file.absolutePath)) {
callback() callback()
return return
} }
@ -164,12 +164,12 @@ fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) {
fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = null) { fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = null) {
val file = File(path, NOMEDIA) val file = File(path, NOMEDIA)
if (!file.exists()) { if (!getDoesFilePathExist(file.absolutePath)) {
callback?.invoke() callback?.invoke()
return return
} }
tryDeleteFileDirItem(file.toFileDirItem(), false, false) { tryDeleteFileDirItem(file.toFileDirItem(applicationContext), false, false) {
callback?.invoke() 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)?) { fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, mediumDao: MediumDao = galleryDB.MediumDao(), callback: ((wasSuccess: Boolean) -> Unit)?) {
ensureBackgroundThread { ensureBackgroundThread {
var pathsCnt = paths.size var pathsCnt = paths.size
paths.forEach { val OTGPath = config.OTGPath
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--
if (config.keepLastModified) { for (source in paths) {
internalFile.setLastModified(lastModified) 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) callback?.invoke(pathsCnt == 0)
@ -271,8 +306,19 @@ fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, mediumDa
try { try {
out = getFileOutputStreamSync(destination, source.getMimeType()) out = getFileOutputStreamSync(destination, source.getMimeType())
inputStream = getFileInputStreamSync(source) 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") mediumDao.updateDeleted(destination.removePrefix(recycleBinPath), 0, "$RECYCLE_BIN$destination")
} }
newPaths.add(destination) newPaths.add(destination)
@ -519,7 +565,9 @@ fun BaseSimpleActivity.copyFile(source: String, destination: String) {
try { try {
out = getFileOutputStreamSync(destination, source.getMimeType()) out = getFileOutputStreamSync(destination, source.getMimeType())
inputStream = getFileInputStreamSync(source) inputStream = getFileInputStreamSync(source)
inputStream.copyTo(out!!) inputStream!!.copyTo(out!!)
} catch (e: Exception) {
showErrorToast(e)
} finally { } finally {
inputStream?.close() inputStream?.close()
out?.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 selection = "${MediaStore.Files.FileColumns.MEDIA_TYPE} = ? AND ${MediaStore.Files.FileColumns.TITLE} LIKE ?"
val selectionArgs = arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_NONE.toString(), "%$NOMEDIA%") val selectionArgs = arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_NONE.toString(), "%$NOMEDIA%")
val sortOrder = "${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC" val sortOrder = "${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
val OTGPath = config.OTGPath
var cursor: Cursor? = null var cursor: Cursor? = null
try { try {
@ -374,7 +375,7 @@ fun Context.getNoMediaFolders(callback: (folders: ArrayList<String>) -> Unit) {
do { do {
val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA) ?: continue val path = cursor.getStringValue(MediaStore.Files.FileColumns.DATA) ?: continue
val noMediaFile = File(path) val noMediaFile = File(path)
if (noMediaFile.exists() && noMediaFile.name == NOMEDIA) { if (getDoesFilePathExist(noMediaFile.absolutePath, OTGPath) && noMediaFile.name == NOMEDIA) {
folders.add("${noMediaFile.parent}/") folders.add("${noMediaFile.parent}/")
} }
} while (cursor.moveToNext()) } while (cursor.moveToNext())
@ -661,9 +662,10 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
mediaFetcher.sortMedia(media, config.getFileSorting(pathToUse)) mediaFetcher.sortMedia(media, config.getFileSorting(pathToUse))
val grouped = mediaFetcher.groupMedia(media, pathToUse) val grouped = mediaFetcher.groupMedia(media, pathToUse)
callback(grouped.clone() as ArrayList<ThumbnailItem>) callback(grouped.clone() as ArrayList<ThumbnailItem>)
val OTGPath = config.OTGPath
val mediaToDelete = ArrayList<Medium>() val mediaToDelete = ArrayList<Medium>()
media.filter { !File(it.path).exists() }.forEach { media.filter { !getDoesFilePathExist(it.path, OTGPath) }.forEach {
if (it.path.startsWith(recycleBinPath)) { if (it.path.startsWith(recycleBinPath)) {
deleteDBPath(mediumDao, it.path) deleteDBPath(mediumDao, it.path)
} else { } 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()) { fun Context.removeInvalidDBDirectories(dirs: ArrayList<Directory>? = null, directoryDao: DirectoryDao = galleryDB.DirectoryDao()) {
val dirsToCheck = dirs ?: directoryDao.getAll() 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 { try {
directoryDao.deleteDirPath(it.path) directoryDao.deleteDirPath(it.path)
} catch (ignored: Exception) { } 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> { fun Context.getFavoritePaths(): ArrayList<String> {
return try { return try {
galleryDB.MediumDao().getFavoritePaths() as ArrayList<String> 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) { fun Context.addPathToDB(path: String) {
ensureBackgroundThread { ensureBackgroundThread {
if (!File(path).exists()) { if (!getDoesFilePathExist(path)) {
return@ensureBackgroundThread return@ensureBackgroundThread
} }
@ -833,13 +840,18 @@ fun Context.addPathToDB(path: String) {
fun Context.createDirectoryFromMedia(path: String, curMedia: ArrayList<Medium>, albumCovers: ArrayList<AlbumCover>, hiddenString: String, fun Context.createDirectoryFromMedia(path: String, curMedia: ArrayList<Medium>, albumCovers: ArrayList<AlbumCover>, hiddenString: String,
includedFolders: MutableSet<String>, isSortingAscending: Boolean, getProperFileSize: Boolean): Directory { 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 { albumCovers.forEach {
if (it.path == path && File(it.tmb).exists()) { if (it.path == path && getDoesFilePathExist(it.tmb, OTGPath)) {
thumbnail = it.tmb 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 defaultMedium = Medium(0, "", "", "", 0L, 0L, 0L, 0, 0, false, 0L)
val firstItem = curMedia.firstOrNull() ?: defaultMedium val firstItem = curMedia.firstOrNull() ?: defaultMedium
val lastItem = curMedia.lastOrNull() ?: defaultMedium val lastItem = curMedia.lastOrNull() ?: defaultMedium

View file

@ -357,11 +357,11 @@ class PhotoFragment : ViewPagerFragment() {
private fun loadGif() { private fun loadGif() {
try { try {
val path = mMedium.path val pathToLoad = getPathToLoad(mMedium)
val source = if (path.startsWith("content://") || path.startsWith("file://")) { val source = if (pathToLoad.startsWith("content://") || pathToLoad.startsWith("file://")) {
InputSource.UriSource(context!!.contentResolver, Uri.parse(path)) InputSource.UriSource(context!!.contentResolver, Uri.parse(pathToLoad))
} else { } else {
InputSource.FileSource(path) InputSource.FileSource(pathToLoad)
} }
mView.apply { mView.apply {
@ -444,7 +444,7 @@ class PhotoFragment : ViewPagerFragment() {
} }
} }
override fun onError(e: Exception) {} override fun onError(e: Exception?) {}
}) })
} catch (ignored: 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() { private fun openPanorama() {
Intent(context, PanoramaPhotoActivity::class.java).apply { Intent(context, PanoramaPhotoActivity::class.java).apply {

View file

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

View file

@ -2,6 +2,7 @@ package com.simplemobiletools.gallery.pro.helpers
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri
import android.os.Environment import android.os.Environment
import android.provider.BaseColumns import android.provider.BaseColumns
import android.provider.MediaStore import android.provider.MediaStore
@ -27,8 +28,15 @@ class MediaFetcher(val context: Context) {
} }
val curMedia = ArrayList<Medium>() val curMedia = ArrayList<Medium>()
val newMedia = getMediaInFolder(curPath, isPickImage, isPickVideo, filterMedia, getProperDateTaken, getProperFileSize, favoritePaths, getVideoDurations) if (context.isPathOnOTG(curPath)) {
curMedia.addAll(newMedia) 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) { if (sortMedia) {
sortMedia(curMedia, context.config.getFileSorting(curPath)) sortMedia(curMedia, context.config.getFileSorting(curPath))
@ -39,12 +47,13 @@ class MediaFetcher(val context: Context) {
fun getFoldersToScan(): ArrayList<String> { fun getFoldersToScan(): ArrayList<String> {
return try { return try {
val OTGPath = context.config.OTGPath
val folders = getLatestFileFolders() val folders = getLatestFileFolders()
folders.addAll(arrayListOf( folders.addAll(arrayListOf(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString(), Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString(),
"${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera", "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)}/Camera",
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString()
).filter { File(it).exists() }) ).filter { context.getDoesFilePathExist(it, OTGPath) })
val filterMedia = context.config.filterMedia val filterMedia = context.config.filterMedia
val uri = MediaStore.Files.getContentUri("external") val uri = MediaStore.Files.getContentUri("external")
@ -167,7 +176,8 @@ class MediaFetcher(val context: Context) {
val foldersToIgnore = arrayListOf("/storage/emulated/legacy") val foldersToIgnore = arrayListOf("/storage/emulated/legacy")
val config = context.config val config = context.config
val includedFolders = config.includedFolders 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 { cursor.use {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
@ -327,6 +337,72 @@ class MediaFetcher(val context: Context) {
return media 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> { private fun getFolderDateTakens(folder: String): HashMap<String, Long> {
val projection = arrayOf( val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DISPLAY_NAME,

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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. --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Navegueu pels vostres records sense interrupcions.</string> <string name="app_short_description">Navegueu pels vostres records sense interrupcions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -234,7 +234,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Ξεφυλλίστε τις αναμνήσεις σας χωρίς διακοπές.</string> <string name="app_short_description">Ξεφυλλίστε τις αναμνήσεις σας χωρίς διακοπές.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Explore sus recuerdos sin interrupciones.</string> <string name="app_short_description">Explore sus recuerdos sin interrupciones.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -233,7 +233,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <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 --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -233,7 +233,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -234,7 +234,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Просматривайте свои воспоминания без перерывов.</string> <string name="app_short_description">Просматривайте свои воспоминания без перерывов.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Prehliadajte svoje spomienky bez prerušenia.</string> <string name="app_short_description">Prehliadajte svoje spomienky bez prerušenia.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -233,7 +233,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">毫無阻礙地瀏覽您的回憶。</string> <string name="app_short_description">毫無阻礙地瀏覽您的回憶。</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -235,7 +235,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- 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 --> <!-- 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 --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions.</string> <string name="app_short_description">Browse your memories without any interruptions.</string>
<string name="app_long_description"> <string name="app_long_description">

View file

@ -9,7 +9,7 @@ buildscript {
} }
dependencies { 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" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // 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 - 相片管理&編輯器