diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b43279c0..71859cfcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,37 @@ 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)* ---------------------------- diff --git a/app/build.gradle b/app/build.gradle index 01162895e..93eefa930 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.simplemobiletools.gallery" minSdkVersion 16 targetSdkVersion 27 - versionCode 180 - versionName "4.2.0" + versionCode 184 + versionName "4.3.2" multiDexEnabled true setProperty("archivesBaseName", "gallery") } @@ -47,17 +47,20 @@ ext { } 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.android.support:multidex:1.0.3' implementation 'it.sephiroth.android.exif:library:1.0.1' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.12' implementation 'com.github.chrisbanes:PhotoView:2.1.3' 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" - implementation "android.arch.persistence.room:runtime:1.1.0" - annotationProcessor "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.1" + annotationProcessor "android.arch.persistence.room:compiler:1.1.1" //implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.9.0' implementation 'com.github.tibbi:subsampling-scale-image-view:v3.10.0-fork' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index a9c0f371b..9bd07e413 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,2 +1,3 @@ -keep class com.simplemobiletools.** { *; } -dontwarn com.simplemobiletools.** +-dontwarn org.apache.** diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 601881fd6..633440e5e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,12 +1,16 @@ + + + + 1 + menu.apply { + 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.stop_showing_hidden).isVisible = config.temporarilyShowHidden @@ -264,7 +281,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { if (newFolder.exists() && newFolder.isDirectory) { if (newFolder.list()?.isEmpty() == true) { 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 = "" @@ -317,10 +334,13 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { private fun showSortingDialog() { 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) { getDirectories() } else { - gotDirectories(getCurrentlyDisplayedDirs()) + Thread { + gotDirectories(getCurrentlyDisplayedDirs()) + }.start() } } } @@ -329,6 +349,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { FilterMediaDialog(this) { mLoadedInitialPhotos = false directories_refresh_layout.isRefreshing = true + directories_grid.adapter = null getDirectories() } } @@ -336,7 +357,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { private fun showAllMedia() { config.showAll = true Intent(this, MediaActivity::class.java).apply { - putExtra(DIRECTORY, "/") + putExtra(DIRECTORY, "") if (mIsThirdPartyIntent) { handleMediaIntent(this) @@ -375,12 +396,37 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { private fun toggleTemporarilyShowHidden(show: Boolean) { mLoadedInitialPhotos = false config.temporarilyShowHidden = show + directories_grid.adapter = null getDirectories() invalidateOptionsMenu() } override fun deleteFolders(folders: ArrayList) { val fileDirItems = folders.map { FileDirItem(it.absolutePath, it.name, true) } as ArrayList + fileDirItems.forEach { + toast(String.format(getString(R.string.deleting_folder), it.name), Toast.LENGTH_LONG) + } + + if (config.useRecycleBin) { + val pathsToDelete = ArrayList() + 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, folders: ArrayList) { deleteFolders(fileDirItems) { runOnUiThread { refreshItems() @@ -416,6 +462,32 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { layoutManager.spanCount = config.dirColumnCnt } + private fun measureRecyclerViewContent(directories: ArrayList) { + directories_grid.onGlobalLayout { + if (config.scrollHorizontally) { + calculateContentWidth(directories) + } else { + calculateContentHeight(directories) + } + } + } + + private fun calculateContentWidth(directories: ArrayList) { + 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) { + 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() { if (config.viewTypeFolders == VIEW_TYPE_GRID) { val layoutManager = directories_grid.layoutManager as MyGridLayoutManager @@ -451,25 +523,29 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) { CreateNewFolderDialog(this, it) { config.tempFolderPath = it - gotDirectories(addTempFolderIfNeeded(getCurrentlyDisplayedDirs())) + Thread { + gotDirectories(addTempFolderIfNeeded(getCurrentlyDisplayedDirs())) + }.start() } } } private fun increaseColumnCount() { - directories_vertical_fastscroller.measureRecyclerViewOnRedraw() - directories_horizontal_fastscroller.measureRecyclerViewOnRedraw() config.dirColumnCnt = ++(directories_grid.layoutManager as MyGridLayoutManager).spanCount - invalidateOptionsMenu() - directories_grid.adapter?.notifyDataSetChanged() + columnCountChanged() } private fun reduceColumnCount() { - directories_vertical_fastscroller.measureRecyclerViewOnRedraw() - directories_horizontal_fastscroller.measureRecyclerViewOnRedraw() config.dirColumnCnt = --(directories_grid.layoutManager as MyGridLayoutManager).spanCount + columnCountChanged() + } + + private fun columnCountChanged() { invalidateOptionsMenu() directories_grid.adapter?.notifyDataSetChanged() + getRecyclerAdapter()?.dirs?.apply { + measureRecyclerViewContent(this) + } } 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) { + // 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) 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 - Thread { - val mediaFetcher = MediaFetcher(applicationContext) - val getImagesOnly = mIsPickImageIntent || mIsGetImageContentIntent - val getVideosOnly = mIsPickVideoIntent || mIsGetVideoContentIntent - val hiddenString = getString(R.string.hidden) - val albumCovers = config.parseAlbumCovers() - val includedFolders = config.includedFolders - val isSortingAscending = config.directorySorting and SORT_DESCENDING == 0 - val mediumDao = galleryDB.MediumDao() - val directoryDao = galleryDB.DirectoryDao() - val getProperDateTaken = config.directorySorting and SORT_BY_DATE_TAKEN != 0 - val favoritePaths = getFavoritePaths() + val mediaFetcher = MediaFetcher(applicationContext) + val getImagesOnly = mIsPickImageIntent || mIsGetImageContentIntent + val getVideosOnly = mIsPickVideoIntent || mIsGetVideoContentIntent + val hiddenString = getString(R.string.hidden) + val albumCovers = config.parseAlbumCovers() + val includedFolders = config.includedFolders + val isSortingAscending = config.directorySorting and SORT_DESCENDING == 0 + val mediumDao = galleryDB.MediumDao() + val directoryDao = galleryDB.DirectoryDao() + val getProperDateTaken = config.directorySorting and SORT_BY_DATE_TAKEN != 0 + val favoritePaths = getFavoritePaths() - try { - for (directory in dirs) { - val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths) - val newDir = if (curMedia.isEmpty()) { - directory - } else { - createDirectoryFromMedia(directory.path, curMedia, albumCovers, hiddenString, includedFolders, isSortingAscending) - } + try { + for (directory in dirs) { + val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths) + val newDir = if (curMedia.isEmpty()) { + directory + } else { + 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 - if (directory == newDir) { - continue - } + // we are looping through the already displayed folders looking for changes, do not do anything if nothing changed + if (directory == newDir) { + continue + } - directory.apply { - tmb = newDir.tmb - name = newDir.name - mediaCnt = newDir.mediaCnt - modified = newDir.modified - taken = newDir.taken - this@apply.size = newDir.size - types = newDir.types - } + directory.apply { + tmb = newDir.tmb + name = newDir.name + mediaCnt = newDir.mediaCnt + modified = newDir.modified + taken = newDir.taken + this@apply.size = newDir.size + types = newDir.types + } - showSortedDirs(dirs) + showSortedDirs(dirs) - // update directories and media files in the local db, delete invalid items - updateDBDirectory(directory) + // update directories and media files in the local db, delete invalid items + updateDBDirectory(directory) + if (!directory.isRecycleBin()) { mediumDao.insertAll(curMedia) - getCachedMedia(directory.path, getVideosOnly, getImagesOnly) { - it.forEach { - if (!curMedia.contains(it)) { - mediumDao.deleteMediumPath(it.path) + } + getCachedMedia(directory.path, getVideosOnly, getImagesOnly) { + it.forEach { + 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() - dirs.forEach { - foldersToScan.remove(it.path) + if (isPlaceholderVisible) { + isPlaceholderVisible = false + runOnUiThread { + directories_empty_text_label.beGone() + directories_empty_text.beGone() + directories_grid.beVisible() + } } - // 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 - } - - 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) + val newDir = createDirectoryFromMedia(folder, newMedia, albumCovers, hiddenString, includedFolders, isSortingAscending) + dirs.add(newDir) + showSortedDirs(dirs) + directoryDao.insert(newDir) + if (folder != RECYCLE_BIN) { mediumDao.insertAll(newMedia) } + } - mIsGettingDirs = false - mLoadedInitialPhotos = true - checkLastMediaChanged() + mIsGettingDirs = false + mLoadedInitialPhotos = true + checkLastMediaChanged() - runOnUiThread { - directories_refresh_layout.isRefreshing = false - directories_vertical_fastscroller.measureRecyclerView() - checkPlaceholderVisibility(dirs) - } - checkInvalidDirectories(dirs, directoryDao) - }.start() + runOnUiThread { + directories_refresh_layout.isRefreshing = false + checkPlaceholderVisibility(dirs) + } + checkInvalidDirectories(dirs, directoryDao) + + 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) { @@ -703,7 +802,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { sortedDirs = sortedDirs.distinctBy { it.path.getDistinctPath() } as ArrayList 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 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 lastItem = curMedia.last() @@ -733,10 +836,11 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { private fun setupAdapter(dirs: ArrayList) { val currAdapter = directories_grid.adapter + val directories = dirs.clone() as ArrayList if (currAdapter == null) { initZoomListener() 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 if (path != config.tempFolderPath) { itemClicked(path) @@ -746,15 +850,17 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { directories_grid.adapter = this } } else { - (currAdapter as DirectoryAdapter).updateDirs(dirs) + (currAdapter as DirectoryAdapter).updateDirs(directories) } + getRecyclerAdapter()?.dirs?.apply { + measureRecyclerViewContent(this) + } setupScrollDirection() } private fun setupScrollDirection() { val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID - directories_vertical_fastscroller.isHorizontal = false directories_vertical_fastscroller.beGoneIf(allowHorizontalScroll) @@ -776,7 +882,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { private fun checkInvalidDirectories(dirs: ArrayList, directoryDao: DirectoryDao) { val invalidDirs = ArrayList() - dirs.forEach { + dirs.filter { !it.areFavorites() && !it.isRecycleBin() }.forEach { if (!getDoesFilePathExist(it.path)) { invalidDirs.add(it) } 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()) { dirs.removeAll(invalidDirs) showSortedDirs(dirs) @@ -833,12 +953,28 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { }, 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() { getDirectories() } override fun recheckPinnedFolders() { - gotDirectories(movePinnedDirectoriesToFront(getCurrentlyDisplayedDirs())) + Thread { + gotDirectories(movePinnedDirectoriesToFront(getCurrentlyDisplayedDirs())) + }.start() } override fun updateDirectories(directories: ArrayList) { @@ -896,6 +1032,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener { add(Release(177, R.string.release_177)) add(Release(178, R.string.release_178)) 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) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt index 29c5d956b..11d2f36ad 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt @@ -33,18 +33,22 @@ import com.simplemobiletools.commons.views.MyRecyclerView import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.adapters.MediaAdapter import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask +import com.simplemobiletools.gallery.dialogs.ChangeGroupingDialog import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog import com.simplemobiletools.gallery.dialogs.FilterMediaDialog import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.helpers.* +import com.simplemobiletools.gallery.interfaces.MediaOperationsListener 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 java.io.File import java.io.IOException import java.util.* -class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { +class MediaActivity : SimpleActivity(), MediaOperationsListener { private val LAST_MEDIA_CHECK_PERIOD = 3000L private var mPath = "" @@ -72,7 +76,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { private var mStoredPrimaryColor = 0 companion object { - var mMedia = ArrayList() + var mMedia = ArrayList() } override fun onCreate(savedInstanceState: Bundle?) { @@ -121,8 +125,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { } if (mStoredScrollHorizontally != config.scrollHorizontally) { - getMediaAdapter()?.updateScrollHorizontally(config.viewTypeFiles != VIEW_TYPE_LIST || !config.scrollHorizontally) - setupScrollDirection() + mLoadedInitialPhotos = false + media_grid.adapter = null + getMedia() } if (mStoredTextColor != config.textColor) { @@ -188,9 +193,15 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { val isFolderHidden = File(mPath).containsNoMedia() menu.apply { - findItem(R.id.hide_folder).isVisible = !isFolderHidden && !mShowAll - findItem(R.id.unhide_folder).isVisible = isFolderHidden && !mShowAll - findItem(R.id.exclude_folder).isVisible = !mShowAll + findItem(R.id.group).isVisible = !config.scrollHorizontally + + 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.open_camera).isVisible = mShowAll @@ -213,10 +224,14 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { when (item.itemId) { R.id.sort -> showSortingDialog() 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.open_camera -> launchCamera() R.id.folder_view -> switchToFolderView() R.id.change_view_type -> changeViewType() + R.id.group -> showGroupByDialog() R.id.hide_folder -> tryHideFolder() R.id.unhide_folder -> unhideFolder() R.id.exclude_folder -> tryExcludeFolder() @@ -268,9 +283,13 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { return true } + // this triggers on device rotation too, avoid doing anything override fun onMenuItemActionCollapse(item: MenuItem?): Boolean { - mIsSearchOpen = false - media_refresh_layout.isEnabled = config.enablePullToRefresh + if (mIsSearchOpen) { + mIsSearchOpen = false + media_refresh_layout.isEnabled = config.enablePullToRefresh + searchQueryChanged("") + } return true } }) @@ -278,10 +297,12 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { private fun searchQueryChanged(text: String) { Thread { - val filtered = mMedia.filter { it.name.contains(text, true) } as ArrayList - filtered.sortBy { !it.name.startsWith(text, true) } + val filtered = mMedia.filter { it is Medium && it.name.contains(text, true) } as ArrayList + filtered.sortBy { it is Medium && !it.name.startsWith(text, true) } + val grouped = MediaFetcher(applicationContext).groupMedia(filtered as ArrayList, mPath) runOnUiThread { - getMediaAdapter()?.updateMedia(filtered) + getMediaAdapter()?.updateMedia(grouped) + measureRecyclerViewContent(grouped) } }.start() } @@ -290,6 +311,8 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { handlePermission(PERMISSION_WRITE_STORAGE) { if (it) { 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.startsWith(OTG_PATH) -> mPath.trimEnd('/').substringAfterLast('/') else -> getHumanizedFilename(mPath) @@ -316,14 +339,19 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { initZoomListener() val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_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 { setupZoomListener(mZoomListener) media_grid.adapter = this } + setupLayoutManager() } else { (currAdapter as MediaAdapter).updateMedia(mMedia) } + + measureRecyclerViewContent(mMedia) setupScrollDirection() } @@ -335,8 +363,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { media_horizontal_fastscroller.isHorizontal = true media_horizontal_fastscroller.beVisibleIf(allowHorizontalScroll) - val sorting = config.getFileSorting(mPath) - + val sorting = config.getFileSorting(if (mShowAll) SHOW_ALL else mPath) if (allowHorizontalScroll) { media_horizontal_fastscroller.allowBubbleDisplay = config.showInfoBubble 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() { - if (isActivityDestroyed()) + if (isActivityDestroyed()) { return + } mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.postDelayed({ @@ -375,8 +410,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { } private fun showSortingDialog() { - ChangeSortingDialog(this, false, !config.showAll, mPath) { + ChangeSortingDialog(this, false, true, mPath) { mLoadedInitialPhotos = false + media_grid.adapter = null getMedia() } } @@ -385,10 +421,37 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { FilterMediaDialog(this) { mLoadedInitialPhotos = false media_refresh_layout.isRefreshing = true + media_grid.adapter = null 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 + restoreRecycleBinPaths(paths) { + Thread { + galleryDB.DirectoryDao().deleteDirPath(RECYCLE_BIN) + }.start() + finish() + } + } + private fun toggleFilenameVisibility() { config.displayFileNames = !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() { if (config.wasHideFolderTooltipShown) { hideFolder() @@ -454,7 +525,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { private fun deleteDirectoryIfEmpty() { val fileDirItem = FileDirItem(mPath, mPath.getFilenameFromPath()) 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 { gotMedia(it, true) } + startAsyncTask() } } else { media_refresh_layout.isRefreshing = true + startAsyncTask() } mLoadedInitialPhotos = true + } + + private fun startAsyncTask() { mCurrAsyncTask?.stopFetching() mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetImageIntent, mIsGetVideoIntent, mShowAll) { - gotMedia(it) + Thread { + gotMedia(it) + }.start() } mCurrAsyncTask!!.execute() @@ -489,8 +567,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { private fun isDirEmpty(): Boolean { return if (mMedia.size <= 0 && config.filterMedia > 0) { - deleteDirectoryIfEmpty() - deleteDBDirectory() + if (mPath != FAVORITES && mPath != RECYCLE_BIN) { + deleteDirectoryIfEmpty() + deleteDBDirectory() + } finish() true } else { @@ -540,6 +620,62 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { } 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) { + media_grid.onGlobalLayout { + if (config.scrollHorizontally) { + calculateContentWidth(media) + } else { + calculateContentHeight(media) + } + } + } + + private fun calculateContentWidth(media: ArrayList) { + 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) { + 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() { @@ -574,19 +710,19 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { } private fun increaseColumnCount() { - media_vertical_fastscroller.measureRecyclerViewOnRedraw() - media_horizontal_fastscroller.measureRecyclerViewOnRedraw() config.mediaColumnCnt = ++(media_grid.layoutManager as MyGridLayoutManager).spanCount - invalidateOptionsMenu() - media_grid.adapter?.notifyDataSetChanged() + columnCountChanged() } private fun reduceColumnCount() { - media_vertical_fastscroller.measureRecyclerViewOnRedraw() - media_horizontal_fastscroller.measureRecyclerViewOnRedraw() config.mediaColumnCnt = --(media_grid.layoutManager as MyGridLayoutManager).spanCount + columnCountChanged() + } + + private fun columnCountChanged() { invalidateOptionsMenu() media_grid.adapter?.notifyDataSetChanged() + measureRecyclerViewContent(mMedia) } private fun isSetWallpaperIntent() = intent.getBooleanExtra(SET_WALLPAPER_INTENT, false) @@ -642,22 +778,15 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { Intent(this, ViewPagerActivity::class.java).apply { putExtra(PATH, path) putExtra(SHOW_ALL, mShowAll) + putExtra(SHOW_FAVORITES, mPath == FAVORITES) + putExtra(SHOW_RECYCLE_BIN, mPath == RECYCLE_BIN) startActivity(this) } } } } - private fun gotMedia(media: ArrayList, isFromCache: Boolean = false) { - val mediaToInsert = media.clone() as ArrayList - Thread { - mLatestMediaId = getLatestMediaId() - mLatestMediaDateId = getLatestMediaByDateId() - if (!isFromCache) { - galleryDB.MediumDao().insertAll(mediaToInsert) - } - }.start() - + private fun gotMedia(media: ArrayList, isFromCache: Boolean = false) { mIsGettingMedia = false checkLastMediaChanged() mMedia = media @@ -671,25 +800,51 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID media_vertical_fastscroller.beVisibleIf(media_grid.isVisible() && !allowHorizontalScroll) media_horizontal_fastscroller.beVisibleIf(media_grid.isVisible() && allowHorizontalScroll) - setupAdapter() } + + mLatestMediaId = getLatestMediaId() + mLatestMediaDateId = getLatestMediaByDateId() + if (!isFromCache) { + val mediaToInsert = (mMedia.clone() as ArrayList).filter { it is Medium && it.deletedTS == 0L }.map { it as Medium } + galleryDB.MediumDao().insertAll(mediaToInsert) + } } override fun tryDeleteFiles(fileDirItems: 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) { + if (it) { + deleteFilteredFiles(filtered) + } else { + toast(R.string.unknown_error_occurred) + } + } + } else { + deleteFilteredFiles(filtered) + } + } + + private fun deleteFilteredFiles(filtered: ArrayList) { deleteFiles(filtered) { if (!it) { toast(R.string.unknown_error_occurred) return@deleteFiles } - mMedia.removeAll { filtered.map { it.path }.contains(it.path) } + mMedia.removeAll { filtered.map { it.path }.contains((it as? Medium)?.path) } Thread { val mediumDao = galleryDB.MediumDao() + val useRecycleBin = config.useRecycleBin filtered.forEach { - mediumDao.deleteMediumPath(it.path) + if (!useRecycleBin) { + mediumDao.deleteMediumPath(it.path) + } } }.start() diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PanoramaActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PanoramaActivity.kt new file mode 100644 index 000000000..576a5c792 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PanoramaActivity.kt @@ -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) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt index 516b24a16..10c636fea 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt @@ -85,7 +85,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList } } - showSystemUI() + showSystemUI(true) val bundle = Bundle() val file = File(mUri.toString()) val type = when { @@ -95,7 +95,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList 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 bundle.putSerializable(MEDIUM, mMedium) @@ -178,24 +178,32 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList } private fun initBottomActionButtons() { - bottom_favorite.beGone() - bottom_delete.beGone() - - bottom_edit.setOnClickListener { - openEditor(mUri!!.toString()) + arrayListOf(bottom_favorite, bottom_delete, bottom_rotate, bottom_properties, bottom_change_orientation, bottom_slideshow, bottom_show_on_map, bottom_toggle_file_visibility).forEach { + it.beGone() } + 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 { - sharePath(mUri!!.toString()) + if (mUri != null && bottom_actions.alpha == 1f) { + sharePath(mUri!!.toString()) + } } } override fun fragmentClicked() { mIsFullScreen = !mIsFullScreen if (mIsFullScreen) { - hideSystemUI() + hideSystemUI(true) } else { - showSystemUI() + showSystemUI(true) } if (!bottom_actions.isGone()) { diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SettingsActivity.kt index 197d2071a..db12b773a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SettingsActivity.kt @@ -6,16 +6,19 @@ import android.os.Bundle import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.SecurityDialog -import com.simplemobiletools.commons.extensions.beVisibleIf -import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor -import com.simplemobiletools.commons.extensions.handleHiddenFolderPasswordProtection -import com.simplemobiletools.commons.extensions.updateTextColors +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.PROTECTION_FINGERPRINT import com.simplemobiletools.commons.helpers.SHOW_ALL_TABS +import com.simplemobiletools.commons.helpers.sumByLong import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.gallery.R +import com.simplemobiletools.gallery.dialogs.ManageBottomActionsDialog import com.simplemobiletools.gallery.dialogs.ManageExtendedDetailsDialog 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_DEVICE_ROTATION import com.simplemobiletools.gallery.helpers.ROTATE_BY_SYSTEM_SETTING @@ -24,6 +27,7 @@ import java.util.* class SettingsActivity : SimpleActivity() { lateinit var res: Resources + private var mRecycleBinContentSize = 0L override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -68,6 +72,9 @@ class SettingsActivity : SimpleActivity() { setupHideExtendedDetails() setupManageExtendedDetails() setupSkipDeleteConfirmation() + setupManageBottomActions() + setupUseRecycleBin() + setupEmptyRecycleBin() updateTextColors(settings_holder) setupSectionColors() } @@ -75,7 +82,7 @@ class SettingsActivity : SimpleActivity() { private fun setupSectionColors() { val adjustedPrimaryColor = getAdjustedPrimaryColor() 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) } } @@ -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() { settings_show_media_count.isChecked = config.showMediaCount settings_show_media_count_holder.setOnClickListener { @@ -409,4 +408,57 @@ class SettingsActivity : SimpleActivity() { ROTATE_BY_DEVICE_ROTATION -> R.string.screen_rotation_device_rotation 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() + } + } + } + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt index dfd2b21d5..87e029a27 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt @@ -44,6 +44,7 @@ import com.simplemobiletools.gallery.fragments.VideoFragment import com.simplemobiletools.gallery.fragments.ViewPagerFragment import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.models.Medium +import com.simplemobiletools.gallery.models.ThumbnailItem import kotlinx.android.synthetic.main.activity_medium.* import kotlinx.android.synthetic.main.bottom_actions.* import java.io.File @@ -70,7 +71,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View private var mIsOrientationLocked = false private var mStoredReplaceZoomableImages = false - private var mStoredBottomActions = true private var mMediaFiles = ArrayList() private var mFavoritePaths = ArrayList() @@ -83,7 +83,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_medium) - mMediaFiles = MediaActivity.mMedia.clone() as ArrayList + (MediaActivity.mMedia.clone() as ArrayList).filter { it is Medium }.mapTo(mMediaFiles) { it as Medium } handlePermission(PERMISSION_WRITE_STORAGE) { if (it) { @@ -95,7 +95,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } storeStateVariables() - initBottomActions() initFavorites() } @@ -120,10 +119,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View refreshViewPager() } - if (mStoredBottomActions != config.bottomActions) { - initBottomActions() - } - + initBottomActions() supportActionBar?.setBackgroundDrawable(resources.getDrawable(R.drawable.actionbar_gradient_background)) if (config.maxBrightness) { @@ -197,9 +193,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } if (!getDoesFilePathExist(mPath)) { - Thread { - scanPathRecursively(mPath) - }.start() finish() return } @@ -214,9 +207,15 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View 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('/'))) { mDirectory += "/" } @@ -225,7 +224,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View view_pager.onGlobalLayout { if (!isActivityDestroyed()) { if (mMediaFiles.isNotEmpty()) { - gotMedia(mMediaFiles) + gotMedia(mMediaFiles as ArrayList) } } } @@ -251,6 +250,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View checkSystemUI() if (!bottom_actions.isGone()) { 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() { - if (mIsOrientationLocked) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED + if (!mIsOrientationLocked) { + 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 } - } 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) val currentMedium = getCurrentMedium() ?: return true currentMedium.isFavorite = mFavoritePaths.contains(currentMedium.path) + val visibleBottomActions = if (config.bottomActions) config.visibleBottomActions else 0 menu.apply { - findItem(R.id.menu_delete).isVisible = !config.bottomActions - findItem(R.id.menu_share).isVisible = !config.bottomActions - findItem(R.id.menu_edit).isVisible = !config.bottomActions - findItem(R.id.menu_rotate).isVisible = currentMedium.isImage() + findItem(R.id.menu_show_on_map).isVisible = visibleBottomActions and BOTTOM_ACTION_SHOW_ON_MAP == 0 + findItem(R.id.menu_slideshow).isVisible = visibleBottomActions and BOTTOM_ACTION_SLIDESHOW == 0 + findItem(R.id.menu_properties).isVisible = visibleBottomActions and BOTTOM_ACTION_PROPERTIES == 0 + 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_hide).isVisible = !currentMedium.name.startsWith('.') - findItem(R.id.menu_unhide).isVisible = currentMedium.name.startsWith('.') - findItem(R.id.menu_add_to_favorites).isVisible = !currentMedium.isFavorite && !config.bottomActions - findItem(R.id.menu_remove_from_favorites).isVisible = currentMedium.isFavorite && !config.bottomActions - findItem(R.id.menu_lock_orientation).isVisible = mRotationDegrees == 0 - findItem(R.id.menu_lock_orientation).title = getString(if (mIsOrientationLocked) R.string.unlock_orientation else R.string.lock_orientation) + findItem(R.id.menu_hide).isVisible = !currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0 + 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 && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0 + findItem(R.id.menu_remove_from_favorites).isVisible = currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0 + findItem(R.id.menu_restore_file).isVisible = currentMedium.path.startsWith(filesDir.toString()) + 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( if (mRotationDegrees != 0) { MenuItem.SHOW_AS_ACTION_ALWAYS @@ -304,8 +311,8 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View }) } - if (config.bottomActions) { - updateFavoriteIcon(currentMedium) + if (visibleBottomActions != 0) { + updateBottomActionIcons(currentMedium) } return true } @@ -322,7 +329,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View R.id.menu_open_with -> openPath(getCurrentPath(), true) R.id.menu_hide -> toggleFileVisibility(true) 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_rename -> renameFile() 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_rotate_right -> rotateImage(90) 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_remove_from_favorites -> toggleFavorite() - R.id.menu_rotate_one_eighty -> rotateImage(180) - R.id.menu_lock_orientation -> toggleLockOrientation() + R.id.menu_restore_file -> restoreFile() + 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_settings -> launchSettings() else -> return super.onOptionsItemSelected(item) @@ -344,7 +354,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View private fun storeStateVariables() { config.apply { mStoredReplaceZoomableImages = replaceZoomableImages - mStoredBottomActions = bottomActions } } @@ -370,7 +379,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View if (getMediaForSlideshow()) { view_pager.onGlobalLayout { if (!isActivityDestroyed()) { - hideSystemUI() + hideSystemUI(true) mSlideshowInterval = config.slideshowInterval mSlideshowMoveBackwards = config.slideshowMoveBackwards mIsSlideshowActive = true @@ -448,7 +457,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View private fun stopSlideshow() { if (mIsSlideshowActive) { mIsSlideshowActive = false - showSystemUI() + showSystemUI(true) mSlideshowHandler.removeCallbacksAndMessages(null) 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) { val newFileName = it.getFilenameFromPath() supportActionBar?.title = newFileName @@ -527,6 +536,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View getCurrentMedia()[mPos] = this } invalidateOptionsMenu() + callback?.invoke() } } @@ -538,16 +548,22 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View supportInvalidateOptionsMenu() } - private fun toggleLockOrientation() { - mIsOrientationLocked = !mIsOrientationLocked - if (mIsOrientationLocked) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED + private fun toggleOrientation(orientation: Int) { + requestedOrientation = orientation + mIsOrientationLocked = orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + invalidateOptionsMenu() + } + + 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 { - setupRotation() + R.drawable.ic_orientation_auto } - invalidateOptionsMenu() } private fun saveImageAs() { @@ -589,7 +605,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } if (getDoesFilePathExist(newPath)) { - tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath())) + tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath()), false, true) } copyFile(tmpPath, newPath) @@ -618,7 +634,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } catch (e: Exception) { showErrorToast(e) } finally { - tryDeleteFileDirItem(tmpFileDirItem) + tryDeleteFileDirItem(tmpFileDirItem, false, true) } } @@ -761,26 +777,85 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } 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 { toggleFavorite() } + bottom_edit.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_EDIT != 0) bottom_edit.setOnClickListener { openEditor(getCurrentPath()) } + bottom_share.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_SHARE != 0) bottom_share.setOnClickListener { - shareMedium(getCurrentMedium()!!) + shareMediumPath(getCurrentPath()) } + bottom_delete.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_DELETE != 0) bottom_delete.setOnClickListener { 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) { - val icon = if (medium.isFavorite) R.drawable.ic_star_on else R.drawable.ic_star_off - bottom_favorite.setImageResource(icon) + private fun updateBottomActionIcons(medium: Medium?) { + if (medium == null) { + 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() { @@ -797,6 +872,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View }.start() } + private fun restoreFile() { + restoreRecycleBinPath(getCurrentPath()) { + refreshViewPager() + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { if (requestCode == REQUEST_EDIT_IMAGE) { if (resultCode == Activity.RESULT_OK && resultData != null) { @@ -813,6 +894,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } private fun checkDeleteConfirmation() { + if (getCurrentMedium() == null) { + return + } + if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) { deleteConfirmed() } else { @@ -821,16 +906,34 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View } 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 deleteConfirmed() } } private fun deleteConfirmed() { - val path = getCurrentMedia()[mPos].path - tryDeleteFileDirItem(FileDirItem(path, path.getFilenameFromPath())) { - refreshViewPager() + val path = getCurrentMedia().getOrNull(mPos)?.path ?: return + if (getIsPathDirectory(path)) { + 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() } - private fun gotMedia(media: ArrayList) { + private fun gotMedia(thumbnailItems: ArrayList) { + val media = thumbnailItems.filter { it is Medium }.map { it as Medium } as ArrayList if (isDirEmpty(media) || media.hashCode() == mPrevHashcode) { return } @@ -901,6 +1005,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View updatePagerItems(mMediaFiles.toMutableList()) invalidateOptionsMenu() checkOrientation() + initBottomActions() } private fun getPositionInList(items: MutableList): Int { @@ -916,7 +1021,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View private fun deleteDirectoryIfEmpty() { val fileDirItem = FileDirItem(mDirectory, mDirectory.getFilenameFromPath(), getIsPathDirectory(mDirectory)) if (config.deleteEmptyFolders && !fileDirItem.isDownloadsFolder() && fileDirItem.isDirectory && fileDirItem.getProperFileCount(applicationContext, true) == 0) { - tryDeleteFileDirItem(fileDirItem, true) + tryDeleteFileDirItem(fileDirItem, true, true) } scanPathRecursively(mDirectory) @@ -967,10 +1072,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View private fun checkSystemUI() { if (mIsFullScreen) { - hideSystemUI() + hideSystemUI(true) } else { stopSlideshow() - showSystemUI() + showSystemUI(true) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt index b44e0f7f4..6a165bdeb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt @@ -21,6 +21,7 @@ import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog import com.simplemobiletools.gallery.dialogs.PickMediumDialog import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.helpers.* +import com.simplemobiletools.gallery.interfaces.DirectoryOperationsListener import com.simplemobiletools.gallery.models.AlbumCover import com.simplemobiletools.gallery.models.Directory import kotlinx.android.synthetic.main.directory_item_list.view.* @@ -28,7 +29,7 @@ import java.io.File import java.util.* import kotlin.collections.ArrayList -class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList, val listener: DirOperationsListener?, recyclerView: MyRecyclerView, +class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList, val listener: DirectoryOperationsListener?, recyclerView: MyRecyclerView, val isPickIntent: Boolean, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) { @@ -62,7 +63,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList + val view = holder.bindView(dir, true, !isPickIntent) { itemView, adapterPosition -> setupView(itemView, dir) } bindViewHolder(holder, position, view) @@ -71,10 +72,14 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList pinFolders(true) R.id.cab_unpin -> pinFolders(false) 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_exclude -> tryExcludeFolder() R.id.cab_copy_to -> copyMoveTo(true) @@ -104,6 +111,8 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList() - selectedPositions.forEach { paths.add(dirs[it].path) } - PropertiesDialog(activity, paths, config.shouldShowHidden) + PropertiesDialog(activity, getSelectedPaths().filter { it != FAVORITES && it != RECYCLE_BIN }.toMutableList(), config.shouldShowHidden) } } @@ -178,7 +188,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList