Merge pull request #32 from SimpleMobileTools/master

гзв
This commit is contained in:
solokot 2018-07-04 19:47:35 +03:00 committed by GitHub
commit b371709fa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 2891 additions and 621 deletions

View file

@ -1,6 +1,37 @@
Changelog Changelog
========== ==========
Version 4.3.2 *(2018-07-04)*
----------------------------
* Added Panorama photo support
* Allow customizing visible fullscreen bottom actions
* Allow forcing portrait/landscape modes at fullscreen view
* Use Exoplayer for playing videos
* Many smaller UX and stability improvements
Version 4.3.1 *(2018-06-28)*
----------------------------
* Adding some crashfixes
Version 4.3.0 *(2018-06-28)*
----------------------------
* Added a Recycle Bin
* Allow grouping media thumbnails by different criteria
* Fixed some calculation glitches around fastscroller
* Change the fullscreen Edit icon to a pencil
* Allow sorting "Show All" separately
* Many smaller stability and UX improvements
Version 4.2.1 *(2018-06-20)*
----------------------------
* Allow selecting Favorite items for easy access
* Fix sorting by Date Taken after files have been copied
* Couple other stability and UX improvements
Version 4.2.0 *(2018-06-18)* Version 4.2.0 *(2018-06-18)*
---------------------------- ----------------------------

View file

@ -11,8 +11,8 @@ android {
applicationId "com.simplemobiletools.gallery" applicationId "com.simplemobiletools.gallery"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 27
versionCode 180 versionCode 184
versionName "4.2.0" versionName "4.3.2"
multiDexEnabled true multiDexEnabled true
setProperty("archivesBaseName", "gallery") setProperty("archivesBaseName", "gallery")
} }
@ -47,17 +47,20 @@ ext {
} }
dependencies { dependencies {
implementation 'com.simplemobiletools:commons:4.2.8' implementation 'com.simplemobiletools:commons:4.3.27'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.7.0' implementation 'com.theartofdev.edmodo:android-image-cropper:2.7.0'
implementation 'com.android.support:multidex:1.0.3' implementation 'com.android.support:multidex:1.0.3'
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.12' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.12'
implementation 'com.github.chrisbanes:PhotoView:2.1.3' implementation 'com.github.chrisbanes:PhotoView:2.1.3'
implementation 'com.android.support.constraint:constraint-layout:1.1.2' implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.google.android.exoplayer:exoplayer-core:2.8.2'
implementation 'com.google.vr:sdk-panowidget:1.150.0'
implementation 'org.apache.sanselan:sanselan:0.97-incubator'
kapt "android.arch.persistence.room:compiler:1.1.0" kapt "android.arch.persistence.room:compiler:1.1.1"
implementation "android.arch.persistence.room:runtime:1.1.0" implementation "android.arch.persistence.room:runtime:1.1.1"
annotationProcessor "android.arch.persistence.room:compiler:1.1.0" annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
//implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.9.0' //implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.9.0'
implementation 'com.github.tibbi:subsampling-scale-image-view:v3.10.0-fork' implementation 'com.github.tibbi:subsampling-scale-image-view:v3.10.0-fork'

View file

@ -1,2 +1,3 @@
-keep class com.simplemobiletools.** { *; } -keep class com.simplemobiletools.** { *; }
-dontwarn com.simplemobiletools.** -dontwarn com.simplemobiletools.**
-dontwarn org.apache.**

View file

@ -1,12 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.simplemobiletools.gallery" package="com.simplemobiletools.gallery"
android:installLocation="auto"> android:installLocation="auto">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SET_WALLPAPER"/> <uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-sdk
tools:overrideLibrary="com.google.vr.widgets.common, com.google.vr.sdk.widgets.pano"/>
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
@ -112,6 +116,11 @@
android:name=".activities.PhotoVideoActivity" android:name=".activities.PhotoVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"/> android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity
android:name=".activities.PanoramaActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/FullScreenTheme"/>
<activity <activity
android:name=".activities.IncludedFoldersActivity" android:name=".activities.IncludedFoldersActivity"
android:label="@string/include_folders" android:label="@string/include_folders"

View file

@ -32,6 +32,7 @@ import com.simplemobiletools.gallery.dialogs.FilterMediaDialog
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.interfaces.DirectoryDao import com.simplemobiletools.gallery.interfaces.DirectoryDao
import com.simplemobiletools.gallery.interfaces.DirectoryOperationsListener
import com.simplemobiletools.gallery.models.AlbumCover import com.simplemobiletools.gallery.models.AlbumCover
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
@ -39,7 +40,7 @@ import kotlinx.android.synthetic.main.activity_main.*
import java.io.* import java.io.*
import java.util.* import java.util.*
class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private val PICK_MEDIA = 2 private val PICK_MEDIA = 2
private val PICK_WALLPAPER = 3 private val PICK_WALLPAPER = 3
private val LAST_MEDIA_CHECK_PERIOD = 3000L private val LAST_MEDIA_CHECK_PERIOD = 3000L
@ -109,6 +110,19 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
if (!config.wasOTGHandled && hasPermission(PERMISSION_WRITE_STORAGE)) { if (!config.wasOTGHandled && hasPermission(PERMISSION_WRITE_STORAGE)) {
checkOTGInclusion() checkOTGInclusion()
} }
if (!config.wereFavoritesPinned) {
config.addPinnedFolders(hashSetOf(FAVORITES))
config.wereFavoritesPinned = true
}
if (!config.wasRecycleBinPinned) {
config.addPinnedFolders(hashSetOf(RECYCLE_BIN))
config.wasRecycleBinPinned = true
config.saveFolderGrouping(SHOW_ALL, GROUP_BY_DATE_TAKEN or GROUP_DESCENDING)
}
checkRecycleBinItems()
} }
override fun onStart() { override fun onStart() {
@ -133,8 +147,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
if (mStoredScrollHorizontally != config.scrollHorizontally) { if (mStoredScrollHorizontally != config.scrollHorizontally) {
getRecyclerAdapter()?.updateScrollHorizontally(config.viewTypeFolders != VIEW_TYPE_LIST && config.scrollHorizontally) mLoadedInitialPhotos = false
setupScrollDirection() directories_grid.adapter = null
getDirectories()
} }
if (mStoredTextColor != config.textColor) { if (mStoredTextColor != config.textColor) {
@ -207,8 +222,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
menuInflater.inflate(R.menu.menu_main_intent, menu) menuInflater.inflate(R.menu.menu_main_intent, menu)
} else { } else {
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
menu.findItem(R.id.increase_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt < MAX_COLUMN_COUNT menu.apply {
menu.findItem(R.id.reduce_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt > 1 findItem(R.id.increase_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt < MAX_COLUMN_COUNT
findItem(R.id.reduce_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt > 1
}
} }
menu.findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden menu.findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
menu.findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden menu.findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
@ -264,7 +281,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
if (newFolder.exists() && newFolder.isDirectory) { if (newFolder.exists() && newFolder.isDirectory) {
if (newFolder.list()?.isEmpty() == true) { if (newFolder.list()?.isEmpty() == true) {
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(applicationContext), true) tryDeleteFileDirItem(newFolder.toFileDirItem(applicationContext), true, true)
} }
} }
config.tempFolderPath = "" config.tempFolderPath = ""
@ -317,10 +334,13 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun showSortingDialog() { private fun showSortingDialog() {
ChangeSortingDialog(this, true, false) { ChangeSortingDialog(this, true, false) {
directories_grid.adapter = null
if (config.directorySorting and SORT_BY_DATE_MODIFIED > 0 || config.directorySorting and SORT_BY_DATE_TAKEN > 0) { if (config.directorySorting and SORT_BY_DATE_MODIFIED > 0 || config.directorySorting and SORT_BY_DATE_TAKEN > 0) {
getDirectories() getDirectories()
} else { } else {
gotDirectories(getCurrentlyDisplayedDirs()) Thread {
gotDirectories(getCurrentlyDisplayedDirs())
}.start()
} }
} }
} }
@ -329,6 +349,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
FilterMediaDialog(this) { FilterMediaDialog(this) {
mLoadedInitialPhotos = false mLoadedInitialPhotos = false
directories_refresh_layout.isRefreshing = true directories_refresh_layout.isRefreshing = true
directories_grid.adapter = null
getDirectories() getDirectories()
} }
} }
@ -336,7 +357,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun showAllMedia() { private fun showAllMedia() {
config.showAll = true config.showAll = true
Intent(this, MediaActivity::class.java).apply { Intent(this, MediaActivity::class.java).apply {
putExtra(DIRECTORY, "/") putExtra(DIRECTORY, "")
if (mIsThirdPartyIntent) { if (mIsThirdPartyIntent) {
handleMediaIntent(this) handleMediaIntent(this)
@ -375,12 +396,37 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun toggleTemporarilyShowHidden(show: Boolean) { private fun toggleTemporarilyShowHidden(show: Boolean) {
mLoadedInitialPhotos = false mLoadedInitialPhotos = false
config.temporarilyShowHidden = show config.temporarilyShowHidden = show
directories_grid.adapter = null
getDirectories() getDirectories()
invalidateOptionsMenu() invalidateOptionsMenu()
} }
override fun deleteFolders(folders: ArrayList<File>) { override fun deleteFolders(folders: ArrayList<File>) {
val fileDirItems = folders.map { FileDirItem(it.absolutePath, it.name, true) } as ArrayList<FileDirItem> val fileDirItems = folders.map { FileDirItem(it.absolutePath, it.name, true) } as ArrayList<FileDirItem>
fileDirItems.forEach {
toast(String.format(getString(R.string.deleting_folder), it.name), Toast.LENGTH_LONG)
}
if (config.useRecycleBin) {
val pathsToDelete = ArrayList<String>()
fileDirItems.filter { it.isDirectory }.forEach {
val files = File(it.path).listFiles()
files?.filter { it.absolutePath.isImageVideoGif() }?.mapTo(pathsToDelete) { it.absolutePath }
}
movePathsInRecycleBin(pathsToDelete) {
if (it) {
deleteFilteredFolders(fileDirItems, folders)
} else {
toast(R.string.unknown_error_occurred)
}
}
} else {
deleteFilteredFolders(fileDirItems, folders)
}
}
private fun deleteFilteredFolders(fileDirItems: ArrayList<FileDirItem>, folders: ArrayList<File>) {
deleteFolders(fileDirItems) { deleteFolders(fileDirItems) {
runOnUiThread { runOnUiThread {
refreshItems() refreshItems()
@ -416,6 +462,32 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
layoutManager.spanCount = config.dirColumnCnt layoutManager.spanCount = config.dirColumnCnt
} }
private fun measureRecyclerViewContent(directories: ArrayList<Directory>) {
directories_grid.onGlobalLayout {
if (config.scrollHorizontally) {
calculateContentWidth(directories)
} else {
calculateContentHeight(directories)
}
}
}
private fun calculateContentWidth(directories: ArrayList<Directory>) {
val layoutManager = directories_grid.layoutManager as MyGridLayoutManager
val thumbnailWidth = layoutManager.getChildAt(0)?.width ?: 0
val fullWidth = ((directories.size - 1) / layoutManager.spanCount + 1) * thumbnailWidth
directories_horizontal_fastscroller.setContentWidth(fullWidth)
directories_horizontal_fastscroller.setScrollToX(directories_grid.computeHorizontalScrollOffset())
}
private fun calculateContentHeight(directories: ArrayList<Directory>) {
val layoutManager = directories_grid.layoutManager as MyGridLayoutManager
val thumbnailHeight = layoutManager.getChildAt(0)?.height ?: 0
val fullHeight = ((directories.size - 1) / layoutManager.spanCount + 1) * thumbnailHeight
directories_vertical_fastscroller.setContentHeight(fullHeight)
directories_vertical_fastscroller.setScrollToY(directories_grid.computeVerticalScrollOffset())
}
private fun initZoomListener() { private fun initZoomListener() {
if (config.viewTypeFolders == VIEW_TYPE_GRID) { if (config.viewTypeFolders == VIEW_TYPE_GRID) {
val layoutManager = directories_grid.layoutManager as MyGridLayoutManager val layoutManager = directories_grid.layoutManager as MyGridLayoutManager
@ -451,25 +523,29 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) { FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) {
CreateNewFolderDialog(this, it) { CreateNewFolderDialog(this, it) {
config.tempFolderPath = it config.tempFolderPath = it
gotDirectories(addTempFolderIfNeeded(getCurrentlyDisplayedDirs())) Thread {
gotDirectories(addTempFolderIfNeeded(getCurrentlyDisplayedDirs()))
}.start()
} }
} }
} }
private fun increaseColumnCount() { private fun increaseColumnCount() {
directories_vertical_fastscroller.measureRecyclerViewOnRedraw()
directories_horizontal_fastscroller.measureRecyclerViewOnRedraw()
config.dirColumnCnt = ++(directories_grid.layoutManager as MyGridLayoutManager).spanCount config.dirColumnCnt = ++(directories_grid.layoutManager as MyGridLayoutManager).spanCount
invalidateOptionsMenu() columnCountChanged()
directories_grid.adapter?.notifyDataSetChanged()
} }
private fun reduceColumnCount() { private fun reduceColumnCount() {
directories_vertical_fastscroller.measureRecyclerViewOnRedraw()
directories_horizontal_fastscroller.measureRecyclerViewOnRedraw()
config.dirColumnCnt = --(directories_grid.layoutManager as MyGridLayoutManager).spanCount config.dirColumnCnt = --(directories_grid.layoutManager as MyGridLayoutManager).spanCount
columnCountChanged()
}
private fun columnCountChanged() {
invalidateOptionsMenu() invalidateOptionsMenu()
directories_grid.adapter?.notifyDataSetChanged() directories_grid.adapter?.notifyDataSetChanged()
getRecyclerAdapter()?.dirs?.apply {
measureRecyclerViewContent(this)
}
} }
private fun isPickImageIntent(intent: Intent) = isPickIntent(intent) && (hasImageContentData(intent) || isImageType(intent)) private fun isPickImageIntent(intent: Intent) = isPickIntent(intent) && (hasImageContentData(intent) || isImageType(intent))
@ -585,6 +661,14 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
private fun gotDirectories(newDirs: ArrayList<Directory>) { private fun gotDirectories(newDirs: ArrayList<Directory>) {
// if hidden item showing is disabled but all Favorite items are hidden, hide the Favorites folder
if (!config.shouldShowHidden) {
val favoritesFolder = newDirs.firstOrNull { it.areFavorites() }
if (favoritesFolder != null && favoritesFolder.tmb.getFilenameFromPath().startsWith('.')) {
newDirs.remove(favoritesFolder)
}
}
val dirs = getSortedDirectories(newDirs) val dirs = getSortedDirectories(newDirs)
var isPlaceholderVisible = dirs.isEmpty() var isPlaceholderVisible = dirs.isEmpty()
@ -598,98 +682,113 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
// cached folders have been loaded, recheck folders one by one starting with the first displayed // cached folders have been loaded, recheck folders one by one starting with the first displayed
Thread { val mediaFetcher = MediaFetcher(applicationContext)
val mediaFetcher = MediaFetcher(applicationContext) val getImagesOnly = mIsPickImageIntent || mIsGetImageContentIntent
val getImagesOnly = mIsPickImageIntent || mIsGetImageContentIntent val getVideosOnly = mIsPickVideoIntent || mIsGetVideoContentIntent
val getVideosOnly = mIsPickVideoIntent || mIsGetVideoContentIntent val hiddenString = getString(R.string.hidden)
val hiddenString = getString(R.string.hidden) val albumCovers = config.parseAlbumCovers()
val albumCovers = config.parseAlbumCovers() val includedFolders = config.includedFolders
val includedFolders = config.includedFolders val isSortingAscending = config.directorySorting and SORT_DESCENDING == 0
val isSortingAscending = config.directorySorting and SORT_DESCENDING == 0 val mediumDao = galleryDB.MediumDao()
val mediumDao = galleryDB.MediumDao() val directoryDao = galleryDB.DirectoryDao()
val directoryDao = galleryDB.DirectoryDao() val getProperDateTaken = config.directorySorting and SORT_BY_DATE_TAKEN != 0
val getProperDateTaken = config.directorySorting and SORT_BY_DATE_TAKEN != 0 val favoritePaths = getFavoritePaths()
val favoritePaths = getFavoritePaths()
try { try {
for (directory in dirs) { for (directory in dirs) {
val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths) val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths)
val newDir = if (curMedia.isEmpty()) { val newDir = if (curMedia.isEmpty()) {
directory directory
} else { } else {
createDirectoryFromMedia(directory.path, curMedia, albumCovers, hiddenString, includedFolders, isSortingAscending) createDirectoryFromMedia(directory.path, curMedia, albumCovers, hiddenString, includedFolders, isSortingAscending)
} }
// we are looping through the already displayed folders looking for changes, do not do anything if nothing changed // we are looping through the already displayed folders looking for changes, do not do anything if nothing changed
if (directory == newDir) { if (directory == newDir) {
continue continue
} }
directory.apply { directory.apply {
tmb = newDir.tmb tmb = newDir.tmb
name = newDir.name name = newDir.name
mediaCnt = newDir.mediaCnt mediaCnt = newDir.mediaCnt
modified = newDir.modified modified = newDir.modified
taken = newDir.taken taken = newDir.taken
this@apply.size = newDir.size this@apply.size = newDir.size
types = newDir.types types = newDir.types
} }
showSortedDirs(dirs) showSortedDirs(dirs)
// update directories and media files in the local db, delete invalid items // update directories and media files in the local db, delete invalid items
updateDBDirectory(directory) updateDBDirectory(directory)
if (!directory.isRecycleBin()) {
mediumDao.insertAll(curMedia) mediumDao.insertAll(curMedia)
getCachedMedia(directory.path, getVideosOnly, getImagesOnly) { }
it.forEach { getCachedMedia(directory.path, getVideosOnly, getImagesOnly) {
if (!curMedia.contains(it)) { it.forEach {
mediumDao.deleteMediumPath(it.path) if (!curMedia.contains(it)) {
val path = (it as? Medium)?.path
if (path != null) {
mediumDao.deleteMediumPath(path)
} }
} }
} }
} }
} catch (ignored: ConcurrentModificationException) { }
} catch (ignored: Exception) {
}
val foldersToScan = mediaFetcher.getFoldersToScan()
foldersToScan.add(FAVORITES)
foldersToScan.add(RECYCLE_BIN)
dirs.forEach {
foldersToScan.remove(it.path)
}
// check the remaining folders which were not cached at all yet
for (folder in foldersToScan) {
val newMedia = mediaFetcher.getFilesFrom(folder, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths)
if (newMedia.isEmpty()) {
continue
} }
val foldersToScan = mediaFetcher.getFoldersToScan() if (isPlaceholderVisible) {
dirs.forEach { isPlaceholderVisible = false
foldersToScan.remove(it.path) runOnUiThread {
directories_empty_text_label.beGone()
directories_empty_text.beGone()
directories_grid.beVisible()
}
} }
// check the remaining folders which were not cached at all yet val newDir = createDirectoryFromMedia(folder, newMedia, albumCovers, hiddenString, includedFolders, isSortingAscending)
for (folder in foldersToScan) { dirs.add(newDir)
val newMedia = mediaFetcher.getFilesFrom(folder, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths) showSortedDirs(dirs)
if (newMedia.isEmpty()) { directoryDao.insert(newDir)
continue if (folder != RECYCLE_BIN) {
}
if (isPlaceholderVisible) {
isPlaceholderVisible = false
runOnUiThread {
directories_empty_text_label.beGone()
directories_empty_text.beGone()
directories_grid.beVisible()
}
}
val newDir = createDirectoryFromMedia(folder, newMedia, albumCovers, hiddenString, includedFolders, isSortingAscending)
dirs.add(newDir)
showSortedDirs(dirs)
directoryDao.insert(newDir)
mediumDao.insertAll(newMedia) mediumDao.insertAll(newMedia)
} }
}
mIsGettingDirs = false mIsGettingDirs = false
mLoadedInitialPhotos = true mLoadedInitialPhotos = true
checkLastMediaChanged() checkLastMediaChanged()
runOnUiThread { runOnUiThread {
directories_refresh_layout.isRefreshing = false directories_refresh_layout.isRefreshing = false
directories_vertical_fastscroller.measureRecyclerView() checkPlaceholderVisibility(dirs)
checkPlaceholderVisibility(dirs) }
} checkInvalidDirectories(dirs, directoryDao)
checkInvalidDirectories(dirs, directoryDao)
}.start() val everShownFolders = config.everShownFolders as HashSet
dirs.mapTo(everShownFolders) { it.path }
try {
config.everShownFolders = everShownFolders
} catch (e: Exception) {
config.everShownFolders = HashSet()
}
} }
private fun checkPlaceholderVisibility(dirs: ArrayList<Directory>) { private fun checkPlaceholderVisibility(dirs: ArrayList<Directory>) {
@ -703,7 +802,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
sortedDirs = sortedDirs.distinctBy { it.path.getDistinctPath() } as ArrayList<Directory> sortedDirs = sortedDirs.distinctBy { it.path.getDistinctPath() } as ArrayList<Directory>
runOnUiThread { runOnUiThread {
(directories_grid.adapter as DirectoryAdapter).updateDirs(sortedDirs) (directories_grid.adapter as? DirectoryAdapter)?.updateDirs(sortedDirs)
} }
} }
@ -721,7 +820,11 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
val mediaTypes = curMedia.getDirMediaTypes() val mediaTypes = curMedia.getDirMediaTypes()
val dirName = checkAppendingHidden(path, hiddenString, includedFolders) val dirName = when (path) {
FAVORITES -> getString(R.string.favorites)
RECYCLE_BIN -> getString(R.string.recycle_bin)
else -> checkAppendingHidden(path, hiddenString, includedFolders)
}
val firstItem = curMedia.first() val firstItem = curMedia.first()
val lastItem = curMedia.last() val lastItem = curMedia.last()
@ -733,10 +836,11 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun setupAdapter(dirs: ArrayList<Directory>) { private fun setupAdapter(dirs: ArrayList<Directory>) {
val currAdapter = directories_grid.adapter val currAdapter = directories_grid.adapter
val directories = dirs.clone() as ArrayList<Directory>
if (currAdapter == null) { if (currAdapter == null) {
initZoomListener() initZoomListener()
val fastscroller = if (config.scrollHorizontally) directories_horizontal_fastscroller else directories_vertical_fastscroller val fastscroller = if (config.scrollHorizontally) directories_horizontal_fastscroller else directories_vertical_fastscroller
DirectoryAdapter(this, dirs, this, directories_grid, isPickIntent(intent) || isGetAnyContentIntent(intent), fastscroller) { DirectoryAdapter(this, directories, this, directories_grid, isPickIntent(intent) || isGetAnyContentIntent(intent), fastscroller) {
val path = (it as Directory).path val path = (it as Directory).path
if (path != config.tempFolderPath) { if (path != config.tempFolderPath) {
itemClicked(path) itemClicked(path)
@ -746,15 +850,17 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
directories_grid.adapter = this directories_grid.adapter = this
} }
} else { } else {
(currAdapter as DirectoryAdapter).updateDirs(dirs) (currAdapter as DirectoryAdapter).updateDirs(directories)
} }
getRecyclerAdapter()?.dirs?.apply {
measureRecyclerViewContent(this)
}
setupScrollDirection() setupScrollDirection()
} }
private fun setupScrollDirection() { private fun setupScrollDirection() {
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID
directories_vertical_fastscroller.isHorizontal = false directories_vertical_fastscroller.isHorizontal = false
directories_vertical_fastscroller.beGoneIf(allowHorizontalScroll) directories_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
@ -776,7 +882,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun checkInvalidDirectories(dirs: ArrayList<Directory>, directoryDao: DirectoryDao) { private fun checkInvalidDirectories(dirs: ArrayList<Directory>, directoryDao: DirectoryDao) {
val invalidDirs = ArrayList<Directory>() val invalidDirs = ArrayList<Directory>()
dirs.forEach { dirs.filter { !it.areFavorites() && !it.isRecycleBin() }.forEach {
if (!getDoesFilePathExist(it.path)) { if (!getDoesFilePathExist(it.path)) {
invalidDirs.add(it) invalidDirs.add(it)
} else if (it.path != config.tempFolderPath) { } else if (it.path != config.tempFolderPath) {
@ -788,6 +894,20 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
} }
if (getFavoritePaths().isEmpty()) {
val favoritesFolder = dirs.firstOrNull { it.areFavorites() }
if (favoritesFolder != null) {
invalidDirs.add(favoritesFolder)
}
}
if (config.useRecycleBin) {
val binFolder = dirs.firstOrNull { it.path == RECYCLE_BIN }
if (binFolder != null && galleryDB.MediumDao().getDeletedMedia().isEmpty()) {
invalidDirs.add(binFolder)
}
}
if (invalidDirs.isNotEmpty()) { if (invalidDirs.isNotEmpty()) {
dirs.removeAll(invalidDirs) dirs.removeAll(invalidDirs)
showSortedDirs(dirs) showSortedDirs(dirs)
@ -833,12 +953,28 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
}, LAST_MEDIA_CHECK_PERIOD) }, LAST_MEDIA_CHECK_PERIOD)
} }
private fun checkRecycleBinItems() {
if (config.useRecycleBin) {
Thread {
val mediumDao = galleryDB.MediumDao()
val deletedMedia = mediumDao.getDeletedMedia()
deletedMedia.forEach {
if (System.currentTimeMillis() > it.deletedTS + MONTH_MILLISECONDS) {
mediumDao.deleteMediumPath(it.path)
}
}
}.start()
}
}
override fun refreshItems() { override fun refreshItems() {
getDirectories() getDirectories()
} }
override fun recheckPinnedFolders() { override fun recheckPinnedFolders() {
gotDirectories(movePinnedDirectoriesToFront(getCurrentlyDisplayedDirs())) Thread {
gotDirectories(movePinnedDirectoriesToFront(getCurrentlyDisplayedDirs()))
}.start()
} }
override fun updateDirectories(directories: ArrayList<Directory>) { override fun updateDirectories(directories: ArrayList<Directory>) {
@ -896,6 +1032,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
add(Release(177, R.string.release_177)) add(Release(177, R.string.release_177))
add(Release(178, R.string.release_178)) add(Release(178, R.string.release_178))
add(Release(180, R.string.release_180)) add(Release(180, R.string.release_180))
add(Release(181, R.string.release_181))
add(Release(182, R.string.release_182))
add(Release(184, R.string.release_184))
checkWhatsNew(this, BuildConfig.VERSION_CODE) checkWhatsNew(this, BuildConfig.VERSION_CODE)
} }
} }

View file

@ -33,18 +33,22 @@ import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.adapters.MediaAdapter import com.simplemobiletools.gallery.adapters.MediaAdapter
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
import com.simplemobiletools.gallery.dialogs.ChangeGroupingDialog
import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog
import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
import com.simplemobiletools.gallery.dialogs.FilterMediaDialog import com.simplemobiletools.gallery.dialogs.FilterMediaDialog
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.interfaces.MediaOperationsListener
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import com.simplemobiletools.gallery.models.ThumbnailSection
import kotlinx.android.synthetic.main.activity_media.* import kotlinx.android.synthetic.main.activity_media.*
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { class MediaActivity : SimpleActivity(), MediaOperationsListener {
private val LAST_MEDIA_CHECK_PERIOD = 3000L private val LAST_MEDIA_CHECK_PERIOD = 3000L
private var mPath = "" private var mPath = ""
@ -72,7 +76,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private var mStoredPrimaryColor = 0 private var mStoredPrimaryColor = 0
companion object { companion object {
var mMedia = ArrayList<Medium>() var mMedia = ArrayList<ThumbnailItem>()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -121,8 +125,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
if (mStoredScrollHorizontally != config.scrollHorizontally) { if (mStoredScrollHorizontally != config.scrollHorizontally) {
getMediaAdapter()?.updateScrollHorizontally(config.viewTypeFiles != VIEW_TYPE_LIST || !config.scrollHorizontally) mLoadedInitialPhotos = false
setupScrollDirection() media_grid.adapter = null
getMedia()
} }
if (mStoredTextColor != config.textColor) { if (mStoredTextColor != config.textColor) {
@ -188,9 +193,15 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
val isFolderHidden = File(mPath).containsNoMedia() val isFolderHidden = File(mPath).containsNoMedia()
menu.apply { menu.apply {
findItem(R.id.hide_folder).isVisible = !isFolderHidden && !mShowAll findItem(R.id.group).isVisible = !config.scrollHorizontally
findItem(R.id.unhide_folder).isVisible = isFolderHidden && !mShowAll
findItem(R.id.exclude_folder).isVisible = !mShowAll findItem(R.id.hide_folder).isVisible = !isFolderHidden && !mShowAll && mPath != FAVORITES && mPath != RECYCLE_BIN
findItem(R.id.unhide_folder).isVisible = isFolderHidden && !mShowAll && mPath != FAVORITES && mPath != RECYCLE_BIN
findItem(R.id.exclude_folder).isVisible = !mShowAll && mPath != FAVORITES && mPath != RECYCLE_BIN
findItem(R.id.empty_recycle_bin).isVisible = mPath == RECYCLE_BIN
findItem(R.id.empty_disable_recycle_bin).isVisible = mPath == RECYCLE_BIN
findItem(R.id.restore_all_files).isVisible = mPath == RECYCLE_BIN
findItem(R.id.folder_view).isVisible = mShowAll findItem(R.id.folder_view).isVisible = mShowAll
findItem(R.id.open_camera).isVisible = mShowAll findItem(R.id.open_camera).isVisible = mShowAll
@ -213,10 +224,14 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
when (item.itemId) { when (item.itemId) {
R.id.sort -> showSortingDialog() R.id.sort -> showSortingDialog()
R.id.filter -> showFilterMediaDialog() R.id.filter -> showFilterMediaDialog()
R.id.empty_recycle_bin -> emptyRecycleBin()
R.id.empty_disable_recycle_bin -> emptyAndDisableRecycleBin()
R.id.restore_all_files -> restoreAllFiles()
R.id.toggle_filename -> toggleFilenameVisibility() R.id.toggle_filename -> toggleFilenameVisibility()
R.id.open_camera -> launchCamera() R.id.open_camera -> launchCamera()
R.id.folder_view -> switchToFolderView() R.id.folder_view -> switchToFolderView()
R.id.change_view_type -> changeViewType() R.id.change_view_type -> changeViewType()
R.id.group -> showGroupByDialog()
R.id.hide_folder -> tryHideFolder() R.id.hide_folder -> tryHideFolder()
R.id.unhide_folder -> unhideFolder() R.id.unhide_folder -> unhideFolder()
R.id.exclude_folder -> tryExcludeFolder() R.id.exclude_folder -> tryExcludeFolder()
@ -268,9 +283,13 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
return true return true
} }
// this triggers on device rotation too, avoid doing anything
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean { override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
mIsSearchOpen = false if (mIsSearchOpen) {
media_refresh_layout.isEnabled = config.enablePullToRefresh mIsSearchOpen = false
media_refresh_layout.isEnabled = config.enablePullToRefresh
searchQueryChanged("")
}
return true return true
} }
}) })
@ -278,10 +297,12 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private fun searchQueryChanged(text: String) { private fun searchQueryChanged(text: String) {
Thread { Thread {
val filtered = mMedia.filter { it.name.contains(text, true) } as ArrayList val filtered = mMedia.filter { it is Medium && it.name.contains(text, true) } as ArrayList
filtered.sortBy { !it.name.startsWith(text, true) } filtered.sortBy { it is Medium && !it.name.startsWith(text, true) }
val grouped = MediaFetcher(applicationContext).groupMedia(filtered as ArrayList<Medium>, mPath)
runOnUiThread { runOnUiThread {
getMediaAdapter()?.updateMedia(filtered) getMediaAdapter()?.updateMedia(grouped)
measureRecyclerViewContent(grouped)
} }
}.start() }.start()
} }
@ -290,6 +311,8 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
handlePermission(PERMISSION_WRITE_STORAGE) { handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) { if (it) {
val dirName = when { val dirName = when {
mPath == FAVORITES -> getString(R.string.favorites)
mPath == RECYCLE_BIN -> getString(R.string.recycle_bin)
mPath == OTG_PATH -> getString(R.string.otg) mPath == OTG_PATH -> getString(R.string.otg)
mPath.startsWith(OTG_PATH) -> mPath.trimEnd('/').substringAfterLast('/') mPath.startsWith(OTG_PATH) -> mPath.trimEnd('/').substringAfterLast('/')
else -> getHumanizedFilename(mPath) else -> getHumanizedFilename(mPath)
@ -316,14 +339,19 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
initZoomListener() initZoomListener()
val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_fastscroller val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_fastscroller
MediaAdapter(this, mMedia, this, mIsGetImageIntent || mIsGetVideoIntent || mIsGetAnyIntent, mAllowPickingMultiple, media_grid, fastscroller) { MediaAdapter(this, mMedia, this, mIsGetImageIntent || mIsGetVideoIntent || mIsGetAnyIntent, mAllowPickingMultiple, media_grid, fastscroller) {
itemClicked((it as Medium).path) if (it is Medium) {
itemClicked(it.path)
}
}.apply { }.apply {
setupZoomListener(mZoomListener) setupZoomListener(mZoomListener)
media_grid.adapter = this media_grid.adapter = this
} }
setupLayoutManager()
} else { } else {
(currAdapter as MediaAdapter).updateMedia(mMedia) (currAdapter as MediaAdapter).updateMedia(mMedia)
} }
measureRecyclerViewContent(mMedia)
setupScrollDirection() setupScrollDirection()
} }
@ -335,8 +363,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
media_horizontal_fastscroller.isHorizontal = true media_horizontal_fastscroller.isHorizontal = true
media_horizontal_fastscroller.beVisibleIf(allowHorizontalScroll) media_horizontal_fastscroller.beVisibleIf(allowHorizontalScroll)
val sorting = config.getFileSorting(mPath) val sorting = config.getFileSorting(if (mShowAll) SHOW_ALL else mPath)
if (allowHorizontalScroll) { if (allowHorizontalScroll) {
media_horizontal_fastscroller.allowBubbleDisplay = config.showInfoBubble media_horizontal_fastscroller.allowBubbleDisplay = config.showInfoBubble
media_horizontal_fastscroller.setViews(media_grid, media_refresh_layout) { media_horizontal_fastscroller.setViews(media_grid, media_refresh_layout) {
@ -350,11 +377,19 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
} }
private fun getBubbleTextItem(index: Int, sorting: Int) = getMediaAdapter()?.media?.getOrNull(index)?.getBubbleText(sorting) ?: "" private fun getBubbleTextItem(index: Int, sorting: Int): String {
var realIndex = index
val mediaAdapter = getMediaAdapter()
if (mediaAdapter?.isASectionTitle(index) == true) {
realIndex++
}
return mediaAdapter?.getItemBubbleText(realIndex, sorting) ?: ""
}
private fun checkLastMediaChanged() { private fun checkLastMediaChanged() {
if (isActivityDestroyed()) if (isActivityDestroyed()) {
return return
}
mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.removeCallbacksAndMessages(null)
mLastMediaHandler.postDelayed({ mLastMediaHandler.postDelayed({
@ -375,8 +410,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
private fun showSortingDialog() { private fun showSortingDialog() {
ChangeSortingDialog(this, false, !config.showAll, mPath) { ChangeSortingDialog(this, false, true, mPath) {
mLoadedInitialPhotos = false mLoadedInitialPhotos = false
media_grid.adapter = null
getMedia() getMedia()
} }
} }
@ -385,10 +421,37 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
FilterMediaDialog(this) { FilterMediaDialog(this) {
mLoadedInitialPhotos = false mLoadedInitialPhotos = false
media_refresh_layout.isRefreshing = true media_refresh_layout.isRefreshing = true
media_grid.adapter = null
getMedia() getMedia()
} }
} }
private fun emptyRecycleBin() {
showRecycleBinEmptyingDialog {
emptyTheRecycleBin {
finish()
}
}
}
private fun emptyAndDisableRecycleBin() {
showRecycleBinEmptyingDialog {
emptyAndDisableTheRecycleBin {
finish()
}
}
}
private fun restoreAllFiles() {
val paths = mMedia.filter { it is Medium }.map { (it as Medium).path } as ArrayList<String>
restoreRecycleBinPaths(paths) {
Thread {
galleryDB.DirectoryDao().deleteDirPath(RECYCLE_BIN)
}.start()
finish()
}
}
private fun toggleFilenameVisibility() { private fun toggleFilenameVisibility() {
config.displayFileNames = !config.displayFileNames config.displayFileNames = !config.displayFileNames
getMediaAdapter()?.updateDisplayFilenames(config.displayFileNames) getMediaAdapter()?.updateDisplayFilenames(config.displayFileNames)
@ -414,6 +477,14 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
} }
private fun showGroupByDialog() {
ChangeGroupingDialog(this, mPath) {
mLoadedInitialPhotos = false
media_grid.adapter = null
getMedia()
}
}
private fun tryHideFolder() { private fun tryHideFolder() {
if (config.wasHideFolderTooltipShown) { if (config.wasHideFolderTooltipShown) {
hideFolder() hideFolder()
@ -454,7 +525,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private fun deleteDirectoryIfEmpty() { private fun deleteDirectoryIfEmpty() {
val fileDirItem = FileDirItem(mPath, mPath.getFilenameFromPath()) val fileDirItem = FileDirItem(mPath, mPath.getFilenameFromPath())
if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(applicationContext, true) == 0) { if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(applicationContext, true) == 0) {
tryDeleteFileDirItem(fileDirItem, true) tryDeleteFileDirItem(fileDirItem, true, true)
} }
} }
@ -473,15 +544,22 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} else { } else {
gotMedia(it, true) gotMedia(it, true)
} }
startAsyncTask()
} }
} else { } else {
media_refresh_layout.isRefreshing = true media_refresh_layout.isRefreshing = true
startAsyncTask()
} }
mLoadedInitialPhotos = true mLoadedInitialPhotos = true
}
private fun startAsyncTask() {
mCurrAsyncTask?.stopFetching() mCurrAsyncTask?.stopFetching()
mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetImageIntent, mIsGetVideoIntent, mShowAll) { mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetImageIntent, mIsGetVideoIntent, mShowAll) {
gotMedia(it) Thread {
gotMedia(it)
}.start()
} }
mCurrAsyncTask!!.execute() mCurrAsyncTask!!.execute()
@ -489,8 +567,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private fun isDirEmpty(): Boolean { private fun isDirEmpty(): Boolean {
return if (mMedia.size <= 0 && config.filterMedia > 0) { return if (mMedia.size <= 0 && config.filterMedia > 0) {
deleteDirectoryIfEmpty() if (mPath != FAVORITES && mPath != RECYCLE_BIN) {
deleteDBDirectory() deleteDirectoryIfEmpty()
deleteDBDirectory()
}
finish() finish()
true true
} else { } else {
@ -540,6 +620,62 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
layoutManager.spanCount = config.mediaColumnCnt layoutManager.spanCount = config.mediaColumnCnt
val adapter = getMediaAdapter()
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter?.isASectionTitle(position) == true) {
layoutManager.spanCount
} else {
1
}
}
}
}
private fun measureRecyclerViewContent(media: ArrayList<ThumbnailItem>) {
media_grid.onGlobalLayout {
if (config.scrollHorizontally) {
calculateContentWidth(media)
} else {
calculateContentHeight(media)
}
}
}
private fun calculateContentWidth(media: ArrayList<ThumbnailItem>) {
val layoutManager = media_grid.layoutManager as MyGridLayoutManager
val thumbnailWidth = layoutManager.getChildAt(0)?.width ?: 0
val fullWidth = ((media.size - 1) / layoutManager.spanCount + 1) * thumbnailWidth
media_horizontal_fastscroller.setContentWidth(fullWidth)
media_horizontal_fastscroller.setScrollToX(media_grid.computeHorizontalScrollOffset())
}
private fun calculateContentHeight(media: ArrayList<ThumbnailItem>) {
val layoutManager = media_grid.layoutManager as MyGridLayoutManager
val pathToCheck = if (mPath.isEmpty()) SHOW_ALL else mPath
val hasSections = config.getFolderGrouping(pathToCheck) and GROUP_BY_NONE == 0 && !config.scrollHorizontally
val sectionTitleHeight = if (hasSections) layoutManager.getChildAt(0)?.height ?: 0 else 0
val thumbnailHeight = if (hasSections) layoutManager.getChildAt(1)?.height ?: 0 else layoutManager.getChildAt(0)?.height
?: 0
var fullHeight = 0
var curSectionItems = 0
media.forEach {
if (it is ThumbnailSection) {
fullHeight += sectionTitleHeight
if (curSectionItems != 0) {
val rows = ((curSectionItems - 1) / layoutManager.spanCount + 1)
fullHeight += rows * thumbnailHeight
}
curSectionItems = 0
} else {
curSectionItems++
}
}
fullHeight += ((curSectionItems - 1) / layoutManager.spanCount + 1) * thumbnailHeight
media_vertical_fastscroller.setContentHeight(fullHeight)
media_vertical_fastscroller.setScrollToY(media_grid.computeVerticalScrollOffset())
} }
private fun initZoomListener() { private fun initZoomListener() {
@ -574,19 +710,19 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
private fun increaseColumnCount() { private fun increaseColumnCount() {
media_vertical_fastscroller.measureRecyclerViewOnRedraw()
media_horizontal_fastscroller.measureRecyclerViewOnRedraw()
config.mediaColumnCnt = ++(media_grid.layoutManager as MyGridLayoutManager).spanCount config.mediaColumnCnt = ++(media_grid.layoutManager as MyGridLayoutManager).spanCount
invalidateOptionsMenu() columnCountChanged()
media_grid.adapter?.notifyDataSetChanged()
} }
private fun reduceColumnCount() { private fun reduceColumnCount() {
media_vertical_fastscroller.measureRecyclerViewOnRedraw()
media_horizontal_fastscroller.measureRecyclerViewOnRedraw()
config.mediaColumnCnt = --(media_grid.layoutManager as MyGridLayoutManager).spanCount config.mediaColumnCnt = --(media_grid.layoutManager as MyGridLayoutManager).spanCount
columnCountChanged()
}
private fun columnCountChanged() {
invalidateOptionsMenu() invalidateOptionsMenu()
media_grid.adapter?.notifyDataSetChanged() media_grid.adapter?.notifyDataSetChanged()
measureRecyclerViewContent(mMedia)
} }
private fun isSetWallpaperIntent() = intent.getBooleanExtra(SET_WALLPAPER_INTENT, false) private fun isSetWallpaperIntent() = intent.getBooleanExtra(SET_WALLPAPER_INTENT, false)
@ -642,22 +778,15 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
Intent(this, ViewPagerActivity::class.java).apply { Intent(this, ViewPagerActivity::class.java).apply {
putExtra(PATH, path) putExtra(PATH, path)
putExtra(SHOW_ALL, mShowAll) putExtra(SHOW_ALL, mShowAll)
putExtra(SHOW_FAVORITES, mPath == FAVORITES)
putExtra(SHOW_RECYCLE_BIN, mPath == RECYCLE_BIN)
startActivity(this) startActivity(this)
} }
} }
} }
} }
private fun gotMedia(media: ArrayList<Medium>, isFromCache: Boolean = false) { private fun gotMedia(media: ArrayList<ThumbnailItem>, isFromCache: Boolean = false) {
val mediaToInsert = media.clone() as ArrayList<Medium>
Thread {
mLatestMediaId = getLatestMediaId()
mLatestMediaDateId = getLatestMediaByDateId()
if (!isFromCache) {
galleryDB.MediumDao().insertAll(mediaToInsert)
}
}.start()
mIsGettingMedia = false mIsGettingMedia = false
checkLastMediaChanged() checkLastMediaChanged()
mMedia = media mMedia = media
@ -671,25 +800,51 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
media_vertical_fastscroller.beVisibleIf(media_grid.isVisible() && !allowHorizontalScroll) media_vertical_fastscroller.beVisibleIf(media_grid.isVisible() && !allowHorizontalScroll)
media_horizontal_fastscroller.beVisibleIf(media_grid.isVisible() && allowHorizontalScroll) media_horizontal_fastscroller.beVisibleIf(media_grid.isVisible() && allowHorizontalScroll)
setupAdapter() setupAdapter()
} }
mLatestMediaId = getLatestMediaId()
mLatestMediaDateId = getLatestMediaByDateId()
if (!isFromCache) {
val mediaToInsert = (mMedia.clone() as ArrayList<ThumbnailItem>).filter { it is Medium && it.deletedTS == 0L }.map { it as Medium }
galleryDB.MediumDao().insertAll(mediaToInsert)
}
} }
override fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>) { override fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>) {
val filtered = fileDirItems.filter { it.path.isImageVideoGif() } as ArrayList val filtered = fileDirItems.filter { it.path.isImageVideoGif() } as ArrayList
val deletingItems = resources.getQuantityString(R.plurals.deleting_items, filtered.size, filtered.size)
toast(deletingItems)
if (config.useRecycleBin && !filtered.first().path.startsWith(filesDir.toString())) {
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) {
if (it) {
deleteFilteredFiles(filtered)
} else {
toast(R.string.unknown_error_occurred)
}
}
} else {
deleteFilteredFiles(filtered)
}
}
private fun deleteFilteredFiles(filtered: ArrayList<FileDirItem>) {
deleteFiles(filtered) { deleteFiles(filtered) {
if (!it) { if (!it) {
toast(R.string.unknown_error_occurred) toast(R.string.unknown_error_occurred)
return@deleteFiles return@deleteFiles
} }
mMedia.removeAll { filtered.map { it.path }.contains(it.path) } mMedia.removeAll { filtered.map { it.path }.contains((it as? Medium)?.path) }
Thread { Thread {
val mediumDao = galleryDB.MediumDao() val mediumDao = galleryDB.MediumDao()
val useRecycleBin = config.useRecycleBin
filtered.forEach { filtered.forEach {
mediumDao.deleteMediumPath(it.path) if (!useRecycleBin) {
mediumDao.deleteMediumPath(it.path)
}
} }
}.start() }.start()

View file

@ -0,0 +1,176 @@
package com.simplemobiletools.gallery.activities
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.view.Window
import android.widget.RelativeLayout
import com.google.vr.sdk.widgets.pano.VrPanoramaEventListener
import com.google.vr.sdk.widgets.pano.VrPanoramaView
import com.simplemobiletools.commons.extensions.beVisible
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.PATH
import kotlinx.android.synthetic.main.activity_panorama.*
open class PanoramaActivity : SimpleActivity() {
private val CARDBOARD_DISPLAY_MODE = 3
private var isFullScreen = false
private var isExploreEnabled = true
private var isRendering = false
public override fun onCreate(savedInstanceState: Bundle?) {
useDynamicTheme = false
requestWindowFeature(Window.FEATURE_NO_TITLE)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_panorama)
supportActionBar?.hide()
setupButtonMargins()
cardboard.setOnClickListener {
panorama_view.displayMode = CARDBOARD_DISPLAY_MODE
}
explore.setOnClickListener {
isExploreEnabled = !isExploreEnabled
panorama_view.setPureTouchTracking(isExploreEnabled)
explore.setImageResource(if (isExploreEnabled) R.drawable.ic_explore else R.drawable.ic_explore_off)
}
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
checkIntent()
} else {
toast(R.string.no_storage_permissions)
finish()
}
}
}
override fun onResume() {
super.onResume()
panorama_view.resumeRendering()
isRendering = true
if (config.blackBackground) {
updateStatusbarColor(Color.BLACK)
}
}
override fun onPause() {
super.onPause()
panorama_view.pauseRendering()
isRendering = false
}
override fun onDestroy() {
super.onDestroy()
if (isRendering) {
panorama_view.shutdown()
}
}
private fun checkIntent() {
val path = intent.getStringExtra(PATH)
if (path == null) {
toast(R.string.invalid_image_path)
finish()
return
}
intent.removeExtra(PATH)
try {
val options = VrPanoramaView.Options()
options.inputType = VrPanoramaView.Options.TYPE_MONO
Thread {
val bitmap = getBitmapToLoad(path)
runOnUiThread {
panorama_view.apply {
beVisible()
loadImageFromBitmap(bitmap, options)
setFlingingEnabled(true)
setPureTouchTracking(true)
setEventListener(object : VrPanoramaEventListener() {
override fun onClick() {
handleClick()
}
})
// add custom buttons so we can position them and toggle visibility as desired
setFullscreenButtonEnabled(false)
setInfoButtonEnabled(false)
setTransitionViewEnabled(false)
setStereoModeButtonEnabled(false)
setOnClickListener {
handleClick()
}
}
}
}.start()
} catch (e: Exception) {
showErrorToast(e)
}
window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
isFullScreen = visibility and View.SYSTEM_UI_FLAG_FULLSCREEN != 0
toggleButtonVisibility()
}
}
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
setupButtonMargins()
}
private fun getBitmapToLoad(path: String): Bitmap? {
val options = BitmapFactory.Options()
options.inSampleSize = 1
var bitmap: Bitmap? = null
for (i in 0..10) {
try {
bitmap = BitmapFactory.decodeFile(path, options)
break
} catch (e: OutOfMemoryError) {
options.inSampleSize *= 2
}
}
return bitmap
}
private fun setupButtonMargins() {
(cardboard.layoutParams as RelativeLayout.LayoutParams).apply {
bottomMargin = navigationBarHeight
rightMargin = navigationBarWidth
}
(explore.layoutParams as RelativeLayout.LayoutParams).bottomMargin = navigationBarHeight
}
private fun toggleButtonVisibility() {
cardboard.animate().alpha(if (isFullScreen) 0f else 1f)
cardboard.isClickable = !isFullScreen
explore.animate().alpha(if (isFullScreen) 0f else 1f)
explore.isClickable = !isFullScreen
}
private fun handleClick() {
isFullScreen = !isFullScreen
toggleButtonVisibility()
if (isFullScreen) {
hideSystemUI(false)
} else {
showSystemUI(false)
}
}
}

View file

@ -85,7 +85,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
} }
} }
showSystemUI() showSystemUI(true)
val bundle = Bundle() val bundle = Bundle()
val file = File(mUri.toString()) val file = File(mUri.toString())
val type = when { val type = when {
@ -95,7 +95,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
else -> TYPE_RAWS else -> TYPE_RAWS
} }
mMedium = Medium(null, getFilenameFromUri(mUri!!), mUri.toString(), mUri!!.path.getParentPath(), 0, 0, file.length(), type, false) mMedium = Medium(null, getFilenameFromUri(mUri!!), mUri.toString(), mUri!!.path.getParentPath(), 0, 0, file.length(), type, false, 0L)
supportActionBar?.title = mMedium!!.name supportActionBar?.title = mMedium!!.name
bundle.putSerializable(MEDIUM, mMedium) bundle.putSerializable(MEDIUM, mMedium)
@ -178,24 +178,32 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
} }
private fun initBottomActionButtons() { private fun initBottomActionButtons() {
bottom_favorite.beGone() arrayListOf(bottom_favorite, bottom_delete, bottom_rotate, bottom_properties, bottom_change_orientation, bottom_slideshow, bottom_show_on_map, bottom_toggle_file_visibility).forEach {
bottom_delete.beGone() it.beGone()
bottom_edit.setOnClickListener {
openEditor(mUri!!.toString())
} }
val visibleBottomActions = if (config.bottomActions) config.visibleBottomActions else 0
bottom_edit.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_EDIT != 0)
bottom_edit.setOnClickListener {
if (mUri != null && bottom_actions.alpha == 1f) {
openEditor(mUri!!.toString())
}
}
bottom_share.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_SHARE != 0)
bottom_share.setOnClickListener { bottom_share.setOnClickListener {
sharePath(mUri!!.toString()) if (mUri != null && bottom_actions.alpha == 1f) {
sharePath(mUri!!.toString())
}
} }
} }
override fun fragmentClicked() { override fun fragmentClicked() {
mIsFullScreen = !mIsFullScreen mIsFullScreen = !mIsFullScreen
if (mIsFullScreen) { if (mIsFullScreen) {
hideSystemUI() hideSystemUI(true)
} else { } else {
showSystemUI() showSystemUI(true)
} }
if (!bottom_actions.isGone()) { if (!bottom_actions.isGone()) {

View file

@ -6,16 +6,19 @@ import android.os.Bundle
import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.dialogs.SecurityDialog import com.simplemobiletools.commons.dialogs.SecurityDialog
import com.simplemobiletools.commons.extensions.beVisibleIf import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
import com.simplemobiletools.commons.extensions.handleHiddenFolderPasswordProtection
import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.commons.helpers.PROTECTION_FINGERPRINT import com.simplemobiletools.commons.helpers.PROTECTION_FINGERPRINT
import com.simplemobiletools.commons.helpers.SHOW_ALL_TABS import com.simplemobiletools.commons.helpers.SHOW_ALL_TABS
import com.simplemobiletools.commons.helpers.sumByLong
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.dialogs.ManageBottomActionsDialog
import com.simplemobiletools.gallery.dialogs.ManageExtendedDetailsDialog import com.simplemobiletools.gallery.dialogs.ManageExtendedDetailsDialog
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.emptyTheRecycleBin
import com.simplemobiletools.gallery.extensions.galleryDB
import com.simplemobiletools.gallery.extensions.showRecycleBinEmptyingDialog
import com.simplemobiletools.gallery.helpers.DEFAULT_BOTTOM_ACTIONS
import com.simplemobiletools.gallery.helpers.ROTATE_BY_ASPECT_RATIO import com.simplemobiletools.gallery.helpers.ROTATE_BY_ASPECT_RATIO
import com.simplemobiletools.gallery.helpers.ROTATE_BY_DEVICE_ROTATION import com.simplemobiletools.gallery.helpers.ROTATE_BY_DEVICE_ROTATION
import com.simplemobiletools.gallery.helpers.ROTATE_BY_SYSTEM_SETTING import com.simplemobiletools.gallery.helpers.ROTATE_BY_SYSTEM_SETTING
@ -24,6 +27,7 @@ import java.util.*
class SettingsActivity : SimpleActivity() { class SettingsActivity : SimpleActivity() {
lateinit var res: Resources lateinit var res: Resources
private var mRecycleBinContentSize = 0L
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -68,6 +72,9 @@ class SettingsActivity : SimpleActivity() {
setupHideExtendedDetails() setupHideExtendedDetails()
setupManageExtendedDetails() setupManageExtendedDetails()
setupSkipDeleteConfirmation() setupSkipDeleteConfirmation()
setupManageBottomActions()
setupUseRecycleBin()
setupEmptyRecycleBin()
updateTextColors(settings_holder) updateTextColors(settings_holder)
setupSectionColors() setupSectionColors()
} }
@ -75,7 +82,7 @@ class SettingsActivity : SimpleActivity() {
private fun setupSectionColors() { private fun setupSectionColors() {
val adjustedPrimaryColor = getAdjustedPrimaryColor() val adjustedPrimaryColor = getAdjustedPrimaryColor()
arrayListOf(visibility_label, videos_label, thumbnails_label, scrolling_label, fullscreen_media_label, security_label, arrayListOf(visibility_label, videos_label, thumbnails_label, scrolling_label, fullscreen_media_label, security_label,
file_operations_label, extended_details_label).forEach { file_operations_label, extended_details_label, bottom_actions_label, recycle_bin_label).forEach {
it.setTextColor(adjustedPrimaryColor) it.setTextColor(adjustedPrimaryColor)
} }
} }
@ -287,14 +294,6 @@ class SettingsActivity : SimpleActivity() {
} }
} }
private fun setupBottomActions() {
settings_bottom_actions.isChecked = config.bottomActions
settings_bottom_actions_holder.setOnClickListener {
settings_bottom_actions.toggle()
config.bottomActions = settings_bottom_actions.isChecked
}
}
private fun setupShowMediaCount() { private fun setupShowMediaCount() {
settings_show_media_count.isChecked = config.showMediaCount settings_show_media_count.isChecked = config.showMediaCount
settings_show_media_count_holder.setOnClickListener { settings_show_media_count_holder.setOnClickListener {
@ -409,4 +408,57 @@ class SettingsActivity : SimpleActivity() {
ROTATE_BY_DEVICE_ROTATION -> R.string.screen_rotation_device_rotation ROTATE_BY_DEVICE_ROTATION -> R.string.screen_rotation_device_rotation
else -> R.string.screen_rotation_aspect_ratio else -> R.string.screen_rotation_aspect_ratio
}) })
private fun setupBottomActions() {
settings_bottom_actions.isChecked = config.bottomActions
settings_bottom_actions_holder.setOnClickListener {
settings_bottom_actions.toggle()
config.bottomActions = settings_bottom_actions.isChecked
settings_manage_bottom_actions_holder.beVisibleIf(config.bottomActions)
}
}
private fun setupManageBottomActions() {
settings_manage_bottom_actions_holder.beVisibleIf(config.bottomActions)
settings_manage_bottom_actions_holder.setOnClickListener {
ManageBottomActionsDialog(this) {
if (config.visibleBottomActions == 0) {
settings_bottom_actions_holder.callOnClick()
config.bottomActions = false
config.visibleBottomActions = DEFAULT_BOTTOM_ACTIONS
}
}
}
}
private fun setupUseRecycleBin() {
settings_empty_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
settings_use_recycle_bin.isChecked = config.useRecycleBin
settings_use_recycle_bin_holder.setOnClickListener {
settings_use_recycle_bin.toggle()
config.useRecycleBin = settings_use_recycle_bin.isChecked
settings_empty_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
}
}
private fun setupEmptyRecycleBin() {
Thread {
mRecycleBinContentSize = galleryDB.MediumDao().getDeletedMedia().sumByLong { it.size }
runOnUiThread {
settings_empty_recycle_bin_size.text = mRecycleBinContentSize.formatSize()
}
}.start()
settings_empty_recycle_bin_holder.setOnClickListener {
if (mRecycleBinContentSize == 0L) {
toast(R.string.recycle_bin_empty)
} else {
showRecycleBinEmptyingDialog {
emptyTheRecycleBin()
mRecycleBinContentSize = 0L
settings_empty_recycle_bin_size.text = 0L.formatSize()
}
}
}
}
} }

View file

@ -44,6 +44,7 @@ import com.simplemobiletools.gallery.fragments.VideoFragment
import com.simplemobiletools.gallery.fragments.ViewPagerFragment import com.simplemobiletools.gallery.fragments.ViewPagerFragment
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import kotlinx.android.synthetic.main.activity_medium.* import kotlinx.android.synthetic.main.activity_medium.*
import kotlinx.android.synthetic.main.bottom_actions.* import kotlinx.android.synthetic.main.bottom_actions.*
import java.io.File import java.io.File
@ -70,7 +71,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private var mIsOrientationLocked = false private var mIsOrientationLocked = false
private var mStoredReplaceZoomableImages = false private var mStoredReplaceZoomableImages = false
private var mStoredBottomActions = true
private var mMediaFiles = ArrayList<Medium>() private var mMediaFiles = ArrayList<Medium>()
private var mFavoritePaths = ArrayList<String>() private var mFavoritePaths = ArrayList<String>()
@ -83,7 +83,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_medium) setContentView(R.layout.activity_medium)
mMediaFiles = MediaActivity.mMedia.clone() as ArrayList<Medium> (MediaActivity.mMedia.clone() as ArrayList<ThumbnailItem>).filter { it is Medium }.mapTo(mMediaFiles) { it as Medium }
handlePermission(PERMISSION_WRITE_STORAGE) { handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) { if (it) {
@ -95,7 +95,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
storeStateVariables() storeStateVariables()
initBottomActions()
initFavorites() initFavorites()
} }
@ -120,10 +119,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
refreshViewPager() refreshViewPager()
} }
if (mStoredBottomActions != config.bottomActions) { initBottomActions()
initBottomActions()
}
supportActionBar?.setBackgroundDrawable(resources.getDrawable(R.drawable.actionbar_gradient_background)) supportActionBar?.setBackgroundDrawable(resources.getDrawable(R.drawable.actionbar_gradient_background))
if (config.maxBrightness) { if (config.maxBrightness) {
@ -197,9 +193,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
if (!getDoesFilePathExist(mPath)) { if (!getDoesFilePathExist(mPath)) {
Thread {
scanPathRecursively(mPath)
}.start()
finish() finish()
return return
} }
@ -214,9 +207,15 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
config.isThirdPartyIntent = true config.isThirdPartyIntent = true
} }
showSystemUI() showSystemUI(true)
mDirectory = mPath.getParentPath() val isShowingFavorites = intent.getBooleanExtra(SHOW_FAVORITES, false)
val isShowingRecycleBin = intent.getBooleanExtra(SHOW_RECYCLE_BIN, false)
mDirectory = when {
isShowingFavorites -> FAVORITES
isShowingRecycleBin -> RECYCLE_BIN
else -> mPath.getParentPath()
}
if (mDirectory.startsWith(OTG_PATH.trimEnd('/'))) { if (mDirectory.startsWith(OTG_PATH.trimEnd('/'))) {
mDirectory += "/" mDirectory += "/"
} }
@ -225,7 +224,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
view_pager.onGlobalLayout { view_pager.onGlobalLayout {
if (!isActivityDestroyed()) { if (!isActivityDestroyed()) {
if (mMediaFiles.isNotEmpty()) { if (mMediaFiles.isNotEmpty()) {
gotMedia(mMediaFiles) gotMedia(mMediaFiles as ArrayList<ThumbnailItem>)
} }
} }
} }
@ -251,6 +250,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
checkSystemUI() checkSystemUI()
if (!bottom_actions.isGone()) { if (!bottom_actions.isGone()) {
bottom_actions.animate().alpha(if (mIsFullScreen) 0f else 1f).start() bottom_actions.animate().alpha(if (mIsFullScreen) 0f else 1f).start()
arrayOf(bottom_favorite, bottom_edit, bottom_share, bottom_delete, bottom_rotate, bottom_properties, bottom_change_orientation,
bottom_slideshow, bottom_show_on_map, bottom_toggle_file_visibility, bottom_rename).forEach {
it.isClickable = !mIsFullScreen
}
} }
} }
} }
@ -268,14 +271,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
private fun setupRotation() { private fun setupRotation() {
if (mIsOrientationLocked) { if (!mIsOrientationLocked) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { if (config.screenRotation == ROTATE_BY_DEVICE_ROTATION) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
} else if (config.screenRotation == ROTATE_BY_SYSTEM_SETTING) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
} }
} else if (config.screenRotation == ROTATE_BY_DEVICE_ROTATION) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
} else if (config.screenRotation == ROTATE_BY_SYSTEM_SETTING) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
} }
} }
@ -283,19 +284,25 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
menuInflater.inflate(R.menu.menu_viewpager, menu) menuInflater.inflate(R.menu.menu_viewpager, menu)
val currentMedium = getCurrentMedium() ?: return true val currentMedium = getCurrentMedium() ?: return true
currentMedium.isFavorite = mFavoritePaths.contains(currentMedium.path) currentMedium.isFavorite = mFavoritePaths.contains(currentMedium.path)
val visibleBottomActions = if (config.bottomActions) config.visibleBottomActions else 0
menu.apply { menu.apply {
findItem(R.id.menu_delete).isVisible = !config.bottomActions findItem(R.id.menu_show_on_map).isVisible = visibleBottomActions and BOTTOM_ACTION_SHOW_ON_MAP == 0
findItem(R.id.menu_share).isVisible = !config.bottomActions findItem(R.id.menu_slideshow).isVisible = visibleBottomActions and BOTTOM_ACTION_SLIDESHOW == 0
findItem(R.id.menu_edit).isVisible = !config.bottomActions findItem(R.id.menu_properties).isVisible = visibleBottomActions and BOTTOM_ACTION_PROPERTIES == 0
findItem(R.id.menu_rotate).isVisible = currentMedium.isImage() findItem(R.id.menu_delete).isVisible = visibleBottomActions and BOTTOM_ACTION_DELETE == 0
findItem(R.id.menu_share).isVisible = visibleBottomActions and BOTTOM_ACTION_SHARE == 0
findItem(R.id.menu_edit).isVisible = visibleBottomActions and BOTTOM_ACTION_EDIT == 0
findItem(R.id.menu_rename).isVisible = visibleBottomActions and BOTTOM_ACTION_RENAME == 0
findItem(R.id.menu_rotate).isVisible = currentMedium.isImage() && visibleBottomActions and BOTTOM_ACTION_ROTATE == 0
findItem(R.id.menu_save_as).isVisible = mRotationDegrees != 0 findItem(R.id.menu_save_as).isVisible = mRotationDegrees != 0
findItem(R.id.menu_hide).isVisible = !currentMedium.name.startsWith('.') findItem(R.id.menu_hide).isVisible = !currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0
findItem(R.id.menu_unhide).isVisible = currentMedium.name.startsWith('.') findItem(R.id.menu_unhide).isVisible = currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0
findItem(R.id.menu_add_to_favorites).isVisible = !currentMedium.isFavorite && !config.bottomActions findItem(R.id.menu_add_to_favorites).isVisible = !currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0
findItem(R.id.menu_remove_from_favorites).isVisible = currentMedium.isFavorite && !config.bottomActions findItem(R.id.menu_remove_from_favorites).isVisible = currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0
findItem(R.id.menu_lock_orientation).isVisible = mRotationDegrees == 0 findItem(R.id.menu_restore_file).isVisible = currentMedium.path.startsWith(filesDir.toString())
findItem(R.id.menu_lock_orientation).title = getString(if (mIsOrientationLocked) R.string.unlock_orientation else R.string.lock_orientation) findItem(R.id.menu_change_orientation).isVisible = mRotationDegrees == 0 && visibleBottomActions and BOTTOM_ACTION_CHANGE_ORIENTATION == 0
findItem(R.id.menu_change_orientation).icon = resources.getDrawable(getChangeOrientationIcon())
findItem(R.id.menu_rotate).setShowAsAction( findItem(R.id.menu_rotate).setShowAsAction(
if (mRotationDegrees != 0) { if (mRotationDegrees != 0) {
MenuItem.SHOW_AS_ACTION_ALWAYS MenuItem.SHOW_AS_ACTION_ALWAYS
@ -304,8 +311,8 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}) })
} }
if (config.bottomActions) { if (visibleBottomActions != 0) {
updateFavoriteIcon(currentMedium) updateBottomActionIcons(currentMedium)
} }
return true return true
} }
@ -322,7 +329,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
R.id.menu_open_with -> openPath(getCurrentPath(), true) R.id.menu_open_with -> openPath(getCurrentPath(), true)
R.id.menu_hide -> toggleFileVisibility(true) R.id.menu_hide -> toggleFileVisibility(true)
R.id.menu_unhide -> toggleFileVisibility(false) R.id.menu_unhide -> toggleFileVisibility(false)
R.id.menu_share -> shareMedium(getCurrentMedium()!!) R.id.menu_share -> shareMediumPath(getCurrentPath())
R.id.menu_delete -> checkDeleteConfirmation() R.id.menu_delete -> checkDeleteConfirmation()
R.id.menu_rename -> renameFile() R.id.menu_rename -> renameFile()
R.id.menu_edit -> openEditor(getCurrentPath()) R.id.menu_edit -> openEditor(getCurrentPath())
@ -330,10 +337,13 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
R.id.menu_show_on_map -> showOnMap() R.id.menu_show_on_map -> showOnMap()
R.id.menu_rotate_right -> rotateImage(90) R.id.menu_rotate_right -> rotateImage(90)
R.id.menu_rotate_left -> rotateImage(270) R.id.menu_rotate_left -> rotateImage(270)
R.id.menu_rotate_one_eighty -> rotateImage(180)
R.id.menu_add_to_favorites -> toggleFavorite() R.id.menu_add_to_favorites -> toggleFavorite()
R.id.menu_remove_from_favorites -> toggleFavorite() R.id.menu_remove_from_favorites -> toggleFavorite()
R.id.menu_rotate_one_eighty -> rotateImage(180) R.id.menu_restore_file -> restoreFile()
R.id.menu_lock_orientation -> toggleLockOrientation() R.id.menu_force_portrait -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
R.id.menu_force_landscape -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
R.id.menu_default_orientation -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
R.id.menu_save_as -> saveImageAs() R.id.menu_save_as -> saveImageAs()
R.id.menu_settings -> launchSettings() R.id.menu_settings -> launchSettings()
else -> return super.onOptionsItemSelected(item) else -> return super.onOptionsItemSelected(item)
@ -344,7 +354,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun storeStateVariables() { private fun storeStateVariables() {
config.apply { config.apply {
mStoredReplaceZoomableImages = replaceZoomableImages mStoredReplaceZoomableImages = replaceZoomableImages
mStoredBottomActions = bottomActions
} }
} }
@ -370,7 +379,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
if (getMediaForSlideshow()) { if (getMediaForSlideshow()) {
view_pager.onGlobalLayout { view_pager.onGlobalLayout {
if (!isActivityDestroyed()) { if (!isActivityDestroyed()) {
hideSystemUI() hideSystemUI(true)
mSlideshowInterval = config.slideshowInterval mSlideshowInterval = config.slideshowInterval
mSlideshowMoveBackwards = config.slideshowMoveBackwards mSlideshowMoveBackwards = config.slideshowMoveBackwards
mIsSlideshowActive = true mIsSlideshowActive = true
@ -448,7 +457,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun stopSlideshow() { private fun stopSlideshow() {
if (mIsSlideshowActive) { if (mIsSlideshowActive) {
mIsSlideshowActive = false mIsSlideshowActive = false
showSystemUI() showSystemUI(true)
mSlideshowHandler.removeCallbacksAndMessages(null) mSlideshowHandler.removeCallbacksAndMessages(null)
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} }
@ -516,7 +525,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
} }
private fun toggleFileVisibility(hide: Boolean) { private fun toggleFileVisibility(hide: Boolean, callback: (() -> Unit)? = null) {
toggleFileVisibility(getCurrentPath(), hide) { toggleFileVisibility(getCurrentPath(), hide) {
val newFileName = it.getFilenameFromPath() val newFileName = it.getFilenameFromPath()
supportActionBar?.title = newFileName supportActionBar?.title = newFileName
@ -527,6 +536,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
getCurrentMedia()[mPos] = this getCurrentMedia()[mPos] = this
} }
invalidateOptionsMenu() invalidateOptionsMenu()
callback?.invoke()
} }
} }
@ -538,16 +548,22 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
supportInvalidateOptionsMenu() supportInvalidateOptionsMenu()
} }
private fun toggleLockOrientation() { private fun toggleOrientation(orientation: Int) {
mIsOrientationLocked = !mIsOrientationLocked requestedOrientation = orientation
if (mIsOrientationLocked) { mIsOrientationLocked = orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { invalidateOptionsMenu()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED }
private fun getChangeOrientationIcon(): Int {
return if (mIsOrientationLocked) {
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
R.drawable.ic_orientation_portrait
} else {
R.drawable.ic_orientation_landscape
} }
} else { } else {
setupRotation() R.drawable.ic_orientation_auto
} }
invalidateOptionsMenu()
} }
private fun saveImageAs() { private fun saveImageAs() {
@ -589,7 +605,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
if (getDoesFilePathExist(newPath)) { if (getDoesFilePathExist(newPath)) {
tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath())) tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath()), false, true)
} }
copyFile(tmpPath, newPath) copyFile(tmpPath, newPath)
@ -618,7 +634,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
} finally { } finally {
tryDeleteFileDirItem(tmpFileDirItem) tryDeleteFileDirItem(tmpFileDirItem, false, true)
} }
} }
@ -761,26 +777,85 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
private fun initBottomActionButtons() { private fun initBottomActionButtons() {
val visibleBottomActions = if (config.bottomActions) config.visibleBottomActions else 0
bottom_favorite.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE != 0)
bottom_favorite.setOnClickListener { bottom_favorite.setOnClickListener {
toggleFavorite() toggleFavorite()
} }
bottom_edit.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_EDIT != 0)
bottom_edit.setOnClickListener { bottom_edit.setOnClickListener {
openEditor(getCurrentPath()) openEditor(getCurrentPath())
} }
bottom_share.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_SHARE != 0)
bottom_share.setOnClickListener { bottom_share.setOnClickListener {
shareMedium(getCurrentMedium()!!) shareMediumPath(getCurrentPath())
} }
bottom_delete.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_DELETE != 0)
bottom_delete.setOnClickListener { bottom_delete.setOnClickListener {
checkDeleteConfirmation() checkDeleteConfirmation()
} }
bottom_rotate.setOnClickListener {
rotateImage(90)
}
bottom_properties.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_PROPERTIES != 0)
bottom_properties.setOnClickListener {
showProperties()
}
bottom_change_orientation.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_CHANGE_ORIENTATION != 0)
bottom_change_orientation.setOnClickListener {
requestedOrientation = when (requestedOrientation) {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
else -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
mIsOrientationLocked = requestedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
updateBottomActionIcons(getCurrentMedium())
}
bottom_slideshow.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_SLIDESHOW != 0)
bottom_slideshow.setOnClickListener {
initSlideshow()
}
bottom_show_on_map.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_SHOW_ON_MAP != 0)
bottom_show_on_map.setOnClickListener {
showOnMap()
}
bottom_toggle_file_visibility.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY != 0)
bottom_toggle_file_visibility.setOnClickListener {
getCurrentMedium()?.apply {
toggleFileVisibility(!isHidden()) {
updateBottomActionIcons(getCurrentMedium())
}
}
}
bottom_rename.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_RENAME != 0)
bottom_rename.setOnClickListener {
renameFile()
}
} }
private fun updateFavoriteIcon(medium: Medium) { private fun updateBottomActionIcons(medium: Medium?) {
val icon = if (medium.isFavorite) R.drawable.ic_star_on else R.drawable.ic_star_off if (medium == null) {
bottom_favorite.setImageResource(icon) return
}
val favoriteIcon = if (medium.isFavorite) R.drawable.ic_star_on else R.drawable.ic_star_off
bottom_favorite.setImageResource(favoriteIcon)
val hideIcon = if (medium.isHidden()) R.drawable.ic_unhide else R.drawable.ic_hide
bottom_toggle_file_visibility.setImageResource(hideIcon)
bottom_rotate.beVisibleIf(config.visibleBottomActions and BOTTOM_ACTION_ROTATE != 0 && getCurrentMedium()?.isImage() == true)
bottom_change_orientation.setImageResource(getChangeOrientationIcon())
} }
private fun toggleFavorite() { private fun toggleFavorite() {
@ -797,6 +872,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}.start() }.start()
} }
private fun restoreFile() {
restoreRecycleBinPath(getCurrentPath()) {
refreshViewPager()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == REQUEST_EDIT_IMAGE) { if (requestCode == REQUEST_EDIT_IMAGE) {
if (resultCode == Activity.RESULT_OK && resultData != null) { if (resultCode == Activity.RESULT_OK && resultData != null) {
@ -813,6 +894,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
private fun checkDeleteConfirmation() { private fun checkDeleteConfirmation() {
if (getCurrentMedium() == null) {
return
}
if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) { if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) {
deleteConfirmed() deleteConfirmed()
} else { } else {
@ -821,16 +906,34 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
private fun askConfirmDelete() { private fun askConfirmDelete() {
DeleteWithRememberDialog(this, getString(R.string.proceed_with_deletion)) { val message = if (config.useRecycleBin && !getCurrentMedium()!!.getIsInRecycleBin()) R.string.are_you_sure_recycle_bin else R.string.are_you_sure_delete
DeleteWithRememberDialog(this, getString(message)) {
config.tempSkipDeleteConfirmation = it config.tempSkipDeleteConfirmation = it
deleteConfirmed() deleteConfirmed()
} }
} }
private fun deleteConfirmed() { private fun deleteConfirmed() {
val path = getCurrentMedia()[mPos].path val path = getCurrentMedia().getOrNull(mPos)?.path ?: return
tryDeleteFileDirItem(FileDirItem(path, path.getFilenameFromPath())) { if (getIsPathDirectory(path)) {
refreshViewPager() return
}
val fileDirItem = FileDirItem(path, path.getFilenameFromPath())
if (config.useRecycleBin && !getCurrentMedium()!!.getIsInRecycleBin()) {
movePathsInRecycleBin(arrayListOf(path)) {
if (it) {
tryDeleteFileDirItem(fileDirItem, false, false) {
refreshViewPager()
}
} else {
toast(R.string.unknown_error_occurred)
}
}
} else {
tryDeleteFileDirItem(fileDirItem, false, true) {
refreshViewPager()
}
} }
} }
@ -884,7 +987,8 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}.execute() }.execute()
} }
private fun gotMedia(media: ArrayList<Medium>) { private fun gotMedia(thumbnailItems: ArrayList<ThumbnailItem>) {
val media = thumbnailItems.filter { it is Medium }.map { it as Medium } as ArrayList<Medium>
if (isDirEmpty(media) || media.hashCode() == mPrevHashcode) { if (isDirEmpty(media) || media.hashCode() == mPrevHashcode) {
return return
} }
@ -901,6 +1005,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
updatePagerItems(mMediaFiles.toMutableList()) updatePagerItems(mMediaFiles.toMutableList())
invalidateOptionsMenu() invalidateOptionsMenu()
checkOrientation() checkOrientation()
initBottomActions()
} }
private fun getPositionInList(items: MutableList<Medium>): Int { private fun getPositionInList(items: MutableList<Medium>): Int {
@ -916,7 +1021,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun deleteDirectoryIfEmpty() { private fun deleteDirectoryIfEmpty() {
val fileDirItem = FileDirItem(mDirectory, mDirectory.getFilenameFromPath(), getIsPathDirectory(mDirectory)) val fileDirItem = FileDirItem(mDirectory, mDirectory.getFilenameFromPath(), getIsPathDirectory(mDirectory))
if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(applicationContext, true) == 0) { if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(applicationContext, true) == 0) {
tryDeleteFileDirItem(fileDirItem, true) tryDeleteFileDirItem(fileDirItem, true, true)
} }
scanPathRecursively(mDirectory) scanPathRecursively(mDirectory)
@ -967,10 +1072,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun checkSystemUI() { private fun checkSystemUI() {
if (mIsFullScreen) { if (mIsFullScreen) {
hideSystemUI() hideSystemUI(true)
} else { } else {
stopSlideshow() stopSlideshow()
showSystemUI() showSystemUI(true)
} }
} }

View file

@ -21,6 +21,7 @@ import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
import com.simplemobiletools.gallery.dialogs.PickMediumDialog import com.simplemobiletools.gallery.dialogs.PickMediumDialog
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.interfaces.DirectoryOperationsListener
import com.simplemobiletools.gallery.models.AlbumCover import com.simplemobiletools.gallery.models.AlbumCover
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import kotlinx.android.synthetic.main.directory_item_list.view.* import kotlinx.android.synthetic.main.directory_item_list.view.*
@ -28,7 +29,7 @@ import java.io.File
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directory>, val listener: DirOperationsListener?, recyclerView: MyRecyclerView, class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directory>, val listener: DirectoryOperationsListener?, recyclerView: MyRecyclerView,
val isPickIntent: Boolean, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) : val isPickIntent: Boolean, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
@ -62,7 +63,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) { override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val dir = dirs.getOrNull(position) ?: return val dir = dirs.getOrNull(position) ?: return
val view = holder.bindView(dir, !isPickIntent) { itemView, adapterPosition -> val view = holder.bindView(dir, true, !isPickIntent) { itemView, adapterPosition ->
setupView(itemView, dir) setupView(itemView, dir)
} }
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)
@ -71,10 +72,14 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
override fun getItemCount() = dirs.size override fun getItemCount() = dirs.size
override fun prepareActionMode(menu: Menu) { override fun prepareActionMode(menu: Menu) {
val selectedPaths = getSelectedPaths()
menu.apply { menu.apply {
findItem(R.id.cab_rename).isVisible = isOneItemSelected() findItem(R.id.cab_rename).isVisible = isOneItemSelected() && !selectedPaths.contains(FAVORITES) && !selectedPaths.contains(RECYCLE_BIN)
findItem(R.id.cab_change_cover_image).isVisible = isOneItemSelected() findItem(R.id.cab_change_cover_image).isVisible = isOneItemSelected()
findItem(R.id.cab_empty_recycle_bin).isVisible = isOneItemSelected() && selectedPaths.first() == RECYCLE_BIN
findItem(R.id.cab_empty_disable_recycle_bin).isVisible = isOneItemSelected() && selectedPaths.first() == RECYCLE_BIN
checkHideBtnVisibility(this) checkHideBtnVisibility(this)
checkPinBtnVisibility(this) checkPinBtnVisibility(this)
} }
@ -91,6 +96,8 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
R.id.cab_pin -> pinFolders(true) R.id.cab_pin -> pinFolders(true)
R.id.cab_unpin -> pinFolders(false) R.id.cab_unpin -> pinFolders(false)
R.id.cab_hide -> toggleFoldersVisibility(true) R.id.cab_hide -> toggleFoldersVisibility(true)
R.id.cab_empty_recycle_bin -> emptyRecycleBin()
R.id.cab_empty_disable_recycle_bin -> emptyAndDisableRecycleBin()
R.id.cab_unhide -> toggleFoldersVisibility(false) R.id.cab_unhide -> toggleFoldersVisibility(false)
R.id.cab_exclude -> tryExcludeFolder() R.id.cab_exclude -> tryExcludeFolder()
R.id.cab_copy_to -> copyMoveTo(true) R.id.cab_copy_to -> copyMoveTo(true)
@ -104,6 +111,8 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
override fun getSelectableItemCount() = dirs.size override fun getSelectableItemCount() = dirs.size
override fun getIsItemSelectable(position: Int) = true
override fun onViewRecycled(holder: ViewHolder) { override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder) super.onViewRecycled(holder)
if (!activity.isActivityDestroyed()) { if (!activity.isActivityDestroyed()) {
@ -144,11 +153,12 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
private fun showProperties() { private fun showProperties() {
if (selectedPositions.size <= 1) { if (selectedPositions.size <= 1) {
PropertiesDialog(activity, dirs[selectedPositions.first()].path, config.shouldShowHidden) val path = dirs[selectedPositions.first()].path
if (path != FAVORITES && path != RECYCLE_BIN) {
PropertiesDialog(activity, dirs[selectedPositions.first()].path, config.shouldShowHidden)
}
} else { } else {
val paths = ArrayList<String>() PropertiesDialog(activity, getSelectedPaths().filter { it != FAVORITES && it != RECYCLE_BIN }.toMutableList(), config.shouldShowHidden)
selectedPositions.forEach { paths.add(dirs[it].path) }
PropertiesDialog(activity, paths, config.shouldShowHidden)
} }
} }
@ -178,7 +188,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
} }
private fun toggleFoldersVisibility(hide: Boolean) { private fun toggleFoldersVisibility(hide: Boolean) {
getSelectedPaths().forEach { getSelectedPaths().filter { it != FAVORITES && it != RECYCLE_BIN }.forEach {
val path = it val path = it
if (hide) { if (hide) {
if (config.wasHideFolderTooltipShown) { if (config.wasHideFolderTooltipShown) {
@ -204,13 +214,29 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
} }
} }
private fun emptyRecycleBin() {
activity.showRecycleBinEmptyingDialog {
activity.emptyTheRecycleBin {
listener?.refreshItems()
}
}
}
private fun emptyAndDisableRecycleBin() {
activity.showRecycleBinEmptyingDialog {
activity.emptyAndDisableTheRecycleBin {
listener?.refreshItems()
}
}
}
private fun updateFolderNames() { private fun updateFolderNames() {
val includedFolders = activity.config.includedFolders val includedFolders = activity.config.includedFolders
val hidden = activity.getString(R.string.hidden) val hidden = activity.getString(R.string.hidden)
dirs.forEach { dirs.forEach {
it.name = activity.checkAppendingHidden(it.path, hidden, includedFolders) it.name = activity.checkAppendingHidden(it.path, hidden, includedFolders)
} }
listener?.updateDirectories(dirs.toList() as ArrayList) listener?.updateDirectories(dirs.toMutableList() as ArrayList)
activity.runOnUiThread { activity.runOnUiThread {
updateDirs(dirs) updateDirs(dirs)
} }
@ -252,7 +278,6 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
dirs = newDirs dirs = newDirs
finishActMode() finishActMode()
fastScroller?.measureRecyclerView()
listener?.updateDirectories(newDirs) listener?.updateDirectories(newDirs)
} }
} }
@ -260,13 +285,13 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
} }
private fun tryExcludeFolder() { private fun tryExcludeFolder() {
val paths = getSelectedPaths() val paths = getSelectedPaths().filter { it != PATH }.toSet()
if (paths.size == 1) { if (paths.size == 1) {
ExcludeFolderDialog(activity, paths.toList()) { ExcludeFolderDialog(activity, paths.toMutableList()) {
listener?.refreshItems() listener?.refreshItems()
finishActMode() finishActMode()
} }
} else { } else if (paths.size > 1) {
activity.config.addExcludedFolders(paths) activity.config.addExcludedFolders(paths)
listener?.refreshItems() listener?.refreshItems()
finishActMode() finishActMode()
@ -293,7 +318,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
val path = dirs[it].path val path = dirs[it].path
if (path.startsWith(OTG_PATH)) { if (path.startsWith(OTG_PATH)) {
paths.addAll(getOTGFilePaths(path, showHidden)) paths.addAll(getOTGFilePaths(path, showHidden))
} else { } else if (path != FAVORITES) {
File(path).listFiles()?.filter { File(path).listFiles()?.filter {
!activity.getIsPathDirectory(it.absolutePath) && it.isImageVideoGif() && (showHidden || !it.name.startsWith('.')) !activity.getIsPathDirectory(it.absolutePath) && it.isImageVideoGif() && (showHidden || !it.name.startsWith('.'))
}?.mapTo(paths) { it.absolutePath } }?.mapTo(paths) { it.absolutePath }
@ -325,7 +350,8 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
} else { } else {
val itemsCnt = selectedPositions.size val itemsCnt = selectedPositions.size
val items = resources.getQuantityString(R.plurals.delete_items, itemsCnt, itemsCnt) val items = resources.getQuantityString(R.plurals.delete_items, itemsCnt, itemsCnt)
var question = String.format(resources.getString(R.string.deletion_confirmation), items) val baseString = if (config.useRecycleBin) R.string.move_to_recycle_bin_confirmation else R.string.deletion_confirmation
var question = String.format(resources.getString(baseString), items)
val warning = resources.getQuantityString(R.plurals.delete_warning, itemsCnt, itemsCnt) val warning = resources.getQuantityString(R.plurals.delete_warning, itemsCnt, itemsCnt)
question += "\n\n$warning" question += "\n\n$warning"
ConfirmationDialog(activity, question) { ConfirmationDialog(activity, question) {
@ -355,13 +381,21 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
activity.handleSAFDialog(SAFPath) { activity.handleSAFDialog(SAFPath) {
selectedPositions.sortedDescending().forEach { selectedPositions.sortedDescending().forEach {
val directory = dirs[it] val directory = dirs[it]
folders.add(File(directory.path)) if (directory.areFavorites() || directory.isRecycleBin()) {
removeFolders.add(directory) if (selectedPositions.size == 1) {
finishActMode()
} else {
selectedPositions.remove(it)
toggleItemSelection(false, it)
}
} else {
folders.add(File(directory.path))
removeFolders.add(directory)
}
} }
dirs.removeAll(removeFolders) dirs.removeAll(removeFolders)
listener?.deleteFolders(folders) listener?.deleteFolders(folders)
removeSelectedItems()
} }
} }
@ -409,10 +443,9 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
fun updateDirs(newDirs: ArrayList<Directory>) { fun updateDirs(newDirs: ArrayList<Directory>) {
if (newDirs.hashCode() != currentDirectoriesHash) { if (newDirs.hashCode() != currentDirectoriesHash) {
currentDirectoriesHash = newDirs.hashCode() currentDirectoriesHash = newDirs.hashCode()
dirs = newDirs.clone() as ArrayList<Directory> dirs = newDirs
notifyDataSetChanged() notifyDataSetChanged()
finishActMode() finishActMode()
fastScroller?.measureRecyclerView()
} }
} }
@ -431,11 +464,6 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
notifyDataSetChanged() notifyDataSetChanged()
} }
fun updateScrollHorizontally(scrollHorizontally: Boolean) {
this.scrollHorizontally = scrollHorizontally
notifyDataSetChanged()
}
private fun setupView(view: View, directory: Directory) { private fun setupView(view: View, directory: Directory) {
view.apply { view.apply {
dir_name.text = directory.name dir_name.text = directory.name
@ -466,14 +494,4 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
} }
} }
} }
interface DirOperationsListener {
fun refreshItems()
fun deleteFolders(folders: ArrayList<File>)
fun recheckPinnedFolders()
fun updateDirectories(directories: ArrayList<Directory>)
}
} }

View file

@ -39,11 +39,13 @@ class ManageFoldersAdapter(activity: BaseSimpleActivity, var folders: ArrayList<
override fun getSelectableItemCount() = folders.size override fun getSelectableItemCount() = folders.size
override fun getIsItemSelectable(position: Int) = true
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_folder, parent) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_folder, parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val folder = folders[position] val folder = folders[position]
val view = holder.bindView(folder) { itemView, adapterPosition -> val view = holder.bindView(folder, true, true) { itemView, adapterPosition ->
setupView(itemView, folder) setupView(itemView, folder)
} }
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)

View file

@ -41,11 +41,13 @@ class ManageHiddenFoldersAdapter(activity: BaseSimpleActivity, var folders: Arra
override fun getSelectableItemCount() = folders.size override fun getSelectableItemCount() = folders.size
override fun getIsItemSelectable(position: Int) = true
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_folder, parent) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_folder, parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val folder = folders[position] val folder = folders[position]
val view = holder.bindView(folder) { itemView, adapterPosition -> val view = holder.bindView(folder, true, true) { itemView, adapterPosition ->
setupView(itemView, folder) setupView(itemView, folder)
} }
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)

View file

@ -19,15 +19,21 @@ import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
import com.simplemobiletools.gallery.interfaces.MediaOperationsListener
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import com.simplemobiletools.gallery.models.ThumbnailSection
import kotlinx.android.synthetic.main.photo_video_item_grid.view.* import kotlinx.android.synthetic.main.photo_video_item_grid.view.*
import kotlinx.android.synthetic.main.thumbnail_section.view.*
class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean, class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private val INSTANT_LOAD_DURATION = 2000L private val INSTANT_LOAD_DURATION = 2000L
private val IMAGE_LOAD_DELAY = 100L private val IMAGE_LOAD_DELAY = 100L
private val ITEM_SECTION = 0
private val ITEM_MEDIUM = 1
private val config = activity.config private val config = activity.config
private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST
@ -58,28 +64,55 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutType = if (isListViewType) R.layout.photo_video_item_list else R.layout.photo_video_item_grid val layoutType = if (viewType == ITEM_SECTION) {
R.layout.thumbnail_section
} else {
if (isListViewType) {
R.layout.photo_video_item_list
} else {
R.layout.photo_video_item_grid
}
}
return createViewHolder(layoutType, parent) return createViewHolder(layoutType, parent)
} }
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) { override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val medium = media.getOrNull(position) ?: return val tmbItem = media.getOrNull(position) ?: return
visibleItemPaths.add(medium.path) if (tmbItem is Medium) {
val view = holder.bindView(medium, !allowMultiplePicks) { itemView, adapterPosition -> visibleItemPaths.add(tmbItem.path)
setupView(itemView, medium) }
val allowLongPress = !allowMultiplePicks && tmbItem is Medium
val view = holder.bindView(tmbItem, tmbItem is Medium, allowLongPress) { itemView, adapterPosition ->
if (tmbItem is Medium) {
setupThumbnail(itemView, tmbItem)
} else {
setupSection(itemView, tmbItem as ThumbnailSection)
}
} }
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)
} }
override fun getItemCount() = media.size override fun getItemCount() = media.size
override fun getItemViewType(position: Int): Int {
val tmbItem = media[position]
return if (tmbItem is ThumbnailSection) {
ITEM_SECTION
} else {
ITEM_MEDIUM
}
}
override fun prepareActionMode(menu: Menu) { override fun prepareActionMode(menu: Menu) {
menu.apply { menu.apply {
findItem(R.id.cab_rename).isVisible = isOneItemSelected() findItem(R.id.cab_rename).isVisible = isOneItemSelected()
findItem(R.id.cab_open_with).isVisible = isOneItemSelected() findItem(R.id.cab_open_with).isVisible = isOneItemSelected()
findItem(R.id.cab_confirm_selection).isVisible = isAGetIntent && allowMultiplePicks && selectedPositions.size > 0 findItem(R.id.cab_confirm_selection).isVisible = isAGetIntent && allowMultiplePicks && selectedPositions.size > 0
findItem(R.id.cab_restore_recycle_bin_files).isVisible = getSelectedPaths().all { it.startsWith(activity.filesDir.toString()) }
checkHideBtnVisibility(this) checkHideBtnVisibility(this)
checkFavoriteBtnVisibility(this)
} }
} }
@ -95,6 +128,9 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
R.id.cab_edit -> editFile() R.id.cab_edit -> editFile()
R.id.cab_hide -> toggleFileVisibility(true) R.id.cab_hide -> toggleFileVisibility(true)
R.id.cab_unhide -> toggleFileVisibility(false) R.id.cab_unhide -> toggleFileVisibility(false)
R.id.cab_add_to_favorites -> toggleFavorites(true)
R.id.cab_remove_from_favorites -> toggleFavorites(false)
R.id.cab_restore_recycle_bin_files -> restoreFiles()
R.id.cab_share -> shareMedia() R.id.cab_share -> shareMedia()
R.id.cab_copy_to -> copyMoveTo(true) R.id.cab_copy_to -> copyMoveTo(true)
R.id.cab_move_to -> copyMoveTo(false) R.id.cab_move_to -> copyMoveTo(false)
@ -105,22 +141,29 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
} }
override fun getSelectableItemCount() = media.size override fun getSelectableItemCount() = media.filter { it is Medium }.size
override fun getIsItemSelectable(position: Int) = !isASectionTitle(position)
override fun onViewRecycled(holder: ViewHolder) { override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder) super.onViewRecycled(holder)
if (!activity.isActivityDestroyed()) { if (!activity.isActivityDestroyed()) {
val itemView = holder.itemView val itemView = holder.itemView
visibleItemPaths.remove(itemView?.photo_name?.tag) visibleItemPaths.remove(itemView?.photo_name?.tag)
Glide.with(activity).clear(itemView?.medium_thumbnail!!) val tmb = itemView?.medium_thumbnail
if (tmb != null) {
Glide.with(activity).clear(tmb)
}
} }
} }
fun isASectionTitle(position: Int) = media.getOrNull(position) is ThumbnailSection
private fun checkHideBtnVisibility(menu: Menu) { private fun checkHideBtnVisibility(menu: Menu) {
var hiddenCnt = 0 var hiddenCnt = 0
var unhiddenCnt = 0 var unhiddenCnt = 0
selectedPositions.mapNotNull { media.getOrNull(it) }.forEach { getSelectedMedia().forEach {
if (it.name.startsWith('.')) { if (it.isHidden()) {
hiddenCnt++ hiddenCnt++
} else { } else {
unhiddenCnt++ unhiddenCnt++
@ -131,17 +174,30 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
menu.findItem(R.id.cab_unhide).isVisible = hiddenCnt > 0 menu.findItem(R.id.cab_unhide).isVisible = hiddenCnt > 0
} }
private fun checkFavoriteBtnVisibility(menu: Menu) {
var favoriteCnt = 0
var nonFavoriteCnt = 0
getSelectedMedia().forEach {
if (it.isFavorite) {
favoriteCnt++
} else {
nonFavoriteCnt++
}
}
menu.findItem(R.id.cab_add_to_favorites).isVisible = nonFavoriteCnt > 0
menu.findItem(R.id.cab_remove_from_favorites).isVisible = favoriteCnt > 0
}
private fun confirmSelection() { private fun confirmSelection() {
val paths = getSelectedMedia().map { it.path } as ArrayList<String> listener?.selectedPaths(getSelectedPaths())
listener?.selectedPaths(paths)
} }
private fun showProperties() { private fun showProperties() {
if (selectedPositions.size <= 1) { if (selectedPositions.size <= 1) {
PropertiesDialog(activity, media[selectedPositions.first()].path, config.shouldShowHidden) PropertiesDialog(activity, (media[selectedPositions.first()] as Medium).path, config.shouldShowHidden)
} else { } else {
val paths = ArrayList<String>() val paths = getSelectedPaths()
selectedPositions.forEach { paths.add(media[it].path) }
PropertiesDialog(activity, paths, config.shouldShowHidden) PropertiesDialog(activity, paths, config.shouldShowHidden)
} }
} }
@ -177,19 +233,42 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
}.start() }.start()
} }
private fun toggleFavorites(add: Boolean) {
Thread {
val mediumDao = activity.galleryDB.MediumDao()
getSelectedMedia().forEach {
it.isFavorite = add
mediumDao.updateFavorite(it.path, add)
}
activity.runOnUiThread {
listener?.refreshItems()
finishActMode()
}
}.start()
}
private fun restoreFiles() {
activity.restoreRecycleBinPaths(getSelectedPaths()) {
listener?.refreshItems()
finishActMode()
}
}
private fun shareMedia() { private fun shareMedia() {
if (selectedPositions.size == 1 && selectedPositions.first() != -1) { if (selectedPositions.size == 1 && selectedPositions.first() != -1) {
activity.shareMedium(getSelectedMedia()[0]) activity.shareMediumPath(getSelectedMedia().first().path)
} else if (selectedPositions.size > 1) { } else if (selectedPositions.size > 1) {
activity.shareMedia(getSelectedMedia()) activity.shareMediaPaths(getSelectedPaths())
} }
} }
private fun copyMoveTo(isCopyOperation: Boolean) { private fun copyMoveTo(isCopyOperation: Boolean) {
val paths = ArrayList<String>() val paths = getSelectedPaths()
selectedPositions.forEach { paths.add(media[it].path) }
val fileDirItems = paths.map {
FileDirItem(it, it.getFilenameFromPath())
} as ArrayList
val fileDirItems = paths.map { FileDirItem(it, it.getFilenameFromPath()) } as ArrayList
activity.tryCopyMoveFilesTo(fileDirItems, isCopyOperation) { activity.tryCopyMoveFilesTo(fileDirItems, isCopyOperation) {
config.tempFolderPath = "" config.tempFolderPath = ""
activity.applicationContext.rescanFolderMedia(it) activity.applicationContext.rescanFolderMedia(it)
@ -210,14 +289,16 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
private fun askConfirmDelete() { private fun askConfirmDelete() {
val items = resources.getQuantityString(R.plurals.delete_items, selectedPositions.size, selectedPositions.size) val items = resources.getQuantityString(R.plurals.delete_items, selectedPositions.size, selectedPositions.size)
val question = String.format(resources.getString(R.string.deletion_confirmation), items) val isRecycleBin = getSelectedPaths().first().startsWith(activity.filesDir.toString())
val baseString = if (config.useRecycleBin && !isRecycleBin) R.string.move_to_recycle_bin_confirmation else R.string.deletion_confirmation
val question = String.format(resources.getString(baseString), items)
DeleteWithRememberDialog(activity, question) { DeleteWithRememberDialog(activity, question) {
config.tempSkipDeleteConfirmation = it config.tempSkipDeleteConfirmation = it
deleteFiles() deleteFiles()
} }
} }
private fun getCurrentPath() = media[selectedPositions.first()].path private fun getCurrentPath() = (media[selectedPositions.first()] as Medium).path
private fun deleteFiles() { private fun deleteFiles() {
if (selectedPositions.isEmpty()) { if (selectedPositions.isEmpty()) {
@ -232,12 +313,14 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
return return
} }
val SAFPath = media[selectedPositions.first()].path val SAFPath = (media[selectedPositions.first()] as Medium).path
activity.handleSAFDialog(SAFPath) { activity.handleSAFDialog(SAFPath) {
selectedPositions.sortedDescending().forEach { selectedPositions.sortedDescending().forEach {
val medium = media[it] val thumbnailItem = media[it]
fileDirItems.add(FileDirItem(medium.path, medium.name)) if (thumbnailItem is Medium) {
removeMedia.add(medium) fileDirItems.add(FileDirItem(thumbnailItem.path, thumbnailItem.name))
removeMedia.add(thumbnailItem)
}
} }
media.removeAll(removeMedia) media.removeAll(removeMedia)
@ -248,19 +331,24 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
private fun getSelectedMedia(): List<Medium> { private fun getSelectedMedia(): List<Medium> {
val selectedMedia = ArrayList<Medium>(selectedPositions.size) val selectedMedia = ArrayList<Medium>(selectedPositions.size)
selectedPositions.forEach { selectedMedia.add(media[it]) } selectedPositions.forEach {
(media.getOrNull(it) as? Medium)?.apply {
selectedMedia.add(this)
}
}
return selectedMedia return selectedMedia
} }
fun updateMedia(newMedia: ArrayList<Medium>) { private fun getSelectedPaths() = getSelectedMedia().map { it.path } as ArrayList<String>
fun updateMedia(newMedia: ArrayList<ThumbnailItem>) {
if (newMedia.hashCode() != currentMediaHash) { if (newMedia.hashCode() != currentMediaHash) {
currentMediaHash = newMedia.hashCode() currentMediaHash = newMedia.hashCode()
Handler().postDelayed({ Handler().postDelayed({
media = newMedia.clone() as ArrayList<Medium> media = newMedia
enableInstantLoad() enableInstantLoad()
notifyDataSetChanged() notifyDataSetChanged()
finishActMode() finishActMode()
fastScroller?.measureRecyclerView()
}, 100L) }, 100L)
} }
} }
@ -281,11 +369,6 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
notifyDataSetChanged() notifyDataSetChanged()
} }
fun updateScrollHorizontally(scrollHorizontally: Boolean) {
this.scrollHorizontally = scrollHorizontally
notifyDataSetChanged()
}
private fun enableInstantLoad() { private fun enableInstantLoad() {
loadImageInstantly = true loadImageInstantly = true
delayHandler.postDelayed({ delayHandler.postDelayed({
@ -293,27 +376,29 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
}, INSTANT_LOAD_DURATION) }, INSTANT_LOAD_DURATION)
} }
private fun setupView(view: View, medium: Medium) { fun getItemBubbleText(position: Int, sorting: Int) = (media[position] as? Medium)?.getBubbleText(sorting)
private fun setupThumbnail(view: View, medium: Medium) {
view.apply { view.apply {
play_outline.beVisibleIf(medium.isVideo()) play_outline.beVisibleIf(medium.isVideo())
photo_name.beVisibleIf(displayFilenames || isListViewType) photo_name.beVisibleIf(displayFilenames || isListViewType)
photo_name.text = medium.name photo_name.text = medium.name
photo_name.tag = medium.path photo_name.tag = medium.path
var thumbnailPath = medium.path var path = medium.path
if (hasOTGConnected && thumbnailPath.startsWith(OTG_PATH)) { if (hasOTGConnected && path.startsWith(OTG_PATH)) {
thumbnailPath = thumbnailPath.getOTGPublicPath(context) path = path.getOTGPublicPath(context)
} }
if (loadImageInstantly) { if (loadImageInstantly) {
activity.loadImage(medium.type, thumbnailPath, medium_thumbnail, scrollHorizontally, animateGifs, cropThumbnails) activity.loadImage(medium.type, path, medium_thumbnail, scrollHorizontally, animateGifs, cropThumbnails)
} else { } else {
medium_thumbnail.setImageDrawable(null) medium_thumbnail.setImageDrawable(null)
medium_thumbnail.isHorizontalScrolling = scrollHorizontally medium_thumbnail.isHorizontalScrolling = scrollHorizontally
delayHandler.postDelayed({ delayHandler.postDelayed({
val isVisible = visibleItemPaths.contains(medium.path) val isVisible = visibleItemPaths.contains(medium.path)
if (isVisible) { if (isVisible) {
activity.loadImage(medium.type, thumbnailPath, medium_thumbnail, scrollHorizontally, animateGifs, cropThumbnails) activity.loadImage(medium.type, path, medium_thumbnail, scrollHorizontally, animateGifs, cropThumbnails)
} }
}, IMAGE_LOAD_DELAY) }, IMAGE_LOAD_DELAY)
} }
@ -325,11 +410,10 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
} }
} }
interface MediaOperationsListener { private fun setupSection(view: View, section: ThumbnailSection) {
fun refreshItems() view.apply {
thumbnail_section.text = section.title
fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>) thumbnail_section.setTextColor(textColor)
}
fun selectedPaths(paths: ArrayList<String>)
} }
} }

View file

@ -6,18 +6,21 @@ import com.simplemobiletools.commons.helpers.SORT_BY_DATE_TAKEN
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getFavoritePaths import com.simplemobiletools.gallery.extensions.getFavoritePaths
import com.simplemobiletools.gallery.helpers.MediaFetcher import com.simplemobiletools.gallery.helpers.MediaFetcher
import com.simplemobiletools.gallery.helpers.SHOW_ALL
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import java.util.* import java.util.*
class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage: Boolean = false, val isPickVideo: Boolean = false, class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage: Boolean = false, val isPickVideo: Boolean = false,
val showAll: Boolean, val callback: (media: ArrayList<Medium>) -> Unit) : val showAll: Boolean, val callback: (media: ArrayList<ThumbnailItem>) -> Unit) :
AsyncTask<Void, Void, ArrayList<Medium>>() { AsyncTask<Void, Void, ArrayList<ThumbnailItem>>() {
private val mediaFetcher = MediaFetcher(context) private val mediaFetcher = MediaFetcher(context)
override fun doInBackground(vararg params: Void): ArrayList<Medium> { override fun doInBackground(vararg params: Void): ArrayList<ThumbnailItem> {
val getProperDateTaken = context.config.getFileSorting(mPath) and SORT_BY_DATE_TAKEN != 0 val pathToUse = if (showAll) SHOW_ALL else mPath
val getProperDateTaken = context.config.getFileSorting(pathToUse) and SORT_BY_DATE_TAKEN != 0
val favoritePaths = context.getFavoritePaths() val favoritePaths = context.getFavoritePaths()
return if (showAll) { val media = if (showAll) {
val foldersToScan = mediaFetcher.getFoldersToScan() val foldersToScan = mediaFetcher.getFoldersToScan()
val media = ArrayList<Medium>() val media = ArrayList<Medium>()
foldersToScan.forEach { foldersToScan.forEach {
@ -25,14 +28,15 @@ class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage
media.addAll(newMedia) media.addAll(newMedia)
} }
MediaFetcher(context).sortMedia(media, context.config.getFileSorting("")) mediaFetcher.sortMedia(media, context.config.getFileSorting(SHOW_ALL))
media media
} else { } else {
mediaFetcher.getFilesFrom(mPath, isPickImage, isPickVideo, getProperDateTaken, favoritePaths) mediaFetcher.getFilesFrom(mPath, isPickImage, isPickVideo, getProperDateTaken, favoritePaths)
} }
return mediaFetcher.groupMedia(media, pathToUse)
} }
override fun onPostExecute(media: ArrayList<Medium>) { override fun onPostExecute(media: ArrayList<ThumbnailItem>) {
super.onPostExecute(media) super.onPostExecute(media)
callback(media) callback(media)
} }

View file

@ -9,7 +9,7 @@ import com.simplemobiletools.gallery.interfaces.MediumDao
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
@Database(entities = [(Directory::class), (Medium::class)], version = 3) @Database(entities = [(Directory::class), (Medium::class)], version = 4)
abstract class GalleryDatabase : RoomDatabase() { abstract class GalleryDatabase : RoomDatabase() {
abstract fun DirectoryDao(): DirectoryDao abstract fun DirectoryDao(): DirectoryDao

View file

@ -0,0 +1,86 @@
package com.simplemobiletools.gallery.dialogs
import android.content.DialogInterface
import android.support.v7.app.AlertDialog
import android.view.View
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.helpers.*
import kotlinx.android.synthetic.main.dialog_change_grouping.view.*
class ChangeGroupingDialog(val activity: BaseSimpleActivity, val path: String = "", val callback: () -> Unit) :
DialogInterface.OnClickListener {
private var currGrouping = 0
private var config = activity.config
private val pathToUse = if (path.isEmpty()) SHOW_ALL else path
private var view: View
init {
view = activity.layoutInflater.inflate(R.layout.dialog_change_grouping, null).apply {
grouping_dialog_use_for_this_folder.isChecked = config.hasCustomGrouping(pathToUse)
grouping_dialog_radio_folder.beVisibleIf(path.isEmpty())
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, this)
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this, R.string.group_by)
}
currGrouping = config.getFolderGrouping(pathToUse)
setupGroupRadio()
setupOrderRadio()
}
private fun setupGroupRadio() {
val groupingRadio = view.grouping_dialog_radio_grouping
val groupBtn = when {
currGrouping and GROUP_BY_NONE != 0 -> groupingRadio.grouping_dialog_radio_none
currGrouping and GROUP_BY_LAST_MODIFIED != 0 -> groupingRadio.grouping_dialog_radio_last_modified
currGrouping and GROUP_BY_DATE_TAKEN != 0 -> groupingRadio.grouping_dialog_radio_date_taken
currGrouping and GROUP_BY_FILE_TYPE != 0 -> groupingRadio.grouping_dialog_radio_file_type
currGrouping and GROUP_BY_EXTENSION != 0 -> groupingRadio.grouping_dialog_radio_extension
else -> groupingRadio.grouping_dialog_radio_folder
}
groupBtn.isChecked = true
}
private fun setupOrderRadio() {
val orderRadio = view.grouping_dialog_radio_order
var orderBtn = orderRadio.grouping_dialog_radio_ascending
if (currGrouping and GROUP_DESCENDING != 0) {
orderBtn = orderRadio.grouping_dialog_radio_descending
}
orderBtn.isChecked = true
}
override fun onClick(dialog: DialogInterface, which: Int) {
val groupingRadio = view.grouping_dialog_radio_grouping
var grouping = when (groupingRadio.checkedRadioButtonId) {
R.id.grouping_dialog_radio_none -> GROUP_BY_NONE
R.id.grouping_dialog_radio_last_modified -> GROUP_BY_LAST_MODIFIED
R.id.grouping_dialog_radio_date_taken -> GROUP_BY_DATE_TAKEN
R.id.grouping_dialog_radio_file_type -> GROUP_BY_FILE_TYPE
R.id.grouping_dialog_radio_extension -> GROUP_BY_EXTENSION
else -> GROUP_BY_FOLDER
}
if (view.grouping_dialog_radio_order.checkedRadioButtonId == R.id.grouping_dialog_radio_descending) {
grouping = grouping or GROUP_DESCENDING
}
if (view.grouping_dialog_use_for_this_folder.isChecked) {
config.saveFolderGrouping(pathToUse, grouping)
} else {
config.removeFolderGrouping(pathToUse)
config.groupBy = grouping
}
callback()
}
}

View file

@ -9,6 +9,7 @@ import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.helpers.SHOW_ALL
import kotlinx.android.synthetic.main.dialog_change_sorting.view.* import kotlinx.android.synthetic.main.dialog_change_sorting.view.*
class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorting: Boolean, showFolderCheckbox: Boolean, class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorting: Boolean, showFolderCheckbox: Boolean,
@ -16,13 +17,14 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
DialogInterface.OnClickListener { DialogInterface.OnClickListener {
private var currSorting = 0 private var currSorting = 0
private var config = activity.config private var config = activity.config
private var pathToUse = if (!isDirectorySorting && path.isEmpty()) SHOW_ALL else path
private var view: View private var view: View
init { init {
view = activity.layoutInflater.inflate(R.layout.dialog_change_sorting, null).apply { view = activity.layoutInflater.inflate(R.layout.dialog_change_sorting, null).apply {
use_for_this_folder_divider.beVisibleIf(showFolderCheckbox) use_for_this_folder_divider.beVisibleIf(showFolderCheckbox)
sorting_dialog_use_for_this_folder.beVisibleIf(showFolderCheckbox) sorting_dialog_use_for_this_folder.beVisibleIf(showFolderCheckbox)
sorting_dialog_use_for_this_folder.isChecked = config.hasCustomSorting(path) sorting_dialog_use_for_this_folder.isChecked = config.hasCustomSorting(pathToUse)
} }
AlertDialog.Builder(activity) AlertDialog.Builder(activity)
@ -32,7 +34,7 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
activity.setupDialogStuff(view, this, R.string.sort_by) activity.setupDialogStuff(view, this, R.string.sort_by)
} }
currSorting = if (isDirectorySorting) config.directorySorting else config.getFileSorting(path) currSorting = if (isDirectorySorting) config.directorySorting else config.getFileSorting(pathToUse)
setupSortRadio() setupSortRadio()
setupOrderRadio() setupOrderRadio()
} }
@ -78,9 +80,9 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
config.directorySorting = sorting config.directorySorting = sorting
} else { } else {
if (view.sorting_dialog_use_for_this_folder.isChecked) { if (view.sorting_dialog_use_for_this_folder.isChecked) {
config.saveFileSorting(path, sorting) config.saveFileSorting(pathToUse, sorting)
} else { } else {
config.removeFileSorting(path) config.removeFileSorting(pathToUse)
config.sorting = sorting config.sorting = sorting
} }
} }

View file

@ -13,7 +13,7 @@ class DeleteWithRememberDialog(val activity: Activity, val message: String, val
init { init {
view.delete_remember_title.text = message view.delete_remember_title.text = message
val builder = AlertDialog.Builder(activity) val builder = AlertDialog.Builder(activity)
.setPositiveButton(R.string.yes, { dialog, which -> dialogConfirmed() }) .setPositiveButton(R.string.yes) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)
dialog = builder.create().apply { dialog = builder.create().apply {

View file

@ -0,0 +1,68 @@
package com.simplemobiletools.gallery.dialogs
import android.support.v7.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.helpers.*
import kotlinx.android.synthetic.main.dialog_manage_bottom_actions.view.*
class ManageBottomActionsDialog(val activity: BaseSimpleActivity, val callback: (result: Int) -> Unit) {
private var view = activity.layoutInflater.inflate(R.layout.dialog_manage_bottom_actions, null)
init {
val actions = activity.config.visibleBottomActions
view.apply {
manage_bottom_actions_toggle_favorite.isChecked = actions and BOTTOM_ACTION_TOGGLE_FAVORITE != 0
manage_bottom_actions_edit.isChecked = actions and BOTTOM_ACTION_EDIT != 0
manage_bottom_actions_share.isChecked = actions and BOTTOM_ACTION_SHARE != 0
manage_bottom_actions_delete.isChecked = actions and BOTTOM_ACTION_DELETE != 0
manage_bottom_actions_rotate.isChecked = actions and BOTTOM_ACTION_ROTATE != 0
manage_bottom_actions_properties.isChecked = actions and BOTTOM_ACTION_PROPERTIES != 0
manage_bottom_actions_change_orientation.isChecked = actions and BOTTOM_ACTION_CHANGE_ORIENTATION != 0
manage_bottom_actions_slideshow.isChecked = actions and BOTTOM_ACTION_PROPERTIES != 0
manage_bottom_actions_show_on_map.isChecked = actions and BOTTOM_ACTION_SHOW_ON_MAP != 0
manage_bottom_actions_toggle_visibility.isChecked = actions and BOTTOM_ACTION_TOGGLE_VISIBILITY != 0
manage_bottom_actions_rename.isChecked = actions and BOTTOM_ACTION_RENAME != 0
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
}
private fun dialogConfirmed() {
var result = 0
view.apply {
if (manage_bottom_actions_toggle_favorite.isChecked)
result += BOTTOM_ACTION_TOGGLE_FAVORITE
if (manage_bottom_actions_edit.isChecked)
result += BOTTOM_ACTION_EDIT
if (manage_bottom_actions_share.isChecked)
result += BOTTOM_ACTION_SHARE
if (manage_bottom_actions_delete.isChecked)
result += BOTTOM_ACTION_DELETE
if (manage_bottom_actions_rotate.isChecked)
result += BOTTOM_ACTION_ROTATE
if (manage_bottom_actions_properties.isChecked)
result += BOTTOM_ACTION_PROPERTIES
if (manage_bottom_actions_change_orientation.isChecked)
result += BOTTOM_ACTION_CHANGE_ORIENTATION
if (manage_bottom_actions_slideshow.isChecked)
result += BOTTOM_ACTION_SLIDESHOW
if (manage_bottom_actions_show_on_map.isChecked)
result += BOTTOM_ACTION_SHOW_ON_MAP
if (manage_bottom_actions_toggle_visibility.isChecked)
result += BOTTOM_ACTION_TOGGLE_VISIBILITY
if (manage_bottom_actions_rename.isChecked)
result += BOTTOM_ACTION_RENAME
}
activity.config.visibleBottomActions = result
callback(result)
}
}

View file

@ -28,7 +28,7 @@ class ManageExtendedDetailsDialog(val activity: BaseSimpleActivity, val callback
} }
AlertDialog.Builder(activity) AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() }) .setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.create().apply { .create().apply {
activity.setupDialogStuff(view, this) activity.setupDialogStuff(view, this)

View file

@ -12,13 +12,15 @@ import com.simplemobiletools.gallery.adapters.MediaAdapter
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getCachedMedia import com.simplemobiletools.gallery.extensions.getCachedMedia
import com.simplemobiletools.gallery.helpers.SHOW_ALL
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import kotlinx.android.synthetic.main.dialog_medium_picker.view.* import kotlinx.android.synthetic.main.dialog_medium_picker.view.*
class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) { class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) {
var dialog: AlertDialog var dialog: AlertDialog
var shownMedia = ArrayList<Medium>() var shownMedia = ArrayList<ThumbnailItem>()
val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null) val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null)
var isGridViewType = activity.config.viewTypeFiles == VIEW_TYPE_GRID var isGridViewType = activity.config.viewTypeFiles == VIEW_TYPE_GRID
@ -31,13 +33,13 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
dialog = AlertDialog.Builder(activity) dialog = AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.other_folder, { dialogInterface, i -> showOtherFolder() }) .setNeutralButton(R.string.other_folder) { dialogInterface, i -> showOtherFolder() }
.create().apply { .create().apply {
activity.setupDialogStuff(view, this, R.string.select_photo) activity.setupDialogStuff(view, this, R.string.select_photo)
} }
activity.getCachedMedia(path) { activity.getCachedMedia(path) {
val media = it.filter { !it.isVideo() } as ArrayList val media = it.filter { it is Medium && !it.isVideo() } as ArrayList
if (media.isNotEmpty()) { if (media.isNotEmpty()) {
activity.runOnUiThread { activity.runOnUiThread {
gotMedia(media) gotMedia(media)
@ -57,18 +59,20 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
} }
} }
private fun gotMedia(media: ArrayList<Medium>) { private fun gotMedia(media: ArrayList<ThumbnailItem>) {
if (media.hashCode() == shownMedia.hashCode()) if (media.hashCode() == shownMedia.hashCode())
return return
shownMedia = media shownMedia = media
val adapter = MediaAdapter(activity, media, null, true, false, view.media_grid) { val adapter = MediaAdapter(activity, shownMedia, null, true, false, view.media_grid, null) {
callback((it as Medium).path) if (it is Medium) {
dialog.dismiss() callback(it.path)
dialog.dismiss()
}
} }
val scrollHorizontally = activity.config.scrollHorizontally && isGridViewType val scrollHorizontally = activity.config.scrollHorizontally && isGridViewType
val sorting = activity.config.getFileSorting(path) val sorting = activity.config.getFileSorting(if (path.isEmpty()) SHOW_ALL else path)
view.apply { view.apply {
media_grid.adapter = adapter media_grid.adapter = adapter
@ -81,12 +85,12 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
if (scrollHorizontally) { if (scrollHorizontally) {
media_horizontal_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble media_horizontal_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble
media_horizontal_fastscroller.setViews(media_grid) { media_horizontal_fastscroller.setViews(media_grid) {
media_horizontal_fastscroller.updateBubbleText(media[it].getBubbleText(sorting)) media_horizontal_fastscroller.updateBubbleText((media[it] as? Medium)?.getBubbleText(sorting) ?: "")
} }
} else { } else {
media_vertical_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble media_vertical_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble
media_vertical_fastscroller.setViews(media_grid) { media_vertical_fastscroller.setViews(media_grid) {
media_vertical_fastscroller.updateBubbleText(media[it].getBubbleText(sorting)) media_vertical_fastscroller.updateBubbleText((media[it] as? Medium)?.getBubbleText(sorting) ?: "")
} }
} }
} }

View file

@ -6,6 +6,7 @@ import android.provider.MediaStore
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.view.View import android.view.View
import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FAQItem import com.simplemobiletools.commons.models.FAQItem
@ -15,8 +16,9 @@ import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.SimpleActivity import com.simplemobiletools.gallery.activities.SimpleActivity
import com.simplemobiletools.gallery.dialogs.PickDirectoryDialog import com.simplemobiletools.gallery.dialogs.PickDirectoryDialog
import com.simplemobiletools.gallery.helpers.NOMEDIA import com.simplemobiletools.gallery.helpers.NOMEDIA
import com.simplemobiletools.gallery.models.Medium
import java.io.File import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.util.* import java.util.*
fun Activity.sharePath(path: String) { fun Activity.sharePath(path: String) {
@ -27,12 +29,11 @@ fun Activity.sharePaths(paths: ArrayList<String>) {
sharePathsIntent(paths, BuildConfig.APPLICATION_ID) sharePathsIntent(paths, BuildConfig.APPLICATION_ID)
} }
fun Activity.shareMedium(medium: Medium) { fun Activity.shareMediumPath(path: String) {
sharePath(medium.path) sharePath(path)
} }
fun Activity.shareMedia(media: List<Medium>) { fun Activity.shareMediaPaths(paths: ArrayList<String>) {
val paths = media.map { it.path } as ArrayList
sharePaths(paths) sharePaths(paths)
} }
@ -59,7 +60,7 @@ fun Activity.launchCamera() {
fun SimpleActivity.launchAbout() { fun SimpleActivity.launchAbout() {
val faqItems = arrayListOf( val faqItems = arrayListOf(
FAQItem(R.string.faq_3_title_commons, R.string.faq_3_text_commons), FAQItem(R.string.faq_5_title_commons, R.string.faq_5_text_commons),
FAQItem(R.string.faq_1_title, R.string.faq_1_text), FAQItem(R.string.faq_1_title, R.string.faq_1_text),
FAQItem(R.string.faq_2_title, R.string.faq_2_text), FAQItem(R.string.faq_2_title, R.string.faq_2_text),
FAQItem(R.string.faq_3_title, R.string.faq_3_text), FAQItem(R.string.faq_3_title, R.string.faq_3_text),
@ -70,21 +71,29 @@ fun SimpleActivity.launchAbout() {
FAQItem(R.string.faq_8_title, R.string.faq_8_text), FAQItem(R.string.faq_8_title, R.string.faq_8_text),
FAQItem(R.string.faq_9_title, R.string.faq_9_text), FAQItem(R.string.faq_9_title, R.string.faq_9_text),
FAQItem(R.string.faq_10_title, R.string.faq_10_text), FAQItem(R.string.faq_10_title, R.string.faq_10_text),
FAQItem(R.string.faq_11_title, R.string.faq_11_text),
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons)) FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons))
startAboutActivity(R.string.app_name, LICENSE_GLIDE or LICENSE_CROPPER or LICENSE_MULTISELECT or LICENSE_RTL startAboutActivity(R.string.app_name, LICENSE_GLIDE or LICENSE_CROPPER or LICENSE_MULTISELECT or LICENSE_RTL
or LICENSE_SUBSAMPLING or LICENSE_PATTERN or LICENSE_REPRINT or LICENSE_GIF_DRAWABLE or LICENSE_PHOTOVIEW, BuildConfig.VERSION_NAME, faqItems) or LICENSE_SUBSAMPLING or LICENSE_PATTERN or LICENSE_REPRINT or LICENSE_GIF_DRAWABLE or LICENSE_PHOTOVIEW or LICENSE_EXOPLAYER or
LICENSE_PANORAMA_VIEW or LICENSE_SANSELAN, BuildConfig.VERSION_NAME, faqItems)
} }
fun AppCompatActivity.showSystemUI() { fun AppCompatActivity.showSystemUI(toggleActionBarVisibility: Boolean) {
supportActionBar?.show() if (toggleActionBarVisibility) {
supportActionBar?.show()
}
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
} }
fun AppCompatActivity.hideSystemUI() { fun AppCompatActivity.hideSystemUI(toggleActionBarVisibility: Boolean) {
supportActionBar?.hide() if (toggleActionBarVisibility) {
supportActionBar?.hide()
}
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
@ -134,7 +143,7 @@ fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = nul
return return
} }
tryDeleteFileDirItem(file.toFileDirItem(applicationContext)) { tryDeleteFileDirItem(file.toFileDirItem(applicationContext), false, false) {
callback?.invoke() callback?.invoke()
} }
} }
@ -169,12 +178,92 @@ fun BaseSimpleActivity.tryCopyMoveFilesTo(fileDirItems: ArrayList<FileDirItem>,
} }
} }
fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) { fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, deleteFromDatabase: Boolean,
callback: ((wasSuccess: Boolean) -> Unit)? = null) {
deleteFile(fileDirItem, allowDeleteFolder) { deleteFile(fileDirItem, allowDeleteFolder) {
callback?.invoke(it) if (deleteFromDatabase) {
Thread {
Thread { galleryDB.MediumDao().deleteMediumPath(fileDirItem.path)
galleryDB.MediumDao().deleteMediumPath(fileDirItem.path) runOnUiThread {
}.start() callback?.invoke(it)
}
}.start()
} else {
callback?.invoke(it)
}
}
}
fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback: ((wasSuccess: Boolean) -> Unit)?) {
Thread {
var pathsCnt = paths.size
paths.forEach {
val file = File(it)
val internalFile = File(filesDir, it)
try {
file.copyRecursively(internalFile, true)
galleryDB.MediumDao().updateDeleted(it, System.currentTimeMillis())
pathsCnt--
} catch (ignored: Exception) {
}
}
callback?.invoke(pathsCnt == 0)
}.start()
}
fun BaseSimpleActivity.restoreRecycleBinPath(path: String, callback: () -> Unit) {
restoreRecycleBinPaths(arrayListOf(path), callback)
}
fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList<String>, callback: () -> Unit) {
Thread {
val mediumDao = galleryDB.MediumDao()
paths.forEach {
val source = it
val destination = it.removePrefix(filesDir.toString())
var inputStream: InputStream? = null
var out: OutputStream? = null
try {
out = getFileOutputStreamSync(destination, source.getMimeType())
inputStream = getFileInputStreamSync(source)!!
inputStream.copyTo(out!!)
mediumDao.updateDeleted(destination, 0)
} catch (e: Exception) {
showErrorToast(e)
} finally {
inputStream?.close()
out?.close()
}
}
runOnUiThread {
callback()
}
}.start()
}
fun BaseSimpleActivity.emptyTheRecycleBin(callback: (() -> Unit)? = null) {
Thread {
filesDir.deleteRecursively()
galleryDB.MediumDao().clearRecycleBin()
galleryDB.DirectoryDao().deleteRecycleBin()
toast(R.string.recycle_bin_emptied)
callback?.invoke()
}.start()
}
fun BaseSimpleActivity.emptyAndDisableTheRecycleBin(callback: () -> Unit) {
Thread {
emptyTheRecycleBin {
config.useRecycleBin = false
callback()
}
}.start()
}
fun BaseSimpleActivity.showRecycleBinEmptyingDialog(callback: () -> Unit) {
ConfirmationDialog(this, "", R.string.empty_recycle_bin_confirmation, R.string.yes, R.string.no) {
callback()
} }
} }

View file

@ -24,8 +24,10 @@ import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
import com.simplemobiletools.gallery.databases.GalleryDatabase import com.simplemobiletools.gallery.databases.GalleryDatabase
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.interfaces.DirectoryDao import com.simplemobiletools.gallery.interfaces.DirectoryDao
import com.simplemobiletools.gallery.interfaces.MediumDao
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import com.simplemobiletools.gallery.views.MySquareImageView import com.simplemobiletools.gallery.views.MySquareImageView
import pl.droidsonroids.gif.GifDrawable import pl.droidsonroids.gif.GifDrawable
import java.io.File import java.io.File
@ -163,11 +165,15 @@ fun Context.rescanFolderMediaSync(path: String) {
Thread { Thread {
val newMedia = it val newMedia = it
val mediumDao = galleryDB.MediumDao() val mediumDao = galleryDB.MediumDao()
mediumDao.insertAll(newMedia) val media = newMedia.filter { it is Medium } as ArrayList<Medium>
mediumDao.insertAll(media)
cached.forEach { cached.forEach {
if (!newMedia.contains(it)) { if (!newMedia.contains(it)) {
mediumDao.deleteMediumPath(it.path) val mediumPath = (it as? Medium)?.path
if (mediumPath != null) {
mediumDao.deleteMediumPath(mediumPath)
}
} }
} }
}.start() }.start()
@ -315,11 +321,20 @@ fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly:
}.start() }.start()
} }
fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, callback: (ArrayList<Medium>) -> Unit) { fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, callback: (ArrayList<ThumbnailItem>) -> Unit) {
Thread { Thread {
val mediaFetcher = MediaFetcher(this)
val mediumDao = galleryDB.MediumDao() val mediumDao = galleryDB.MediumDao()
val foldersToScan = if (path == "/") MediaFetcher(this).getFoldersToScan() else arrayListOf(path) val foldersToScan = if (path.isEmpty()) mediaFetcher.getFoldersToScan() else arrayListOf(path)
var media = ArrayList<Medium>() var media = ArrayList<Medium>()
if (path == FAVORITES) {
media.addAll(mediumDao.getFavorites())
}
if (path == RECYCLE_BIN) {
media.addAll(getUpdatedDeletedMedia(mediumDao))
}
val shouldShowHidden = config.shouldShowHidden val shouldShowHidden = config.shouldShowHidden
foldersToScan.forEach { foldersToScan.forEach {
try { try {
@ -345,18 +360,25 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
} }
}) as ArrayList<Medium> }) as ArrayList<Medium>
MediaFetcher(this).sortMedia(media, config.getFileSorting(path)) val pathToUse = if (path.isEmpty()) SHOW_ALL else path
callback(media.clone() as ArrayList<Medium>) mediaFetcher.sortMedia(media, config.getFileSorting(pathToUse))
val grouped = mediaFetcher.groupMedia(media, pathToUse)
callback(grouped.clone() as ArrayList<ThumbnailItem>)
val recycleBinPath = filesDir.toString()
media.filter { !getDoesFilePathExist(it.path) }.forEach { media.filter { !getDoesFilePathExist(it.path) }.forEach {
mediumDao.deleteMediumPath(it.path) if (it.path.startsWith(recycleBinPath)) {
mediumDao.deleteMediumPath(it.path.removePrefix(recycleBinPath))
} else {
mediumDao.deleteMediumPath(it.path)
}
} }
}.start() }.start()
} }
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 { !getDoesFilePathExist(it.path) && it.path != config.tempFolderPath }.forEach { dirsToCheck.filter { !it.areFavorites() && !it.isRecycleBin() && !getDoesFilePathExist(it.path) && it.path != config.tempFolderPath }.forEach {
directoryDao.deleteDirPath(it.path) directoryDao.deleteDirPath(it.path)
} }
} }
@ -373,6 +395,14 @@ fun Context.updateDBDirectory(directory: Directory) {
fun Context.getOTGFolderChildren(path: String) = getDocumentFile(path)?.listFiles() fun Context.getOTGFolderChildren(path: String) = getDocumentFile(path)?.listFiles()
fun Context.getOTGFolderChildrenNames(path: String) = getOTGFolderChildren(path)?.map { it.name }?.toList() fun Context.getOTGFolderChildrenNames(path: String) = getOTGFolderChildren(path)?.map { it.name }?.toMutableList()
fun Context.getFavoritePaths() = galleryDB.MediumDao().getFavoritePaths() as ArrayList<String> fun Context.getFavoritePaths() = galleryDB.MediumDao().getFavoritePaths() as ArrayList<String>
fun Context.getUpdatedDeletedMedia(mediumDao: MediumDao): ArrayList<Medium> {
val media = mediumDao.getDeletedMedia() as ArrayList<Medium>
media.forEach {
it.path = File(filesDir, it.path).toString()
}
return media
}

View file

@ -1,5 +1,6 @@
package com.simplemobiletools.gallery.fragments package com.simplemobiletools.gallery.fragments
import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
@ -26,16 +27,21 @@ import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.OTG_PATH import com.simplemobiletools.commons.helpers.OTG_PATH
import com.simplemobiletools.commons.helpers.isLollipopPlus
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.PanoramaActivity
import com.simplemobiletools.gallery.activities.PhotoActivity import com.simplemobiletools.gallery.activities.PhotoActivity
import com.simplemobiletools.gallery.activities.ViewPagerActivity import com.simplemobiletools.gallery.activities.ViewPagerActivity
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.GlideRotateTransformation import com.simplemobiletools.gallery.helpers.GlideRotateTransformation
import com.simplemobiletools.gallery.helpers.MEDIUM import com.simplemobiletools.gallery.helpers.MEDIUM
import com.simplemobiletools.gallery.helpers.PATH
import com.simplemobiletools.gallery.helpers.ROTATE_BY_ASPECT_RATIO import com.simplemobiletools.gallery.helpers.ROTATE_BY_ASPECT_RATIO
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import it.sephiroth.android.library.exif2.ExifInterface import it.sephiroth.android.library.exif2.ExifInterface
import kotlinx.android.synthetic.main.pager_photo_item.view.* import kotlinx.android.synthetic.main.pager_photo_item.view.*
import org.apache.sanselan.common.byteSources.ByteSourceInputStream
import org.apache.sanselan.formats.jpeg.JpegImageParser
import pl.droidsonroids.gif.GifDrawable import pl.droidsonroids.gif.GifDrawable
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -46,6 +52,7 @@ class PhotoFragment : ViewPagerFragment() {
private var isFullscreen = false private var isFullscreen = false
private var wasInit = false private var wasInit = false
private var useHalfResolution = false private var useHalfResolution = false
private var isPanorama = false
private var imageOrientation = -1 private var imageOrientation = -1
private var gifDrawable: GifDrawable? = null private var gifDrawable: GifDrawable? = null
@ -62,6 +69,7 @@ class PhotoFragment : ViewPagerFragment() {
photo_view.setOnClickListener { photoClicked() } photo_view.setOnClickListener { photoClicked() }
instant_prev_item.setOnClickListener { listener?.goToPrevItem() } instant_prev_item.setOnClickListener { listener?.goToPrevItem() }
instant_next_item.setOnClickListener { listener?.goToNextItem() } instant_next_item.setOnClickListener { listener?.goToNextItem() }
panorama_outline.setOnClickListener { openPanorama() }
instant_prev_item.parentView = container instant_prev_item.parentView = container
instant_next_item.parentView = container instant_next_item.parentView = container
@ -118,6 +126,7 @@ class PhotoFragment : ViewPagerFragment() {
loadImage() loadImage()
initExtendedDetails() initExtendedDetails()
wasInit = true wasInit = true
checkIfPanorama()
return view return view
} }
@ -286,6 +295,13 @@ class PhotoFragment : ViewPagerFragment() {
} }
} }
private fun openPanorama() {
Intent(context, PanoramaActivity::class.java).apply {
putExtra(PATH, medium.path)
startActivity(this)
}
}
private fun addZoomableView() { private fun addZoomableView() {
if (!context!!.config.replaceZoomableImages && medium.isImage() && isFragmentVisible && view.subsampling_view.isGone()) { if (!context!!.config.replaceZoomableImages && medium.isImage() && isFragmentVisible && view.subsampling_view.isGone()) {
ViewPagerActivity.wasDecodedByGlide = false ViewPagerActivity.wasDecodedByGlide = false
@ -329,6 +345,18 @@ class PhotoFragment : ViewPagerFragment() {
} }
} }
private fun checkIfPanorama() {
isPanorama = try {
val inputStream = if (medium.path.startsWith("content:/")) context!!.contentResolver.openInputStream(Uri.parse(medium.path)) else File(medium.path).inputStream()
val imageParser = JpegImageParser().getXmpXml(ByteSourceInputStream(inputStream, medium.name), HashMap<String, Any>())
imageParser.contains("GPano:UsePanoramaViewer=\"True\"", true)
} catch (e: Exception) {
false
}
view.panorama_outline.beVisibleIf(isPanorama && isLollipopPlus())
}
private fun getImageOrientation(): Int { private fun getImageOrientation(): Int {
val defaultOrientation = -1 val defaultOrientation = -1
var orient = defaultOrientation var orient = defaultOrientation

View file

@ -1,21 +1,32 @@
package com.simplemobiletools.gallery.fragments package com.simplemobiletools.gallery.fragments
import android.annotation.TargetApi
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Point
import android.graphics.SurfaceTexture
import android.media.AudioManager import android.media.AudioManager
import android.media.MediaPlayer
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.annotation.RequiresApi
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.* import android.view.*
import android.view.animation.AnimationUtils import android.view.animation.AnimationUtils
import android.widget.SeekBar import android.widget.SeekBar
import android.widget.TextView import android.widget.TextView
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.TrackGroupArray
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
import com.google.android.exoplayer2.upstream.ContentDataSource
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DataSpec
import com.google.android.exoplayer2.upstream.FileDataSource
import com.google.android.exoplayer2.video.VideoListener
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isJellyBean1Plus import com.simplemobiletools.commons.helpers.isJellyBean1Plus
import com.simplemobiletools.gallery.BuildConfig
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.VideoActivity import com.simplemobiletools.gallery.activities.VideoActivity
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
@ -24,32 +35,30 @@ import com.simplemobiletools.gallery.helpers.MediaSideScroll
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import kotlinx.android.synthetic.main.pager_video_item.view.* import kotlinx.android.synthetic.main.pager_video_item.view.*
import java.io.File import java.io.File
import java.io.IOException
class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSeekBarChangeListener { class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener {
private val PROGRESS = "progress" private val PROGRESS = "progress"
private val MIN_SKIP_LENGTH = 2000 private val MIN_SKIP_LENGTH = 2000
private val HIDE_PAUSE_DELAY = 2000L
private val PLAY_PAUSE_VISIBLE_ALPHA = 0.8f
private var mMediaPlayer: MediaPlayer? = null private var mTextureView: TextureView? = null
private var mSurfaceView: SurfaceView? = null
private var mSurfaceHolder: SurfaceHolder? = null
private var mCurrTimeView: TextView? = null private var mCurrTimeView: TextView? = null
private var mTimerHandler: Handler? = null
private var mSeekBar: SeekBar? = null private var mSeekBar: SeekBar? = null
private var mTimeHolder: View? = null private var mTimeHolder: View? = null
private var mView: View? = null private var mView: View? = null
private var mExoPlayer: SimpleExoPlayer? = null
private var mVideoSize = Point(0, 0)
private var mTimerHandler = Handler()
private var mHidePauseHandler = Handler()
private var mIsPlaying = false private var mIsPlaying = false
private var mIsDragged = false private var mIsDragged = false
private var mIsFullscreen = false private var mIsFullscreen = false
private var mIsFragmentVisible = false private var mIsFragmentVisible = false
private var mPlayOnPrepare = false private var mWasInit = false
private var wasEncoded = false
private var wasInit = false
private var isPrepared = false
private var mCurrTime = 0 private var mCurrTime = 0
private var mDuration = 0 private var mDuration = 0
private var mEncodedPath = ""
private var mStoredShowExtendedDetails = false private var mStoredShowExtendedDetails = false
private var mStoredHideExtendedDetails = false private var mStoredHideExtendedDetails = false
@ -77,7 +86,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
mIsFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN mIsFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN
mView!!.video_play_outline.alpha = if (mIsFullscreen) 0f else 1f mView!!.video_play_outline.alpha = if (mIsFullscreen) 0f else PLAY_PAUSE_VISIBLE_ALPHA
setupPlayer() setupPlayer()
if (savedInstanceState != null) { if (savedInstanceState != null) {
@ -85,7 +94,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
checkFullscreen() checkFullscreen()
wasInit = true mWasInit = true
mView!!.apply { mView!!.apply {
brightnessSideScroll = video_brightness_controller brightnessSideScroll = video_brightness_controller
@ -102,6 +111,66 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
video_duration.setOnClickListener { skip(true) } video_duration.setOnClickListener { skip(true) }
} }
mExoPlayer = ExoPlayerFactory.newSimpleInstance(context, DefaultTrackSelector())
mExoPlayer!!.addListener(object : Player.EventListener {
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {}
override fun onSeekProcessed() {}
override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
override fun onPlayerError(error: ExoPlaybackException?) {
activity?.showErrorToast(error.toString())
}
override fun onLoadingChanged(isLoading: Boolean) {}
override fun onPositionDiscontinuity(reason: Int) {}
override fun onRepeatModeChanged(repeatMode: Int) {}
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
when (playbackState) {
Player.STATE_READY -> videoPrepared()
Player.STATE_ENDED -> videoCompleted()
}
}
})
mExoPlayer!!.addVideoListener(object : VideoListener {
override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
mVideoSize.x = width
mVideoSize.y = height
setVideoSize()
}
override fun onRenderedFirstFrame() {}
})
val isContentUri = medium.path.startsWith("content://")
val uri = if (isContentUri) Uri.parse(medium.path) else Uri.fromFile(File(medium.path))
val dataSpec = DataSpec(uri)
val fileDataSource = if (isContentUri) ContentDataSource(context) else FileDataSource()
try {
fileDataSource.open(dataSpec)
} catch (e: Exception) {
activity?.showErrorToast(e)
}
val factory = DataSource.Factory { fileDataSource }
val audioSource = ExtractorMediaSource(fileDataSource.uri, factory, DefaultExtractorsFactory(), null, null)
mExoPlayer!!.audioStreamType = AudioManager.STREAM_MUSIC
mExoPlayer!!.prepare(audioSource)
medium.path.getVideoResolution()?.apply {
mVideoSize.x = x
mVideoSize.y = y
setVideoSize()
}
return mView return mView
} }
@ -157,26 +226,23 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mView!!.video_play_outline.setOnClickListener { togglePlayPause() } mView!!.video_play_outline.setOnClickListener { togglePlayPause() }
mSurfaceView = mView!!.video_surface mTextureView = mView!!.video_surface
mSurfaceHolder = mSurfaceView!!.holder mTextureView!!.setOnClickListener { toggleFullscreen() }
mSurfaceHolder!!.addCallback(this) mTextureView!!.surfaceTextureListener = this
mSurfaceView!!.setOnClickListener { toggleFullscreen() }
mView!!.video_holder.setOnClickListener { toggleFullscreen() } mView!!.video_holder.setOnClickListener { toggleFullscreen() }
initTimeHolder() initTimeHolder()
checkExtendedDetails() checkExtendedDetails()
initMediaPlayer()
} }
override fun setMenuVisibility(menuVisible: Boolean) { override fun setMenuVisibility(menuVisible: Boolean) {
super.setMenuVisibility(menuVisible) super.setMenuVisibility(menuVisible)
if (mIsFragmentVisible && !menuVisible) { if (mIsFragmentVisible && !menuVisible) {
pauseVideo() pauseVideo()
releaseMediaPlayer()
} }
mIsFragmentVisible = menuVisible mIsFragmentVisible = menuVisible
if (menuVisible && wasInit) { if (menuVisible && mWasInit) {
initMediaPlayer()
if (context?.config?.autoplayVideos == true) { if (context?.config?.autoplayVideos == true) {
playVideo() playVideo()
} }
@ -252,20 +318,19 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
private fun setupTimeHolder() { private fun setupTimeHolder() {
mSeekBar!!.max = mDuration mSeekBar!!.max = mDuration
mView!!.video_duration.text = mDuration.getFormattedDuration() mView!!.video_duration.text = mDuration.getFormattedDuration()
mTimerHandler = Handler()
setupTimer() setupTimer()
} }
private fun setupTimer() { private fun setupTimer() {
activity!!.runOnUiThread(object : Runnable { activity!!.runOnUiThread(object : Runnable {
override fun run() { override fun run() {
if (mMediaPlayer != null && !mIsDragged && mIsPlaying) { if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
mCurrTime = mMediaPlayer!!.currentPosition / 1000 mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
mSeekBar!!.progress = mCurrTime mSeekBar!!.progress = mCurrTime
mCurrTimeView!!.text = mCurrTime.getFormattedDuration() mCurrTimeView!!.text = mCurrTime.getFormattedDuration()
} }
mTimerHandler!!.postDelayed(this, 1000) mTimerHandler.postDelayed(this, 1000)
} }
}) })
} }
@ -299,9 +364,8 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
if (activity == null || !isAdded) if (activity == null || !isAdded)
return return
initMediaPlayer()
mIsPlaying = !mIsPlaying mIsPlaying = !mIsPlaying
mHidePauseHandler.removeCallbacksAndMessages(null)
if (mIsPlaying) { if (mIsPlaying) {
playVideo() playVideo()
} else { } else {
@ -310,102 +374,65 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
fun playVideo() { fun playVideo() {
if (mMediaPlayer != null && isPrepared) { if (mExoPlayer == null) {
mIsPlaying = true
mMediaPlayer?.start()
} else {
mPlayOnPrepare = true
}
mView!!.video_play_outline.setImageDrawable(resources.getDrawable(R.drawable.img_pause_outline_big))
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun pauseVideo() {
mIsPlaying = false
mMediaPlayer?.pause()
mView?.video_play_outline?.setImageDrawable(resources.getDrawable(R.drawable.img_play_outline_big))
activity!!.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun initMediaPlayer() {
if (mMediaPlayer != null || !mIsFragmentVisible) {
return return
} }
val mediumPath = if (wasEncoded) mEncodedPath else getPathToLoad(medium) if (videoEnded()) {
setProgress(0)
// this workaround is needed for example if the filename contains a colon
val fileUri = if (mediumPath.startsWith("/")) context!!.getFilePublicUri(File(mediumPath), BuildConfig.APPLICATION_ID) else Uri.parse(mediumPath)
try {
mMediaPlayer = MediaPlayer().apply {
setDataSource(context, fileUri)
setDisplay(mSurfaceHolder)
setOnCompletionListener { videoCompleted() }
setOnVideoSizeChangedListener { mediaPlayer, width, height -> setVideoSize() }
setOnPreparedListener { videoPrepared(it) }
setAudioStreamType(AudioManager.STREAM_MUSIC)
prepare()
}
} catch (e: IOException) {
mEncodedPath = Uri.encode(getPathToLoad(medium))
if (wasEncoded) {
releaseMediaPlayer()
} else {
wasEncoded = true
mMediaPlayer = null
initMediaPlayer()
}
} catch (e: Exception) {
releaseMediaPlayer()
} }
mIsPlaying = true
mExoPlayer?.playWhenReady = true
mView!!.video_play_outline.setImageDrawable(resources.getDrawable(R.drawable.img_pause_outline_big))
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
mHidePauseHandler.postDelayed({
mView!!.video_play_outline.animate().alpha(0f).start()
}, HIDE_PAUSE_DELAY)
} }
private fun pauseVideo() {
if (mExoPlayer == null) {
return
}
mIsPlaying = false
if (!videoEnded()) {
mExoPlayer?.playWhenReady = false
}
mView?.video_play_outline?.setImageDrawable(resources.getDrawable(R.drawable.img_play_outline_big))
mView!!.video_play_outline.alpha = PLAY_PAUSE_VISIBLE_ALPHA
activity!!.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun videoEnded() = mExoPlayer!!.currentPosition >= mExoPlayer!!.duration
private fun setProgress(seconds: Int) { private fun setProgress(seconds: Int) {
mMediaPlayer!!.seekTo(seconds * 1000) mExoPlayer!!.seekTo(seconds * 1000L)
mSeekBar!!.progress = seconds mSeekBar!!.progress = seconds
mCurrTimeView!!.text = seconds.getFormattedDuration() mCurrTimeView!!.text = seconds.getFormattedDuration()
} }
private fun addPreviewImage() { private fun videoPrepared() {
mMediaPlayer!!.start() if (mDuration == 0) {
mMediaPlayer!!.pause() mDuration = (mExoPlayer!!.duration / 1000).toInt()
} setupTimeHolder()
setProgress(mCurrTime)
private fun cleanup() { if (mIsFragmentVisible && (context!!.config.autoplayVideos)) {
pauseVideo() playVideo()
mCurrTimeView?.text = 0.getFormattedDuration() }
releaseMediaPlayer()
mSeekBar?.progress = 0
mTimerHandler?.removeCallbacksAndMessages(null)
mSurfaceView = null
mSurfaceHolder?.removeCallback(this)
mSurfaceHolder = null
}
private fun releaseMediaPlayer() {
mMediaPlayer?.setSurface(null)
mMediaPlayer?.release()
mMediaPlayer = null
}
private fun videoPrepared(mediaPlayer: MediaPlayer) {
isPrepared = true
mDuration = mediaPlayer.duration / 1000
addPreviewImage()
setupTimeHolder()
setProgress(mCurrTime)
if (mIsFragmentVisible && (context!!.config.autoplayVideos || mPlayOnPrepare)) {
playVideo()
} }
} }
private fun videoCompleted() { private fun videoCompleted() {
if (!isAdded) { if (!isAdded || mExoPlayer == null) {
return return
} }
mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
if (listener?.videoEnded() == false && context!!.config.loopVideos) { if (listener?.videoEnded() == false && context!!.config.loopVideos) {
playVideo() playVideo()
} else { } else {
@ -415,37 +442,43 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
} }
override fun surfaceCreated(holder: SurfaceHolder) { private fun cleanup() {
mSurfaceHolder = holder pauseVideo()
if (mIsFragmentVisible) { mCurrTimeView?.text = 0.getFormattedDuration()
initMediaPlayer() releaseExoPlayer()
} mSeekBar?.progress = 0
mTimerHandler.removeCallbacksAndMessages(null)
mHidePauseHandler.removeCallbacksAndMessages(null)
mTextureView = null
} }
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { private fun releaseExoPlayer() {
if (width != 0 && height != 0 && mSurfaceView != null) { mExoPlayer?.stop()
setVideoSize() mExoPlayer?.release()
} mExoPlayer = null
} }
override fun surfaceDestroyed(holder: SurfaceHolder) { override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
releaseMediaPlayer()
} }
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
releaseExoPlayer()
return false
}
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
mExoPlayer?.setVideoSurface(Surface(mTextureView!!.surfaceTexture))
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private fun setVideoSize() { private fun setVideoSize() {
if (mSurfaceHolder == null) if (activity == null || mTextureView == null)
mSurfaceHolder = mSurfaceView!!.holder
if (activity == null || mSurfaceHolder == null || !mSurfaceHolder!!.surface.isValid)
return return
initMediaPlayer() val videoProportion = mVideoSize.x.toFloat() / mVideoSize.y.toFloat()
if (mMediaPlayer == null) {
return
}
val videoProportion = mMediaPlayer!!.videoWidth.toFloat() / mMediaPlayer!!.videoHeight.toFloat()
val display = activity!!.windowManager.defaultDisplay val display = activity!!.windowManager.defaultDisplay
val screenWidth: Int val screenWidth: Int
val screenHeight: Int val screenHeight: Int
@ -462,7 +495,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
val screenProportion = screenWidth.toFloat() / screenHeight.toFloat() val screenProportion = screenWidth.toFloat() / screenHeight.toFloat()
mSurfaceView!!.layoutParams.apply { mTextureView!!.layoutParams.apply {
if (videoProportion > screenProportion) { if (videoProportion > screenProportion) {
width = screenWidth width = screenWidth
height = (screenWidth.toFloat() / videoProportion).toInt() height = (screenWidth.toFloat() / videoProportion).toInt()
@ -470,7 +503,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
width = (videoProportion * screenHeight.toFloat()).toInt() width = (videoProportion * screenHeight.toFloat()).toInt()
height = screenHeight height = screenHeight
} }
mSurfaceView!!.layoutParams = this mTextureView!!.layoutParams = this
} }
} }
@ -496,15 +529,15 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
private fun skip(forward: Boolean) { private fun skip(forward: Boolean) {
if (mMediaPlayer == null) { if (mExoPlayer == null) {
return return
} }
val curr = mMediaPlayer!!.currentPosition val curr = mExoPlayer!!.currentPosition
val twoPercents = Math.max(mMediaPlayer!!.duration / 50, MIN_SKIP_LENGTH) val twoPercents = Math.max((mExoPlayer!!.duration / 50).toInt(), MIN_SKIP_LENGTH)
val newProgress = if (forward) curr + twoPercents else curr - twoPercents val newProgress = if (forward) curr + twoPercents else curr - twoPercents
val roundProgress = Math.round(newProgress / 1000f) val roundProgress = Math.round(newProgress / 1000f)
val limitedProgress = Math.max(Math.min(mMediaPlayer!!.duration / 1000, roundProgress), 0) val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt(), roundProgress), 0)
setProgress(limitedProgress) setProgress(limitedProgress)
if (!mIsPlaying) { if (!mIsPlaying) {
togglePlayPause() togglePlayPause()
@ -512,17 +545,16 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (mMediaPlayer != null && fromUser) { if (mExoPlayer != null && fromUser) {
setProgress(progress) setProgress(progress)
} }
} }
override fun onStartTrackingTouch(seekBar: SeekBar) { override fun onStartTrackingTouch(seekBar: SeekBar) {
initMediaPlayer() if (mExoPlayer == null)
if (mMediaPlayer == null)
return return
mMediaPlayer!!.pause() mExoPlayer!!.playWhenReady = false
mIsDragged = true mIsDragged = true
} }
@ -530,7 +562,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
if (!mIsPlaying) { if (!mIsPlaying) {
togglePlayPause() togglePlayPause()
} else { } else {
mMediaPlayer?.start() mExoPlayer!!.playWhenReady = true
} }
mIsDragged = false mIsDragged = false
@ -549,7 +581,16 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
} }
} }
mView!!.video_play_outline.animate().alpha(if (isFullscreen) 0f else 1f).start() mView!!.video_play_outline.animate().alpha(if (isFullscreen && mIsPlaying) 0f else PLAY_PAUSE_VISIBLE_ALPHA).start()
if (isFullscreen) {
mHidePauseHandler.removeCallbacksAndMessages(null)
} else {
mHidePauseHandler.postDelayed({
if (mExoPlayer?.currentPosition ?: 0 > 0) {
mView!!.video_play_outline.animate().alpha(0f).start()
}
}, HIDE_PAUSE_DELAY)
}
} }
private fun getExtendedDetailsY(height: Int): Float { private fun getExtendedDetailsY(height: Int): Float {

View file

@ -36,6 +36,28 @@ class Config(context: Context) : BaseConfig(context) {
fun hasCustomSorting(path: String) = prefs.contains(SORT_FOLDER_PREFIX + path.toLowerCase()) fun hasCustomSorting(path: String) = prefs.contains(SORT_FOLDER_PREFIX + path.toLowerCase())
fun saveFolderGrouping(path: String, value: Int) {
if (path.isEmpty()) {
groupBy = value
} else {
prefs.edit().putInt(GROUP_FOLDER_PREFIX + path.toLowerCase(), value).apply()
}
}
fun getFolderGrouping(path: String): Int {
var groupBy = prefs.getInt(GROUP_FOLDER_PREFIX + path.toLowerCase(), groupBy)
if (path != SHOW_ALL && groupBy and GROUP_BY_FOLDER != 0) {
groupBy -= GROUP_BY_FOLDER + 1
}
return groupBy
}
fun removeFolderGrouping(path: String) {
prefs.edit().remove(GROUP_FOLDER_PREFIX + path.toLowerCase()).apply()
}
fun hasCustomGrouping(path: String) = prefs.contains(GROUP_FOLDER_PREFIX + path.toLowerCase())
var wasHideFolderTooltipShown: Boolean var wasHideFolderTooltipShown: Boolean
get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false) get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false)
set(wasShown) = prefs.edit().putBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, wasShown).apply() set(wasShown) = prefs.edit().putBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, wasShown).apply()
@ -239,10 +261,6 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(ALLOW_VIDEO_GESTURES, true) get() = prefs.getBoolean(ALLOW_VIDEO_GESTURES, true)
set(allowVideoGestures) = prefs.edit().putBoolean(ALLOW_VIDEO_GESTURES, allowVideoGestures).apply() set(allowVideoGestures) = prefs.edit().putBoolean(ALLOW_VIDEO_GESTURES, allowVideoGestures).apply()
var bottomActions: Boolean
get() = prefs.getBoolean(BOTTOM_ACTIONS, true)
set(bottomActions) = prefs.edit().putBoolean(BOTTOM_ACTIONS, bottomActions).apply()
var showMediaCount: Boolean var showMediaCount: Boolean
get() = prefs.getBoolean(SHOW_MEDIA_COUNT, true) get() = prefs.getBoolean(SHOW_MEDIA_COUNT, true)
set(showMediaCount) = prefs.edit().putBoolean(SHOW_MEDIA_COUNT, showMediaCount).apply() set(showMediaCount) = prefs.edit().putBoolean(SHOW_MEDIA_COUNT, showMediaCount).apply()
@ -322,4 +340,33 @@ class Config(context: Context) : BaseConfig(context) {
var tempSkipDeleteConfirmation: Boolean var tempSkipDeleteConfirmation: Boolean
get() = prefs.getBoolean(TEMP_SKIP_DELETE_CONFIRMATION, false) get() = prefs.getBoolean(TEMP_SKIP_DELETE_CONFIRMATION, false)
set(tempSkipDeleteConfirmation) = prefs.edit().putBoolean(TEMP_SKIP_DELETE_CONFIRMATION, tempSkipDeleteConfirmation).apply() set(tempSkipDeleteConfirmation) = prefs.edit().putBoolean(TEMP_SKIP_DELETE_CONFIRMATION, tempSkipDeleteConfirmation).apply()
var wereFavoritesPinned: Boolean
get() = prefs.getBoolean(WERE_FAVORITES_PINNED, false)
set(wereFavoritesPinned) = prefs.edit().putBoolean(WERE_FAVORITES_PINNED, wereFavoritesPinned).apply()
var wasRecycleBinPinned: Boolean
get() = prefs.getBoolean(WAS_RECYCLE_BIN_PINNED, false)
set(wasRecycleBinPinned) = prefs.edit().putBoolean(WAS_RECYCLE_BIN_PINNED, wasRecycleBinPinned).apply()
var groupBy: Int
get() = prefs.getInt(GROUP_BY, GROUP_BY_NONE)
set(groupBy) = prefs.edit().putInt(GROUP_BY, groupBy).apply()
var useRecycleBin: Boolean
get() = prefs.getBoolean(USE_RECYCLE_BIN, true)
set(useRecycleBin) = prefs.edit().putBoolean(USE_RECYCLE_BIN, useRecycleBin).apply()
var bottomActions: Boolean
get() = prefs.getBoolean(BOTTOM_ACTIONS, true)
set(bottomActions) = prefs.edit().putBoolean(BOTTOM_ACTIONS, bottomActions).apply()
var visibleBottomActions: Int
get() = prefs.getInt(VISIBLE_BOTTOM_ACTIONS, DEFAULT_BOTTOM_ACTIONS)
set(visibleBottomActions) = prefs.edit().putInt(VISIBLE_BOTTOM_ACTIONS, visibleBottomActions).apply()
// if a user hides a folder, then enables temporary hidden folder displaying, make sure we show it properly
var everShownFolders: Set<String>
get() = prefs.getStringSet(EVER_SHOWN_FOLDERS, HashSet<String>())
set(everShownFolders) = prefs.edit().putStringSet(EVER_SHOWN_FOLDERS, everShownFolders).apply()
} }

View file

@ -1,8 +1,11 @@
package com.simplemobiletools.gallery.helpers package com.simplemobiletools.gallery.helpers
import com.simplemobiletools.commons.helpers.MONTH_SECONDS
// shared preferences // shared preferences
const val DIRECTORY_SORT_ORDER = "directory_sort_order" const val DIRECTORY_SORT_ORDER = "directory_sort_order"
const val SORT_FOLDER_PREFIX = "sort_folder_" const val SORT_FOLDER_PREFIX = "sort_folder_"
const val GROUP_FOLDER_PREFIX = "group_folder_"
const val SHOW_HIDDEN_MEDIA = "show_hidden_media" const val SHOW_HIDDEN_MEDIA = "show_hidden_media"
const val TEMPORARILY_SHOW_HIDDEN = "temporarily_show_hidden" const val TEMPORARILY_SHOW_HIDDEN = "temporarily_show_hidden"
const val IS_THIRD_PARTY_INTENT = "is_third_party_intent" const val IS_THIRD_PARTY_INTENT = "is_third_party_intent"
@ -49,6 +52,12 @@ const val LAST_FILEPICKER_PATH = "last_filepicker_path"
const val WAS_OTG_HANDLED = "was_otg_handled" const val WAS_OTG_HANDLED = "was_otg_handled"
const val TEMP_SKIP_DELETE_CONFIRMATION = "temp_skip_delete_confirmation" const val TEMP_SKIP_DELETE_CONFIRMATION = "temp_skip_delete_confirmation"
const val BOTTOM_ACTIONS = "bottom_actions" const val BOTTOM_ACTIONS = "bottom_actions"
const val VISIBLE_BOTTOM_ACTIONS = "visible_bottom_actions"
const val WERE_FAVORITES_PINNED = "were_favorites_pinned"
const val WAS_RECYCLE_BIN_PINNED = "was_recycle_bin_pinned"
const val USE_RECYCLE_BIN = "use_recycle_bin"
const val GROUP_BY = "group_by"
const val EVER_SHOWN_FOLDERS = "ever_shown_folders"
// slideshow // slideshow
const val SLIDESHOW_INTERVAL = "slideshow_interval" const val SLIDESHOW_INTERVAL = "slideshow_interval"
@ -64,10 +73,14 @@ const val SLIDESHOW_SCROLL_DURATION = 500L
const val NOMEDIA = ".nomedia" const val NOMEDIA = ".nomedia"
const val FAVORITES = "favorites" const val FAVORITES = "favorites"
const val RECYCLE_BIN = "recycle_bin"
const val SHOW_FAVORITES = "show_favorites"
const val SHOW_RECYCLE_BIN = "show_recycle_bin"
const val MAX_COLUMN_COUNT = 20 const val MAX_COLUMN_COUNT = 20
const val SHOW_TEMP_HIDDEN_DURATION = 300000L const val SHOW_TEMP_HIDDEN_DURATION = 300000L
const val CLICK_MAX_DURATION = 150 const val CLICK_MAX_DURATION = 150
const val DRAG_THRESHOLD = 8 const val DRAG_THRESHOLD = 8
const val MONTH_MILLISECONDS = MONTH_SECONDS * 1000L
const val DIRECTORY = "directory" const val DIRECTORY = "directory"
const val MEDIUM = "medium" const val MEDIUM = "medium"
@ -110,3 +123,26 @@ const val TYPE_RAWS = 8
const val LOCAITON_INTERNAL = 1 const val LOCAITON_INTERNAL = 1
const val LOCATION_SD = 2 const val LOCATION_SD = 2
const val LOCATION_OTG = 3 const val LOCATION_OTG = 3
const val GROUP_BY_NONE = 1
const val GROUP_BY_LAST_MODIFIED = 2
const val GROUP_BY_DATE_TAKEN = 4
const val GROUP_BY_FILE_TYPE = 8
const val GROUP_BY_EXTENSION = 16
const val GROUP_BY_FOLDER = 32
const val GROUP_DESCENDING = 1024
// bottom actions
const val BOTTOM_ACTION_TOGGLE_FAVORITE = 1
const val BOTTOM_ACTION_EDIT = 2
const val BOTTOM_ACTION_SHARE = 4
const val BOTTOM_ACTION_DELETE = 8
const val BOTTOM_ACTION_ROTATE = 16
const val BOTTOM_ACTION_PROPERTIES = 32
const val BOTTOM_ACTION_CHANGE_ORIENTATION = 64
const val BOTTOM_ACTION_SLIDESHOW = 128
const val BOTTOM_ACTION_SHOW_ON_MAP = 256
const val BOTTOM_ACTION_TOGGLE_VISIBILITY = 512
const val BOTTOM_ACTION_RENAME = 1024
const val DEFAULT_BOTTOM_ACTIONS = BOTTOM_ACTION_TOGGLE_FAVORITE or BOTTOM_ACTION_EDIT or BOTTOM_ACTION_SHARE or BOTTOM_ACTION_DELETE

View file

@ -4,14 +4,16 @@ import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.MediaStore import android.provider.MediaStore
import android.text.format.DateFormat
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.getDistinctPath import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.extensions.getOTGFolderChildren
import com.simplemobiletools.gallery.extensions.shouldFolderBeVisible
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import com.simplemobiletools.gallery.models.ThumbnailItem
import com.simplemobiletools.gallery.models.ThumbnailSection
import java.io.File import java.io.File
import java.util.*
class MediaFetcher(val context: Context) { class MediaFetcher(val context: Context) {
var shouldStop = false var shouldStop = false
@ -112,7 +114,7 @@ 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 = ArrayList<String>() var foldersToScan = config.everShownFolders.toMutableList() as ArrayList
cursor.use { cursor.use {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
@ -159,7 +161,19 @@ class MediaFetcher(val context: Context) {
private fun getMediaInFolder(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int, getProperDateTaken: Boolean, private fun getMediaInFolder(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int, getProperDateTaken: Boolean,
favoritePaths: ArrayList<String>): ArrayList<Medium> { favoritePaths: ArrayList<String>): ArrayList<Medium> {
val media = ArrayList<Medium>() val media = ArrayList<Medium>()
val files = File(folder).listFiles() ?: return media
val deletedMedia = if (folder == RECYCLE_BIN) {
context.getUpdatedDeletedMedia(context.galleryDB.MediumDao())
} else {
ArrayList()
}
val files = when (folder) {
FAVORITES -> favoritePaths.map { File(it) }.toTypedArray()
RECYCLE_BIN -> deletedMedia.map { File(it.path) }.toTypedArray()
else -> File(folder).listFiles() ?: return media
}
val doExtraCheck = context.config.doExtraCheck val doExtraCheck = context.config.doExtraCheck
val showHidden = context.config.shouldShowHidden val showHidden = context.config.shouldShowHidden
val dateTakens = if (getProperDateTaken) getFolderDateTakens(folder) else HashMap() val dateTakens = if (getProperDateTaken) getFolderDateTakens(folder) else HashMap()
@ -197,24 +211,30 @@ class MediaFetcher(val context: Context) {
if (size <= 0L || (doExtraCheck && !file.exists())) if (size <= 0L || (doExtraCheck && !file.exists()))
continue continue
val lastModified = file.lastModified()
var dateTaken = lastModified
if (getProperDateTaken) {
dateTaken = dateTakens.remove(filename) ?: lastModified
}
val type = when {
isImage -> TYPE_IMAGES
isVideo -> TYPE_VIDEOS
isGif -> TYPE_GIFS
else -> TYPE_RAWS
}
val path = file.absolutePath val path = file.absolutePath
val isFavorite = favoritePaths.contains(path) if (folder == RECYCLE_BIN) {
val medium = Medium(null, filename, path, folder, lastModified, dateTaken, size, type, isFavorite) deletedMedia.firstOrNull { it.path == path }?.apply {
media.add(medium) media.add(this)
}
} else {
val lastModified = file.lastModified()
var dateTaken = lastModified
if (getProperDateTaken) {
dateTaken = dateTakens.remove(filename) ?: lastModified
}
val type = when {
isImage -> TYPE_IMAGES
isVideo -> TYPE_VIDEOS
isGif -> TYPE_GIFS
else -> TYPE_RAWS
}
val isFavorite = favoritePaths.contains(path)
val medium = Medium(null, filename, path, file.parent, lastModified, dateTaken, size, type, isFavorite, 0L)
media.add(medium)
}
} }
return media return media
} }
@ -270,7 +290,7 @@ class MediaFetcher(val context: Context) {
val path = Uri.decode(file.uri.toString().replaceFirst("${context.config.OTGTreeUri}/document/${context.config.OTGPartition}%3A", OTG_PATH)) val path = Uri.decode(file.uri.toString().replaceFirst("${context.config.OTGTreeUri}/document/${context.config.OTGPartition}%3A", OTG_PATH))
val isFavorite = favoritePaths.contains(path) val isFavorite = favoritePaths.contains(path)
val medium = Medium(null, filename, path, folder, dateModified, dateTaken, size, type, isFavorite) val medium = Medium(null, filename, path, folder, dateModified, dateTaken, size, type, isFavorite, 0L)
media.add(medium) media.add(medium)
} }
@ -323,4 +343,82 @@ class MediaFetcher(val context: Context) {
result result
}) })
} }
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) {
return media as ArrayList<ThumbnailItem>
}
val thumbnailItems = ArrayList<ThumbnailItem>()
if (context.config.scrollHorizontally) {
media.mapTo(thumbnailItems) { it }
return thumbnailItems
}
media.forEach {
val key = it.getGroupingKey(currentGrouping)
if (!mediumGroups.containsKey(key)) {
mediumGroups[key] = ArrayList()
}
mediumGroups[key]!!.add(it)
}
val sortDescending = currentGrouping and GROUP_DESCENDING != 0
val sorted = mediumGroups.toSortedMap(if (sortDescending) compareByDescending { it } else compareBy { it })
mediumGroups.clear()
for ((key, value) in sorted) {
mediumGroups[key] = value
}
val today = formatDate(System.currentTimeMillis().toString())
val yesterday = formatDate((System.currentTimeMillis() - DAY_SECONDS * 1000).toString())
for ((key, value) in mediumGroups) {
val sectionKey = getFormattedKey(key, currentGrouping, today, yesterday)
thumbnailItems.add(ThumbnailSection(sectionKey))
thumbnailItems.addAll(value)
}
return thumbnailItems
}
private fun getFormattedKey(key: String, grouping: Int, today: String, yesterday: String): String {
return when {
grouping and GROUP_BY_LAST_MODIFIED != 0 || grouping and GROUP_BY_DATE_TAKEN != 0 -> getFinalDate(formatDate(key), today, yesterday)
grouping and GROUP_BY_FILE_TYPE != 0 -> getFileTypeString(key)
grouping and GROUP_BY_EXTENSION != 0 -> key.toUpperCase()
grouping and GROUP_BY_FOLDER != 0 -> context.humanizePath(key)
else -> key
}
}
private fun getFinalDate(date: String, today: String, yesterday: String): String {
return when (date) {
today -> context.getString(R.string.today)
yesterday -> context.getString(R.string.yesterday)
else -> date
}
}
private fun formatDate(timestamp: String): String {
return if (timestamp.areDigitsOnly()) {
val cal = Calendar.getInstance(Locale.ENGLISH)
cal.timeInMillis = timestamp.toLong()
return DateFormat.format("dd MMM yyyy", cal).toString()
} else {
""
}
}
private fun getFileTypeString(key: String): String {
val stringId = when (key.toInt()) {
TYPE_IMAGES -> R.string.images
TYPE_VIDEOS -> R.string.videos
TYPE_GIFS -> R.string.gifs
else -> R.string.raw_images
}
return context.getString(stringId)
}
} }

View file

@ -4,6 +4,7 @@ import android.arch.persistence.room.Dao
import android.arch.persistence.room.Insert import android.arch.persistence.room.Insert
import android.arch.persistence.room.OnConflictStrategy.REPLACE import android.arch.persistence.room.OnConflictStrategy.REPLACE
import android.arch.persistence.room.Query import android.arch.persistence.room.Query
import com.simplemobiletools.gallery.helpers.RECYCLE_BIN
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
@Dao @Dao
@ -17,12 +18,15 @@ interface DirectoryDao {
@Insert(onConflict = REPLACE) @Insert(onConflict = REPLACE)
fun insertAll(directories: List<Directory>) fun insertAll(directories: List<Directory>)
@Query("DELETE FROM directories WHERE path = :path") @Query("DELETE FROM directories WHERE path = :path COLLATE NOCASE")
fun deleteDirPath(path: String) fun deleteDirPath(path: String)
@Query("UPDATE OR REPLACE directories SET thumbnail = :thumbnail, media_count = :mediaCnt, last_modified = :lastModified, date_taken = :dateTaken, size = :size, media_types = :mediaTypes WHERE path = :path") @Query("UPDATE OR REPLACE directories SET thumbnail = :thumbnail, media_count = :mediaCnt, last_modified = :lastModified, date_taken = :dateTaken, size = :size, media_types = :mediaTypes WHERE path = :path COLLATE NOCASE")
fun updateDirectory(path: String, thumbnail: String, mediaCnt: Int, lastModified: Long, dateTaken: Long, size: Long, mediaTypes: Int) fun updateDirectory(path: String, thumbnail: String, mediaCnt: Int, lastModified: Long, dateTaken: Long, size: Long, mediaTypes: Int)
@Query("UPDATE directories SET thumbnail = :thumbnail, filename = :name, path = :newPath WHERE path = :oldPath") @Query("UPDATE directories SET thumbnail = :thumbnail, filename = :name, path = :newPath WHERE path = :oldPath COLLATE NOCASE")
fun updateDirectoryAfterRename(thumbnail: String, name: String, newPath: String, oldPath: String) fun updateDirectoryAfterRename(thumbnail: String, name: String, newPath: String, oldPath: String)
@Query("DELETE FROM directories WHERE path = \'$RECYCLE_BIN\' COLLATE NOCASE")
fun deleteRecycleBin()
} }

