Merge branch 'master' into fix/hiding-ux
This commit is contained in:
commit
b9f762e1e4
13 changed files with 167 additions and 151 deletions
|
@ -78,12 +78,12 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:571e8e3ef2'
|
implementation 'com.github.SimpleMobileTools:Simple-Commons:ae8713396b'
|
||||||
implementation 'com.vanniktech:android-image-cropper:4.5.0'
|
implementation 'com.vanniktech:android-image-cropper:4.5.0'
|
||||||
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.25'
|
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.18.7'
|
implementation 'androidx.media3:media3-exoplayer:1.1.0'
|
||||||
implementation 'com.google.vr:sdk-panowidget:1.180.0'
|
implementation 'com.google.vr:sdk-panowidget:1.180.0'
|
||||||
implementation 'com.google.vr:sdk-videowidget:1.180.0'
|
implementation 'com.google.vr:sdk-videowidget:1.180.0'
|
||||||
implementation 'org.apache.sanselan:sanselan:0.97-incubator'
|
implementation 'org.apache.sanselan:sanselan:0.97-incubator'
|
||||||
|
|
|
@ -15,15 +15,16 @@ import android.util.DisplayMetrics
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import com.google.android.exoplayer2.*
|
import androidx.media3.common.*
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
import androidx.media3.common.util.UnstableApi
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import androidx.media3.datasource.ContentDataSource
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import androidx.media3.datasource.DataSource
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
import androidx.media3.datasource.DataSpec
|
||||||
import com.google.android.exoplayer2.upstream.ContentDataSource
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import com.google.android.exoplayer2.upstream.DataSource
|
import androidx.media3.exoplayer.SeekParameters
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
import androidx.media3.exoplayer.source.MediaSource
|
||||||
|
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
import com.simplemobiletools.gallery.pro.R
|
import com.simplemobiletools.gallery.pro.R
|
||||||
import com.simplemobiletools.gallery.pro.extensions.*
|
import com.simplemobiletools.gallery.pro.extensions.*
|
||||||
|
@ -31,7 +32,7 @@ import com.simplemobiletools.gallery.pro.helpers.*
|
||||||
import kotlinx.android.synthetic.main.activity_video_player.*
|
import kotlinx.android.synthetic.main.activity_video_player.*
|
||||||
import kotlinx.android.synthetic.main.bottom_video_time_holder.*
|
import kotlinx.android.synthetic.main.bottom_video_time_holder.*
|
||||||
|
|
||||||
open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener {
|
@UnstableApi open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener {
|
||||||
private val PLAY_WHEN_READY_DRAG_DELAY = 100L
|
private val PLAY_WHEN_READY_DRAG_DELAY = 100L
|
||||||
|
|
||||||
private var mIsFullscreen = false
|
private var mIsFullscreen = false
|
||||||
|
|
|
@ -1070,15 +1070,18 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
||||||
val size = fileDirItem.getProperSize(this, countHidden = true).formatSize()
|
val size = fileDirItem.getProperSize(this, countHidden = true).formatSize()
|
||||||
val filename = "\"${getCurrentPath().getFilenameFromPath()}\""
|
val filename = "\"${getCurrentPath().getFilenameFromPath()}\""
|
||||||
val filenameAndSize = "$filename ($size)"
|
val filenameAndSize = "$filename ($size)"
|
||||||
|
val isInRecycleBin = getCurrentMedium()!!.getIsInRecycleBin()
|
||||||
|
|
||||||
val baseString = if (config.useRecycleBin && !config.tempSkipRecycleBin && !getCurrentMedium()!!.getIsInRecycleBin()) {
|
val baseString = if (config.useRecycleBin && !config.tempSkipRecycleBin && !isInRecycleBin) {
|
||||||
R.string.move_to_recycle_bin_confirmation
|
R.string.move_to_recycle_bin_confirmation
|
||||||
} else {
|
} else {
|
||||||
R.string.deletion_confirmation
|
R.string.deletion_confirmation
|
||||||
}
|
}
|
||||||
|
|
||||||
val message = String.format(resources.getString(baseString), filenameAndSize)
|
val message = String.format(resources.getString(baseString), filenameAndSize)
|
||||||
DeleteWithRememberDialog(this, message, config.useRecycleBin) { remember, skipRecycleBin ->
|
val showSkipRecycleBinOption = config.useRecycleBin && !isInRecycleBin
|
||||||
|
|
||||||
|
DeleteWithRememberDialog(this, message, showSkipRecycleBinOption) { remember, skipRecycleBin ->
|
||||||
config.tempSkipDeleteConfirmation = remember
|
config.tempSkipDeleteConfirmation = remember
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ import kotlinx.android.synthetic.main.directory_item_list.view.dir_holder
|
||||||
import kotlinx.android.synthetic.main.directory_item_list.view.photo_cnt
|
import kotlinx.android.synthetic.main.directory_item_list.view.photo_cnt
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class DirectoryAdapter(
|
class DirectoryAdapter(
|
||||||
activity: BaseSimpleActivity, var dirs: ArrayList<Directory>, val listener: DirectoryOperationsListener?, recyclerView: MyRecyclerView,
|
activity: BaseSimpleActivity, var dirs: ArrayList<Directory>, val listener: DirectoryOperationsListener?, recyclerView: MyRecyclerView,
|
||||||
|
@ -152,7 +153,7 @@ class DirectoryAdapter(
|
||||||
R.id.cab_exclude -> tryExcludeFolder()
|
R.id.cab_exclude -> tryExcludeFolder()
|
||||||
R.id.cab_lock -> tryLockFolder()
|
R.id.cab_lock -> tryLockFolder()
|
||||||
R.id.cab_unlock -> unlockFolder()
|
R.id.cab_unlock -> unlockFolder()
|
||||||
R.id.cab_copy_to -> copyMoveTo(true)
|
R.id.cab_copy_to -> copyFilesTo()
|
||||||
R.id.cab_move_to -> moveFilesTo()
|
R.id.cab_move_to -> moveFilesTo()
|
||||||
R.id.cab_select_all -> selectAll()
|
R.id.cab_select_all -> selectAll()
|
||||||
R.id.cab_create_shortcut -> tryCreateShortcut()
|
R.id.cab_create_shortcut -> tryCreateShortcut()
|
||||||
|
@ -510,16 +511,24 @@ class DirectoryAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moveFilesTo() {
|
private fun copyFilesTo() {
|
||||||
activity.handleDeletePasswordProtection {
|
handleLockedFolderOpeningForFolders(getSelectedPaths()) {
|
||||||
copyMoveTo(false)
|
copyMoveTo(it, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyMoveTo(isCopyOperation: Boolean) {
|
private fun moveFilesTo() {
|
||||||
|
activity.handleDeletePasswordProtection {
|
||||||
|
handleLockedFolderOpeningForFolders(getSelectedPaths()) {
|
||||||
|
copyMoveTo(it, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyMoveTo(selectedPaths: Collection<String>, isCopyOperation: Boolean) {
|
||||||
val paths = ArrayList<String>()
|
val paths = ArrayList<String>()
|
||||||
val showHidden = config.shouldShowHidden
|
val showHidden = config.shouldShowHidden
|
||||||
getSelectedPaths().forEach {
|
selectedPaths.forEach {
|
||||||
val filter = config.filterMedia
|
val filter = config.filterMedia
|
||||||
File(it).listFiles()?.filter {
|
File(it).listFiles()?.filter {
|
||||||
!File(it.absolutePath).isDirectory &&
|
!File(it.absolutePath).isDirectory &&
|
||||||
|
@ -660,20 +669,26 @@ class DirectoryAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foldersToDelete.size == 1) {
|
handleLockedFolderOpeningForFolders(foldersToDelete.map { it.absolutePath }) {
|
||||||
activity.handleLockedFolderOpening(foldersToDelete.first().absolutePath) { success ->
|
listener?.deleteFolders(it.map { File(it) }.toMutableList() as ArrayList<File>)
|
||||||
if (success) {
|
|
||||||
listener?.deleteFolders(foldersToDelete)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foldersToDelete = foldersToDelete.filter { !config.isFolderProtected(it.absolutePath) }.toMutableList() as ArrayList<File>
|
|
||||||
listener?.deleteFolders(foldersToDelete)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleLockedFolderOpeningForFolders(folders: Collection<String>, callback: (Collection<String>) -> Unit) {
|
||||||
|
if (folders.size == 1) {
|
||||||
|
activity.handleLockedFolderOpening(folders.first()) { success ->
|
||||||
|
if (success) {
|
||||||
|
callback(folders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val filtered = folders.filter { !config.isFolderProtected(it) }
|
||||||
|
callback(filtered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun tryChangeAlbumCover(useDefault: Boolean) {
|
private fun tryChangeAlbumCover(useDefault: Boolean) {
|
||||||
activity.handleLockedFolderOpening(getFirstSelectedItemPath() ?: "") { success ->
|
activity.handleLockedFolderOpening(getFirstSelectedItemPath() ?: "") { success ->
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|
|
@ -508,8 +508,9 @@ class MediaAdapter(
|
||||||
val baseString =
|
val baseString =
|
||||||
if (config.useRecycleBin && !config.tempSkipRecycleBin && !isRecycleBin) R.string.move_to_recycle_bin_confirmation else R.string.deletion_confirmation
|
if (config.useRecycleBin && !config.tempSkipRecycleBin && !isRecycleBin) R.string.move_to_recycle_bin_confirmation else R.string.deletion_confirmation
|
||||||
val question = String.format(resources.getString(baseString), itemsAndSize)
|
val question = String.format(resources.getString(baseString), itemsAndSize)
|
||||||
|
val showSkipRecycleBinOption = config.useRecycleBin && !isRecycleBin
|
||||||
|
|
||||||
DeleteWithRememberDialog(activity, question, config.useRecycleBin) { remember, skipRecycleBin ->
|
DeleteWithRememberDialog(activity, question, showSkipRecycleBinOption) { remember, skipRecycleBin ->
|
||||||
config.tempSkipDeleteConfirmation = remember
|
config.tempSkipDeleteConfirmation = remember
|
||||||
|
|
||||||
if (remember) {
|
if (remember) {
|
||||||
|
|
|
@ -105,6 +105,7 @@ class PickDirectoryDialog(
|
||||||
onSearchOpenListener = {
|
onSearchOpenListener = {
|
||||||
updateSearchViewLeftIcon(R.drawable.ic_cross_vector)
|
updateSearchViewLeftIcon(R.drawable.ic_cross_vector)
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchClosedListener = {
|
onSearchClosedListener = {
|
||||||
searchEditText.clearFocus()
|
searchEditText.clearFocus()
|
||||||
activity.hideKeyboard(searchEditText)
|
activity.hideKeyboard(searchEditText)
|
||||||
|
|
|
@ -136,6 +136,7 @@ fun Context.getSortedDirectories(source: ArrayList<Directory>): ArrayList<Direct
|
||||||
o1.sortValue.normalizeString().toLowerCase().compareTo(o2.sortValue.normalizeString().toLowerCase())
|
o1.sortValue.normalizeString().toLowerCase().compareTo(o2.sortValue.normalizeString().toLowerCase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sorting and SORT_BY_PATH != 0 -> {
|
sorting and SORT_BY_PATH != 0 -> {
|
||||||
if (o1.sortValue.isEmpty()) {
|
if (o1.sortValue.isEmpty()) {
|
||||||
o1.sortValue = o1.path.toLowerCase()
|
o1.sortValue = o1.path.toLowerCase()
|
||||||
|
@ -151,6 +152,7 @@ fun Context.getSortedDirectories(source: ArrayList<Directory>): ArrayList<Direct
|
||||||
o1.sortValue.toLowerCase().compareTo(o2.sortValue.toLowerCase())
|
o1.sortValue.toLowerCase().compareTo(o2.sortValue.toLowerCase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sorting and SORT_BY_PATH != 0 -> AlphanumericComparator().compare(o1.sortValue.toLowerCase(), o2.sortValue.toLowerCase())
|
sorting and SORT_BY_PATH != 0 -> AlphanumericComparator().compare(o1.sortValue.toLowerCase(), o2.sortValue.toLowerCase())
|
||||||
sorting and SORT_BY_SIZE != 0 -> (o1.sortValue.toLongOrNull() ?: 0).compareTo(o2.sortValue.toLongOrNull() ?: 0)
|
sorting and SORT_BY_SIZE != 0 -> (o1.sortValue.toLongOrNull() ?: 0).compareTo(o2.sortValue.toLongOrNull() ?: 0)
|
||||||
sorting and SORT_BY_DATE_MODIFIED != 0 -> (o1.sortValue.toLongOrNull() ?: 0).compareTo(o2.sortValue.toLongOrNull() ?: 0)
|
sorting and SORT_BY_DATE_MODIFIED != 0 -> (o1.sortValue.toLongOrNull() ?: 0).compareTo(o2.sortValue.toLongOrNull() ?: 0)
|
||||||
|
@ -195,90 +197,89 @@ fun Context.getDirsToShow(dirs: ArrayList<Directory>, allDirs: ArrayList<Directo
|
||||||
|
|
||||||
fun Context.getDirectParentSubfolders(dirs: ArrayList<Directory>, currentPathPrefix: String): ArrayList<Directory> {
|
fun Context.getDirectParentSubfolders(dirs: ArrayList<Directory>, currentPathPrefix: String): ArrayList<Directory> {
|
||||||
val folders = dirs.map { it.path }.sorted().toMutableSet() as HashSet<String>
|
val folders = dirs.map { it.path }.sorted().toMutableSet() as HashSet<String>
|
||||||
val currentPaths = LinkedHashSet<String>()
|
// Sort by path length, to ensure that parents get processed first
|
||||||
val foldersWithoutMediaFiles = ArrayList<String>()
|
val foldersByPathLength = dirs.filter {
|
||||||
|
currentPathPrefix.isEmpty() ||
|
||||||
|
(it.path.startsWith(currentPathPrefix, true) && it.path != currentPathPrefix)
|
||||||
|
}.sortedBy {
|
||||||
|
it.path.length
|
||||||
|
}
|
||||||
|
|
||||||
var newDirId = 1000L
|
var newDirId = 1000L
|
||||||
|
val groups = mutableMapOf<String, MutableList<Directory>>()
|
||||||
|
|
||||||
for (path in folders) {
|
for (folder in foldersByPathLength) {
|
||||||
if (path == RECYCLE_BIN || path == FAVORITES) {
|
val parent = groups.keys.firstOrNull { folder.path.startsWith(it, true) }
|
||||||
continue
|
if (parent != null) {
|
||||||
}
|
// If we have parent in top level groups
|
||||||
|
// Add this folder to that group,
|
||||||
|
// but also add all folders in between which may not have media files
|
||||||
|
groups.getOrPut(parent, ::mutableListOf).apply {
|
||||||
|
var midParent = File(folder.path).parent
|
||||||
|
while (midParent != null && none { it.path.equals(midParent, true) }) {
|
||||||
|
val isSortingAscending = config.sorting.isSortingAscending()
|
||||||
|
val subDirs = dirs.filter { File(it.path).parent.equals(midParent, true) } as ArrayList<Directory>
|
||||||
|
if (subDirs.isNotEmpty()) {
|
||||||
|
val lastModified = if (isSortingAscending) {
|
||||||
|
subDirs.minByOrNull { it.modified }?.modified
|
||||||
|
} else {
|
||||||
|
subDirs.maxByOrNull { it.modified }?.modified
|
||||||
|
} ?: 0
|
||||||
|
|
||||||
if (currentPathPrefix.isNotEmpty()) {
|
val dateTaken = if (isSortingAscending) {
|
||||||
if (!path.startsWith(currentPathPrefix, true)) {
|
subDirs.minByOrNull { it.taken }?.taken
|
||||||
continue
|
} else {
|
||||||
}
|
subDirs.maxByOrNull { it.taken }?.taken
|
||||||
|
} ?: 0
|
||||||
|
|
||||||
if (!File(path).parent.equals(currentPathPrefix, true)) {
|
var mediaTypes = 0
|
||||||
continue
|
subDirs.forEach {
|
||||||
}
|
mediaTypes = mediaTypes or it.types
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPathPrefix.isNotEmpty() && path.equals(currentPathPrefix, true) || File(path).parent.equals(currentPathPrefix, true)) {
|
val directory = Directory(
|
||||||
currentPaths.add(path)
|
newDirId++,
|
||||||
} else if (folders.any { !it.equals(path, true) && (File(path).parent.equals(it, true) || File(it).parent.equals(File(path).parent, true)) }) {
|
midParent,
|
||||||
// if we have folders like
|
subDirs.first().tmb,
|
||||||
// /storage/emulated/0/Pictures/Images and
|
getFolderNameFromPath(midParent),
|
||||||
// /storage/emulated/0/Pictures/Screenshots,
|
subDirs.sumBy { it.mediaCnt },
|
||||||
// but /storage/emulated/0/Pictures is empty, still Pictures with the first folders thumbnails and proper other info
|
lastModified,
|
||||||
val parent = File(path).parent
|
dateTaken,
|
||||||
if (parent != null && !folders.contains(parent) && dirs.none { it.path.equals(parent, true) }) {
|
subDirs.sumByLong { it.size },
|
||||||
currentPaths.add(parent)
|
getPathLocation(midParent),
|
||||||
val isSortingAscending = config.sorting.isSortingAscending()
|
mediaTypes,
|
||||||
val subDirs = dirs.filter { File(it.path).parent.equals(File(path).parent, true) } as ArrayList<Directory>
|
""
|
||||||
if (subDirs.isNotEmpty()) {
|
)
|
||||||
val lastModified = if (isSortingAscending) {
|
|
||||||
subDirs.minByOrNull { it.modified }?.modified
|
|
||||||
} else {
|
|
||||||
subDirs.maxByOrNull { it.modified }?.modified
|
|
||||||
} ?: 0
|
|
||||||
|
|
||||||
val dateTaken = if (isSortingAscending) {
|
directory.containsMediaFilesDirectly = false
|
||||||
subDirs.minByOrNull { it.taken }?.taken
|
dirs.add(directory)
|
||||||
} else {
|
add(directory)
|
||||||
subDirs.maxByOrNull { it.taken }?.taken
|
|
||||||
} ?: 0
|
|
||||||
|
|
||||||
var mediaTypes = 0
|
|
||||||
subDirs.forEach {
|
|
||||||
mediaTypes = mediaTypes or it.types
|
|
||||||
}
|
}
|
||||||
|
midParent = File(midParent).parent
|
||||||
val directory = Directory(
|
|
||||||
newDirId++,
|
|
||||||
parent,
|
|
||||||
subDirs.first().tmb,
|
|
||||||
getFolderNameFromPath(parent),
|
|
||||||
subDirs.sumBy { it.mediaCnt },
|
|
||||||
lastModified,
|
|
||||||
dateTaken,
|
|
||||||
subDirs.sumByLong { it.size },
|
|
||||||
getPathLocation(parent),
|
|
||||||
mediaTypes,
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
directory.containsMediaFilesDirectly = false
|
|
||||||
dirs.add(directory)
|
|
||||||
currentPaths.add(parent)
|
|
||||||
foldersWithoutMediaFiles.add(parent)
|
|
||||||
}
|
}
|
||||||
|
add(folder)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentPaths.add(path)
|
// If we have don't have parent in top level groups
|
||||||
}
|
// Set this folder as top level group if it is direct child
|
||||||
}
|
if (currentPathPrefix.isEmpty() || File(folder.path).parent.equals(currentPathPrefix, true)) {
|
||||||
|
groups.getOrPut(folder.path, ::mutableListOf).add(folder)
|
||||||
var areDirectSubfoldersAvailable = false
|
} else {
|
||||||
currentPaths.forEach {
|
// Otherwise find its parent which is a direct child of current path prefix
|
||||||
val path = it
|
// And create a group for it
|
||||||
currentPaths.forEach {
|
var firstVisibleParent = File(folder.path).parent
|
||||||
if (!foldersWithoutMediaFiles.contains(it) && !it.equals(path, true) && File(it).parent?.equals(path, true) == true) {
|
while (firstVisibleParent != null && !File(firstVisibleParent).parent.equals(currentPathPrefix, true)) {
|
||||||
areDirectSubfoldersAvailable = true
|
firstVisibleParent = File(firstVisibleParent).parent
|
||||||
|
}
|
||||||
|
if (firstVisibleParent != null) {
|
||||||
|
groups.getOrPut(firstVisibleParent, ::mutableListOf).add(folder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val currentPaths = groups.keys.toMutableList()
|
||||||
|
|
||||||
if (currentPathPrefix.isEmpty() && folders.contains(RECYCLE_BIN)) {
|
if (currentPathPrefix.isEmpty() && folders.contains(RECYCLE_BIN)) {
|
||||||
currentPaths.add(RECYCLE_BIN)
|
currentPaths.add(RECYCLE_BIN)
|
||||||
}
|
}
|
||||||
|
@ -294,12 +295,7 @@ fun Context.getDirectParentSubfolders(dirs: ArrayList<Directory>, currentPathPre
|
||||||
folders.clear()
|
folders.clear()
|
||||||
folders.addAll(currentPaths)
|
folders.addAll(currentPaths)
|
||||||
|
|
||||||
val dirsToShow = dirs.filter { folders.contains(it.path) } as ArrayList<Directory>
|
return dirs.filter { folders.contains(it.path) } as ArrayList<Directory>
|
||||||
return if (areDirectSubfoldersAvailable) {
|
|
||||||
getDirectParentSubfolders(dirsToShow, currentPathPrefix)
|
|
||||||
} else {
|
|
||||||
dirsToShow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.updateSubfolderCounts(children: ArrayList<Directory>, parentDirs: ArrayList<Directory>) {
|
fun Context.updateSubfolderCounts(children: ArrayList<Directory>, parentDirs: ArrayList<Directory>) {
|
||||||
|
|
|
@ -665,7 +665,7 @@ class PhotoFragment : ViewPagerFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val regionDecoder = object : DecoderFactory<ImageRegionDecoder> {
|
val regionDecoder = object : DecoderFactory<ImageRegionDecoder> {
|
||||||
override fun make() = PicassoRegionDecoder(showHighestQuality, mScreenWidth, mScreenHeight, minTileDpi, mMedium.isHeic())
|
override fun make() = PicassoRegionDecoder(showHighestQuality, mScreenWidth, mScreenHeight, minTileDpi)
|
||||||
}
|
}
|
||||||
|
|
||||||
var newOrientation = (rotation + mCurrentRotationDegrees) % 360
|
var newOrientation = (rotation + mCurrentRotationDegrees) % 360
|
||||||
|
|
|
@ -13,17 +13,18 @@ import android.widget.ImageView
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.media3.common.*
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.google.android.exoplayer2.*
|
import androidx.media3.datasource.ContentDataSource
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
import androidx.media3.datasource.DataSource
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import androidx.media3.datasource.DataSpec
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import androidx.media3.datasource.FileDataSource
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import com.google.android.exoplayer2.upstream.ContentDataSource
|
import androidx.media3.exoplayer.SeekParameters
|
||||||
import com.google.android.exoplayer2.upstream.DataSource
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec
|
import androidx.media3.exoplayer.source.MediaSource
|
||||||
import com.google.android.exoplayer2.upstream.FileDataSource
|
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
|
||||||
import com.simplemobiletools.commons.extensions.*
|
import com.simplemobiletools.commons.extensions.*
|
||||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
import com.simplemobiletools.gallery.pro.R
|
import com.simplemobiletools.gallery.pro.R
|
||||||
|
@ -40,7 +41,7 @@ import kotlinx.android.synthetic.main.pager_video_item.view.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener {
|
@UnstableApi class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener {
|
||||||
private val PROGRESS = "progress"
|
private val PROGRESS = "progress"
|
||||||
|
|
||||||
private var mIsFullscreen = false
|
private var mIsFullscreen = false
|
||||||
|
@ -673,7 +674,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
|
||||||
mPlayPauseButton.setImageResource(R.drawable.ic_play_outline_vector)
|
mPlayPauseButton.setImageResource(R.drawable.ic_play_outline_vector)
|
||||||
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
mPositionAtPause = mExoPlayer?.currentPosition ?: 0L
|
mPositionAtPause = mExoPlayer?.currentPosition ?: 0L
|
||||||
releaseExoPlayer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun videoEnded(): Boolean {
|
private fun videoEnded(): Boolean {
|
||||||
|
|
|
@ -9,8 +9,7 @@ class PicassoRegionDecoder(
|
||||||
val showHighestQuality: Boolean,
|
val showHighestQuality: Boolean,
|
||||||
val screenWidth: Int,
|
val screenWidth: Int,
|
||||||
val screenHeight: Int,
|
val screenHeight: Int,
|
||||||
val minTileDpi: Int,
|
val minTileDpi: Int
|
||||||
val isHeic: Boolean
|
|
||||||
) : ImageRegionDecoder {
|
) : ImageRegionDecoder {
|
||||||
private var decoder: BitmapRegionDecoder? = null
|
private var decoder: BitmapRegionDecoder? = null
|
||||||
private val decoderLock = Any()
|
private val decoderLock = Any()
|
||||||
|
@ -35,7 +34,7 @@ class PicassoRegionDecoder(
|
||||||
|
|
||||||
val options = BitmapFactory.Options()
|
val options = BitmapFactory.Options()
|
||||||
options.inSampleSize = newSampleSize
|
options.inSampleSize = newSampleSize
|
||||||
options.inPreferredConfig = if (showHighestQuality || isHeic) Bitmap.Config.ARGB_8888 else Bitmap.Config.RGB_565
|
options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||||
val bitmap = decoder!!.decodeRegion(rect, options)
|
val bitmap = decoder!!.decodeRegion(rect, options)
|
||||||
return bitmap ?: throw RuntimeException("Region decoder returned null bitmap - image format may not be supported")
|
return bitmap ?: throw RuntimeException("Region decoder returned null bitmap - image format may not be supported")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<string name="edit">Редакция</string>
|
<string name="edit">Редакция</string>
|
||||||
<string name="open_camera">Камера</string>
|
<string name="open_camera">Камера</string>
|
||||||
<string name="hidden">(скрито)</string>
|
<string name="hidden">(скрито)</string>
|
||||||
<string name="excluded">(excluded)</string>
|
<string name="excluded">(изключено)</string>
|
||||||
<string name="pin_folder">Закачете папката</string>
|
<string name="pin_folder">Закачете папката</string>
|
||||||
<string name="unpin_folder">Откачете папката</string>
|
<string name="unpin_folder">Откачете папката</string>
|
||||||
<string name="pin_to_the_top">Закачете отгоре</string>
|
<string name="pin_to_the_top">Закачете отгоре</string>
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
<string name="hidden_folders">Скрити папки</string>
|
<string name="hidden_folders">Скрити папки</string>
|
||||||
<string name="manage_hidden_folders">Управление на скритите папки</string>
|
<string name="manage_hidden_folders">Управление на скритите папки</string>
|
||||||
<string name="hidden_folders_placeholder">Изглежда нямате папки скрити чрез \'.nomedia\' файл.</string>
|
<string name="hidden_folders_placeholder">Изглежда нямате папки скрити чрез \'.nomedia\' файл.</string>
|
||||||
<string name="hidden_all_files">You have to grant the app All Files access to see hidden files, else it cannot work.</string>
|
<string name="hidden_all_files">Трябва да предоставите на приложението достъп до всички файлове, за да видите скритите файлове, в противен случай то не може да работи.</string>
|
||||||
<string name="cant_unhide_folder">If a folder or one of its parent folders has a dot before its name, it is hidden and cannot be unhidden like this. You have to remove the dot by renaming it.</string>
|
<string name="cant_unhide_folder">If a folder or one of its parent folders has a dot before its name, it is hidden and cannot be unhidden like this. You have to remove the dot by renaming it.</string>
|
||||||
<!-- Include folders -->
|
<!-- Include folders -->
|
||||||
<string name="include_folders">Включени папки</string>
|
<string name="include_folders">Включени папки</string>
|
||||||
|
@ -70,19 +70,19 @@
|
||||||
<string name="height">Височина</string>
|
<string name="height">Височина</string>
|
||||||
<string name="keep_aspect_ratio">Запазете съотношението на страните</string>
|
<string name="keep_aspect_ratio">Запазете съотношението на страните</string>
|
||||||
<string name="invalid_values">Моля въведете валидна резолюция</string>
|
<string name="invalid_values">Моля въведете валидна резолюция</string>
|
||||||
<string name="resize_multiple_images">Resize multiple images</string>
|
<string name="resize_multiple_images">Преоразмеряване на няколко изображения</string>
|
||||||
<string name="resize_factor">Resize factor</string>
|
<string name="resize_factor">Коефициент на преоразмеряване</string>
|
||||||
<string name="resize_factor_info">Resize images to the given percentage, value must be within 10 and 90.</string>
|
<string name="resize_factor_info">Преоразмерете изображенията до дадения процент, стойността трябва да е в рамките на 10 и 90.</string>
|
||||||
<string name="resize_factor_error">Enter a number between 10 and 90</string>
|
<string name="resize_factor_error">Въведете число между 10 и 90</string>
|
||||||
<plurals name="failed_to_resize_images">
|
<plurals name="failed_to_resize_images">
|
||||||
<item quantity="one">Failed to resize %d image</item>
|
<item quantity="one">Преоразмеряването неуспешно за %d изображение</item>
|
||||||
<item quantity="other">Failed to resize %d images</item>
|
<item quantity="other">Преоразмеряването неуспешно за %d изображения</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="images_resized_successfully">Images resized successfully</string>
|
<string name="images_resized_successfully">Изображенията са преоразмерени успешно</string>
|
||||||
<!-- Editor -->
|
<!-- Editor -->
|
||||||
<string name="editor">Редактор</string>
|
<string name="editor">Редактор</string>
|
||||||
<string name="basic_editor">Basic Editor</string>
|
<string name="basic_editor">Базов редактор</string>
|
||||||
<string name="advanced_editor">Advanced Editor</string>
|
<string name="advanced_editor">Разширен редактор</string>
|
||||||
<string name="rotate">Завъртане</string>
|
<string name="rotate">Завъртане</string>
|
||||||
<string name="invalid_image_path">Неправилен път до изображението</string>
|
<string name="invalid_image_path">Неправилен път до изображението</string>
|
||||||
<string name="invalid_video_path">Неправилен път до видеото</string>
|
<string name="invalid_video_path">Неправилен път до видеото</string>
|
||||||
|
@ -102,9 +102,9 @@
|
||||||
<string name="rotate_left">Завъртане наляво</string>
|
<string name="rotate_left">Завъртане наляво</string>
|
||||||
<string name="rotate_right">Завъртане надясно</string>
|
<string name="rotate_right">Завъртане надясно</string>
|
||||||
<string name="rotate_one_eighty">Завъртане на 180º</string>
|
<string name="rotate_one_eighty">Завъртане на 180º</string>
|
||||||
<string name="transform">Transform</string>
|
<string name="transform">Трансформирай</string>
|
||||||
<string name="crop">Crop</string>
|
<string name="crop">Изрежи</string>
|
||||||
<string name="draw">Draw</string>
|
<string name="draw">Рисувай</string>
|
||||||
<string name="flip">Oбръщане</string>
|
<string name="flip">Oбръщане</string>
|
||||||
<string name="flip_horizontally">Хоризонтално обръщане</string>
|
<string name="flip_horizontally">Хоризонтално обръщане</string>
|
||||||
<string name="flip_vertically">Вертикално обръщане</string>
|
<string name="flip_vertically">Вертикално обръщане</string>
|
||||||
|
@ -210,19 +210,19 @@
|
||||||
<string name="limit_folder_title">Ограничаване на заглавията на папките до един ред</string>
|
<string name="limit_folder_title">Ограничаване на заглавията на папките до един ред</string>
|
||||||
<string name="square">Квадрат</string>
|
<string name="square">Квадрат</string>
|
||||||
<string name="rounded_corners">Заоблени ъгли</string>
|
<string name="rounded_corners">Заоблени ъгли</string>
|
||||||
<string name="export_favorite_paths">Експортиране на пътищата на любимите файлове</string>
|
<string name="export_favorite_paths">Експортиране на любимите</string>
|
||||||
<string name="import_favorite_paths">Import favorites</string>
|
<string name="import_favorite_paths">Импортиране на любими</string>
|
||||||
<string name="paths_imported_successfully">Paths imported successfully</string>
|
<string name="paths_imported_successfully">Пътищата са импортирани успешно</string>
|
||||||
<string name="media_management_prompt">За да работят надеждно всички операции, моля задайте това приложение да управлява медията в настройките на вашето устройство.</string>
|
<string name="media_management_prompt">За да работят надеждно всички операции, моля задайте това приложение да управлява медията в настройките на вашето устройство.</string>
|
||||||
<string name="password_protect_excluded">Защита на изключена папка с парола</string>
|
<string name="password_protect_excluded">Защита на изключена папка с парола</string>
|
||||||
<string name="media_management_manual">Нещо се обърка, моля, влезте в Настройки на вашето устройство - Приложения - Достъп до специални приложения - Приложения за управление на медии и разрешете на това приложение да управлява медиите.</string>
|
<string name="media_management_manual">Нещо се обърка, моля, влезте в Настройки на вашето устройство - Приложения - Достъп до специални приложения - Приложения за управление на медии и разрешете на това приложение да управлява медиите.</string>
|
||||||
<string name="media_management_note">Ако пренасочването не работи, моля, влезте в Настройки на вашето устройство - Приложения - Достъп до специални приложения - Приложения за управление на медии и разрешете на това приложение да управлява медиите.</string>
|
<string name="media_management_note">Ако пренасочването не работи, моля, влезте в Настройки на вашето устройство - Приложения - Достъп до специални приложения - Приложения за управление на медии и разрешете на това приложение да управлява медиите.</string>
|
||||||
<string name="media_management_alternative">If you do not want to do it, you can also go into your device Settings - Apps - Special app access - Media management apps and allow this app to manage media.</string>
|
<string name="media_management_alternative">Ако не искате да го направите, можете също да отидете в Настройки на вашето устройство - Приложения - Специален достъп до приложения - Приложения за управление на медии и да разрешите на това приложение да управлява медии.</string>
|
||||||
<string name="alternative_media_access">Alternatively, you can allow accessing media files only. In that case you will not be able to work with hidden files though.</string>
|
<string name="alternative_media_access">Като алтернатива можете да разрешите достъп само до медийни файлове. В този случай обаче няма да можете да работите със скрити файлове.</string>
|
||||||
<string name="media_only">Media only</string>
|
<string name="media_only">Само медия</string>
|
||||||
<string name="all_files">All files</string>
|
<string name="all_files">Всички файлове</string>
|
||||||
<string name="search_all_files">Search all files instead of folders on the main screen</string>
|
<string name="search_all_files">Търсете всички файлове вместо папки на главния екран</string>
|
||||||
<string name="show_all_folders">Show a menu button for toggling Show All Folders Content quickly</string>
|
<string name="show_all_folders">Показване на бутон на менюто за бързо превключване на Показване на съдържанието на всички папки</string>
|
||||||
<!-- Setting sections -->
|
<!-- Setting sections -->
|
||||||
<string name="thumbnails">Миниатюри</string>
|
<string name="thumbnails">Миниатюри</string>
|
||||||
<string name="fullscreen_media">Медия на цял екран</string>
|
<string name="fullscreen_media">Медия на цял екран</string>
|
||||||
|
@ -375,7 +375,7 @@
|
||||||
<string name="faq_15_text">Кешът на приложението може да отнеме до 250 MB, което гарантира по-бързо зареждане на изображението. Ако приложението заема още повече място, най-вероятно това е причинено от това, че имате елементи в кошчето. Тези файлове се броят към размера на приложението. Можете да изчистите кошчето, като го отворите и изтриете всички файлове или от настройките на приложението. Всеки файл в кошчето се изтрива автоматично след 30 дни.</string>
|
<string name="faq_15_text">Кешът на приложението може да отнеме до 250 MB, което гарантира по-бързо зареждане на изображението. Ако приложението заема още повече място, най-вероятно това е причинено от това, че имате елементи в кошчето. Тези файлове се броят към размера на приложението. Можете да изчистите кошчето, като го отворите и изтриете всички файлове или от настройките на приложението. Всеки файл в кошчето се изтрива автоматично след 30 дни.</string>
|
||||||
<string name="faq_16_title">Какво се случи със скриването на файлове и папки и защо вече не мога да виждам скрити елементи\?</string>
|
<string name="faq_16_title">Какво се случи със скриването на файлове и папки и защо вече не мога да виждам скрити елементи\?</string>
|
||||||
<string name="faq_16_text">Започвайки с Android 11, вече не можете да скривате или показвате файлове или папки, не можете да виждате скритите в приложения за галерия поради системни ограничения. За това ще трябва да използвате някакъв файлов мениджър.</string>
|
<string name="faq_16_text">Започвайки с Android 11, вече не можете да скривате или показвате файлове или папки, не можете да виждате скритите в приложения за галерия поради системни ограничения. За това ще трябва да използвате някакъв файлов мениджър.</string>
|
||||||
<string name="faq_16_text_extra">Or you can also grant this gallery access to All Files through your device settings, that will allow us showing hidden items and make file operations more reliable in general.</string>
|
<string name="faq_16_text_extra">Или можете също да предоставите на тази галерия достъп до всички файлове през настройките на вашето устройство, което ще ни позволи да показваме скрити елементи и ще направи файловите операции по-надеждни като цяло.</string>
|
||||||
<string name="faq_17_title">Защо вече не мога да добавя липсващи папки\?</string>
|
<string name="faq_17_title">Защо вече не мога да добавя липсващи папки\?</string>
|
||||||
<string name="faq_17_text">Това спря да работи поради системните промени, които дойдоха и с Android 11, приложението вече не може да преглежда истински папки, разчита на така наречения MediaStore при извличане на данни.</string>
|
<string name="faq_17_text">Това спря да работи поради системните промени, които дойдоха и с Android 11, приложението вече не може да преглежда истински папки, разчита на така наречения MediaStore при извличане на данни.</string>
|
||||||
<string name="faq_18_title">Защо виждам реклами по време на възпроизвеждане на видео\?</string>
|
<string name="faq_18_title">Защо виждам реклами по време на възпроизвеждане на видео\?</string>
|
||||||
|
|
|
@ -75,8 +75,8 @@
|
||||||
<string name="resize_factor_info">Αλλάξτε το μέγεθος των εικόνων στο δεδομένο ποσοστό. Η τιμή πρέπει να είναι μεταξύ 10 και 90.</string>
|
<string name="resize_factor_info">Αλλάξτε το μέγεθος των εικόνων στο δεδομένο ποσοστό. Η τιμή πρέπει να είναι μεταξύ 10 και 90.</string>
|
||||||
<string name="resize_factor_error">Εισάγετε έναν αριθμό μεταξύ 10 και 90</string>
|
<string name="resize_factor_error">Εισάγετε έναν αριθμό μεταξύ 10 και 90</string>
|
||||||
<plurals name="failed_to_resize_images">
|
<plurals name="failed_to_resize_images">
|
||||||
<item quantity="one">Αποτυχία αλλαγής μεγέθους εικόνας %d</item>
|
<item quantity="one">Αποτυχία αλλαγής μεγέθους %d εικόνας</item>
|
||||||
<item quantity="other">Αποτυχία αλλαγής μεγέθους εικόνων %d</item>
|
<item quantity="other">Αποτυχία αλλαγής μεγέθους %d εικόνων</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="images_resized_successfully">Το μέγεθος των εικόνων άλλαξε με επιτυχία</string>
|
<string name="images_resized_successfully">Το μέγεθος των εικόνων άλλαξε με επιτυχία</string>
|
||||||
<!-- Editor -->
|
<!-- Editor -->
|
||||||
|
|
|
@ -214,7 +214,7 @@
|
||||||
<string name="import_favorite_paths">Import favorites</string>
|
<string name="import_favorite_paths">Import favorites</string>
|
||||||
<string name="paths_imported_successfully">Paths imported successfully</string>
|
<string name="paths_imported_successfully">Paths imported successfully</string>
|
||||||
<string name="media_management_prompt">To make sure that all file operations work reliably, please make this app a Media management app in your device settings.</string>
|
<string name="media_management_prompt">To make sure that all file operations work reliably, please make this app a Media management app in your device settings.</string>
|
||||||
<string name="password_protect_excluded">Password protect excluded folder visibility</string>
|
<string name="password_protect_excluded">設定密碼保護,隱藏不顯示的資料夾</string>
|
||||||
<string name="media_management_manual">出錯了,請至裝置的設定 - 應用程式(和通知) - 特殊應用程式存取權 - 媒體管理應用程式,允許本 app 管理媒體。</string>
|
<string name="media_management_manual">出錯了,請至裝置的設定 - 應用程式(和通知) - 特殊應用程式存取權 - 媒體管理應用程式,允許本 app 管理媒體。</string>
|
||||||
<string name="media_management_note">若重新導向未順利運作,請至裝置的設定 - 應用程式(和通知) - 特殊應用程式存取權 - 媒體管理應用程式,允許本 app 管理媒體。</string>
|
<string name="media_management_note">若重新導向未順利運作,請至裝置的設定 - 應用程式(和通知) - 特殊應用程式存取權 - 媒體管理應用程式,允許本 app 管理媒體。</string>
|
||||||
<string name="media_management_alternative">If you do not want to do it, you can also go into your device Settings - Apps - Special app access - Media management apps and allow this app to manage media.</string>
|
<string name="media_management_alternative">If you do not want to do it, you can also go into your device Settings - Apps - Special app access - Media management apps and allow this app to manage media.</string>
|
||||||
|
|
Loading…
Reference in a new issue