Matheus Victor 2020-01-31 12:03:42 -03:00
commit 0c6d728a65
36 changed files with 479 additions and 362 deletions

View file

@ -1,6 +1,18 @@
Changelog
==========
Version 6.12.0 *(2020-01-28)*
----------------------------
* Properly handle sorting by date taken after using "Fix Date Taken values" on the wrong files
* Fixed some issues at copying files, when the source was on an SD card
* Change the way Favorite items are stored, to avoid accidental removal
* Improved video looping (by ForgottenUmbrella)
* Recognize a new type of panoramic photos
* Properly remember last video playback position if the video is paused on exit
* Properly color the top status bar icons at using light primary colors
* Other UX and translation improvements
Version 6.11.8 *(2020-01-19)*
----------------------------

View file

@ -17,8 +17,8 @@ android {
applicationId "com.simplemobiletools.gallery.pro"
minSdkVersion 21
targetSdkVersion 28
versionCode 288
versionName "6.11.8"
versionCode 289
versionName "6.12.0"
multiDexEnabled true
setProperty("archivesBaseName", "gallery")
vectorDrawables.useSupportLibrary = true
@ -69,7 +69,7 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.21.25'
implementation 'com.simplemobiletools:commons:5.21.28'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'it.sephiroth.android.exif:library:1.0.1'

View file

@ -36,9 +36,7 @@ import com.simplemobiletools.gallery.pro.dialogs.ChangeViewTypeDialog
import com.simplemobiletools.gallery.pro.dialogs.FilterMediaDialog
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
import com.simplemobiletools.gallery.pro.interfaces.DirectoryOperationsListener
import com.simplemobiletools.gallery.pro.interfaces.MediumDao
import com.simplemobiletools.gallery.pro.jobs.NewPhotoFetcher
import com.simplemobiletools.gallery.pro.models.Directory
import com.simplemobiletools.gallery.pro.models.Medium
@ -85,17 +83,11 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private var mStoredTextColor = 0
private var mStoredPrimaryColor = 0
private lateinit var mMediumDao: MediumDao
private lateinit var mDirectoryDao: DirectoryDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
appLaunched(BuildConfig.APPLICATION_ID)
mMediumDao = galleryDB.MediumDao()
mDirectoryDao = galleryDB.DirectoryDao()
if (savedInstanceState == null) {
config.temporarilyShowHidden = false
config.tempSkipDeleteConfirmation = false
@ -464,7 +456,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
val getImagesOnly = mIsPickImageIntent || mIsGetImageContentIntent
val getVideosOnly = mIsPickVideoIntent || mIsGetVideoContentIntent
getCachedDirectories(getVideosOnly, getImagesOnly, mDirectoryDao) {
getCachedDirectories(getVideosOnly, getImagesOnly) {
gotDirectories(addTempFolderIfNeeded(it))
}
}
@ -569,7 +561,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
val pathsToDelete = ArrayList<String>()
itemsToDelete.mapTo(pathsToDelete) { it.path }
movePathsInRecycleBin(pathsToDelete, mMediumDao) {
movePathsInRecycleBin(pathsToDelete) {
if (it) {
deleteFilteredFileDirItems(itemsToDelete, folders)
} else {
@ -590,7 +582,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
ensureBackgroundThread {
folders.filter { !getDoesFilePathExist(it.absolutePath, OTGPath) }.forEach {
mDirectoryDao.deleteDirPath(it.absolutePath)
directoryDao.deleteDirPath(it.absolutePath)
}
if (config.deleteEmptyFolders) {
@ -936,16 +928,16 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
setupAdapter(dirs)
// update directories and media files in the local db, delete invalid items
updateDBDirectory(directory, mDirectoryDao)
updateDBDirectory(directory)
if (!directory.isRecycleBin()) {
mMediumDao.insertAll(curMedia)
mediaDB.insertAll(curMedia)
}
getCachedMedia(directory.path, getVideosOnly, getImagesOnly, mMediumDao) {
getCachedMedia(directory.path, getVideosOnly, getImagesOnly) {
it.forEach {
if (!curMedia.contains(it)) {
val path = (it as? Medium)?.path
if (path != null) {
deleteDBPath(mMediumDao, path)
deleteDBPath(path)
}
}
}
@ -955,7 +947,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
if (dirPathsToRemove.isNotEmpty()) {
val dirsToRemove = dirs.filter { dirPathsToRemove.contains(it.path) }
dirsToRemove.forEach {
mDirectoryDao.deleteDirPath(it.path)
directoryDao.deleteDirPath(it.path)
}
dirs.removeAll(dirsToRemove)
setupAdapter(dirs)
@ -999,9 +991,9 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
dirs.add(newDir)
setupAdapter(dirs)
try {
mDirectoryDao.insert(newDir)
directoryDao.insert(newDir)
if (folder != RECYCLE_BIN) {
mMediumDao.insertAll(newMedia)
mediaDB.insertAll(newMedia)
}
} catch (ignored: Exception) {
}
@ -1156,7 +1148,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
if (config.useRecycleBin) {
try {
val binFolder = dirs.firstOrNull { it.path == RECYCLE_BIN }
if (binFolder != null && mMediumDao.getDeletedMedia().isEmpty()) {
if (binFolder != null && mediaDB.getDeletedMedia().isEmpty()) {
invalidDirs.add(binFolder)
}
} catch (ignored: Exception) {
@ -1168,7 +1160,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
setupAdapter(dirs)
invalidDirs.forEach {
try {
mDirectoryDao.deleteDirPath(it.path)
directoryDao.deleteDirPath(it.path)
} catch (ignored: Exception) {
}
}
@ -1218,7 +1210,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
Handler().postDelayed({
ensureBackgroundThread {
try {
mMediumDao.deleteOldRecycleBinItems(System.currentTimeMillis() - MONTH_MILLISECONDS)
mediaDB.deleteOldRecycleBinItems(System.currentTimeMillis() - MONTH_MILLISECONDS)
} catch (e: Exception) {
}
}
@ -1283,7 +1275,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
override fun updateDirectories(directories: ArrayList<Directory>) {
ensureBackgroundThread {
storeDirectoryItems(directories, mDirectoryDao)
storeDirectoryItems(directories)
removeInvalidDBDirectories()
}
}

View file

@ -39,9 +39,7 @@ import com.simplemobiletools.gallery.pro.databases.GalleryDatabase
import com.simplemobiletools.gallery.pro.dialogs.*
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
import com.simplemobiletools.gallery.pro.interfaces.MediaOperationsListener
import com.simplemobiletools.gallery.pro.interfaces.MediumDao
import com.simplemobiletools.gallery.pro.models.Medium
import com.simplemobiletools.gallery.pro.models.ThumbnailItem
import com.simplemobiletools.gallery.pro.models.ThumbnailSection
@ -78,9 +76,6 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private var mStoredTextColor = 0
private var mStoredPrimaryColor = 0
private lateinit var mMediumDao: MediumDao
private lateinit var mDirectoryDao: DirectoryDao
companion object {
var mMedia = ArrayList<ThumbnailItem>()
}
@ -89,9 +84,6 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_media)
mMediumDao = galleryDB.MediumDao()
mDirectoryDao = galleryDB.DirectoryDao()
intent.apply {
mIsGetImageIntent = getBooleanExtra(GET_IMAGE_INTENT, false)
mIsGetVideoIntent = getBooleanExtra(GET_VIDEO_INTENT, false)
@ -495,9 +487,9 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun restoreAllFiles() {
val paths = mMedia.filter { it is Medium }.map { (it as Medium).path } as ArrayList<String>
restoreRecycleBinPaths(paths, mMediumDao) {
restoreRecycleBinPaths(paths) {
ensureBackgroundThread {
mDirectoryDao.deleteDirPath(RECYCLE_BIN)
directoryDao.deleteDirPath(RECYCLE_BIN)
}
finish()
}
@ -584,7 +576,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
if (mLoadedInitialPhotos) {
startAsyncTask()
} else {
getCachedMedia(mPath, mIsGetVideoIntent, mIsGetImageIntent, mMediumDao) {
getCachedMedia(mPath, mIsGetVideoIntent, mIsGetImageIntent) {
if (it.isEmpty()) {
runOnUiThread {
media_refresh_layout.isRefreshing = true
@ -608,7 +600,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
try {
gotMedia(newMedia, false)
oldMedia.filter { !newMedia.contains(it) }.mapNotNull { it as? Medium }.filter { !getDoesFilePathExist(it.path) }.forEach {
mMediumDao.deleteMediumPath(it.path)
mediaDB.deleteMediumPath(it.path)
}
} catch (e: Exception) {
}
@ -627,7 +619,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
if (mPath == FAVORITES) {
ensureBackgroundThread {
mDirectoryDao.deleteDirPath(FAVORITES)
directoryDao.deleteDirPath(FAVORITES)
}
}
@ -641,7 +633,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun deleteDBDirectory() {
ensureBackgroundThread {
try {
mDirectoryDao.deleteDirPath(mPath)
directoryDao.deleteDirPath(mPath)
} catch (ignored: Exception) {
}
}
@ -897,7 +889,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
if (!isFromCache) {
val mediaToInsert = (mMedia).filter { it is Medium && it.deletedTS == 0L }.map { it as Medium }
try {
mMediumDao.insertAll(mediaToInsert)
mediaDB.insertAll(mediaToInsert)
} catch (e: Exception) {
}
}
@ -913,7 +905,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size)
toast(movingItems)
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>, mMediumDao) {
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) {
if (it) {
deleteFilteredFiles(filtered)
} else {
@ -940,7 +932,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
val useRecycleBin = config.useRecycleBin
filtered.forEach {
if (it.path.startsWith(recycleBinPath) || !useRecycleBin) {
deleteDBPath(mMediumDao, it.path)
deleteDBPath(it.path)
}
}
}

View file

@ -315,7 +315,7 @@ class SearchActivity : SimpleActivity(), MediaOperationsListener {
val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size)
toast(movingItems)
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>, galleryDB.MediumDao()) {
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) {
if (it) {
deleteFilteredFiles(filtered)
} else {
@ -342,7 +342,7 @@ class SearchActivity : SimpleActivity(), MediaOperationsListener {
val useRecycleBin = config.useRecycleBin
filtered.forEach {
if (it.path.startsWith(recycleBinPath) || !useRecycleBin) {
deleteDBPath(galleryDB.MediumDao(), it.path)
deleteDBPath(it.path)
}
}
}

View file

@ -13,10 +13,7 @@ import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.dialogs.ManageBottomActionsDialog
import com.simplemobiletools.gallery.pro.dialogs.ManageExtendedDetailsDialog
import com.simplemobiletools.gallery.pro.extensions.config
import com.simplemobiletools.gallery.pro.extensions.emptyTheRecycleBin
import com.simplemobiletools.gallery.pro.extensions.galleryDB
import com.simplemobiletools.gallery.pro.extensions.showRecycleBinEmptyingDialog
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.models.AlbumCover
import kotlinx.android.synthetic.main.activity_settings.*
@ -593,7 +590,7 @@ class SettingsActivity : SimpleActivity() {
private fun setupEmptyRecycleBin() {
ensureBackgroundThread {
try {
mRecycleBinContentSize = galleryDB.MediumDao().getDeletedMedia().sumByLong { it.size }
mRecycleBinContentSize = mediaDB.getDeletedMedia().sumByLong { it.size }
} catch (ignored: Exception) {
}
runOnUiThread {

View file

@ -2,9 +2,42 @@ package com.simplemobiletools.gallery.pro.activities
import android.content.Intent
import com.simplemobiletools.commons.activities.BaseSplashActivity
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.gallery.pro.extensions.config
import com.simplemobiletools.gallery.pro.extensions.favoritesDB
import com.simplemobiletools.gallery.pro.extensions.getFavoriteFromPath
import com.simplemobiletools.gallery.pro.extensions.mediaDB
import com.simplemobiletools.gallery.pro.models.Favorite
class SplashActivity : BaseSplashActivity() {
override fun initActivity() {
// check if previously selected favorite items have been properly migrated into the new Favorites table
if (config.wereFavoritesMigrated) {
launchActivity()
} else {
if (config.appRunCount == 0) {
config.wereFavoritesMigrated = true
launchActivity()
} else {
config.wereFavoritesMigrated = true
ensureBackgroundThread {
val favorites = ArrayList<Favorite>()
val favoritePaths = mediaDB.getFavorites().map { it.path }.toMutableList() as ArrayList<String>
favoritePaths.forEach {
favorites.add(getFavoriteFromPath(it))
}
favoritesDB.insertAll(favorites)
runOnUiThread {
launchActivity()
}
}
}
}
}
private fun launchActivity() {
startActivity(Intent(this, MainActivity::class.java))
finish()
}

View file

@ -214,6 +214,9 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
mExoPlayer = ExoPlayerFactory.newSimpleInstance(applicationContext).apply {
seekParameters = SeekParameters.CLOSEST_SYNC
audioStreamType = C.STREAM_TYPE_MUSIC
if (config.loopVideos) {
repeatMode = Player.REPEAT_MODE_ONE
}
prepare(audioSource)
}
initExoPlayerListeners()
@ -231,7 +234,13 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
override fun onLoadingChanged(isLoading: Boolean) {}
override fun onPositionDiscontinuity(reason: Int) {}
override fun onPositionDiscontinuity(reason: Int) {
// Reset progress views when video loops.
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
video_seekbar.progress = 0
video_curr_time.text = 0.getFormattedDuration()
}
}
override fun onRepeatModeChanged(repeatMode: Int) {}
@ -338,13 +347,9 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen
clearLastVideoSavedProgress()
mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
if (config.loopVideos) {
resumeVideo()
} else {
video_seekbar.progress = video_seekbar.max
video_curr_time.text = mDuration.getFormattedDuration()
pauseVideo()
}
video_seekbar.progress = video_seekbar.max
video_curr_time.text = mDuration.getFormattedDuration()
pauseVideo()
}
private fun didVideoEnd(): Boolean {

View file

@ -364,8 +364,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
if (intent.action == "com.android.camera.action.REVIEW") {
ensureBackgroundThread {
val mediumDao = galleryDB.MediumDao()
if (mediumDao.getMediaFromPath(mPath).isEmpty()) {
if (mediaDB.getMediaFromPath(mPath).isEmpty()) {
val type = when {
mPath.isVideoFast() -> TYPE_VIDEOS
mPath.isGif() -> TYPE_GIFS
@ -375,11 +374,11 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
else -> TYPE_IMAGES
}
val isFavorite = mediumDao.isFavorite(mPath)
val isFavorite = favoritesDB.isFavorite(mPath)
val duration = if (type == TYPE_VIDEOS) mPath.getVideoDuration() else 0
val ts = System.currentTimeMillis()
val medium = Medium(null, mPath.getFilenameFromPath(), mPath, mPath.getParentPath(), ts, ts, File(mPath).length(), type, duration, isFavorite, 0)
mediumDao.insert(medium)
mediaDB.insert(medium)
}
}
}
@ -864,7 +863,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
val medium = getCurrentMedium() ?: return
medium.isFavorite = !medium.isFavorite
ensureBackgroundThread {
galleryDB.MediumDao().updateFavorite(medium.path, medium.isFavorite)
updateFavorite(medium.path, medium.isFavorite)
if (medium.isFavorite) {
mFavoritePaths.add(medium.path)
} else {

View file

@ -163,7 +163,7 @@ class WidgetConfigureActivity : SimpleActivity() {
}
ensureBackgroundThread {
val path = directoryDB.getDirectoryThumbnail(folderPath)
val path = directoryDao.getDirectoryThumbnail(folderPath)
if (path != null) {
runOnUiThread {
loadJpg(path, config_image, config.cropThumbnails)

View file

@ -198,7 +198,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
updateDirs(dirs)
ensureBackgroundThread {
try {
activity.galleryDB.DirectoryDao().updateDirectoryAfterRename(firstDir.tmb, firstDir.name, firstDir.path, sourcePath)
activity.directoryDao.updateDirectoryAfterRename(firstDir.tmb, firstDir.name, firstDir.path, sourcePath)
listener?.refreshItems()
} catch (e: Exception) {
activity.showErrorToast(e)
@ -555,7 +555,8 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
tryEmptyRecycleBin(false)
} else {
ensureBackgroundThread {
activity.galleryDB.MediumDao().clearFavorites()
activity.mediaDB.clearFavorites()
activity.favoritesDB.clearFavorites()
listener?.refreshItems()
}
}

View file

@ -266,10 +266,9 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Thumbnai
private fun toggleFavorites(add: Boolean) {
ensureBackgroundThread {
val mediumDao = activity.galleryDB.MediumDao()
getSelectedItems().forEach {
it.isFavorite = add
mediumDao.updateFavorite(it.path, add)
activity.updateFavorite(it.path, add)
}
activity.runOnUiThread {
listener?.refreshItems()

View file

@ -18,9 +18,9 @@ abstract class GalleryDatabase : RoomDatabase() {
abstract fun WidgetsDao(): WidgetsDao
abstract fun DateTakensDAO(): DateTakensDAO
abstract fun DateTakensDao(): DateTakensDao
abstract fun FavoritesDAO(): FavoritesDAO
abstract fun FavoritesDao(): FavoritesDao
companion object {
private var db: GalleryDatabase? = null

View file

@ -31,7 +31,7 @@ import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.activities.SimpleActivity
import com.simplemobiletools.gallery.pro.dialogs.PickDirectoryDialog
import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
import com.simplemobiletools.gallery.pro.interfaces.MediumDao
import com.simplemobiletools.gallery.pro.models.DateTaken
import com.squareup.picasso.Picasso
import java.io.File
import java.io.FileOutputStream
@ -223,7 +223,7 @@ fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDelet
deleteFile(fileDirItem, allowDeleteFolder) {
if (deleteFromDatabase) {
ensureBackgroundThread {
deleteDBPath(galleryDB.MediumDao(), fileDirItem.path)
deleteDBPath(fileDirItem.path)
runOnUiThread {
callback?.invoke(it)
}
@ -234,7 +234,7 @@ 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>, callback: ((wasSuccess: Boolean) -> Unit)?) {
ensureBackgroundThread {
var pathsCnt = paths.size
val OTGPath = config.OTGPath
@ -261,7 +261,7 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, mediumDao
out?.flush()
if (fileDocument?.getItemSize(true) == copiedSize && getDoesFilePathExist(destination)) {
mediumDao.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
mediaDB.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
pathsCnt--
}
} catch (e: Exception) {
@ -277,7 +277,7 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, mediumDao
val lastModified = file.lastModified()
try {
if (file.copyRecursively(internalFile, true)) {
mediumDao.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
mediaDB.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
pathsCnt--
if (config.keepLastModified) {
@ -295,10 +295,10 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, mediumDao
}
fun BaseSimpleActivity.restoreRecycleBinPath(path: String, callback: () -> Unit) {
restoreRecycleBinPaths(arrayListOf(path), galleryDB.MediumDao(), callback)
restoreRecycleBinPaths(arrayListOf(path), callback)
}
fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, mediumDao: MediumDao = galleryDB.MediumDao(), callback: () -> Unit) {
fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, callback: () -> Unit) {
ensureBackgroundThread {
val newPaths = ArrayList<String>()
for (source in paths) {
@ -328,7 +328,7 @@ fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, mediumDa
out?.flush()
if (File(source).length() == copiedSize) {
mediumDao.updateDeleted(destination.removePrefix(recycleBinPath), 0, "$RECYCLE_BIN$destination")
mediaDB.updateDeleted(destination.removePrefix(recycleBinPath), 0, "$RECYCLE_BIN$destination")
}
newPaths.add(destination)
@ -357,8 +357,8 @@ fun BaseSimpleActivity.emptyTheRecycleBin(callback: (() -> Unit)? = null) {
ensureBackgroundThread {
try {
recycleBin.deleteRecursively()
galleryDB.MediumDao().clearRecycleBin()
galleryDB.DirectoryDao().deleteRecycleBin()
mediaDB.clearRecycleBin()
directoryDao.deleteRecycleBin()
toast(R.string.recycle_bin_emptied)
callback?.invoke()
} catch (e: Exception) {
@ -413,9 +413,10 @@ fun Activity.fixDateTaken(paths: ArrayList<String>, showToasts: Boolean, hasResc
try {
var didUpdateFile = false
val operations = ArrayList<ContentProviderOperation>()
val mediumDao = galleryDB.MediumDao()
ensureBackgroundThread {
val dateTakens = ArrayList<DateTaken>()
for (path in paths) {
val dateTime = ExifInterface(path).getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)
?: ExifInterface(path).getAttribute(ExifInterface.TAG_DATETIME) ?: continue
@ -442,9 +443,11 @@ fun Activity.fixDateTaken(paths: ArrayList<String>, showToasts: Boolean, hasResc
operations.clear()
}
mediumDao.updateFavoriteDateTaken(path, timestamp)
mediaDB.updateFavoriteDateTaken(path, timestamp)
didUpdateFile = true
val dateTaken = DateTaken(null, path, path.getFilenameFromPath(), path.getParentPath(), timestamp, (System.currentTimeMillis() / 1000).toInt())
dateTakens.add(dateTaken)
if (!hasRescanned && getFileDateTaken(path) == 0L) {
pathsToRescan.add(path)
}
@ -456,6 +459,10 @@ fun Activity.fixDateTaken(paths: ArrayList<String>, showToasts: Boolean, hasResc
}
if (hasRescanned || pathsToRescan.isEmpty()) {
if (dateTakens.isNotEmpty()) {
dateTakensDB.insertAll(dateTakens)
}
runOnUiThread {
if (showToasts) {
toast(if (didUpdateFile) R.string.dates_fixed_successfully else R.string.unknown_error_occurred)

View file

@ -25,13 +25,8 @@ import com.simplemobiletools.gallery.pro.activities.SettingsActivity
import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
import com.simplemobiletools.gallery.pro.databases.GalleryDatabase
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
import com.simplemobiletools.gallery.pro.interfaces.MediumDao
import com.simplemobiletools.gallery.pro.interfaces.WidgetsDao
import com.simplemobiletools.gallery.pro.models.AlbumCover
import com.simplemobiletools.gallery.pro.models.Directory
import com.simplemobiletools.gallery.pro.models.Medium
import com.simplemobiletools.gallery.pro.models.ThumbnailItem
import com.simplemobiletools.gallery.pro.interfaces.*
import com.simplemobiletools.gallery.pro.models.*
import com.simplemobiletools.gallery.pro.svg.SvgSoftwareLayerSetter
import com.simplemobiletools.gallery.pro.views.MySquareImageView
import pl.droidsonroids.gif.GifDrawable
@ -113,11 +108,15 @@ fun Context.launchSettings() {
val Context.config: Config get() = Config.newInstance(applicationContext)
val Context.galleryDB: GalleryDatabase get() = GalleryDatabase.getInstance(applicationContext)
val Context.widgetsDB: WidgetsDao get() = GalleryDatabase.getInstance(applicationContext).WidgetsDao()
val Context.directoryDB: DirectoryDao get() = GalleryDatabase.getInstance(applicationContext).DirectoryDao()
val Context.mediaDB: MediumDao get() = GalleryDatabase.getInstance(applicationContext).MediumDao()
val Context.directoryDao: DirectoryDao get() = GalleryDatabase.getInstance(applicationContext).DirectoryDao()
val Context.favoritesDB: FavoritesDao get() = GalleryDatabase.getInstance(applicationContext).FavoritesDao()
val Context.dateTakensDB: DateTakensDao get() = GalleryDatabase.getInstance(applicationContext).DateTakensDao()
val Context.recycleBin: File get() = filesDir
@ -400,16 +399,15 @@ fun Context.rescanFolderMediaSync(path: String) {
GetMediaAsynctask(applicationContext, path, false, false, false) {
ensureBackgroundThread {
val newMedia = it
val mediumDao = galleryDB.MediumDao()
val media = newMedia.filter { it is Medium } as ArrayList<Medium>
try {
mediumDao.insertAll(media)
mediaDB.insertAll(media)
cached.forEach {
if (!newMedia.contains(it)) {
val mediumPath = (it as? Medium)?.path
if (mediumPath != null) {
deleteDBPath(mediumDao, mediumPath)
deleteDBPath(mediumPath)
}
}
}
@ -420,7 +418,7 @@ fun Context.rescanFolderMediaSync(path: String) {
}
}
fun Context.storeDirectoryItems(items: ArrayList<Directory>, directoryDao: DirectoryDao) {
fun Context.storeDirectoryItems(items: ArrayList<Directory>) {
ensureBackgroundThread {
directoryDao.insertAll(items)
}
@ -559,7 +557,7 @@ fun Context.loadSVG(path: String, target: MySquareImageView, cropThumbnails: Boo
.into(target)
}
fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, directoryDao: DirectoryDao = galleryDB.DirectoryDao(), forceShowHidden: Boolean = false, callback: (ArrayList<Directory>) -> Unit) {
fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, forceShowHidden: Boolean = false, callback: (ArrayList<Directory>) -> Unit) {
ensureBackgroundThread {
val directories = try {
directoryDao.getAll() as ArrayList<Directory>
@ -602,22 +600,21 @@ fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly:
val clone = filteredDirectories.clone() as ArrayList<Directory>
callback(clone.distinctBy { it.path.getDistinctPath() } as ArrayList<Directory>)
removeInvalidDBDirectories(filteredDirectories, directoryDao)
removeInvalidDBDirectories(filteredDirectories)
}
}
fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, mediumDao: MediumDao = galleryDB.MediumDao(),
callback: (ArrayList<ThumbnailItem>) -> Unit) {
fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, callback: (ArrayList<ThumbnailItem>) -> Unit) {
ensureBackgroundThread {
val mediaFetcher = MediaFetcher(this)
val foldersToScan = if (path.isEmpty()) mediaFetcher.getFoldersToScan() else arrayListOf(path)
var media = ArrayList<Medium>()
if (path == FAVORITES) {
media.addAll(mediumDao.getFavorites())
media.addAll(mediaDB.getFavorites())
}
if (path == RECYCLE_BIN) {
media.addAll(getUpdatedDeletedMedia(mediumDao))
media.addAll(getUpdatedDeletedMedia())
}
if (config.filterMedia and TYPE_PORTRAITS != 0) {
@ -634,7 +631,7 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
val shouldShowHidden = config.shouldShowHidden
foldersToScan.filter { path.isNotEmpty() || !config.isFolderProtected(it) }.forEach {
try {
val currMedia = mediumDao.getMediaFromPath(it)
val currMedia = mediaDB.getMediaFromPath(it)
media.addAll(currMedia)
} catch (ignored: Exception) {
}
@ -667,7 +664,7 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
val mediaToDelete = ArrayList<Medium>()
media.filter { !getDoesFilePathExist(it.path, OTGPath) }.forEach {
if (it.path.startsWith(recycleBinPath)) {
deleteDBPath(mediumDao, it.path)
deleteDBPath(it.path)
} else {
mediaToDelete.add(it)
}
@ -675,14 +672,18 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
try {
if (mediaToDelete.isNotEmpty()) {
mediumDao.deleteMedia(*mediaToDelete.toTypedArray())
mediaDB.deleteMedia(*mediaToDelete.toTypedArray())
mediaToDelete.filter { it.isFavorite }.forEach {
favoritesDB.deleteFavoritePath(it.path)
}
}
} catch (ignored: Exception) {
}
}
}
fun Context.removeInvalidDBDirectories(dirs: ArrayList<Directory>? = null, directoryDao: DirectoryDao = galleryDB.DirectoryDao()) {
fun Context.removeInvalidDBDirectories(dirs: ArrayList<Directory>? = null) {
val dirsToCheck = dirs ?: directoryDao.getAll()
val OTGPath = config.OTGPath
dirsToCheck.filter { !it.areFavorites() && !it.isRecycleBin() && !getDoesFilePathExist(it.path, OTGPath) && it.path != config.tempFolderPath }.forEach {
@ -697,12 +698,13 @@ fun Context.updateDBMediaPath(oldPath: String, newPath: String) {
val newFilename = newPath.getFilenameFromPath()
val newParentPath = newPath.getParentPath()
try {
galleryDB.MediumDao().updateMedium(oldPath, newParentPath, newFilename, newPath)
mediaDB.updateMedium(newFilename, newPath, newParentPath, oldPath)
favoritesDB.updateFavorite(newFilename, newPath, newParentPath, oldPath)
} catch (ignored: Exception) {
}
}
fun Context.updateDBDirectory(directory: Directory, directoryDao: DirectoryDao) {
fun Context.updateDBDirectory(directory: Directory) {
try {
directoryDao.updateDirectory(directory.path, directory.tmb, directory.mediaCnt, directory.modified, directory.taken, directory.size, directory.types)
} catch (ignored: Exception) {
@ -715,16 +717,26 @@ fun Context.getOTGFolderChildrenNames(path: String) = getOTGFolderChildren(path)
fun Context.getFavoritePaths(): ArrayList<String> {
return try {
galleryDB.MediumDao().getFavoritePaths() as ArrayList<String>
favoritesDB.getValidFavoritePaths() as ArrayList<String>
} catch (e: Exception) {
ArrayList()
}
}
fun Context.getFavoriteFromPath(path: String) = Favorite(null, path, path.getFilenameFromPath(), path.getParentPath())
fun Context.updateFavorite(path: String, isFavorite: Boolean) {
if (isFavorite) {
favoritesDB.insert(getFavoriteFromPath(path))
} else {
favoritesDB.deleteFavoritePath(path)
}
}
// remove the "recycle_bin" from the file path prefix, replace it with real bin path /data/user...
fun Context.getUpdatedDeletedMedia(mediumDao: MediumDao): ArrayList<Medium> {
fun Context.getUpdatedDeletedMedia(): ArrayList<Medium> {
val media = try {
mediumDao.getDeletedMedia() as ArrayList<Medium>
mediaDB.getDeletedMedia() as ArrayList<Medium>
} catch (ignored: Exception) {
ArrayList<Medium>()
}
@ -735,9 +747,14 @@ fun Context.getUpdatedDeletedMedia(mediumDao: MediumDao): ArrayList<Medium> {
return media
}
fun Context.deleteDBPath(mediumDao: MediumDao, path: String) {
fun Context.deleteDBPath(path: String) {
deleteMediumWithPath(path.replaceFirst(recycleBinPath, RECYCLE_BIN))
}
fun Context.deleteMediumWithPath(path: String) {
try {
mediumDao.deleteMediumPath(path.replaceFirst(recycleBinPath, RECYCLE_BIN))
mediaDB.deleteMediumPath(path)
favoritesDB.deleteFavoritePath(path)
} catch (ignored: Exception) {
}
}
@ -826,13 +843,12 @@ fun Context.addPathToDB(path: String) {
}
try {
val mediumDao = galleryDB.MediumDao()
val isFavorite = mediumDao.isFavorite(path)
val isFavorite = favoritesDB.isFavorite(path)
val videoDuration = if (type == TYPE_VIDEOS) path.getVideoDuration() else 0
val medium = Medium(null, path.getFilenameFromPath(), path, path.getParentPath(), System.currentTimeMillis(), System.currentTimeMillis(),
File(path).length(), type, videoDuration, isFavorite, 0L)
mediumDao.insert(medium)
mediaDB.insert(medium)
} catch (ignored: Exception) {
}
}
@ -876,7 +892,7 @@ fun Context.updateDirectoryPath(path: String) {
val favoritePaths = getFavoritePaths()
val curMedia = mediaFetcher.getFilesFrom(path, getImagesOnly, getVideosOnly, getProperDateTaken, getProperFileSize, favoritePaths, false)
val directory = createDirectoryFromMedia(path, curMedia, albumCovers, hiddenString, includedFolders, isSortingAscending, getProperFileSize)
updateDBDirectory(directory, galleryDB.DirectoryDao())
updateDBDirectory(directory)
}
fun Context.getFileDateTaken(path: String): Long {

View file

@ -657,7 +657,9 @@ class PhotoFragment : ViewPagerFragment() {
mIsPanorama = try {
val inputStream = if (mMedium.path.startsWith("content:/")) context!!.contentResolver.openInputStream(Uri.parse(mMedium.path)) else File(mMedium.path).inputStream()
val imageParser = JpegImageParser().getXmpXml(ByteSourceInputStream(inputStream, mMedium.name), HashMap<String, Any>())
imageParser.contains("GPano:UsePanoramaViewer=\"True\"", true) || imageParser.contains("<GPano:UsePanoramaViewer>True</GPano:UsePanoramaViewer>", true)
imageParser.contains("GPano:UsePanoramaViewer=\"True\"", true) ||
imageParser.contains("<GPano:UsePanoramaViewer>True</GPano:UsePanoramaViewer>", true) ||
imageParser.contains("GPano:FullPanoWidthPixels=")
} catch (e: Exception) {
false
} catch (e: OutOfMemoryError) {

View file

@ -273,8 +273,12 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
private fun saveVideoProgress() {
if (!videoEnded() && mExoPlayer != null) {
mConfig.saveLastVideoPosition(mMedium.path, mExoPlayer!!.currentPosition.toInt() / 1000)
if (!videoEnded()) {
if (mExoPlayer != null) {
mConfig.saveLastVideoPosition(mMedium.path, mExoPlayer!!.currentPosition.toInt() / 1000)
} else {
mConfig.saveLastVideoPosition(mMedium.path, mPositionAtPause.toInt() / 1000)
}
}
}
@ -313,6 +317,9 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mExoPlayer = ExoPlayerFactory.newSimpleInstance(context)
mExoPlayer!!.seekParameters = SeekParameters.CLOSEST_SYNC
if (mConfig.loopVideos) {
mExoPlayer?.repeatMode = Player.REPEAT_MODE_ONE
}
val isContentUri = mMedium.path.startsWith("content://")
val uri = if (isContentUri) Uri.parse(mMedium.path) else Uri.fromFile(File(mMedium.path))
@ -346,7 +353,13 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
override fun onLoadingChanged(isLoading: Boolean) {}
override fun onPositionDiscontinuity(reason: Int) {}
override fun onPositionDiscontinuity(reason: Int) {
// Reset progress views when video loops.
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
mSeekBar.progress = 0
mCurrTimeView.text = 0.getFormattedDuration()
}
}
override fun onRepeatModeChanged(repeatMode: Int) {}
@ -668,13 +681,9 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
if (listener?.videoEnded() == false && mConfig.loopVideos) {
playVideo()
} else {
mSeekBar.progress = mSeekBar.max
mCurrTimeView.text = mDuration.getFormattedDuration()
pauseVideo()
}
mSeekBar.progress = mSeekBar.max
mCurrTimeView.text = mDuration.getFormattedDuration()
pauseVideo()
}
private fun cleanup() {

View file

@ -494,4 +494,8 @@ class Config(context: Context) : BaseConfig(context) {
var editorBrushSize: Float
get() = prefs.getFloat(EDITOR_BRUSH_SIZE, 0.05f)
set(editorBrushSize) = prefs.edit().putFloat(EDITOR_BRUSH_SIZE, editorBrushSize).apply()
var wereFavoritesMigrated: Boolean
get() = prefs.getBoolean(WERE_FAVORITES_MIGRATED, false)
set(wereFavoritesMigrated) = prefs.edit().putBoolean(WERE_FAVORITES_MIGRATED, wereFavoritesMigrated).apply()
}

View file

@ -80,6 +80,7 @@ const val SHOW_THUMBNAIL_FILE_TYPES = "show_thumbnail_file_types"
const val EDITOR_BRUSH_COLOR = "editor_brush_color"
const val EDITOR_BRUSH_HARDNESS = "editor_brush_hardness"
const val EDITOR_BRUSH_SIZE = "editor_brush_size"
const val WERE_FAVORITES_MIGRATED = "were_favorites_migrated"
// slideshow
const val SLIDESHOW_INTERVAL = "slideshow_interval"

View file

@ -216,7 +216,7 @@ class MediaFetcher(val context: Context) {
val media = ArrayList<Medium>()
val isRecycleBin = folder == RECYCLE_BIN
val deletedMedia = if (isRecycleBin) {
context.getUpdatedDeletedMedia(context.galleryDB.MediumDao())
context.getUpdatedDeletedMedia()
} else {
ArrayList()
}
@ -226,7 +226,7 @@ class MediaFetcher(val context: Context) {
val checkFileExistence = config.fileLoadingPriority == PRIORITY_VALIDITY
val showHidden = config.shouldShowHidden
val showPortraits = filterMedia and TYPE_PORTRAITS != 0
val dateTakens = if (getProperDateTaken && folder != FAVORITES && !isRecycleBin) getFolderDateTakens(folder) else HashMap()
val dateTakens = if (getProperDateTaken && !isRecycleBin) getFolderDateTakens(folder) else HashMap()
val files = when (folder) {
FAVORITES -> favoritePaths.filter { showHidden || !it.contains("/.") }.map { File(it) }.toMutableList() as ArrayList<File>
@ -302,7 +302,7 @@ class MediaFetcher(val context: Context) {
val videoDuration = if (getVideoDurations && isVideo) path.getVideoDuration() else 0
if (getProperDateTaken) {
dateTaken = dateTakens.remove(filename) ?: lastModified
dateTaken = dateTakens.remove(path) ?: lastModified
}
val type = when {
@ -390,32 +390,44 @@ class MediaFetcher(val context: Context) {
}
private fun getFolderDateTakens(folder: String): HashMap<String, Long> {
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_TAKEN
)
val uri = MediaStore.Files.getContentUri("external")
val selection = "${MediaStore.Images.Media.DATA} LIKE ? AND ${MediaStore.Images.Media.DATA} NOT LIKE ?"
val selectionArgs = arrayOf("$folder/%", "$folder/%/%")
val dateTakens = HashMap<String, Long>()
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
cursor?.use {
if (cursor.moveToFirst()) {
do {
try {
val dateTaken = cursor.getLongValue(MediaStore.Images.Media.DATE_TAKEN)
if (dateTaken != 0L) {
val path = cursor.getStringValue(MediaStore.Images.Media.DISPLAY_NAME)
dateTakens[path] = dateTaken
if (folder != FAVORITES) {
val projection = arrayOf(
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_TAKEN
)
val uri = MediaStore.Files.getContentUri("external")
val selection = "${MediaStore.Images.Media.DATA} LIKE ? AND ${MediaStore.Images.Media.DATA} NOT LIKE ?"
val selectionArgs = arrayOf("$folder/%", "$folder/%/%")
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
cursor?.use {
if (cursor.moveToFirst()) {
do {
try {
val dateTaken = cursor.getLongValue(MediaStore.Images.Media.DATE_TAKEN)
if (dateTaken != 0L) {
val name = cursor.getStringValue(MediaStore.Images.Media.DISPLAY_NAME)
dateTakens["$folder/$name"] = dateTaken
}
} catch (e: Exception) {
}
} catch (e: Exception) {
}
} while (cursor.moveToNext())
} while (cursor.moveToNext())
}
}
}
val dateTakenValues = if (folder == FAVORITES) {
context.dateTakensDB.getAllDateTakens()
} else {
context.dateTakensDB.getDateTakensFromPath(folder)
}
dateTakenValues.forEach {
dateTakens[it.fullPath] = it.taken
}
return dateTakens
}
@ -456,7 +468,6 @@ class MediaFetcher(val context: Context) {
}
fun groupMedia(media: ArrayList<Medium>, path: String): ArrayList<ThumbnailItem> {
val mediumGroups = LinkedHashMap<String, ArrayList<Medium>>()
val pathToCheck = if (path.isEmpty()) SHOW_ALL else path
val currentGrouping = context.config.getFolderGrouping(pathToCheck)
if (currentGrouping and GROUP_BY_NONE != 0) {
@ -469,6 +480,7 @@ class MediaFetcher(val context: Context) {
return thumbnailItems
}
val mediumGroups = LinkedHashMap<String, ArrayList<Medium>>()
media.forEach {
val key = it.getGroupingKey(currentGrouping)
if (!mediumGroups.containsKey(key)) {

View file

@ -18,7 +18,7 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.activities.MediaActivity
import com.simplemobiletools.gallery.pro.extensions.config
import com.simplemobiletools.gallery.pro.extensions.directoryDB
import com.simplemobiletools.gallery.pro.extensions.directoryDao
import com.simplemobiletools.gallery.pro.extensions.getFolderNameFromPath
import com.simplemobiletools.gallery.pro.extensions.widgetsDB
import com.simplemobiletools.gallery.pro.models.Widget
@ -45,7 +45,7 @@ class MyWidgetProvider : AppWidgetProvider() {
setText(R.id.widget_folder_name, context.getFolderNameFromPath(it.folderPath))
}
val path = context.directoryDB.getDirectoryThumbnail(it.folderPath) ?: return@forEach
val path = context.directoryDao.getDirectoryThumbnail(it.folderPath) ?: return@forEach
val options = RequestOptions()
.signature(path.getFileSignature())
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)

View file

@ -1,8 +0,0 @@
package com.simplemobiletools.gallery.pro.interfaces
import androidx.room.Dao
@Dao
interface DateTakensDAO {
}

View file

@ -0,0 +1,19 @@
package com.simplemobiletools.gallery.pro.interfaces
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.simplemobiletools.gallery.pro.models.DateTaken
@Dao
interface DateTakensDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(dateTakens: List<DateTaken>)
@Query("SELECT full_path, filename, parent_path, date_taken, last_fixed FROM date_takens WHERE parent_path = :path COLLATE NOCASE")
fun getDateTakensFromPath(path: String): List<DateTaken>
@Query("SELECT full_path, filename, parent_path, date_taken, last_fixed FROM date_takens")
fun getAllDateTakens(): List<DateTaken>
}

View file

@ -1,8 +0,0 @@
package com.simplemobiletools.gallery.pro.interfaces
import androidx.room.Dao
@Dao
interface FavoritesDAO {
}

View file

@ -0,0 +1,31 @@
package com.simplemobiletools.gallery.pro.interfaces
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.simplemobiletools.gallery.pro.models.Favorite
@Dao
interface FavoritesDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(favorite: Favorite)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(favorites: List<Favorite>)
@Query("SELECT favorites.full_path FROM favorites INNER JOIN media ON favorites.full_path = media.full_path WHERE media.deleted_ts = 0")
fun getValidFavoritePaths(): List<String>
@Query("SELECT id FROM favorites WHERE full_path = :path COLLATE NOCASE")
fun isFavorite(path: String): Boolean
@Query("UPDATE OR REPLACE favorites SET filename = :newFilename, full_path = :newFullPath, parent_path = :newParentPath WHERE full_path = :oldPath COLLATE NOCASE")
fun updateFavorite(newFilename: String, newFullPath: String, newParentPath: String, oldPath: String)
@Query("DELETE FROM favorites WHERE full_path = :path COLLATE NOCASE")
fun deleteFavoritePath(path: String)
@Query("DELETE FROM favorites")
fun clearFavorites()
}

View file

@ -15,15 +15,9 @@ interface MediumDao {
@Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, video_duration, is_favorite, deleted_ts FROM media WHERE deleted_ts = 0 AND is_favorite = 1")
fun getFavorites(): List<Medium>
@Query("SELECT full_path FROM media WHERE deleted_ts = 0 AND is_favorite = 1")
fun getFavoritePaths(): List<String>
@Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, video_duration, is_favorite, deleted_ts FROM media WHERE deleted_ts != 0")
fun getDeletedMedia(): List<Medium>
@Query("SELECT is_favorite FROM media WHERE full_path = :path COLLATE NOCASE")
fun isFavorite(path: String): Boolean
@Insert(onConflict = REPLACE)
fun insert(medium: Medium)
@ -40,10 +34,7 @@ interface MediumDao {
fun deleteOldRecycleBinItems(timestmap: Long)
@Query("UPDATE OR REPLACE media SET filename = :newFilename, full_path = :newFullPath, parent_path = :newParentPath WHERE full_path = :oldPath COLLATE NOCASE")
fun updateMedium(oldPath: String, newParentPath: String, newFilename: String, newFullPath: String)
@Query("UPDATE media SET is_favorite = :isFavorite WHERE full_path = :path COLLATE NOCASE")
fun updateFavorite(path: String, isFavorite: Boolean)
fun updateMedium(newFilename: String, newFullPath: String, newParentPath: String, oldPath: String)
@Query("UPDATE OR REPLACE media SET full_path = :newPath, deleted_ts = :deletedTS WHERE full_path = :oldPath COLLATE NOCASE")
fun updateDeleted(newPath: String, deletedTS: Long, oldPath: String)
@ -51,9 +42,9 @@ interface MediumDao {
@Query("UPDATE media SET date_taken = :dateTaken WHERE full_path = :path COLLATE NOCASE")
fun updateFavoriteDateTaken(path: String, dateTaken: Long)
@Query("DELETE FROM media WHERE deleted_ts != 0")
fun clearRecycleBin()
@Query("UPDATE media SET is_favorite = 0")
fun clearFavorites()
@Query("DELETE FROM media WHERE deleted_ts != 0")
fun clearRecycleBin()
}

View file

@ -32,7 +32,7 @@
<string name="fixing">Opravuji…</string>
<string name="dates_fixed_successfully">Datumy byly úspěšně opraveny</string>
<string name="share_resized">Sdílet verzi se změněnou velikostí</string>
<string name="upgraded_from_free">Zdravím,\n\nzdá se, že jse přešli ze staré bezplatné aplikace. Starou aplikaci, která má nahoře v nastavení tlačítko \'Stáhnout Pro verzi\', můžete již odinstalovat.\n\nZtratíte tím pouze soubory v odpadkovém koši, označení oblíbených souborů a také budete muset znovu nastavit položky v nastavení aplikace.\n\nDěkuji!</string>
<string name="upgraded_from_free">Zdravím,\n\nzdá se, že jste přešli ze staré bezplatné aplikace. Starou aplikaci, která má nahoře v nastavení tlačítko \'Stáhnout Pro verzi\', můžete již odinstalovat.\n\nZtratíte tím pouze soubory v odpadkovém koši, označení oblíbených souborů a také budete muset znovu nastavit položky v nastavení aplikace.\n\nDěkuji!</string>
<string name="switch_to_file_search">Přepnout na vyhledávání souborů ve všech viditelných složkách</string>
<!-- Filter -->
@ -43,7 +43,7 @@
<string name="raw_images">RAW obrázky</string>
<string name="svgs">SVGčka</string>
<string name="portraits">Portréty</string>
<string name="no_media_with_filters">Se zvolenými filtry nebyly nalezeny žádné médiální soubory.</string>
<string name="no_media_with_filters">Se zvolenými filtry nebyly nalezeny žádné mediální soubory.</string>
<string name="change_filters_underlined"><u>Změnit filtry</u></string>
<!-- Hide / Exclude -->
@ -53,7 +53,7 @@
<string name="manage_excluded_folders">Spravovat vyloučené složky</string>
<string name="exclude_folder_description">Tato funkce vyloučí výběr včetně podsložek jen z Jednoduché galerie. V nastavení můžete spravovat vyloučené složky.</string>
<string name="exclude_folder_parent">Chcete vyloučit nadřazenou složku?</string>
<string name="excluded_activity_placeholder">Vyloučené složky budou spolu s podsložkami vyloučeny jen z Jednoduché Galerie, ostatní aplikace je nadále uvidí.\n\nPokud je chcete skrýt i před ostatními aplikacemi, použijte funkci Skrýt.</string>
<string name="excluded_activity_placeholder">Vyloučené složky budou spolu s podsložkami vyloučeny jen z Jednoduché galerie, ostatní aplikace je nadále uvidí.\n\nPokud je chcete skrýt i před ostatními aplikacemi, použijte funkci Skrýt.</string>
<string name="remove_all">Odstranit všechny</string>
<string name="remove_all_description">Odstranit všechny složky ze seznamu vyloučených? Tato operace neodstraní obsah složek.</string>
<string name="hidden_folders">Skryté složky</string>
@ -111,7 +111,7 @@
<!-- Slideshow -->
<string name="slideshow">Prezentace</string>
<string name="interval">Interval (vteřin):</string>
<string name="interval">Interval (sekund):</string>
<string name="include_photos">Zahrnout fotografie</string>
<string name="include_videos">Zahrnout videa</string>
<string name="include_gifs">Zahrnout GIFy</string>
@ -169,10 +169,10 @@
<string name="allow_photo_gestures">Povolit ovládání jasu vertikálními tahy</string>
<string name="allow_video_gestures">Povolit ovládání hlasitosti a jasu videí vertikálními tahy</string>
<string name="show_media_count">Zobrazit počet médií ve složce na hlavní obrazovce</string>
<string name="show_extended_details">Zobrazit rozšířené vlastnosti přes celoobrazovkové média</string>
<string name="show_extended_details">Zobrazit rozšířené vlastnosti přes celoobrazovková média</string>
<string name="manage_extended_details">Spravovat rozšířené vlastnosti</string>
<string name="one_finger_zoom">Povolit přibližování jedním prstem v celoobrazovkovém režimu</string>
<string name="allow_instant_change">Povolit okamžité přepínání médií ťuknutím na okraj obrazovky</string>
<string name="allow_instant_change">Povolit okamžité přepínání médií klepnutím na okraj obrazovky</string>
<string name="allow_deep_zooming_images">Povolit hluboké přibližování obrázků</string>
<string name="hide_extended_details">Skrýt rozšířené vlastnosti pokud je skrytá stavová lišta</string>
<string name="show_at_bottom">Zobrazit některá akční tlačítka ve spodní části obrazovky</string>
@ -198,7 +198,7 @@
<string name="bottom_actions">Spodní akční tlačítka</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Upravit viditelné spodní akční tlačítka</string>
<string name="manage_bottom_actions">Upravit viditelná spodní akční tlačítka</string>
<string name="toggle_favorite">Přepnutí oblíbenosti</string>
<string name="toggle_file_visibility">Přepnutí viditelnosti souboru</string>
@ -284,17 +284,17 @@
<string name="pesdk_common_title_pipettableColor">Pipetovatelná barva</string>
<!-- FAQ -->
<string name="faq_1_title">Jak nastavím Jednoduchou Galerii jako výchozí galerii?</string>
<string name="faq_1_title">Jak nastavím Jednoduchou galerii jako výchozí galerii?</string>
<string name="faq_1_text">Nejdříve musíte najít v nastavení zařízení, v sekci Aplikace, současnou výchozí galerii, zvolit tlačítko s textem ve smyslu \"Nastavení výchozího otevírání\" a následně \"Vymazat výchozí nastavení\".
Pokud poté zkusíte otevřít obrázek nebo video, zobrazí se seznam aplikací, kde můžete zvolit Jednoduchou Galerii a nastavit ji jako výchozí.</string>
Pokud poté zkusíte otevřít obrázek nebo video, zobrazí se seznam aplikací, kde můžete zvolit Jednoduchou galerii a nastavit ji jako výchozí.</string>
<string name="faq_2_title">Uzamknul jsem aplikaci heslem, ale zapomněl jsem ho. Co můžu udělat?</string>
<string name="faq_2_text">Můžete to vyriešǐť 2 způsoby. Můžete aplikaci buď přeinstalovat nebo ji najít v nastavení zařízení a zvolit \"Vymazat data\". Vymažou se pouze nastavení aplikace, nikoliv soubory.</string>
<string name="faq_2_text">Můžete to vyriešit 2 způsoby. Můžete aplikaci buď přeinstalovat nebo ji najít v nastavení zařízení a zvolit \"Vymazat data\". Vymažou se pouze nastavení aplikace, nikoliv soubory.</string>
<string name="faq_3_title">Jak mohu dosáhnout, aby bylo dané album stále zobrazeno jako první?</string>
<string name="faq_3_text">Můžete označit danou složku dlouhým podržením a zvolit tlačítko s obrázkem připínáčku, to ji připne na vrch. Můžete připnout i více složek, budou seřazeny podle zvoleného řazení.</string>
<string name="faq_3_text">Můžete označit danou složku dlouhým podržením a zvolit tlačítko s obrázkem připínáčku, to ji připne nahoru. Můžete připnout i více složek, budou seřazeny podle zvoleného řazení.</string>
<string name="faq_4_title">Jak mohu video posunout vpřed?</string>
<string name="faq_4_text">Můžete toho dosáhnout buď tažením prstu vodorovně přes okno přehrávače nebo ťuknutím na text aktuální či celkové délky videa, které najdete po bocích indikátoru aktuální pozice. To posune video buď zpět nebo vpřed.</string>
<string name="faq_4_text">Můžete toho dosáhnout buď tažením prstu vodorovně přes okno přehrávače nebo klepnutím na text aktuální či celkové délky videa, který najdete po bocích indikátoru aktuální pozice. To posune video buď zpět nebo vpřed.</string>
<string name="faq_5_title">Jaký je rozdíl mezi Skrytím a Vyloučením složky?</string>
<string name="faq_5_text">Zatímco vyloučení zamezí zobrazení složky pouze vrámci Jednoduché Galerie, skrytí ji ukryje vrámci celého systému, tedy to ovlivní i ostatní galerie. Skrytí funguje pomocí vytvoření prázdného souboru \".nomedia\" v daném adresáři, který můžete vymazat i libovolným správcem souborů.</string>
<string name="faq_5_text">Zatímco vyloučení zamezí zobrazení složky pouze vrámci Jednoduché galerie, skrytí ji ukryje v celém systému, tedy to ovlivní i ostatní galerie. Skrytí funguje pomocí vytvoření prázdného souboru \".nomedia\" v daném adresáři, který můžete vymazat i libovolným správcem souborů.</string>
<string name="faq_6_title">Proč se mi zobrazují složky s obaly hudebních alb, nebo nálepkami?</string>
<string name="faq_6_text">Může se stát, že se vám zobrazí také neobvyklé složky. Můžete je ale jednoduše skrýt jejich vybráním pomocí dlouhého podržení a zvolením Vyloučit. Pokud na následujícím dialogu zvolíte vyloučení nadřazené složky, pravděpodobně budou vyloučeny i ostatní podobné složky.</string>
<string name="faq_7_title">Složka s fotografiemi se mi nezobrazuje nebo ve složce nevidím všechny soubory. Co s tím?</string>
@ -317,13 +317,13 @@
<!-- 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 fotografií</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions with this photo and video gallery</string>
<string name="app_short_description">Prohlížejte svoje vzpomínky bez prerušení s touto foto a video galerií.</string>
<string name="app_long_description">
Jednoduchá Galerie Pro je vysoce přizpůsobitelná offline galerie. Organizujte a upravujte své fotografie, obnovujte smazané fotografie s funkcí odpadkového koše, chraňte je a skrývejte. Prohlížejte množství různých foto a video formátů včetně RAW, SVG a mnoho dalších.
Jednoduchá galerie Pro je vysoce přizpůsobitelná offline galerie. Organizujte a upravujte své fotografie, obnovujte smazané fotografie s funkcí odpadkového koše, chraňte je a skrývejte. Prohlížejte množství různých foto a video formátů včetně RAW, SVG a mnoho dalších.
Aplikace neobsahuje žádné reklamy ani nepotřebná oprávnění. Tím, že ani nevyžaduje připojení k internetu je vaše soukromí maximálně chráněno.
Aplikace neobsahuje žádné reklamy ani nepotřebná oprávnění. Tím, že ani nevyžaduje připojení k internetu, je vaše soukromí maximálně chráněno.
-------------------------------------------------
<b>JEDNODUCHÁ GALERIE PRO FUNKCE</b>
@ -337,7 +337,7 @@
• Otevírejte mnoho rozličných formátů fotografií a videí (RAW, SVG, GIF, panorama, atd)
• Množství intuitivních gest pro jednoduchou úpravu a organizaci souborů
• Mnoho různých způsobů filtrování, seskupování a řazení souborů
• Změňte si vzhled Jednoduché galerie pro
• Změňte si vzhled Jednoduché galerie Pro
• Dostupná ve 32 jazycích
• Označte si oblíbené soubory pro rychlý přístup
• Chraňte své fotografie a videa pomocí pinu, vzoru nebo otiskem prstu
@ -350,16 +350,16 @@
… a mnoho dalších!
<b>EDITOR OBRÁZKŮ</b>
Jednoduchá Galerie Pro umožňuje snadnou úpravu obrázků. Ořezávejte, překlápějte, otáčejte či měňte jejich velikost. Pokud se cítíte kreativně, můžete také aplikovat filtry nebo do obrázku kreslit!
Jednoduchá galerie Pro umožňuje snadnou úpravu obrázků. Ořezávejte, překlápějte, otáčejte či měňte jejich velikost. Pokud se cítíte kreativně, můžete také aplikovat filtry nebo do obrázku kreslit!
<b>PODPORA MNOHA TYPŮ SOUBORŮ</b>
Na rozdíl od některých galerií podporuje Jednoduchá Galerie Pro velké množství různých druhů souborů včetně JPEG, PNG, MP4, MKV, RAW, SVG, panoramatických fotografií a videí.
Na rozdíl od některých galerií podporuje Jednoduchá galerie Pro velké množství různých druhů souborů včetně JPEG, PNG, MP4, MKV, RAW, SVG, panoramatických fotografií a videí.
<b>Vysoce upravitelný správce galerie</b>
Od vzhledu až po funkční tlačítka na spodní liště, Jednoduchá Galerie Pro je plně nastavitelná a bude fungovat přesně jak si budete přát. Žádná jiná galerie nenabízí takovou flexibilitu! A díky otevřenému kódu je naše aplikace dostupná ve 32 jazycích!
Od vzhledu až po funkční tlačítka na spodní liště, Jednoduchá galerie Pro je plně nastavitelná a bude fungovat přesně jak si budete přát. Žádná jiná galerie nenabízí takovou flexibilitu! A díky otevřenému kódu je naše aplikace dostupná ve 32 jazycích!
<b>OBNOVTE SMAZANÉ FOTOGRAFIE A VIDEA</b>
Smazali jste nechtěně důležitou fotografii nebo video? Žádný strach! Jednoduchá Galerie Pro pro podobné případy nabízí funkci odpadkového koše, odkud smazané fotografie a videa snadno obnovíte.
Smazali jste nechtěně důležitou fotografii nebo video? Žádný strach! Jednoduchá galerie Pro pro podobné případy nabízí funkci odpadkového koše, odkud smazané fotografie a videa snadno obnovíte.
<b>CHRAŇTE A SKRÝVEJTE SVÉ FOTOGRAFIE A VIDEA</b>
Použitím pinu, vzoru nebo otisku prstu snadno své fotografie, videa či celá alba ochráníte nebo skryjete. Můžete ochránit i spuštění samotné aplikace, nebo některé její funkce. Například můžete zabránit náhodnému smazání souboru bez potvrzení otiskem prstu.

View file

@ -203,85 +203,85 @@
<string name="toggle_file_visibility">Alternar visibilidad de archivo</string>
<!-- New editor strings -->
<string name="pesdk_transform_button_freeCrop">Custom</string>
<string name="pesdk_transform_button_resetCrop">Reset</string>
<string name="pesdk_transform_button_squareCrop">Square</string>
<string name="pesdk_transform_title_name">Transform</string>
<string name="pesdk_filter_title_name">Filter</string>
<string name="pesdk_filter_asset_none">None</string>
<string name="pesdk_adjustments_title_name">Adjust</string>
<string name="pesdk_adjustments_button_shadowTool">Shadows</string>
<string name="pesdk_adjustments_button_exposureTool">Exposure</string>
<string name="pesdk_adjustments_button_highlightTool">Highlights</string>
<string name="pesdk_adjustments_button_brightnessTool">Brightness</string>
<string name="pesdk_adjustments_button_contrastTool">Contrast</string>
<string name="pesdk_adjustments_button_saturationTool">Saturation</string>
<string name="pesdk_adjustments_button_clarityTool">Clarity</string>
<string name="pesdk_adjustments_button_gammaTool">Gamma</string>
<string name="pesdk_adjustments_button_blacksTool">Blacks</string>
<string name="pesdk_adjustments_button_whitesTool">Whites</string>
<string name="pesdk_adjustments_button_temperatureTool">Temperature</string>
<string name="pesdk_adjustments_button_sharpnessTool">Sharpness</string>
<string name="pesdk_adjustments_button_reset">Reset</string>
<string name="pesdk_focus_title_name">Focus</string>
<string name="pesdk_focus_title_disabled">None</string>
<string name="pesdk_transform_button_freeCrop">Personalizado</string>
<string name="pesdk_transform_button_resetCrop">Reiniciar</string>
<string name="pesdk_transform_button_squareCrop">Cuadrado</string>
<string name="pesdk_transform_title_name">Recortar</string>
<string name="pesdk_filter_title_name">Filtros</string>
<string name="pesdk_filter_asset_none">Ninguno</string>
<string name="pesdk_adjustments_title_name">Ajustes</string>
<string name="pesdk_adjustments_button_shadowTool">Sombras</string>
<string name="pesdk_adjustments_button_exposureTool">Exposición</string>
<string name="pesdk_adjustments_button_highlightTool">Luces</string>
<string name="pesdk_adjustments_button_brightnessTool">Brillo</string>
<string name="pesdk_adjustments_button_contrastTool">Contraste</string>
<string name="pesdk_adjustments_button_saturationTool">Saturación</string>
<string name="pesdk_adjustments_button_clarityTool">Claridad</string>
<string name="pesdk_adjustments_button_gammaTool">Colores</string>
<string name="pesdk_adjustments_button_blacksTool">Negros</string>
<string name="pesdk_adjustments_button_whitesTool">Blancos</string>
<string name="pesdk_adjustments_button_temperatureTool">Temperatura</string>
<string name="pesdk_adjustments_button_sharpnessTool">Nitidez</string>
<string name="pesdk_adjustments_button_reset">Reiniciar</string>
<string name="pesdk_focus_title_name">Enfoque</string>
<string name="pesdk_focus_title_disabled">Ninguno</string>
<string name="pesdk_focus_button_radial">Radial</string>
<string name="pesdk_focus_button_linear">Linear</string>
<string name="pesdk_focus_button_mirrored">Mirrored</string>
<string name="pesdk_focus_button_gaussian">Gaussian</string>
<string name="pesdk_text_title_name">Text</string>
<string name="pesdk_text_title_options">Text Options</string>
<string name="pesdk_text_title_textColor">Text Color</string>
<string name="pesdk_text_title_font">Font</string>
<string name="pesdk_text_button_add">Add</string>
<string name="pesdk_text_button_edit">Edit</string>
<string name="pesdk_text_button_straighten">Straighten</string>
<string name="pesdk_text_button_font">Font</string>
<string name="pesdk_focus_button_linear">Lineal</string>
<string name="pesdk_focus_button_mirrored">Reflejado</string>
<string name="pesdk_focus_button_gaussian">Gaussiano</string>
<string name="pesdk_text_title_name">Texto</string>
<string name="pesdk_text_title_options">Opciones de texto</string>
<string name="pesdk_text_title_textColor">Color de texto</string>
<string name="pesdk_text_title_font">Fuente</string>
<string name="pesdk_text_button_add">Añadir</string>
<string name="pesdk_text_button_edit">Editar</string>
<string name="pesdk_text_button_straighten">Enderezar</string>
<string name="pesdk_text_button_font">Fuente</string>
<string name="pesdk_text_button_color">Color</string>
<string name="pesdk_text_button_backgroundColor">BG Color</string>
<string name="pesdk_text_button_alignment">Alignment</string>
<string name="pesdk_text_button_bringToFront">To Front</string>
<string name="pesdk_text_button_delete">Delete</string>
<string name="pesdk_text_text_editTextPlaceholder">Your text</string>
<string name="pesdk_brush_title_name">Brush</string>
<string name="pesdk_text_button_backgroundColor">Color de fondo</string>
<string name="pesdk_text_button_alignment">Alineación</string>
<string name="pesdk_text_button_bringToFront">Traer al frente</string>
<string name="pesdk_text_button_delete">Eliminar</string>
<string name="pesdk_text_text_editTextPlaceholder">Tu texto</string>
<string name="pesdk_brush_title_name">Pincel</string>
<string name="pesdk_brush_button_color">Color</string>
<string name="pesdk_brush_button_size">Size</string>
<string name="pesdk_brush_button_hardness">Hardness</string>
<string name="pesdk_brush_button_bringToFront">To Front</string>
<string name="pesdk_brush_button_delete">Delete</string>
<string name="pesdk_brush_title_brushColor">Brush Color</string>
<string name="pesdk_brush_button_size">Tamaño</string>
<string name="pesdk_brush_button_hardness">Dureza</string>
<string name="pesdk_brush_button_bringToFront">Traer al frente</string>
<string name="pesdk_brush_button_delete">Eliminar</string>
<string name="pesdk_brush_title_brushColor">Color del pincel</string>
<string name="pesdk_editor_title_name">Editor</string>
<string name="pesdk_editor_title_closeEditorAlert">Close Editor?</string>
<string name="pesdk_editor_text_closeEditorAlert">Do you really want to discard the image?</string>
<string name="pesdk_editor_button_closeEditorAlertConfirmation">Yes</string>
<string name="pesdk_editor_title_closeEditorAlert">¿Cerrar editor?</string>
<string name="pesdk_editor_text_closeEditorAlert">¿Realmente quieres descartar la imagen?</string>
<string name="pesdk_editor_button_closeEditorAlertConfirmation"></string>
<string name="pesdk_editor_button_closeEditorAlertCancelation">No</string>
<string name="pesdk_editor_cancel">Cancel</string>
<string name="pesdk_editor_accept">Accept</string>
<string name="pesdk_editor_save">Save</string>
<string name="pesdk_editor_text_exportProgressUnknown">Exporting image</string>
<string name="pesdk_editor_text_exportProgress" formatted="false">Exporting image %s.</string>
<string name="pesdk_common_button_flipH">Flip H</string>
<string name="pesdk_common_button_flipV">Flip V</string>
<string name="pesdk_common_button_undo">Undo</string>
<string name="pesdk_common_button_redo">Redo</string>
<string name="pesdk_common_title_colorPicker">Color Picker</string>
<string name="pesdk_common_title_transparentColor">Transparent</string>
<string name="pesdk_common_title_whiteColor">White</string>
<string name="pesdk_common_title_grayColor">Gray</string>
<string name="pesdk_common_title_blackColor">Black</string>
<string name="pesdk_common_title_lightBlueColor">Light blue</string>
<string name="pesdk_common_title_blueColor">Blue</string>
<string name="pesdk_common_title_purpleColor">Purple</string>
<string name="pesdk_common_title_orchidColor">Orchid</string>
<string name="pesdk_common_title_pinkColor">Pink</string>
<string name="pesdk_common_title_redColor">Red</string>
<string name="pesdk_common_title_orangeColor">Orange</string>
<string name="pesdk_common_title_goldColor">Gold</string>
<string name="pesdk_common_title_yellowColor">Yellow</string>
<string name="pesdk_common_title_oliveColor">Olive</string>
<string name="pesdk_common_title_greenColor">Green</string>
<string name="pesdk_common_title_aquamarinColor">Aquamarin</string>
<string name="pesdk_common_title_pipettableColor">Pipettable color</string>
<string name="pesdk_editor_cancel">Cancelar</string>
<string name="pesdk_editor_accept">Aceptar</string>
<string name="pesdk_editor_save">Guardar</string>
<string name="pesdk_editor_text_exportProgressUnknown">Exportando imagen</string>
<string name="pesdk_editor_text_exportProgress" formatted="false">Exportando imagen %s.</string>
<string name="pesdk_common_button_flipH">Girar Hor</string>
<string name="pesdk_common_button_flipV">Girar Vert</string>
<string name="pesdk_common_button_undo">Deshacer</string>
<string name="pesdk_common_button_redo">Rehacer</string>
<string name="pesdk_common_title_colorPicker">Cuentagotas</string>
<string name="pesdk_common_title_transparentColor">Transparente</string>
<string name="pesdk_common_title_whiteColor">Blanco</string>
<string name="pesdk_common_title_grayColor">Gris</string>
<string name="pesdk_common_title_blackColor">Negro</string>
<string name="pesdk_common_title_lightBlueColor">Azul claro</string>
<string name="pesdk_common_title_blueColor">Azul</string>
<string name="pesdk_common_title_purpleColor">Púrpura</string>
<string name="pesdk_common_title_orchidColor">Orquídea</string>
<string name="pesdk_common_title_pinkColor">Rosa</string>
<string name="pesdk_common_title_redColor">Rojo</string>
<string name="pesdk_common_title_orangeColor">Naranja</string>
<string name="pesdk_common_title_goldColor">Dorado</string>
<string name="pesdk_common_title_yellowColor">Amarillo</string>
<string name="pesdk_common_title_oliveColor">Oliva</string>
<string name="pesdk_common_title_greenColor">Verde</string>
<string name="pesdk_common_title_aquamarinColor">Aguamarina</string>
<string name="pesdk_common_title_pipettableColor">Color pipetable</string>
<!-- FAQ -->
<string name="faq_1_title">¿Cómo puedo hacer que Simple Gallery sea la galería de dispositivos predeterminada?</string>

View file

@ -32,8 +32,8 @@
<string name="fixing">修正中…</string>
<string name="dates_fixed_successfully">撮影日が正常に修正されました</string>
<string name="share_resized">リサイズした画像を共有</string>
<string name="upgraded_from_free">Hey,\n\nseems like you upgraded from the old free app. You can now uninstall the old version, which has an \'Upgrade to Pro\' button at the top of the app settings.\n\nYou will only have the Recycle bin items deleted, favorite items unmarked and you will also have to reset your app settings.\n\nThanks!</string>
<string name="switch_to_file_search">Switch to file search across all visible folders</string>
<string name="upgraded_from_free">あなたは無料版からアップグレードしたと思われます。\"Upgrade to Pro\"というボタンが設定の上部にある無料版はアンインストールすることができます。\n\nごみ箱の中身は削除され、お気に入りもアプリの設定もリセットされることになります。\n\nありがとう!</string>
<string name="switch_to_file_search">表示されているすべてのフォルダで検索 に切り替え</string>
<!-- Filter -->
<string name="filter_media">表示する形式</string>
@ -42,7 +42,7 @@
<string name="gifs">GIF</string>
<string name="raw_images">RAW</string>
<string name="svgs">SVG</string>
<string name="portraits">Portraits</string>
<string name="portraits">ポートレイト</string>
<string name="no_media_with_filters">条件に該当するメディアがありません。</string>
<string name="change_filters_underlined"><u>絞り込み条件を変更</u></string>
@ -82,7 +82,7 @@
<string name="path">パス</string>
<string name="invalid_image_path">無効な画像パス</string>
<string name="image_editing_failed">画像の編集に失敗しました</string>
<string name="file_edited_successfully">File edited successfully</string>
<string name="file_edited_successfully">ファイルの編集に成功しました</string>
<string name="edit_image_with">画像を編集:</string>
<string name="no_editor_found">画像エディターが見つかりません</string>
<string name="unknown_file_location">ファイルの場所が不明です</string>
@ -180,11 +180,11 @@
<string name="deep_zoomable_images">画像のズームを深くする</string>
<string name="show_highest_quality">可能な限り高品質で画像を表示</string>
<string name="show_recycle_bin_last">ごみ箱をメイン画面の最後に表示</string>
<string name="allow_down_gesture">Allow closing the fullscreen view with a down gesture</string>
<string name="allow_one_to_one_zoom">Allow 1:1 zooming in with two double taps</string>
<string name="allow_down_gesture">フルスクリーン表示を下にスワイプするジェスチャーで閉じる</string>
<string name="allow_one_to_one_zoom">ダブルタップして 1:1 ズームする</string>
<string name="open_videos_on_separate_screen">Always open videos on a separate screen with new horizontal gestures</string>
<string name="show_notch">Show a notch if available</string>
<string name="allow_rotating_gestures">Allow rotating images with gestures</string>
<string name="allow_rotating_gestures">ジェスチャーで画像を回転する</string>
<string name="file_loading_priority">File loading priority</string>
<string name="speed">Speed</string>
<string name="compromise">Compromise</string>
@ -203,69 +203,69 @@
<string name="toggle_file_visibility">表示/非表示の切替</string>
<!-- New editor strings -->
<string name="pesdk_transform_button_freeCrop">Custom</string>
<string name="pesdk_transform_button_resetCrop">Reset</string>
<string name="pesdk_transform_button_squareCrop">Square</string>
<string name="pesdk_transform_title_name">Transform</string>
<string name="pesdk_filter_title_name">Filter</string>
<string name="pesdk_filter_asset_none">None</string>
<string name="pesdk_adjustments_title_name">Adjust</string>
<string name="pesdk_adjustments_button_shadowTool">Shadows</string>
<string name="pesdk_adjustments_button_exposureTool">Exposure</string>
<string name="pesdk_adjustments_button_highlightTool">Highlights</string>
<string name="pesdk_adjustments_button_brightnessTool">Brightness</string>
<string name="pesdk_adjustments_button_contrastTool">Contrast</string>
<string name="pesdk_adjustments_button_saturationTool">Saturation</string>
<string name="pesdk_adjustments_button_clarityTool">Clarity</string>
<string name="pesdk_adjustments_button_gammaTool">Gamma</string>
<string name="pesdk_adjustments_button_blacksTool">Blacks</string>
<string name="pesdk_adjustments_button_whitesTool">Whites</string>
<string name="pesdk_adjustments_button_temperatureTool">Temperature</string>
<string name="pesdk_adjustments_button_sharpnessTool">Sharpness</string>
<string name="pesdk_adjustments_button_reset">Reset</string>
<string name="pesdk_focus_title_name">Focus</string>
<string name="pesdk_focus_title_disabled">None</string>
<string name="pesdk_focus_button_radial">Radial</string>
<string name="pesdk_focus_button_linear">Linear</string>
<string name="pesdk_focus_button_mirrored">Mirrored</string>
<string name="pesdk_focus_button_gaussian">Gaussian</string>
<string name="pesdk_text_title_name">Text</string>
<string name="pesdk_text_title_options">Text Options</string>
<string name="pesdk_text_title_textColor">Text Color</string>
<string name="pesdk_text_title_font">Font</string>
<string name="pesdk_text_button_add">Add</string>
<string name="pesdk_text_button_edit">Edit</string>
<string name="pesdk_text_button_straighten">Straighten</string>
<string name="pesdk_text_button_font">Font</string>
<string name="pesdk_text_button_color">Color</string>
<string name="pesdk_text_button_backgroundColor">BG Color</string>
<string name="pesdk_text_button_alignment">Alignment</string>
<string name="pesdk_text_button_bringToFront">To Front</string>
<string name="pesdk_text_button_delete">Delete</string>
<string name="pesdk_text_text_editTextPlaceholder">Your text</string>
<string name="pesdk_brush_title_name">Brush</string>
<string name="pesdk_brush_button_color">Color</string>
<string name="pesdk_brush_button_size">Size</string>
<string name="pesdk_brush_button_hardness">Hardness</string>
<string name="pesdk_brush_button_bringToFront">To Front</string>
<string name="pesdk_brush_button_delete">Delete</string>
<string name="pesdk_brush_title_brushColor">Brush Color</string>
<string name="pesdk_editor_title_name">Editor</string>
<string name="pesdk_editor_title_closeEditorAlert">Close Editor?</string>
<string name="pesdk_editor_text_closeEditorAlert">Do you really want to discard the image?</string>
<string name="pesdk_editor_button_closeEditorAlertConfirmation">Yes</string>
<string name="pesdk_editor_button_closeEditorAlertCancelation">No</string>
<string name="pesdk_editor_cancel">Cancel</string>
<string name="pesdk_editor_accept">Accept</string>
<string name="pesdk_editor_save">Save</string>
<string name="pesdk_editor_text_exportProgressUnknown">Exporting image</string>
<string name="pesdk_editor_text_exportProgress" formatted="false">Exporting image %s.</string>
<string name="pesdk_common_button_flipH">Flip H</string>
<string name="pesdk_common_button_flipV">Flip V</string>
<string name="pesdk_common_button_undo">Undo</string>
<string name="pesdk_common_button_redo">Redo</string>
<string name="pesdk_common_title_colorPicker">Color Picker</string>
<string name="pesdk_common_title_transparentColor">Transparent</string>
<string name="pesdk_transform_button_freeCrop">カスタム</string>
<string name="pesdk_transform_button_resetCrop">リセット</string>
<string name="pesdk_transform_button_squareCrop">正方形</string>
<string name="pesdk_transform_title_name">変形</string>
<string name="pesdk_filter_title_name">フィルター</string>
<string name="pesdk_filter_asset_none">なし</string>
<string name="pesdk_adjustments_title_name">調整</string>
<string name="pesdk_adjustments_button_shadowTool">シャドウ</string>
<string name="pesdk_adjustments_button_exposureTool">露出</string>
<string name="pesdk_adjustments_button_highlightTool">ハイライト</string>
<string name="pesdk_adjustments_button_brightnessTool">明るさ</string>
<string name="pesdk_adjustments_button_contrastTool">コントラスト</string>
<string name="pesdk_adjustments_button_saturationTool">彩度</string>
<string name="pesdk_adjustments_button_clarityTool">明瞭度</string>
<string name="pesdk_adjustments_button_gammaTool">ガンマ</string>
<string name="pesdk_adjustments_button_blacksTool">黒レベル</string>
<string name="pesdk_adjustments_button_whitesTool">白レベル</string>
<string name="pesdk_adjustments_button_temperatureTool">色温度</string>
<string name="pesdk_adjustments_button_sharpnessTool">シャープネス</string>
<string name="pesdk_adjustments_button_reset">リセット</string>
<string name="pesdk_focus_title_name">ぼかし</string>
<string name="pesdk_focus_title_disabled">なし</string>
<string name="pesdk_focus_button_radial">円形</string>
<string name="pesdk_focus_button_linear">直線</string>
<string name="pesdk_focus_button_mirrored">ミラー</string>
<string name="pesdk_focus_button_gaussian">ガウス</string>
<string name="pesdk_text_title_name">テキスト</string>
<string name="pesdk_text_title_options">テキスト オプション</string>
<string name="pesdk_text_title_textColor">文字色</string>
<string name="pesdk_text_title_font">フォント</string>
<string name="pesdk_text_button_add">追加</string>
<string name="pesdk_text_button_edit">編集</string>
<string name="pesdk_text_button_straighten">まっすぐにする</string>
<string name="pesdk_text_button_font">フォント</string>
<string name="pesdk_text_button_color"></string>
<string name="pesdk_text_button_backgroundColor">背景色</string>
<string name="pesdk_text_button_alignment">配置</string>
<string name="pesdk_text_button_bringToFront">前面に</string>
<string name="pesdk_text_button_delete">削除</string>
<string name="pesdk_text_text_editTextPlaceholder">テキストを入力</string>
<string name="pesdk_brush_title_name">ブラシ</string>
<string name="pesdk_brush_button_color"></string>
<string name="pesdk_brush_button_size">太さ</string>
<string name="pesdk_brush_button_hardness">硬度</string>
<string name="pesdk_brush_button_bringToFront">前面に</string>
<string name="pesdk_brush_button_delete">削除</string>
<string name="pesdk_brush_title_brushColor">ブラシの色</string>
<string name="pesdk_editor_title_name">エディター</string>
<string name="pesdk_editor_title_closeEditorAlert">エディターを閉じますか?</string>
<string name="pesdk_editor_text_closeEditorAlert">画像を破棄して閉じますか?</string>
<string name="pesdk_editor_button_closeEditorAlertConfirmation">はい</string>
<string name="pesdk_editor_button_closeEditorAlertCancelation">いいえ</string>
<string name="pesdk_editor_cancel">キャンセル</string>
<string name="pesdk_editor_accept">了解</string>
<string name="pesdk_editor_save">保存</string>
<string name="pesdk_editor_text_exportProgressUnknown">画像を保存中…</string>
<string name="pesdk_editor_text_exportProgress" formatted="false">画像を保存中 %s.</string>
<string name="pesdk_common_button_flipH">水平方向に反転</string>
<string name="pesdk_common_button_flipV">垂直方向に反転</string>
<string name="pesdk_common_button_undo">元に戻す</string>
<string name="pesdk_common_button_redo">やり直し</string>
<string name="pesdk_common_title_colorPicker">カラーピッカー</string>
<string name="pesdk_common_title_transparentColor">透明</string>
<string name="pesdk_common_title_whiteColor">White</string>
<string name="pesdk_common_title_grayColor">Gray</string>
<string name="pesdk_common_title_blackColor">Black</string>
@ -366,6 +366,9 @@
<b>Check out the full suite of Simple Tools here:</b>
https://www.simplemobiletools.com
<b>Standalone website of Simple Gallery Pro:</b>
https://www.simplemobiletools.com/gallery
<b>Facebook:</b>
https://www.facebook.com/simplemobiletools

View file

@ -317,9 +317,9 @@
<!-- 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, Düzenleyici</string>
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Browse your memories without any interruptions with this photo and video gallery</string>
<string name="app_short_description">Bu fotoğraf ve video galerisinde kesintisiz olarak anılarınıza göz atın</string>
<string name="app_long_description">
Basit Galeri Pro, özelleştirilebilir bir çevrimdışı galeridir. Fotoğraflarınızı düzenleyin ve organize edin, geri dönüşüm kutusuyla silinen dosyaları kurtarın, dosyaları koruyun ve gizleyin ve RAW, SVG ve çok daha fazlası dahil olmak üzere çok çeşitli fotoğraf ve video formatlarını görüntüleyin.

View file

@ -1,6 +1,6 @@
Jednoduchá Galerie Pro je vysoce přizpůsobitelná offline galerie. Organizujte a upravujte své fotografie, obnovujte smazané fotografie s funkcí odpadkového koše, chraňte je a skrývejte. Prohlížejte množství různých foto a video formátů včetně RAW, SVG a mnoho dalších.
Jednoduchá galerie Pro je vysoce přizpůsobitelná offline galerie. Organizujte a upravujte své fotografie, obnovujte smazané fotografie s funkcí odpadkového koše, chraňte je a skrývejte. Prohlížejte množství různých foto a video formátů včetně RAW, SVG a mnoho dalších.
Aplikace neobsahuje žádné reklamy ani nepotřebná oprávnění. Tím, že ani nevyžaduje připojení k internetu je vaše soukromí maximálně chráněno.
Aplikace neobsahuje žádné reklamy ani nepotřebná oprávnění. Tím, že ani nevyžaduje připojení k internetu, je vaše soukromí maximálně chráněno.
-------------------------------------------------
<b>JEDNODUCHÁ GALERIE PRO FUNKCE</b>
@ -14,7 +14,7 @@ Aplikace neobsahuje žádné reklamy ani nepotřebná oprávnění. Tím, že an
• Otevírejte mnoho rozličných formátů fotografií a videí (RAW, SVG, GIF, panorama, atd)
• Množství intuitivních gest pro jednoduchou úpravu a organizaci souborů
• Mnoho různých způsobů filtrování, seskupování a řazení souborů
• Změňte si vzhled Jednoduché galerie pro
• Změňte si vzhled Jednoduché galerie Pro
• Dostupná ve 32 jazycích
• Označte si oblíbené soubory pro rychlý přístup
• Chraňte své fotografie a videa pomocí pinu, vzoru nebo otiskem prstu
@ -27,16 +27,16 @@ Aplikace neobsahuje žádné reklamy ani nepotřebná oprávnění. Tím, že an
… a mnoho dalších!
<b>EDITOR OBRÁZKŮ</b>
Jednoduchá Galerie Pro umožňuje snadnou úpravu obrázků. Ořezávejte, překlápějte, otáčejte či měňte jejich velikost. Pokud se cítíte kreativně, můžete také aplikovat filtry nebo do obrázku kreslit!
Jednoduchá galerie Pro umožňuje snadnou úpravu obrázků. Ořezávejte, překlápějte, otáčejte či měňte jejich velikost. Pokud se cítíte kreativně, můžete také aplikovat filtry nebo do obrázku kreslit!
<b>PODPORA MNOHA TYPŮ SOUBORŮ</b>
Na rozdíl od některých galerií podporuje Jednoduchá Galerie Pro velké množství různých druhů souborů včetně JPEG, PNG, MP4, MKV, RAW, SVG, panoramatických fotografií a videí.
Na rozdíl od některých galerií podporuje Jednoduchá galerie Pro velké množství různých druhů souborů včetně JPEG, PNG, MP4, MKV, RAW, SVG, panoramatických fotografií a videí.
<b>Vysoce upravitelný správce galerie</b>
Od vzhledu až po funkční tlačítka na spodní liště, Jednoduchá Galerie Pro je plně nastavitelná a bude fungovat přesně jak si budete přát. Žádná jiná galerie nenabízí takovou flexibilitu! A díky otevřenému kódu je naše aplikace dostupná ve 32 jazycích!
Od vzhledu až po funkční tlačítka na spodní liště, Jednoduchá galerie Pro je plně nastavitelná a bude fungovat přesně jak si budete přát. Žádná jiná galerie nenabízí takovou flexibilitu! A díky otevřenému kódu je naše aplikace dostupná ve 32 jazycích!
<b>OBNOVTE SMAZANÉ FOTOGRAFIE A VIDEA</b>
Smazali jste nechtěně důležitou fotografii nebo video? Žádný strach! Jednoduchá Galerie Pro pro podobné případy nabízí funkci odpadkového koše, odkud smazané fotografie a videa snadno obnovíte.
Smazali jste nechtěně důležitou fotografii nebo video? Žádný strach! Jednoduchá galerie Pro pro podobné případy nabízí funkci odpadkového koše, odkud smazané fotografie a videa snadno obnovíte.
<b>CHRAŇTE A SKRÝVEJTE SVÉ FOTOGRAFIE A VIDEA</b>
Použitím pinu, vzoru nebo otisku prstu snadno své fotografie, videa či celá alba ochráníte nebo skryjete. Můžete ochránit i spuštění samotné aplikace, nebo některé její funkce. Například můžete zabránit náhodnému smazání souboru bez potvrzení otiskem prstu.

View file

@ -1 +1 @@
Prohlížejte své vzpomínky bez přerušení.
Prohlížejte svoje vzpomínky bez prerušení s touto foto a video galerií.

View file

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

View file

@ -0,0 +1,8 @@
* Properly handle sorting by date taken after using "Fix Date Taken values" on the wrong files
* Fixed some issues at copying files, when the source was on an SD card
* Change the way Favorite items are stored, to avoid accidental removal
* Improved video looping (by ForgottenUmbrella)
* Recognize a new type of panoramic photos
* Properly remember last video playback position if the video is paused on exit
* Properly color the top status bar icons at using light primary colors
* Other UX and translation improvements

View file

@ -1 +1 @@
Reklamsız çevrimdışı galeri. Fotoğraf & videolarınızı yönetin ve düzenleyin
Bu fotoğraf ve video galerisinde kesintisiz olarak anılarınıza göz atın

View file

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