View file

@ -0,0 +1,14 @@
package com.simplemobiletools.gallery.interfaces
import com.simplemobiletools.gallery.models.Directory
import java.io.File
interface DirectoryOperationsListener {
fun refreshItems()
fun deleteFolders(folders: ArrayList<File>)
fun recheckPinnedFolders()
fun updateDirectories(directories: ArrayList<Directory>)
}

View file

@ -0,0 +1,11 @@
package com.simplemobiletools.gallery.interfaces
import com.simplemobiletools.commons.models.FileDirItem
interface MediaOperationsListener {
fun refreshItems()
fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>)
fun selectedPaths(paths: ArrayList<String>)
}

View file

@ -8,24 +8,36 @@ import com.simplemobiletools.gallery.models.Medium
@Dao @Dao
interface MediumDao { interface MediumDao {
@Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, is_favorite FROM media WHERE parent_path = :path") @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, is_favorite, deleted_ts FROM media WHERE deleted_ts = 0 AND parent_path = :path COLLATE NOCASE")
fun getMediaFromPath(path: String): List<Medium> fun getMediaFromPath(path: String): List<Medium>
@Query("SELECT full_path FROM media WHERE is_favorite = 1") @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, 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> fun getFavoritePaths(): List<String>
@Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, is_favorite, deleted_ts FROM media WHERE deleted_ts != 0")
fun getDeletedMedia(): List<Medium>
@Insert(onConflict = REPLACE) @Insert(onConflict = REPLACE)
fun insert(medium: Medium) fun insert(medium: Medium)
@Insert(onConflict = REPLACE) @Insert(onConflict = REPLACE)
fun insertAll(media: List<Medium>) fun insertAll(media: List<Medium>)
@Query("DELETE FROM media WHERE full_path = :path") @Query("DELETE FROM media WHERE full_path = :path COLLATE NOCASE")
fun deleteMediumPath(path: String) fun deleteMediumPath(path: String)
@Query("UPDATE OR REPLACE media SET filename = :newFilename, full_path = :newFullPath, parent_path = :newParentPath WHERE full_path = :oldPath") @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) fun updateMedium(oldPath: String, newParentPath: String, newFilename: String, newFullPath: String)
@Query("UPDATE media SET is_favorite = :isFavorite WHERE full_path = :path") @Query("UPDATE media SET is_favorite = :isFavorite WHERE full_path = :path COLLATE NOCASE")
fun updateFavorite(path: String, isFavorite: Boolean) fun updateFavorite(path: String, isFavorite: Boolean)
@Query("UPDATE media SET deleted_ts = :deletedTS WHERE full_path = :path COLLATE NOCASE")
fun updateDeleted(path: String, deletedTS: Long)
@Query("DELETE FROM media WHERE deleted_ts != 0")
fun clearRecycleBin()
} }

View file

@ -10,6 +10,8 @@ import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_BY_NAME import com.simplemobiletools.commons.helpers.SORT_BY_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_PATH import com.simplemobiletools.commons.helpers.SORT_BY_PATH
import com.simplemobiletools.commons.helpers.SORT_BY_SIZE import com.simplemobiletools.commons.helpers.SORT_BY_SIZE
import com.simplemobiletools.gallery.helpers.FAVORITES
import com.simplemobiletools.gallery.helpers.RECYCLE_BIN
import java.io.Serializable import java.io.Serializable
@Entity(tableName = "directories", indices = [Index(value = "path", unique = true)]) @Entity(tableName = "directories", indices = [Index(value = "path", unique = true)])
@ -36,4 +38,8 @@ data class Directory(
sorting and SORT_BY_DATE_MODIFIED != 0 -> modified.formatDate() sorting and SORT_BY_DATE_MODIFIED != 0 -> modified.formatDate()
else -> taken.formatDate() else -> taken.formatDate()
} }
fun areFavorites() = path == FAVORITES
fun isRecycleBin() = path == RECYCLE_BIN
} }

View file

@ -6,15 +6,14 @@ import android.arch.persistence.room.Index
import android.arch.persistence.room.PrimaryKey import android.arch.persistence.room.PrimaryKey
import com.simplemobiletools.commons.extensions.formatDate import com.simplemobiletools.commons.extensions.formatDate
import com.simplemobiletools.commons.extensions.formatSize import com.simplemobiletools.commons.extensions.formatSize
import com.simplemobiletools.commons.extensions.getFilenameExtension
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_BY_NAME import com.simplemobiletools.commons.helpers.SORT_BY_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_PATH import com.simplemobiletools.commons.helpers.SORT_BY_PATH
import com.simplemobiletools.commons.helpers.SORT_BY_SIZE import com.simplemobiletools.commons.helpers.SORT_BY_SIZE
import com.simplemobiletools.gallery.helpers.TYPE_GIFS import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.helpers.TYPE_IMAGES
import com.simplemobiletools.gallery.helpers.TYPE_RAWS
import com.simplemobiletools.gallery.helpers.TYPE_VIDEOS
import java.io.Serializable import java.io.Serializable
import java.util.*
@Entity(tableName = "media", indices = [(Index(value = "full_path", unique = true))]) @Entity(tableName = "media", indices = [(Index(value = "full_path", unique = true))])
data class Medium( data class Medium(
@ -26,10 +25,11 @@ data class Medium(
@ColumnInfo(name = "date_taken") var taken: Long, @ColumnInfo(name = "date_taken") var taken: Long,
@ColumnInfo(name = "size") val size: Long, @ColumnInfo(name = "size") val size: Long,
@ColumnInfo(name = "type") val type: Int, @ColumnInfo(name = "type") val type: Int,
@ColumnInfo(name = "is_favorite") var isFavorite: Boolean) : Serializable { @ColumnInfo(name = "is_favorite") var isFavorite: Boolean,
@ColumnInfo(name = "deleted_ts") var deletedTS: Long) : Serializable, ThumbnailItem() {
companion object { companion object {
private const val serialVersionUID = -6553149366975455L private const val serialVersionUID = -6553149366975655L
} }
fun isGif() = type == TYPE_GIFS fun isGif() = type == TYPE_GIFS
@ -40,6 +40,8 @@ data class Medium(
fun isRaw() = type == TYPE_RAWS fun isRaw() = type == TYPE_RAWS
fun isHidden() = name.startsWith('.')
fun getBubbleText(sorting: Int) = when { fun getBubbleText(sorting: Int) = when {
sorting and SORT_BY_NAME != 0 -> name sorting and SORT_BY_NAME != 0 -> name
sorting and SORT_BY_PATH != 0 -> path sorting and SORT_BY_PATH != 0 -> path
@ -47,4 +49,29 @@ data class Medium(
sorting and SORT_BY_DATE_MODIFIED != 0 -> modified.formatDate() sorting and SORT_BY_DATE_MODIFIED != 0 -> modified.formatDate()
else -> taken.formatDate() else -> taken.formatDate()
} }
fun getGroupingKey(groupBy: Int): String {
return when {
groupBy and GROUP_BY_LAST_MODIFIED != 0 -> getDayStartTS(modified)
groupBy and GROUP_BY_DATE_TAKEN != 0 -> getDayStartTS(taken)
groupBy and GROUP_BY_FILE_TYPE != 0 -> type.toString()
groupBy and GROUP_BY_EXTENSION != 0 -> name.getFilenameExtension().toLowerCase()
groupBy and GROUP_BY_FOLDER != 0 -> parentPath
else -> ""
}
}
fun getIsInRecycleBin() = deletedTS != 0L
private fun getDayStartTS(ts: Long): String {
val calendar = Calendar.getInstance(Locale.ENGLISH).apply {
timeInMillis = ts
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
return calendar.timeInMillis.toString()
}
} }

View file

@ -0,0 +1,3 @@
package com.simplemobiletools.gallery.models
open class ThumbnailItem

View file

@ -0,0 +1,3 @@
package com.simplemobiletools.gallery.models
data class ThumbnailSection(val title: String) : ThumbnailItem()

View file

@ -19,7 +19,7 @@ class RefreshMediaReceiver : BroadcastReceiver() {
Thread { Thread {
val medium = Medium(null, path.getFilenameFromPath(), path, path.getParentPath(), System.currentTimeMillis(), System.currentTimeMillis(), val medium = Medium(null, path.getFilenameFromPath(), path, path.getParentPath(), System.currentTimeMillis(), System.currentTimeMillis(),
File(path).length(), getFileType(path), false) File(path).length(), getFileType(path), false, 0L)
context.galleryDB.MediumDao().insert(medium) context.galleryDB.MediumDao().insert(medium)
}.start() }.start()
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

View file

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

View file

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 994 B

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/panorama_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF000000">
<com.google.vr.sdk.widgets.pano.VrPanoramaView
android:id="@+id/panorama_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
<ImageView
android:id="@+id/cardboard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_cardboard"/>
<ImageView
android:id="@+id/explore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_explore"/>
</RelativeLayout>

View file

@ -649,29 +649,6 @@
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/settings_bottom_actions_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_bottom_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/show_at_bottom"/>
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/settings_screen_rotation_holder" android:id="@+id/settings_screen_rotation_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -941,5 +918,144 @@
android:text="@string/skip_delete_confirmation"/> android:text="@string/skip_delete_confirmation"/>
</RelativeLayout> </RelativeLayout>
<View
android:id="@+id/bottom_actions_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/bottom_actions_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/bottom_actions"
android:textAllCaps="true"
android:textSize="@dimen/smaller_text_size"/>
<RelativeLayout
android:id="@+id/settings_bottom_actions_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_bottom_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/show_at_bottom"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_manage_bottom_actions_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_manage_bottom_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/manage_bottom_actions"/>
</RelativeLayout>
<View
android:id="@+id/recycle_bin_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider_grey"
android:importantForAccessibility="no"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/recycle_bin_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/recycle_bin"
android:textAllCaps="true"
android:textSize="@dimen/smaller_text_size"/>
<RelativeLayout
android:id="@+id/settings_use_recycle_bin_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_use_recycle_bin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/move_items_into_recycle_bin"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_empty_recycle_bin_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/bigger_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/bigger_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_empty_recycle_bin_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/settings_empty_recycle_bin_size"
android:layout_toStartOf="@+id/settings_empty_recycle_bin_size"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:text="@string/empty_recycle_bin"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_empty_recycle_bin_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="@dimen/small_margin"
android:layout_marginRight="@dimen/small_margin"
android:background="@null"
android:clickable="false"/>
</RelativeLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/splash_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View file

@ -49,8 +49,85 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="@dimen/medium_margin" android:padding="@dimen/medium_margin"
android:src="@drawable/ic_delete" android:src="@drawable/ic_delete"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/bottom_rotate"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_share"/> app:layout_constraintStart_toEndOf="@+id/bottom_share"/>
<ImageView
android:id="@+id/bottom_rotate"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_rotate_right"
app:layout_constraintEnd_toStartOf="@+id/bottom_properties"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_delete"/>
<ImageView
android:id="@+id/bottom_properties"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_properties"
app:layout_constraintEnd_toStartOf="@+id/bottom_change_orientation"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_rotate"/>
<ImageView
android:id="@+id/bottom_change_orientation"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_orientation_auto"
app:layout_constraintEnd_toStartOf="@+id/bottom_slideshow"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_properties"/>
<ImageView
android:id="@+id/bottom_slideshow"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_slideshow"
app:layout_constraintEnd_toStartOf="@+id/bottom_show_on_map"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_change_orientation"/>
<ImageView
android:id="@+id/bottom_show_on_map"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_place"
app:layout_constraintEnd_toStartOf="@+id/bottom_toggle_file_visibility"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_slideshow"/>
<ImageView
android:id="@+id/bottom_toggle_file_visibility"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_hide"
app:layout_constraintEnd_toStartOf="@+id/bottom_rename"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_show_on_map"/>
<ImageView
android:id="@+id/bottom_rename"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_rename_new"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/bottom_toggle_file_visibility"/>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/grouping_dialog_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/grouping_dialog_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin">
<RadioGroup
android:id="@+id/grouping_dialog_radio_grouping"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/medium_margin">
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_none"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/do_not_group_files"/>
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_last_modified"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/by_last_modified"/>
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_date_taken"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/by_date_taken"/>
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_file_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/by_file_type"/>
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_extension"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/by_extension"/>
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_folder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/by_folder"/>
</RadioGroup>
<include
layout="@layout/divider"/>
<RadioGroup
android:id="@+id/grouping_dialog_radio_order"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin">
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_ascending"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/ascending"/>
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/grouping_dialog_radio_descending"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
android:text="@string/descending"/>
</RadioGroup>
<include
android:id="@+id/use_for_this_folder_divider"
layout="@layout/divider"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/grouping_dialog_use_for_this_folder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/use_for_this_folder"/>
</LinearLayout>
</ScrollView>

View file

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/manage_bottom_actions_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/manage_bottom_actions_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_toggle_favorite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/toggle_favorite"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/edit"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_share"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/share"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/delete"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_rotate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/rotate"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_properties"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/properties"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_change_orientation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/change_orientation"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_slideshow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/slideshow"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_show_on_map"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/show_on_map"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_toggle_visibility"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/toggle_file_visibility"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_bottom_actions_rename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/rename"/>
</LinearLayout>
</ScrollView>

View file

@ -18,6 +18,16 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone"/> android:visibility="gone"/>
<ImageView
android:id="@+id/panorama_outline"
android:layout_width="@dimen/play_outline_size_big"
android:layout_height="@dimen/play_outline_size_big"
android:layout_centerInParent="true"
android:background="@android:color/transparent"
android:padding="@dimen/big_margin"
android:src="@drawable/ic_panorama"
android:visibility="gone"/>
<TextView <TextView
android:id="@+id/photo_details" android:id="@+id/photo_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -6,12 +6,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<SurfaceView <TextureView
android:id="@+id/video_surface" android:id="@+id/video_surface"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"/>
android:background="@android:color/transparent"/>
<com.simplemobiletools.gallery.helpers.MediaSideScroll <com.simplemobiletools.gallery.helpers.MediaSideScroll
android:id="@+id/video_volume_controller" android:id="@+id/video_volume_controller"

View file

@ -45,6 +45,7 @@
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/medium_thumbnail" android:layout_alignRight="@+id/medium_thumbnail"
android:background="@drawable/gradient_background" android:background="@drawable/gradient_background"
android:ellipsize="end"
android:gravity="bottom" android:gravity="bottom"
android:maxLines="3" android:maxLines="3"
android:paddingBottom="@dimen/small_margin" android:paddingBottom="@dimen/small_margin"

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.commons.views.MyTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/thumbnail_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="false"
android:padding="@dimen/activity_margin"
android:textSize="@dimen/bigger_text_size"/>

View file

@ -23,7 +23,7 @@
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/cab_rename" android:id="@+id/cab_rename"
android:icon="@drawable/ic_rename" android:icon="@drawable/ic_rename_new"
android:title="@string/rename" android:title="@string/rename"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
@ -36,6 +36,14 @@
android:icon="@drawable/ic_unhide" android:icon="@drawable/ic_unhide"
android:title="@string/unhide_folder" android:title="@string/unhide_folder"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_empty_recycle_bin"
android:title="@string/empty_recycle_bin"
app:showAsAction="never"/>
<item
android:id="@+id/cab_empty_disable_recycle_bin"
android:title="@string/empty_and_disable_recycle_bin"
app:showAsAction="never"/>
<item <item
android:id="@+id/cab_exclude" android:id="@+id/cab_exclude"
android:title="@string/exclude" android:title="@string/exclude"

View file

@ -23,7 +23,7 @@
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/cab_rename" android:id="@+id/cab_rename"
android:icon="@drawable/ic_rename" android:icon="@drawable/ic_rename_new"
android:title="@string/rename" android:title="@string/rename"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
@ -46,6 +46,20 @@
android:icon="@drawable/ic_select_all" android:icon="@drawable/ic_select_all"
android:title="@string/select_all" android:title="@string/select_all"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_add_to_favorites"
android:icon="@drawable/ic_star_off"
android:title="@string/add_to_favorites"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_remove_from_favorites"
android:icon="@drawable/ic_star_on"
android:title="@string/remove_from_favorites"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_restore_recycle_bin_files"
android:title="@string/restore_selected_files"
app:showAsAction="never"/>
<item <item
android:id="@+id/cab_open_with" android:id="@+id/cab_open_with"
android:title="@string/open_with" android:title="@string/open_with"

View file

@ -22,6 +22,18 @@
android:icon="@drawable/ic_filter" android:icon="@drawable/ic_filter"
android:title="@string/filter_media" android:title="@string/filter_media"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/empty_recycle_bin"
android:title="@string/empty_recycle_bin"
app:showAsAction="never"/>
<item
android:id="@+id/empty_disable_recycle_bin"
android:title="@string/empty_and_disable_recycle_bin"
app:showAsAction="never"/>
<item
android:id="@+id/restore_all_files"
android:title="@string/restore_all_files"
app:showAsAction="never"/>
<item <item
android:id="@+id/folder_view" android:id="@+id/folder_view"
android:title="@string/folder_view" android:title="@string/folder_view"
@ -31,6 +43,10 @@
android:icon="@drawable/ic_camera" android:icon="@drawable/ic_camera"
android:title="@string/open_camera" android:title="@string/open_camera"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/group"
android:title="@string/group_by"
app:showAsAction="never"/>
<item <item
android:id="@+id/change_view_type" android:id="@+id/change_view_type"
android:title="@string/change_view_type" android:title="@string/change_view_type"

View file

@ -45,13 +45,26 @@
android:title="@string/remove_from_favorites" android:title="@string/remove_from_favorites"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/menu_lock_orientation" android:id="@+id/menu_restore_file"
android:title="@string/lock_orientation" android:title="@string/restore_this_file"
app:showAsAction="never"/> app:showAsAction="never"/>
<item <item
android:id="@+id/menu_slideshow" android:id="@+id/menu_change_orientation"
android:title="@string/slideshow" android:icon="@drawable/ic_orientation_auto"
app:showAsAction="never"/> android:title="@string/change_orientation"
app:showAsAction="ifRoom">
<menu>
<item
android:id="@+id/menu_force_portrait"
android:title="@string/force_portrait"/>
<item
android:id="@+id/menu_force_landscape"
android:title="@string/force_landscape"/>
<item
android:id="@+id/menu_default_orientation"
android:title="@string/use_default_orientation"/>
</menu>
</item>
<item <item
android:id="@+id/menu_copy_to" android:id="@+id/menu_copy_to"
android:title="@string/copy_to" android:title="@string/copy_to"
@ -81,16 +94,21 @@
android:icon="@drawable/ic_edit" android:icon="@drawable/ic_edit"
android:title="@string/edit" android:title="@string/edit"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_rename"
android:icon="@drawable/ic_rename"
android:title="@string/rename"
app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/menu_properties" android:id="@+id/menu_properties"
android:icon="@drawable/ic_info" android:icon="@drawable/ic_info"
android:title="@string/properties" android:title="@string/properties"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_slideshow"
android:icon="@drawable/ic_slideshow"
android:title="@string/slideshow"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_rename"
android:icon="@drawable/ic_rename_new"
android:title="@string/rename"
app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/menu_show_on_map" android:id="@+id/menu_show_on_map"
android:title="@string/show_on_map" android:title="@string/show_on_map"

View file

@ -24,6 +24,10 @@
<string name="brightness">السطوع</string> <string name="brightness">السطوع</string>
<string name="lock_orientation">غلق الاتجاه</string> <string name="lock_orientation">غلق الاتجاه</string>
<string name="unlock_orientation">فتح الاتجاه</string> <string name="unlock_orientation">فتح الاتجاه</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">فلتر الميديا</string> <string name="filter_media">فلتر الميديا</string>
@ -158,6 +162,12 @@
<string name="thumbnails">المصغرات</string> <string name="thumbnails">المصغرات</string>
<string name="fullscreen_media">وسائط ملء الشاشة</string> <string name="fullscreen_media">وسائط ملء الشاشة</string>
<string name="extended_details">تفاصيل موسعة</string> <string name="extended_details">تفاصيل موسعة</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">كيف يمكنني جعل الاستوديو البسيط معرض الأجهزة الافتراضي؟</string> <string name="faq_1_title">كيف يمكنني جعل الاستوديو البسيط معرض الأجهزة الافتراضي؟</string>
@ -180,6 +190,8 @@
<string name="faq_9_text">نعم ، هناك تبديل في الإعدادات يقول \"استبدل صورًا ذات زووم عميق بجودة أفضل منها\" ، يمكنك استخدام ذلك. سيؤدي ذلك إلى تحسين جودة الصور ، ولكن ستظهر بشكل واضح عند محاولة التكبير كثيرًا.</string> <string name="faq_9_text">نعم ، هناك تبديل في الإعدادات يقول \"استبدل صورًا ذات زووم عميق بجودة أفضل منها\" ، يمكنك استخدام ذلك. سيؤدي ذلك إلى تحسين جودة الصور ، ولكن ستظهر بشكل واضح عند محاولة التكبير كثيرًا.</string>
<string name="faq_10_title">هل يمكنني قص الصور باستخدام هذا التطبيق؟</string> <string name="faq_10_title">هل يمكنني قص الصور باستخدام هذا التطبيق؟</string>
<string name="faq_10_text">نعم ، يمكنك اقتصاص الصور في المحرر ، عن طريق سحب زوايا الصورة. يمكنك الوصول إلى المحرر إما عن طريق الضغط لفترة طويلة على صورة مصغرة وتحديد تحرير ، أو تحديد تحرير من العرض بملء الشاشة.</string> <string name="faq_10_text">نعم ، يمكنك اقتصاص الصور في المحرر ، عن طريق سحب زوايا الصورة. يمكنك الوصول إلى المحرر إما عن طريق الضغط لفترة طويلة على صورة مصغرة وتحديد تحرير ، أو تحديد تحرير من العرض بملء الشاشة.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brillantor</string> <string name="brightness">Brillantor</string>
<string name="lock_orientation">Bloquejar orientació</string> <string name="lock_orientation">Bloquejar orientació</string>
<string name="unlock_orientation">Desbloquejar orientació</string> <string name="unlock_orientation">Desbloquejar orientació</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtre d\'arxius</string> <string name="filter_media">Filtre d\'arxius</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniatures</string> <string name="thumbnails">Miniatures</string>
<string name="fullscreen_media">Mitjans a pantalla completa</string> <string name="fullscreen_media">Mitjans a pantalla completa</string>
<string name="extended_details">Detalls estesos</string> <string name="extended_details">Detalls estesos</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Com puc fer que Simple Gallery sigui la galeria de dispositius predeterminada?</string> <string name="faq_1_title">Com puc fer que Simple Gallery sigui la galeria de dispositius predeterminada?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Sí, hi ha un commutador a la configuració que diu \"Substituïu imatges ampliades i de gran qualitat\", podeu fer-ho. Millora la qualitat de les imatges, però es borraran una vegada que intenteu fer zoom massa.</string> <string name="faq_9_text">Sí, hi ha un commutador a la configuració que diu \"Substituïu imatges ampliades i de gran qualitat\", podeu fer-ho. Millora la qualitat de les imatges, però es borraran una vegada que intenteu fer zoom massa.</string>
<string name="faq_10_title">Puc retallar imatges amb aquesta aplicació?</string> <string name="faq_10_title">Puc retallar imatges amb aquesta aplicació?</string>
<string name="faq_10_text">Sí, pots retallar imatges a l\'editor, arrossegant les cantonades de la imatge. Pots accedir a l\'editor prement una miniatura d\'imatge i seleccionant Edita o seleccionant Edita des de la visualització de pantalla completa.</string> <string name="faq_10_text">Sí, pots retallar imatges a l\'editor, arrossegant les cantonades de la imatge. Pots accedir a l\'editor prement una miniatura d\'imatge i seleccionant Edita o seleccionant Edita des de la visualització de pantalla completa.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brightness</string> <string name="brightness">Brightness</string>
<string name="lock_orientation">Lock orientation</string> <string name="lock_orientation">Lock orientation</string>
<string name="unlock_orientation">Unlock orientation</string> <string name="unlock_orientation">Unlock orientation</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter media</string> <string name="filter_media">Filter media</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Thumbnails</string> <string name="thumbnails">Thumbnails</string>
<string name="fullscreen_media">Fullscreen media</string> <string name="fullscreen_media">Fullscreen media</string>
<string name="extended_details">Extended details</string> <string name="extended_details">Extended details</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brightness</string> <string name="brightness">Brightness</string>
<string name="lock_orientation">Lås orientering</string> <string name="lock_orientation">Lås orientering</string>
<string name="unlock_orientation">Lås orientering op</string> <string name="unlock_orientation">Lås orientering op</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrer medier</string> <string name="filter_media">Filtrer medier</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Thumbnails</string> <string name="thumbnails">Thumbnails</string>
<string name="fullscreen_media">Fullscreen media</string> <string name="fullscreen_media">Fullscreen media</string>
<string name="extended_details">Flere oplysninger</string> <string name="extended_details">Flere oplysninger</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Helligkeit</string> <string name="brightness">Helligkeit</string>
<string name="lock_orientation">Bildschirmausrichtung sperren</string> <string name="lock_orientation">Bildschirmausrichtung sperren</string>
<string name="unlock_orientation">Bildschirmausrichtung entsperren</string> <string name="unlock_orientation">Bildschirmausrichtung entsperren</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter</string> <string name="filter_media">Filter</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Thumbnails</string> <string name="thumbnails">Thumbnails</string>
<string name="fullscreen_media">Vollbild-Medien</string> <string name="fullscreen_media">Vollbild-Medien</string>
<string name="extended_details">Erweiterte Details</string> <string name="extended_details">Erweiterte Details</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Wie kann ich Schlichte Galerie als Standardanwendung auswählen?</string> <string name="faq_1_title">Wie kann ich Schlichte Galerie als Standardanwendung auswählen?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Ja, es gibt einen Schalter in den Einstellungen gekennzeichnet mit \"Stark zoombare Bilder durch Bilder mit hoher Qualität ersetzen\". Dieser wird die Bildqualität verbessern, aber sie werden bei sehr hoher Zoomstufe unscharf.</string> <string name="faq_9_text">Ja, es gibt einen Schalter in den Einstellungen gekennzeichnet mit \"Stark zoombare Bilder durch Bilder mit hoher Qualität ersetzen\". Dieser wird die Bildqualität verbessern, aber sie werden bei sehr hoher Zoomstufe unscharf.</string>
<string name="faq_10_title">Kann ich mit dieser App Bilder zuschneiden?</string> <string name="faq_10_title">Kann ich mit dieser App Bilder zuschneiden?</string>
<string name="faq_10_text">Ja, du kannst Bilder über das Ziehen der Bildecken im Editor zuschneiden. Du gelangst zum Editor indem du lange auf ein Vorschaubild drückst und Bearbeiten auswählst oder durch Auswählen von Bearbeiten in der Vollbildansicht.</string> <string name="faq_10_text">Ja, du kannst Bilder über das Ziehen der Bildecken im Editor zuschneiden. Du gelangst zum Editor indem du lange auf ein Vorschaubild drückst und Bearbeiten auswählst oder durch Auswählen von Bearbeiten in der Vollbildansicht.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Φωτεινότητα</string> <string name="brightness">Φωτεινότητα</string>
<string name="lock_orientation">Κλείδωμα προσανατολισμού</string> <string name="lock_orientation">Κλείδωμα προσανατολισμού</string>
<string name="unlock_orientation">Ξεκλείδωμα προσανατολισμού</string> <string name="unlock_orientation">Ξεκλείδωμα προσανατολισμού</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Φιλτράρισμα πολυμέσων</string> <string name="filter_media">Φιλτράρισμα πολυμέσων</string>
@ -155,6 +159,12 @@
<string name="thumbnails">Εικονίδια</string> <string name="thumbnails">Εικονίδια</string>
<string name="fullscreen_media">Πολυμέσα πλήρους οθόνης</string> <string name="fullscreen_media">Πολυμέσα πλήρους οθόνης</string>
<string name="extended_details">Περισσότερες λεπτομέρειες</string> <string name="extended_details">Περισσότερες λεπτομέρειες</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Πώς μπορώ να κάνω το Simple Gallery προκαθορισμένη εφαρμογή συλλογής πολυμέσων;</string> <string name="faq_1_title">Πώς μπορώ να κάνω το Simple Gallery προκαθορισμένη εφαρμογή συλλογής πολυμέσων;</string>
@ -177,6 +187,8 @@
Αυτό θα κάνει ορατούς μόνο τους επιλεγμένους φακέλους, καθώς η εξαίρεση και η συμπερίληψη λειτουργούν αναδρομικά και αν ενας φάκελος ανήκει και στα δύο, θα εμφανιστεί.</string> Αυτό θα κάνει ορατούς μόνο τους επιλεγμένους φακέλους, καθώς η εξαίρεση και η συμπερίληψη λειτουργούν αναδρομικά και αν ενας φάκελος ανήκει και στα δύο, θα εμφανιστεί.</string>
<string name="faq_9_title">Οι εικόνες πλήρους οθόνης έχουν κάποια περίεργα σημάδια. Μπορώ κάπως να βελτιώσω την ποιότητα;</string> <string name="faq_9_title">Οι εικόνες πλήρους οθόνης έχουν κάποια περίεργα σημάδια. Μπορώ κάπως να βελτιώσω την ποιότητα;</string>
<string name="faq_9_text">Ναι. Υπάρχει ένας διακόπτης στις Ρυθμίσεις με το κείμενο \"Αντικατάσταση των φωτογραφιών που απαιτούν ζούμ με άλλες καλύτερης ποιότητας\". Μπορείτε να χρησιμοποιήσετε αυτό. Θα βελτιώσει την ποιότητα των φωτογραφιών, αλλά θα θολώσουν στο μεγάλο ζουμ.</string> <string name="faq_9_text">Ναι. Υπάρχει ένας διακόπτης στις Ρυθμίσεις με το κείμενο \"Αντικατάσταση των φωτογραφιών που απαιτούν ζούμ με άλλες καλύτερης ποιότητας\". Μπορείτε να χρησιμοποιήσετε αυτό. Θα βελτιώσει την ποιότητα των φωτογραφιών, αλλά θα θολώσουν στο μεγάλο ζουμ.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brillo</string> <string name="brightness">Brillo</string>
<string name="lock_orientation">Bloquear orientación</string> <string name="lock_orientation">Bloquear orientación</string>
<string name="unlock_orientation">Desbloquear orientación</string> <string name="unlock_orientation">Desbloquear orientación</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtro de medios</string> <string name="filter_media">Filtro de medios</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniaturas</string> <string name="thumbnails">Miniaturas</string>
<string name="fullscreen_media">Medios a pantalla compelta</string> <string name="fullscreen_media">Medios a pantalla compelta</string>
<string name="extended_details">Detalles ampliados</string> <string name="extended_details">Detalles ampliados</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">¿Cómo puedo hacer que Simple Gallery sea la galería de dispositivos predeterminada?</string> <string name="faq_1_title">¿Cómo puedo hacer que Simple Gallery sea la galería de dispositivos predeterminada?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Sí, hay una alternancia en Configuración que dice \"Reemplazar imágenes con zoom profundo con las de mejor calidad\", puedes usar eso. Mejorará la calidad de las imágenes, pero se volverán borrosas una vez que intente ampliar demasiado.</string> <string name="faq_9_text">Sí, hay una alternancia en Configuración que dice \"Reemplazar imágenes con zoom profundo con las de mejor calidad\", puedes usar eso. Mejorará la calidad de las imágenes, pero se volverán borrosas una vez que intente ampliar demasiado.</string>
<string name="faq_10_title">¿Puedo recortar imágenes con esta aplicación?</string> <string name="faq_10_title">¿Puedo recortar imágenes con esta aplicación?</string>
<string name="faq_10_text">Sí, puede recortar imágenes en el editor arrastrando las esquinas de la imagen. Puede acceder al editor pulsando prolongadamente una imagen en miniatura y seleccionando Editar, o seleccionando Editar en la vista de pantalla completa.</string> <string name="faq_10_text">Sí, puede recortar imágenes en el editor arrastrando las esquinas de la imagen. Puede acceder al editor pulsando prolongadamente una imagen en miniatura y seleccionando Editar, o seleccionando Editar en la vista de pantalla completa.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Kirkkaus</string> <string name="brightness">Kirkkaus</string>
<string name="lock_orientation">Lukitse näytönkierto</string> <string name="lock_orientation">Lukitse näytönkierto</string>
<string name="unlock_orientation">Vapauta näytönkierto</string> <string name="unlock_orientation">Vapauta näytönkierto</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Suodata media</string> <string name="filter_media">Suodata media</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Esikatselukuvat</string> <string name="thumbnails">Esikatselukuvat</string>
<string name="fullscreen_media">Täyden näytön media</string> <string name="fullscreen_media">Täyden näytön media</string>
<string name="extended_details">Yksityiskohtaiset tiedot</string> <string name="extended_details">Yksityiskohtaiset tiedot</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Miten voin tehdä Simple Gallerystä oletusgalleriasovelluksen?</string> <string name="faq_1_title">Miten voin tehdä Simple Gallerystä oletusgalleriasovelluksen?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Luminosité</string> <string name="brightness">Luminosité</string>
<string name="lock_orientation">Verrouiller la rotation</string> <string name="lock_orientation">Verrouiller la rotation</string>
<string name="unlock_orientation">Déverrouiller la rotation</string> <string name="unlock_orientation">Déverrouiller la rotation</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrer les médias</string> <string name="filter_media">Filtrer les médias</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Vignettes</string> <string name="thumbnails">Vignettes</string>
<string name="fullscreen_media">Média plein écran</string> <string name="fullscreen_media">Média plein écran</string>
<string name="extended_details">Détails supplémentaires</string> <string name="extended_details">Détails supplémentaires</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Comment faire de Simple Galerie ma galerie par défaut ?</string> <string name="faq_1_title">Comment faire de Simple Galerie ma galerie par défaut ?</string>
@ -177,6 +187,8 @@
<string name="faq_9_text">Oui, il existe dans « Paramètres » une option « Remplacer les images zoomables profondes par des images de meilleure qualité », mais les images seront alors floues si vous zoomez trop.</string> <string name="faq_9_text">Oui, il existe dans « Paramètres » une option « Remplacer les images zoomables profondes par des images de meilleure qualité », mais les images seront alors floues si vous zoomez trop.</string>
<string name="faq_10_title">Puis-je recadrer des images avec cette application?</string> <string name="faq_10_title">Puis-je recadrer des images avec cette application?</string>
<string name="faq_10_text">Oui, vous pouvez recadrer les images dans l\'éditeur en faisant glisser les coins de l\'image. Vous pouvez accéder à l\'éditeur en appuyant longuement sur une vignette d\'image et en sélectionnant Modifier, ou en sélectionnant Modifier en mode plein écran.</string> <string name="faq_10_text">Oui, vous pouvez recadrer les images dans l\'éditeur en faisant glisser les coins de l\'image. Vous pouvez accéder à l\'éditeur en appuyant longuement sur une vignette d\'image et en sélectionnant Modifier, ou en sélectionnant Modifier en mode plein écran.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brillo</string> <string name="brightness">Brillo</string>
<string name="lock_orientation">Fixar orientación</string> <string name="lock_orientation">Fixar orientación</string>
<string name="unlock_orientation">Desbloquear orientación</string> <string name="unlock_orientation">Desbloquear orientación</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrar medios</string> <string name="filter_media">Filtrar medios</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Iconas</string> <string name="thumbnails">Iconas</string>
<string name="fullscreen_media">Medios a pantalla completa</string> <string name="fullscreen_media">Medios a pantalla completa</string>
<string name="extended_details">Detalles ampliados</string> <string name="extended_details">Detalles ampliados</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Cómo podo facer que Simple Gallery sexa a galería por omisión no dispositivo?</string> <string name="faq_1_title">Cómo podo facer que Simple Gallery sexa a galería por omisión no dispositivo?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Si, hai unha opción en Axustes que di \"Substituír imaxes con un gran zoom con imaxes de mellor calidade\", pode usar eso. mellorará a calidade das imaxes, mais farase máis borrosaxa si intenta facer moito zoom.</string> <string name="faq_9_text">Si, hai unha opción en Axustes que di \"Substituír imaxes con un gran zoom con imaxes de mellor calidade\", pode usar eso. mellorará a calidade das imaxes, mais farase máis borrosaxa si intenta facer moito zoom.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Svjetlina</string> <string name="brightness">Svjetlina</string>
<string name="lock_orientation">Zaključaj rotaciju</string> <string name="lock_orientation">Zaključaj rotaciju</string>
<string name="unlock_orientation">Otključaj rotaciju</string> <string name="unlock_orientation">Otključaj rotaciju</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtriranje medija</string> <string name="filter_media">Filtriranje medija</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Sličice</string> <string name="thumbnails">Sličice</string>
<string name="fullscreen_media">Mediji na cijelom zaslonu</string> <string name="fullscreen_media">Mediji na cijelom zaslonu</string>
<string name="extended_details">Proširene pojedinosti</string> <string name="extended_details">Proširene pojedinosti</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Kako mogu postaviti Jednostavnu galeriju kao zadanu galeriju uređaja?</string> <string name="faq_1_title">Kako mogu postaviti Jednostavnu galeriju kao zadanu galeriju uređaja?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Da, u "Postavkama" postoji opcija "Zamjena duboko zumiranih slika s kvalitetnijim slikama", ali slike će biti zamućene ako zumirate previše.</string> <string name="faq_9_text">Da, u "Postavkama" postoji opcija "Zamjena duboko zumiranih slika s kvalitetnijim slikama", ali slike će biti zamućene ako zumirate previše.</string>
<string name="faq_10_title">Mogu li izrezati slike pomoću ove aplikacije?</string> <string name="faq_10_title">Mogu li izrezati slike pomoću ove aplikacije?</string>
<string name="faq_10_text">Da, možete obrezati slike u uređivaču povlačenjem uglova. Možete doći do uređivača dugim pritiskom na minijaturu slike i odabirom Uređivanje ili odabirom Uredi iz prikaza preko cijelog zaslona.</string> <string name="faq_10_text">Da, možete obrezati slike u uređivaču povlačenjem uglova. Možete doći do uređivača dugim pritiskom na minijaturu slike i odabirom Uređivanje ili odabirom Uredi iz prikaza preko cijelog zaslona.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brightness</string> <string name="brightness">Brightness</string>
<string name="lock_orientation">Lock orientation</string> <string name="lock_orientation">Lock orientation</string>
<string name="unlock_orientation">Unlock orientation</string> <string name="unlock_orientation">Unlock orientation</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter media</string> <string name="filter_media">Filter media</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Thumbnails</string> <string name="thumbnails">Thumbnails</string>
<string name="fullscreen_media">Fullscreen media</string> <string name="fullscreen_media">Fullscreen media</string>
<string name="extended_details">Extended details</string> <string name="extended_details">Extended details</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -10,7 +10,7 @@
<string name="unpin_folder">Sblocca cartella</string> <string name="unpin_folder">Sblocca cartella</string>
<string name="pin_to_the_top">Fissa in alto</string> <string name="pin_to_the_top">Fissa in alto</string>
<string name="show_all">Mostra tutti i contenuti</string> <string name="show_all">Mostra tutti i contenuti</string>
<string name="all_folders">Tutti i media</string> <string name="all_folders">Tutte le cartelle</string>
<string name="folder_view">Visualizza a cartelle</string> <string name="folder_view">Visualizza a cartelle</string>
<string name="other_folder">Altra cartella</string> <string name="other_folder">Altra cartella</string>
<string name="show_on_map">Mostra sulla mappa</string> <string name="show_on_map">Mostra sulla mappa</string>
@ -24,13 +24,17 @@
<string name="brightness">Luminosità</string> <string name="brightness">Luminosità</string>
<string name="lock_orientation">Blocca orientamento</string> <string name="lock_orientation">Blocca orientamento</string>
<string name="unlock_orientation">Sblocca orientamento</string> <string name="unlock_orientation">Sblocca orientamento</string>
<string name="change_orientation">Cambia orientamento</string>
<string name="force_portrait">Forza verticale</string>
<string name="force_landscape">Forza orizzontale</string>
<string name="use_default_orientation">Usa l'orientamento predefinito</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtra i media</string> <string name="filter_media">Filtra i media</string>
<string name="images">Immagini</string> <string name="images">Immagini</string>
<string name="videos">Video</string> <string name="videos">Video</string>
<string name="gifs">GIF</string> <string name="gifs">GIF</string>
<string name="raw_images">RAW images</string> <string name="raw_images">Immagini RAW</string>
<string name="no_media_with_filters">Nessun file trovato per il filtro selezionato.</string> <string name="no_media_with_filters">Nessun file trovato per il filtro selezionato.</string>
<string name="change_filters_underlined"><u>Cambia filtro</u></string> <string name="change_filters_underlined"><u>Cambia filtro</u></string>
@ -111,16 +115,16 @@
<string name="change_view_type">Cambia modalità visualizzazione</string> <string name="change_view_type">Cambia modalità visualizzazione</string>
<string name="grid">Griglia</string> <string name="grid">Griglia</string>
<string name="list">Elenco</string> <string name="list">Elenco</string>
<string name="group_direct_subfolders">Group direct subfolders</string> <string name="group_direct_subfolders">Raggruppa sottocartelle dirette</string>
<!-- Grouping at media thumbnails --> <!-- Grouping at media thumbnails -->
<string name="group_by">Group by</string> <string name="group_by">Raggruppa per</string>
<string name="do_not_group_files">Do not group files</string> <string name="do_not_group_files">Non raggruppare i file</string>
<string name="by_folder">Folder</string> <string name="by_folder">Cartella</string>
<string name="by_last_modified">Last modified</string> <string name="by_last_modified">Ultima modifica</string>
<string name="by_date_taken">Date taken</string> <string name="by_date_taken">Data creazione</string>
<string name="by_file_type">File type</string> <string name="by_file_type">Tipo di file</string>
<string name="by_extension">Extension</string> <string name="by_extension">Estensione</string>
<!-- Settings --> <!-- Settings -->
<string name="autoplay_videos">Riproduci i video automaticamente</string> <string name="autoplay_videos">Riproduci i video automaticamente</string>
@ -148,12 +152,18 @@
<string name="replace_zoomable_images">Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità</string> <string name="replace_zoomable_images">Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità</string>
<string name="hide_extended_details">Nascondi i dettagli estesi quando la barra di stato è nascosta</string> <string name="hide_extended_details">Nascondi i dettagli estesi quando la barra di stato è nascosta</string>
<string name="do_extra_check">Fai un controllo ulteriore per evitare di mostrare file non validi</string> <string name="do_extra_check">Fai un controllo ulteriore per evitare di mostrare file non validi</string>
<string name="show_at_bottom">Show some action buttons at the bottom of the screen</string> <string name="show_at_bottom">Mostra alcuni pulsanti azione in fondo allo schermo</string>
<!-- Setting sections --> <!-- Setting sections -->
<string name="thumbnails">Miniature</string> <string name="thumbnails">Miniature</string>
<string name="fullscreen_media">Media a schermo intero</string> <string name="fullscreen_media">Media a schermo intero</string>
<string name="extended_details">Dettagli estesi</string> <string name="extended_details">Dettagli estesi</string>
<string name="bottom_actions">Azioni inferiori</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Gestisci la visibilità delle azioni</string>
<string name="toggle_favorite">Attiva / disattiva preferito</string>
<string name="toggle_file_visibility">Attiva / disattiva la visibilità dei file</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Come posso impostare Simple Gallery come galleria predefinita del dispositivo?</string> <string name="faq_1_title">Come posso impostare Simple Gallery come galleria predefinita del dispositivo?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Sì, c\'è un\'opzione nelle impostazioni che dice \"Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità\", puoi usare quella. Ciò migliorerà la qualità delle immagini, ma saranno sfuocate quando proverai a ingrandirle troppo.</string> <string name="faq_9_text">Sì, c\'è un\'opzione nelle impostazioni che dice \"Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità\", puoi usare quella. Ciò migliorerà la qualità delle immagini, ma saranno sfuocate quando proverai a ingrandirle troppo.</string>
<string name="faq_10_title">Posso ritagliare le immagini con questa app?</string> <string name="faq_10_title">Posso ritagliare le immagini con questa app?</string>
<string name="faq_10_text">Sì, puoi ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Puoi usare l\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, o selezionando Modifica mentre la vedi a schermo intero.</string> <string name="faq_10_text">Sì, puoi ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Puoi usare l\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, o selezionando Modifica mentre la vedi a schermo intero.</string>
<string name="faq_11_title">Posso raggruppare in qualche modo le miniature dei file?</string>
<string name="faq_11_text">Certo, usa il menu \"Raggruppa per\" mentre visualizzi le miniature. Puoi raggruppare i file con diversi criteri, incluso la data di creazione. Se utilizzi la funzione \"Mostra tutti i contenuti\" puoi anche raggrupparli per cartelle.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">明るさ</string> <string name="brightness">明るさ</string>
<string name="lock_orientation">画面の向きを固定する</string> <string name="lock_orientation">画面の向きを固定する</string>
<string name="unlock_orientation">向きの固定を解除する</string> <string name="unlock_orientation">向きの固定を解除する</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">表示するメディアの種類</string> <string name="filter_media">表示するメディアの種類</string>
@ -154,6 +158,12 @@
<string name="thumbnails">サムネイル</string> <string name="thumbnails">サムネイル</string>
<string name="fullscreen_media">メディアのみ</string> <string name="fullscreen_media">メディアのみ</string>
<string name="extended_details">詳細も表示する</string> <string name="extended_details">詳細も表示する</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">밝기</string> <string name="brightness">밝기</string>
<string name="lock_orientation">사진회전 잠금</string> <string name="lock_orientation">사진회전 잠금</string>
<string name="unlock_orientation">사진회전 해제</string> <string name="unlock_orientation">사진회전 해제</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">미디어 필터 설정</string> <string name="filter_media">미디어 필터 설정</string>
@ -154,6 +158,12 @@
<string name="thumbnails">섬네일</string> <string name="thumbnails">섬네일</string>
<string name="fullscreen_media">미디어 전체화면</string> <string name="fullscreen_media">미디어 전체화면</string>
<string name="extended_details">확장 된 세부 정보</string> <string name="extended_details">확장 된 세부 정보</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="do_not_ask_again">Šiame seanse daugiau neklausti</string> <string name="do_not_ask_again">Šiame seanse daugiau neklausti</string>
<string name="lock_orientation">Užrakinti orientaciją</string> <string name="lock_orientation">Užrakinti orientaciją</string>
<string name="unlock_orientation">Atrakinti orientaciją</string> <string name="unlock_orientation">Atrakinti orientaciją</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtruoti mediją</string> <string name="filter_media">Filtruoti mediją</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniatiūros</string> <string name="thumbnails">Miniatiūros</string>
<string name="fullscreen_media">Viso ekrano medija</string> <string name="fullscreen_media">Viso ekrano medija</string>
<string name="extended_details">Išsami informacija</string> <string name="extended_details">Išsami informacija</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Kaip galiu padaryti paprastą galeriją kaip numatytąją įrenginio galeriją?</string> <string name="faq_1_title">Kaip galiu padaryti paprastą galeriją kaip numatytąją įrenginio galeriją?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Taip, "Nustatymuose" perjunkite \ "Pakeisti giliuosius, patobulintus vaizdus geresnės kokybės vaizdais \", galite tai naudoti. Tai pagerins vaizdų kokybę, tačiau kai jūs pabandysite per daug padidinti vaizdą, jie bus neryškūs.</string> <string name="faq_9_text">Taip, "Nustatymuose" perjunkite \ "Pakeisti giliuosius, patobulintus vaizdus geresnės kokybės vaizdais \", galite tai naudoti. Tai pagerins vaizdų kokybę, tačiau kai jūs pabandysite per daug padidinti vaizdą, jie bus neryškūs.</string>
<string name="faq_10_title">Ar galiu apkarpyti vaizdus naudojant šią programėlę?</string> <string name="faq_10_title">Ar galiu apkarpyti vaizdus naudojant šią programėlę?</string>
<string name="faq_10_text">Taip, redaguodami vaizdus, galite juos apkarpyti, vilkdami vaizdo kampus. Galite patekti į redaktorių ilgai paspaudę vaizdo miniatiūrą ir pasirinkę "Redaguoti" arba iš viso ekrano rodinio pasirinkę "Redaguoti".</string> <string name="faq_10_text">Taip, redaguodami vaizdus, galite juos apkarpyti, vilkdami vaizdo kampus. Galite patekti į redaktorių ilgai paspaudę vaizdo miniatiūrą ir pasirinkę "Redaguoti" arba iš viso ekrano rodinio pasirinkę "Redaguoti".</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Lysstyrke</string> <string name="brightness">Lysstyrke</string>
<string name="lock_orientation">Lås skjermorientering</string> <string name="lock_orientation">Lås skjermorientering</string>
<string name="unlock_orientation">Lås opp skjermorientering</string> <string name="unlock_orientation">Lås opp skjermorientering</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrer media</string> <string name="filter_media">Filtrer media</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Minibilder</string> <string name="thumbnails">Minibilder</string>
<string name="fullscreen_media">Mediavisning</string> <string name="fullscreen_media">Mediavisning</string>
<string name="extended_details">Utvidede detaljer</string> <string name="extended_details">Utvidede detaljer</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Helderheid</string> <string name="brightness">Helderheid</string>
<string name="lock_orientation">Schermoriëntatie vergrendelen</string> <string name="lock_orientation">Schermoriëntatie vergrendelen</string>
<string name="unlock_orientation">Schermoriëntatie ontgrendelen</string> <string name="unlock_orientation">Schermoriëntatie ontgrendelen</string>
<string name="change_orientation">Schermoriëntatie wijzigen</string>
<string name="force_portrait">Portretmodus forceren</string>
<string name="force_landscape">Landschapsmodus forceren</string>
<string name="use_default_orientation">Standaardmodus gebruiken</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Media filteren</string> <string name="filter_media">Media filteren</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniatuurvoorbeelden</string> <string name="thumbnails">Miniatuurvoorbeelden</string>
<string name="fullscreen_media">Volledig scherm</string> <string name="fullscreen_media">Volledig scherm</string>
<string name="extended_details">Uitgebreide informatie</string> <string name="extended_details">Uitgebreide informatie</string>
<string name="bottom_actions">Acties onderaan het scherm</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Actieknoppen onderaan beheren</string>
<string name="toggle_favorite">Als favoriet instellen</string>
<string name="toggle_file_visibility">Bestand tonen/verbergen</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -7,7 +7,7 @@
<string name="hidden">(ukryty)</string> <string name="hidden">(ukryty)</string>
<string name="excluded">(wykluczony)</string> <string name="excluded">(wykluczony)</string>
<string name="pin_folder">Przypnij folder</string> <string name="pin_folder">Przypnij folder</string>
<string name="unpin_folder">Wypakuj folder</string> <string name="unpin_folder">Odepnij folder</string>
   <string name="pin_to_the_top">Przypnij na górze</string>    <string name="pin_to_the_top">Przypnij na górze</string>
<string name="show_all">Pokaż wszystko</string> <string name="show_all">Pokaż wszystko</string>
<string name="all_folders">Wszystkie foldery</string> <string name="all_folders">Wszystkie foldery</string>
@ -24,6 +24,10 @@
<string name="brightness">Jasność</string> <string name="brightness">Jasność</string>
<string name="lock_orientation">Zablokuj orientację ekranu</string> <string name="lock_orientation">Zablokuj orientację ekranu</string>
<string name="unlock_orientation">Odblokuj orientację ekranu</string> <string name="unlock_orientation">Odblokuj orientację ekranu</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtruj multimedia</string> <string name="filter_media">Filtruj multimedia</string>
@ -154,6 +158,12 @@
   <string name="thumbnails">Miniatury</string>    <string name="thumbnails">Miniatury</string>
   <string name="fullscreen_media">Widok pełnoekranowy</string>    <string name="fullscreen_media">Widok pełnoekranowy</string>
   <string name="extended_details">Dodatkowe szczegóły</string>    <string name="extended_details">Dodatkowe szczegóły</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
   <string name="faq_1_title">Jak mogę ustawić tą aplikację jako domyślną aplikację galerii?</string>    <string name="faq_1_title">Jak mogę ustawić tą aplikację jako domyślną aplikację galerii?</string>
@ -173,9 +183,11 @@
   <string name="faq_8_title">Co jeśli chcę widzieć tylko.wybrane foldery?</string>    <string name="faq_8_title">Co jeśli chcę widzieć tylko.wybrane foldery?</string>
   <string name="faq_8_text">Przejdź do sekcji z wykluczonymi folderami w ustawieniach aplikacji, dodaj tam folder główny (\"/\"), a następnie dodaj pożądane foldery w sekcji z dołączonymi folderami.</string>    <string name="faq_8_text">Przejdź do sekcji z wykluczonymi folderami w ustawieniach aplikacji, dodaj tam folder główny (\"/\"), a następnie dodaj pożądane foldery w sekcji z dołączonymi folderami.</string>
   <string name="faq_9_title">Zdjęcia w widoku pełnoekranowym mają dziwne artefakty. Jak mogę to naprawić?</string>    <string name="faq_9_title">Zdjęcia w widoku pełnoekranowym mają dziwne artefakty. Jak mogę to naprawić?</string>
   <string name="faq_9_text">U ustawieniach aplikacji włącz opcję \'Zamieniaj powiększalne obrazy na te o lepszej jakości\'. Poprawi ona jakość zdjęć, jednak przy bardzo dużych powiększeniach mogą się stać one zbyt rozmazane.</string> <string name="faq_9_text">W ustawieniach aplikacji włącz opcję \'Zamieniaj powiększalne obrazy na te o lepszej jakości\'. Poprawi ona jakość zdjęć, jednak przy bardzo dużych powiększeniach mogą się stać one zbyt rozmazane.</string>
<string name="faq_10_title">Czy mogę w tej aplikacji przycinać obrazy?</string> <string name="faq_10_title">Czy mogę w tej aplikacji przycinać obrazy?</string>
<string name="faq_10_text">Tak, możesz to zrobić w edytorze, przeciągając krawędzie obrazu. Edytor otworzysz przytrzymując miniaturę obrazu i wybierając opcję \'Edytuj\', bądź wybierając tą samą opcję w menu pełnoekranowym.</string> <string name="faq_10_text">Tak, możesz to zrobić w edytorze, przeciągając krawędzie obrazu. Edytor otworzysz przytrzymując miniaturę obrazu i wybierając opcję \'Edytuj\', bądź wybierając tą samą opcję w menu pełnoekranowym.</string>
<string name="faq_11_title">Czy mogę jakoś grupować miniatury plików?</string>
<string name="faq_11_text">Tak. Użyj opcji \'Grupuj według\', gdy jesteś w widoku miniatur. Grupować je możesz według wielu kryteriów, włącznie z datą ich utworzenia. Ponadto, jeśli użyjesz opcji \'Pokazuj całą zawartość folderów\', możesz je także grupować według folderów.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brilho</string> <string name="brightness">Brilho</string>
<string name="lock_orientation">Travar orientação</string> <string name="lock_orientation">Travar orientação</string>
<string name="unlock_orientation">Destravar orientação</string> <string name="unlock_orientation">Destravar orientação</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrar mídia</string> <string name="filter_media">Filtrar mídia</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniaturas</string> <string name="thumbnails">Miniaturas</string>
<string name="fullscreen_media">Mídia em tela cheia</string> <string name="fullscreen_media">Mídia em tela cheia</string>
<string name="extended_details">Detalhes extendidos</string> <string name="extended_details">Detalhes extendidos</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>v
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Brilho</string> <string name="brightness">Brilho</string>
<string name="lock_orientation">Bloquear orientação</string> <string name="lock_orientation">Bloquear orientação</string>
<string name="unlock_orientation">Desbloquear orientação</string> <string name="unlock_orientation">Desbloquear orientação</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrar multimédia</string> <string name="filter_media">Filtrar multimédia</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniaturas</string> <string name="thumbnails">Miniaturas</string>
<string name="fullscreen_media">Multimédia em ecrã completo</string> <string name="fullscreen_media">Multimédia em ecrã completo</string>
<string name="extended_details">Detalhes extra</string> <string name="extended_details">Detalhes extra</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Яркость</string> <string name="brightness">Яркость</string>
<string name="lock_orientation">Заблокировать ориентацию</string> <string name="lock_orientation">Заблокировать ориентацию</string>
<string name="unlock_orientation">Разблокировать ориентацию</string> <string name="unlock_orientation">Разблокировать ориентацию</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Фильтр медиа</string> <string name="filter_media">Фильтр медиа</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Миниатюры</string> <string name="thumbnails">Миниатюры</string>
<string name="fullscreen_media">Просмотр медиафайлов</string> <string name="fullscreen_media">Просмотр медиафайлов</string>
<string name="extended_details">Подробности</string> <string name="extended_details">Подробности</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Как сделать Simple Gallery галереей по умолчанию?</string> <string name="faq_1_title">Как сделать Simple Gallery галереей по умолчанию?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Да, в настройках есть переключатель \"Заменять масштабируемые изображения высококачественными\", использовуйте его. Это улучшит качество изображений, но они будут размыты, если вы попытаетесь сильно увеличить масштаб отображения.</string> <string name="faq_9_text">Да, в настройках есть переключатель \"Заменять масштабируемые изображения высококачественными\", использовуйте его. Это улучшит качество изображений, но они будут размыты, если вы попытаетесь сильно увеличить масштаб отображения.</string>
<string name="faq_10_title">Можно ли обрезать изображения с помощью данного приложения?</string> <string name="faq_10_title">Можно ли обрезать изображения с помощью данного приложения?</string>
<string name="faq_10_text">Да, вы можете обрезать изображения в редакторе, перетаскивая за углы. К редактированию можно перейти, выбрав соответсвующий пункт в меню, открывающемуся длительным нажатием на миниатюру или изображение в полноэкранном режиме.</string> <string name="faq_10_text">Да, вы можете обрезать изображения в редакторе, перетаскивая за углы. К редактированию можно перейти, выбрав соответсвующий пункт в меню, открывающемуся длительным нажатием на миниатюру или изображение в полноэкранном режиме.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Jas</string> <string name="brightness">Jas</string>
<string name="lock_orientation">Uzamknúť otočenie obrazovky</string> <string name="lock_orientation">Uzamknúť otočenie obrazovky</string>
<string name="unlock_orientation">Odomknúť otočenie obrazovky</string> <string name="unlock_orientation">Odomknúť otočenie obrazovky</string>
<string name="change_orientation">Zmeniť otočenie obrazovky</string>
<string name="force_portrait">Vynútiť orientáciu na výšku</string>
<string name="force_landscape">Vynútiť orientáciu na šírku</string>
<string name="use_default_orientation">Použiť predvolenú orientáciu</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter médií</string> <string name="filter_media">Filter médií</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Náhľady</string> <string name="thumbnails">Náhľady</string>
<string name="fullscreen_media">Celoobrazovkový režim</string> <string name="fullscreen_media">Celoobrazovkový režim</string>
<string name="extended_details">Rozšírené vlastnosti</string> <string name="extended_details">Rozšírené vlastnosti</string>
<string name="bottom_actions">Spodné akčné tlačidlá</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Upraviť viditeľné spodné akčné tlačidlá</string>
<string name="toggle_favorite">Prepnutie obľúbenosti</string>
<string name="toggle_file_visibility">Prepnutie viditeľnosti súboru</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">Ako viem spraviť Jednoduchú Galériu predvolenou galériou zariadenia?</string> <string name="faq_1_title">Ako viem spraviť Jednoduchú Galériu predvolenou galériou zariadenia?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Áno, v nastaveniach je prepínač s textom \"Nahradiť hlboko priblížiteľné obrázky s obrázkami s lepšou kvalitou\", môžete ho skúsiť. Spôsobí to vyššiu kvalitu obrázkov, po priblížení sa ale budú rozmazávať oveľa skôr.</string> <string name="faq_9_text">Áno, v nastaveniach je prepínač s textom \"Nahradiť hlboko priblížiteľné obrázky s obrázkami s lepšou kvalitou\", môžete ho skúsiť. Spôsobí to vyššiu kvalitu obrázkov, po priblížení sa ale budú rozmazávať oveľa skôr.</string>
<string name="faq_10_title">Dá sa s touto aplikáciou orezať obrázky?</string> <string name="faq_10_title">Dá sa s touto aplikáciou orezať obrázky?</string>
<string name="faq_10_text">Áno, orezanie je možné v editore, potiahnutím rohov obrázkov. Do editoru sa môžete dostať buď dlhým podržaním náhľadu obrázku a zvolením menu položky Upraviť, alebo zvolením Upraviť pri celoobrazovkovom režime.</string> <string name="faq_10_text">Áno, orezanie je možné v editore, potiahnutím rohov obrázkov. Do editoru sa môžete dostať buď dlhým podržaním náhľadu obrázku a zvolením menu položky Upraviť, alebo zvolením Upraviť pri celoobrazovkovom režime.</string>
<string name="faq_11_title">Viem nejakým spôsobom zoskupiť náhľady súborov?</string>
<string name="faq_11_text">Áno, použitím funkcie \"Zoskupiť podľa\" na menu obrazovky s náhľadmi. Zoskupenie je možné na základe rozličných kritérií vrátane Dátumu vytvorenia. Ak použijete funkciu \"Zobraziť obsah všetkých priečinkov\", viete ich zoskupiť aj podľa priečinkov.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -24,6 +24,10 @@
<string name="brightness">Ljusstyrka</string> <string name="brightness">Ljusstyrka</string>
<string name="lock_orientation">Aktivera rotationslås</string> <string name="lock_orientation">Aktivera rotationslås</string>
<string name="unlock_orientation">Inaktivera rotationslås</string> <string name="unlock_orientation">Inaktivera rotationslås</string>
<string name="change_orientation">Change orientation</string>
<string name="force_portrait">Force portrait</string>
<string name="force_landscape">Force landscape</string>
<string name="use_default_orientation">Use default orientation</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filtrera media</string> <string name="filter_media">Filtrera media</string>
@ -154,6 +158,12 @@
<string name="thumbnails">Miniatyrer</string> <string name="thumbnails">Miniatyrer</string>
<string name="fullscreen_media">Visning av media i helskärmsläge</string> <string name="fullscreen_media">Visning av media i helskärmsläge</string>
<string name="extended_details">Utökad information</string> <string name="extended_details">Utökad information</string>
<string name="bottom_actions">Bottom actions</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Manage visible bottom actions</string>
<string name="toggle_favorite">Toggle favorite</string>
<string name="toggle_file_visibility">Toggle file visibility</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string> <string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
@ -178,6 +188,8 @@
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string> <string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
<string name="faq_10_title">Can I crop images with this app?</string> <string name="faq_10_title">Can I crop images with this app?</string>
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string> <string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
<string name="faq_11_title">Can I somehow group media file thumbnails?</string>
<string name="faq_11_text">Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

Some files were not shown because too many files have changed in this diff Show more