commit
ef90bc8fce
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -1,6 +1,37 @@
|
|||
Changelog
|
||||
==========
|
||||
|
||||
Version 3.8.2 *(2018-04-26)*
|
||||
----------------------------
|
||||
|
||||
* Rewrote media caching and new file discovery for better performance
|
||||
* Many OTG file handling improvements
|
||||
|
||||
Version 3.8.1 *(2018-04-24)*
|
||||
----------------------------
|
||||
|
||||
* Rewrote media caching and new file discovery for better performance
|
||||
* Some OTG file handling improvements
|
||||
|
||||
Version 3.8.0 *(2018-04-22)*
|
||||
----------------------------
|
||||
|
||||
* Rewrote media caching for better performance
|
||||
* Cache all media items, not just 80 per folder
|
||||
* Some additional performance and stability improvements
|
||||
|
||||
Version 3.7.3 *(2018-04-15)*
|
||||
----------------------------
|
||||
|
||||
* Show hidden folders when appropriate
|
||||
|
||||
Version 3.7.2 *(2018-04-14)*
|
||||
----------------------------
|
||||
|
||||
* Fix Edit intent handled by other apps
|
||||
* Hide folders containing ".nomedia" file, even if explicitly included
|
||||
* Remove sorting by Date Taken until proper implementation
|
||||
|
||||
Version 3.7.1 *(2018-04-12)*
|
||||
----------------------------
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
|
@ -10,8 +11,8 @@ android {
|
|||
applicationId "com.simplemobiletools.gallery"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
versionCode 171
|
||||
versionName "3.7.1"
|
||||
versionCode 176
|
||||
versionName "3.8.2"
|
||||
multiDexEnabled true
|
||||
setProperty("archivesBaseName", "gallery")
|
||||
}
|
||||
|
@ -46,13 +47,17 @@ ext {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.simplemobiletools:commons:3.18.22'
|
||||
implementation 'com.theartofdev.edmodo:android-image-cropper:2.6.0'
|
||||
implementation 'com.simplemobiletools:commons:3.19.21'
|
||||
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'
|
||||
|
||||
kapt "android.arch.persistence.room:compiler:1.0.0"
|
||||
implementation "android.arch.persistence.room:runtime:1.0.0"
|
||||
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
|
||||
|
||||
//implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.9.0'
|
||||
implementation 'com.github.tibbi:subsampling-scale-image-view:v3.10.0-fork'
|
||||
|
||||
|
|
|
@ -205,14 +205,6 @@
|
|||
android:resource="@xml/provider_paths"/>
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.InstallReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.vending.INSTALL_REFERRER"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.RefreshMediaReceiver"
|
||||
android:exported="true">
|
||||
|
|
|
@ -66,7 +66,7 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
|
|||
if (intent.extras?.containsKey(REAL_FILE_PATH) == true) {
|
||||
val realPath = intent.extras.getString(REAL_FILE_PATH)
|
||||
uri = when {
|
||||
realPath.startsWith(OTG_PATH) -> Uri.parse(realPath)
|
||||
realPath.startsWith(OTG_PATH) -> uri
|
||||
realPath.startsWith("file:/") -> Uri.parse(realPath)
|
||||
else -> Uri.fromFile(File(realPath))
|
||||
}
|
||||
|
@ -188,6 +188,15 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
|
|||
} else if (saveUri.scheme == "content") {
|
||||
var newPath = applicationContext.getRealPathFromURI(saveUri) ?: ""
|
||||
var shouldAppendFilename = true
|
||||
if (newPath.isEmpty()) {
|
||||
val filename = applicationContext.getFilenameFromContentUri(saveUri) ?: ""
|
||||
if (filename.isNotEmpty()) {
|
||||
val path = if (intent.extras?.containsKey(REAL_FILE_PATH) == true) intent.getStringExtra(REAL_FILE_PATH).getParentPath() else internalStoragePath
|
||||
newPath = "$path/$filename"
|
||||
shouldAppendFilename = false
|
||||
}
|
||||
}
|
||||
|
||||
if (newPath.isEmpty()) {
|
||||
newPath = "$internalStoragePath/${getCurrentFormattedDateTime()}.${saveUri.toString().getFilenameExtension()}"
|
||||
shouldAppendFilename = false
|
||||
|
|
|
@ -12,14 +12,12 @@ import android.view.Menu
|
|||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.Toast
|
||||
import com.simplemobiletools.commons.dialogs.CreateNewFolderDialog
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_READ_STORAGE
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
|
||||
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
|
||||
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_TAKEN
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.commons.models.FileDirItem
|
||||
import com.simplemobiletools.commons.models.RadioItem
|
||||
import com.simplemobiletools.commons.models.Release
|
||||
|
@ -28,12 +26,15 @@ import com.simplemobiletools.commons.views.MyRecyclerView
|
|||
import com.simplemobiletools.gallery.BuildConfig
|
||||
import com.simplemobiletools.gallery.R
|
||||
import com.simplemobiletools.gallery.adapters.DirectoryAdapter
|
||||
import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask
|
||||
import com.simplemobiletools.gallery.databases.GalleryDataBase
|
||||
import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog
|
||||
import com.simplemobiletools.gallery.dialogs.FilterMediaDialog
|
||||
import com.simplemobiletools.gallery.extensions.*
|
||||
import com.simplemobiletools.gallery.helpers.*
|
||||
import com.simplemobiletools.gallery.interfaces.DirectoryDao
|
||||
import com.simplemobiletools.gallery.models.AlbumCover
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import java.io.*
|
||||
|
||||
|
@ -43,8 +44,6 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
private val LAST_MEDIA_CHECK_PERIOD = 3000L
|
||||
private val NEW_APP_PACKAGE = "com.simplemobiletools.clock"
|
||||
|
||||
lateinit var mDirs: ArrayList<Directory>
|
||||
|
||||
private var mIsPickImageIntent = false
|
||||
private var mIsPickVideoIntent = false
|
||||
private var mIsGetImageContentIntent = false
|
||||
|
@ -60,7 +59,6 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
private var mLatestMediaDateId = 0L
|
||||
private var mLastMediaHandler = Handler()
|
||||
private var mTempShowHiddenHandler = Handler()
|
||||
private var mCurrAsyncTask: GetDirectoriesAsynctask? = null
|
||||
private var mZoomListener: MyRecyclerView.MyZoomListener? = null
|
||||
|
||||
private var mStoredAnimateGifs = true
|
||||
|
@ -88,7 +86,6 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
|
||||
removeTempFolder()
|
||||
directories_refresh_layout.setOnRefreshListener { getDirectories() }
|
||||
mDirs = ArrayList()
|
||||
storeStateVariables()
|
||||
checkWhatsNewDialog()
|
||||
|
||||
|
@ -104,6 +101,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
config.wasNewAppShown = true
|
||||
NewAppDialog(this, NEW_APP_PACKAGE, "Simple Clock")
|
||||
}*/
|
||||
|
||||
if (!config.wasOTGHandled && hasPermission(PERMISSION_WRITE_STORAGE)) {
|
||||
checkOTGInclusion()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -127,7 +128,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
getRecyclerAdapter()?.updateShowMediaCount(config.showMediaCount)
|
||||
}
|
||||
|
||||
if (mStoredScrollHorizontally != config.scrollHorizontally || mStoredShowInfoBubble != config.showInfoBubble) {
|
||||
if (mStoredScrollHorizontally != config.scrollHorizontally) {
|
||||
getRecyclerAdapter()?.updateScrollHorizontally(config.viewTypeFolders != VIEW_TYPE_LIST && config.scrollHorizontally)
|
||||
setupScrollDirection()
|
||||
}
|
||||
|
@ -144,6 +145,8 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
|
||||
directories_horizontal_fastscroller.updateBubbleColors()
|
||||
directories_vertical_fastscroller.updateBubbleColors()
|
||||
directories_horizontal_fastscroller.allowBubbleDisplay = config.showInfoBubble
|
||||
directories_vertical_fastscroller.allowBubbleDisplay = config.showInfoBubble
|
||||
directories_refresh_layout.isEnabled = config.enablePullToRefresh
|
||||
invalidateOptionsMenu()
|
||||
directories_empty_text_label.setTextColor(config.textColor)
|
||||
|
@ -187,9 +190,8 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
config.temporarilyShowHidden = false
|
||||
mTempShowHiddenHandler.removeCallbacksAndMessages(null)
|
||||
removeTempFolder()
|
||||
|
||||
if (!mDirs.isEmpty()) {
|
||||
mCurrAsyncTask?.stopFetching()
|
||||
if (!isChangingConfigurations) {
|
||||
GalleryDataBase.destroyInstance()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,13 +246,27 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
val newFolder = File(config.tempFolderPath)
|
||||
if (newFolder.exists() && newFolder.isDirectory) {
|
||||
if (newFolder.list()?.isEmpty() == true) {
|
||||
deleteFile(newFolder.toFileDirItem(applicationContext), true)
|
||||
toast(String.format(getString(R.string.deleting_folder), config.tempFolderPath), Toast.LENGTH_LONG)
|
||||
tryDeleteFileDirItem(newFolder.toFileDirItem(applicationContext), true)
|
||||
}
|
||||
}
|
||||
config.tempFolderPath = ""
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOTGInclusion() {
|
||||
Thread {
|
||||
if (hasOTGConnected()) {
|
||||
runOnUiThread {
|
||||
handleOTGPermission {
|
||||
config.addIncludedFolder(OTG_PATH)
|
||||
}
|
||||
}
|
||||
config.wasOTGHandled = true
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun tryLoadGallery() {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
|
@ -274,22 +290,17 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
}
|
||||
|
||||
mIsGettingDirs = true
|
||||
val dirs = getCachedDirectories()
|
||||
if (dirs.isNotEmpty() && !mLoadedInitialPhotos) {
|
||||
gotDirectories(dirs, true)
|
||||
}
|
||||
val getImagesOnly = mIsPickImageIntent || mIsGetImageContentIntent
|
||||
val getVideosOnly = mIsPickVideoIntent || mIsGetVideoContentIntent
|
||||
|
||||
if (!mLoadedInitialPhotos) {
|
||||
directories_refresh_layout.isRefreshing = true
|
||||
getCachedDirectories(getVideosOnly, getImagesOnly) {
|
||||
if (!mLoadedInitialPhotos) {
|
||||
runOnUiThread {
|
||||
directories_refresh_layout.isRefreshing = true
|
||||
}
|
||||
}
|
||||
gotDirectories(addTempFolderIfNeeded(it))
|
||||
}
|
||||
|
||||
mLoadedInitialPhotos = true
|
||||
mCurrAsyncTask?.stopFetching()
|
||||
mCurrAsyncTask = GetDirectoriesAsynctask(applicationContext, mIsPickVideoIntent || mIsGetVideoContentIntent, mIsPickImageIntent || mIsGetImageContentIntent) {
|
||||
mCurrAsyncTask = null
|
||||
gotDirectories(addTempFolderIfNeeded(it), false)
|
||||
}
|
||||
mCurrAsyncTask!!.execute()
|
||||
}
|
||||
|
||||
private fun showSortingDialog() {
|
||||
|
@ -297,13 +308,14 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
if (config.directorySorting and SORT_BY_DATE_MODIFIED > 0 || config.directorySorting and SORT_BY_DATE_TAKEN > 0) {
|
||||
getDirectories()
|
||||
} else {
|
||||
gotDirectories(mDirs, true)
|
||||
gotDirectories(getCurrentlyDisplayedDirs())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFilterMediaDialog() {
|
||||
FilterMediaDialog(this) {
|
||||
mLoadedInitialPhotos = false
|
||||
directories_refresh_layout.isRefreshing = true
|
||||
getDirectories()
|
||||
}
|
||||
|
@ -332,8 +344,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
config.viewTypeFolders = it as Int
|
||||
invalidateOptionsMenu()
|
||||
setupLayoutManager()
|
||||
val dirs = getCurrentlyDisplayedDirs()
|
||||
directories_grid.adapter = null
|
||||
setupAdapter()
|
||||
setupAdapter(dirs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,6 +361,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
}
|
||||
|
||||
private fun toggleTemporarilyShowHidden(show: Boolean) {
|
||||
mLoadedInitialPhotos = false
|
||||
config.temporarilyShowHidden = show
|
||||
getDirectories()
|
||||
invalidateOptionsMenu()
|
||||
|
@ -359,6 +373,13 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
runOnUiThread {
|
||||
refreshItems()
|
||||
}
|
||||
|
||||
Thread {
|
||||
val directoryDao = galleryDB.DirectoryDao()
|
||||
folders.filter { !it.exists() }.forEach {
|
||||
directoryDao.deleteDirPath(it.absolutePath)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -418,7 +439,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) {
|
||||
CreateNewFolderDialog(this, it) {
|
||||
config.tempFolderPath = it
|
||||
gotDirectories(addTempFolderIfNeeded(mDirs), true)
|
||||
gotDirectories(addTempFolderIfNeeded(getCurrentlyDisplayedDirs()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -550,68 +571,162 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun gotDirectories(newDirs: ArrayList<Directory>, isFromCache: Boolean) {
|
||||
/*if (!isFromCache) {
|
||||
Thread {
|
||||
checkFolderContentChange(newDirs)
|
||||
}.start()
|
||||
}*/
|
||||
|
||||
private fun gotDirectories(newDirs: ArrayList<Directory>) {
|
||||
val dirs = getSortedDirectories(newDirs)
|
||||
directories_refresh_layout.isRefreshing = false
|
||||
mIsGettingDirs = false
|
||||
|
||||
directories_empty_text_label.beVisibleIf(dirs.isEmpty() && !isFromCache)
|
||||
directories_empty_text.beVisibleIf(dirs.isEmpty() && !isFromCache)
|
||||
directories_grid.beVisibleIf(directories_empty_text_label.isGone())
|
||||
|
||||
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
|
||||
directories_vertical_fastscroller.beVisibleIf(directories_grid.isVisible() && !allowHorizontalScroll)
|
||||
directories_horizontal_fastscroller.beVisibleIf(directories_grid.isVisible() && allowHorizontalScroll)
|
||||
|
||||
checkLastMediaChanged()
|
||||
mDirs = dirs
|
||||
var isPlaceholderVisible = dirs.isEmpty()
|
||||
|
||||
runOnUiThread {
|
||||
setupAdapter()
|
||||
checkPlaceholderVisibility(dirs)
|
||||
|
||||
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
|
||||
directories_vertical_fastscroller.beVisibleIf(directories_grid.isVisible() && !allowHorizontalScroll)
|
||||
directories_horizontal_fastscroller.beVisibleIf(directories_grid.isVisible() && allowHorizontalScroll)
|
||||
setupAdapter(dirs)
|
||||
}
|
||||
|
||||
if (!isFromCache) {
|
||||
storeDirectories()
|
||||
}
|
||||
}
|
||||
// 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()
|
||||
|
||||
private fun checkFolderContentChange(newDirs: ArrayList<Directory>) {
|
||||
newDirs.forEach {
|
||||
val storedShortDirValue = config.loadFolderMediaShort(it.path)
|
||||
if (storedShortDirValue != it.toString()) {
|
||||
config.saveFolderMediaShort(it.path, it.toString())
|
||||
if (storedShortDirValue.isNotEmpty()) {
|
||||
updateStoredFolderItems(it.path)
|
||||
for (directory in dirs) {
|
||||
val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly)
|
||||
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
|
||||
}
|
||||
|
||||
directory.apply {
|
||||
tmb = newDir.tmb
|
||||
mediaCnt = newDir.mediaCnt
|
||||
modified = newDir.modified
|
||||
taken = newDir.taken
|
||||
this@apply.size = newDir.size
|
||||
types = newDir.types
|
||||
}
|
||||
|
||||
showSortedDirs(dirs)
|
||||
|
||||
// update directories and media files in the local db, delete invalid items
|
||||
updateDBDirectory(directory)
|
||||
mediumDao.insertAll(curMedia)
|
||||
getCachedMedia(directory.path, getVideosOnly, getImagesOnly) {
|
||||
it.forEach {
|
||||
if (!curMedia.contains(it)) {
|
||||
mediumDao.deleteMediumPath(it.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val foldersToScan = mediaFetcher.getFoldersToScan("")
|
||||
dirs.forEach {
|
||||
foldersToScan.remove(it.path)
|
||||
}
|
||||
|
||||
for (folder in foldersToScan) {
|
||||
val newMedia = mediaFetcher.getFilesFrom(folder, getImagesOnly, getVideosOnly)
|
||||
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)
|
||||
mediumDao.insertAll(newMedia)
|
||||
}
|
||||
|
||||
mIsGettingDirs = false
|
||||
mLoadedInitialPhotos = true
|
||||
checkLastMediaChanged()
|
||||
|
||||
runOnUiThread {
|
||||
directories_refresh_layout.isRefreshing = false
|
||||
directories_vertical_fastscroller.measureRecyclerView()
|
||||
checkPlaceholderVisibility(dirs)
|
||||
}
|
||||
checkInvalidDirectories(dirs, directoryDao)
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun checkPlaceholderVisibility(dirs: ArrayList<Directory>) {
|
||||
directories_empty_text_label.beVisibleIf(dirs.isEmpty() && mLoadedInitialPhotos)
|
||||
directories_empty_text.beVisibleIf(dirs.isEmpty() && mLoadedInitialPhotos)
|
||||
directories_grid.beVisibleIf(directories_empty_text_label.isGone())
|
||||
}
|
||||
|
||||
private fun showSortedDirs(dirs: ArrayList<Directory>) {
|
||||
var sortedDirs = getSortedDirectories(dirs).clone() as ArrayList<Directory>
|
||||
sortedDirs = sortedDirs.distinctBy { it.path.toLowerCase() } as ArrayList<Directory>
|
||||
|
||||
runOnUiThread {
|
||||
(directories_grid.adapter as DirectoryAdapter).updateDirs(sortedDirs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeDirectories() {
|
||||
if (!config.temporarilyShowHidden && config.tempFolderPath.isEmpty()) {
|
||||
storeDirectoryItems(mDirs)
|
||||
private fun createDirectoryFromMedia(path: String, curMedia: ArrayList<Medium>, albumCovers: ArrayList<AlbumCover>, hiddenString: String,
|
||||
includedFolders: MutableSet<String>, isSortingAscending: Boolean): Directory {
|
||||
var thumbnail = curMedia.firstOrNull { getDoesFilePathExist(it.path) }?.path ?: ""
|
||||
if (thumbnail.startsWith(OTG_PATH)) {
|
||||
thumbnail = thumbnail.getOTGPublicPath(applicationContext)
|
||||
}
|
||||
|
||||
albumCovers.forEach {
|
||||
if (it.path == path && getDoesFilePathExist(it.tmb)) {
|
||||
thumbnail = it.tmb
|
||||
}
|
||||
}
|
||||
|
||||
val mediaTypes = curMedia.getDirMediaTypes()
|
||||
val dirName = checkAppendingHidden(path, hiddenString, includedFolders)
|
||||
|
||||
val firstItem = curMedia.first()
|
||||
val lastItem = curMedia.last()
|
||||
val lastModified = if (isSortingAscending) Math.min(firstItem.modified, lastItem.modified) else Math.max(firstItem.modified, lastItem.modified)
|
||||
val dateTaken = if (isSortingAscending) Math.min(firstItem.taken, lastItem.taken) else Math.max(firstItem.taken, lastItem.taken)
|
||||
val size = curMedia.sumByLong { it.size }
|
||||
return Directory(null, path, thumbnail, dirName, curMedia.size, lastModified, dateTaken, size, getPathLocation(path), mediaTypes)
|
||||
}
|
||||
|
||||
private fun setupAdapter() {
|
||||
private fun setupAdapter(dirs: ArrayList<Directory>) {
|
||||
val currAdapter = directories_grid.adapter
|
||||
if (currAdapter == null) {
|
||||
initZoomListener()
|
||||
val fastscroller = if (config.scrollHorizontally) directories_horizontal_fastscroller else directories_vertical_fastscroller
|
||||
DirectoryAdapter(this, mDirs, this, directories_grid, isPickIntent(intent) || isGetAnyContentIntent(intent), fastscroller) {
|
||||
itemClicked((it as Directory).path)
|
||||
DirectoryAdapter(this, dirs, this, directories_grid, isPickIntent(intent) || isGetAnyContentIntent(intent), fastscroller) {
|
||||
val path = (it as Directory).path
|
||||
if (path != config.tempFolderPath) {
|
||||
itemClicked(path)
|
||||
}
|
||||
}.apply {
|
||||
setupZoomListener(mZoomListener)
|
||||
directories_grid.adapter = this
|
||||
}
|
||||
} else {
|
||||
(currAdapter as DirectoryAdapter).updateDirs(mDirs)
|
||||
(currAdapter as DirectoryAdapter).updateDirs(dirs)
|
||||
}
|
||||
|
||||
setupScrollDirection()
|
||||
|
@ -639,6 +754,31 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkInvalidDirectories(dirs: ArrayList<Directory>, directoryDao: DirectoryDao) {
|
||||
val invalidDirs = ArrayList<Directory>()
|
||||
dirs.forEach {
|
||||
if (!getDoesFilePathExist(it.path)) {
|
||||
invalidDirs.add(it)
|
||||
} else if (it.path != config.tempFolderPath) {
|
||||
val children = if (it.path.startsWith(OTG_PATH)) getOTGFolderChildrenNames(it.path) else File(it.path).list()?.asList()
|
||||
val hasMediaFile = children?.any { it.isImageVideoGif() } ?: false
|
||||
if (!hasMediaFile) {
|
||||
invalidDirs.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidDirs.isNotEmpty()) {
|
||||
dirs.removeAll(invalidDirs)
|
||||
showSortedDirs(dirs)
|
||||
invalidDirs.forEach {
|
||||
directoryDao.deleteDirPath(it.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentlyDisplayedDirs() = getRecyclerAdapter()?.dirs ?: ArrayList()
|
||||
|
||||
private fun getBubbleTextItem(index: Int) = getRecyclerAdapter()?.dirs?.getOrNull(index)?.getBubbleText() ?: ""
|
||||
|
||||
private fun setupLatestMediaId() {
|
||||
|
@ -678,16 +818,14 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
|
|||
}
|
||||
|
||||
override fun recheckPinnedFolders() {
|
||||
gotDirectories(movePinnedDirectoriesToFront(mDirs), true)
|
||||
gotDirectories(movePinnedDirectoriesToFront(getCurrentlyDisplayedDirs()))
|
||||
}
|
||||
|
||||
override fun updateDirectories(directories: ArrayList<Directory>, refreshList: Boolean) {
|
||||
if (refreshList) {
|
||||
gotDirectories(directories, true)
|
||||
} else {
|
||||
mDirs = directories
|
||||
storeDirectories()
|
||||
}
|
||||
override fun updateDirectories(directories: ArrayList<Directory>) {
|
||||
Thread {
|
||||
storeDirectoryItems(directories)
|
||||
removeInvalidDBDirectories()
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun checkWhatsNewDialog() {
|
||||
|
|
|
@ -42,6 +42,7 @@ import com.simplemobiletools.gallery.models.Medium
|
|||
import kotlinx.android.synthetic.main.activity_media.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
||||
private val LAST_MEDIA_CHECK_PERIOD = 3000L
|
||||
|
@ -94,8 +95,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
}
|
||||
|
||||
storeStateVariables()
|
||||
if (mShowAll)
|
||||
|
||||
if (mShowAll) {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(false)
|
||||
}
|
||||
|
||||
media_empty_text.setOnClickListener {
|
||||
showFilterMediaDialog()
|
||||
|
@ -117,7 +120,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
getMediaAdapter()?.updateCropThumbnails(config.cropThumbnails)
|
||||
}
|
||||
|
||||
if (mStoredScrollHorizontally != config.scrollHorizontally || mStoredShowInfoBubble != config.showInfoBubble) {
|
||||
if (mStoredScrollHorizontally != config.scrollHorizontally) {
|
||||
getMediaAdapter()?.updateScrollHorizontally(config.viewTypeFiles != VIEW_TYPE_LIST || !config.scrollHorizontally)
|
||||
setupScrollDirection()
|
||||
}
|
||||
|
@ -134,6 +137,8 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
|
||||
media_horizontal_fastscroller.updateBubbleColors()
|
||||
media_vertical_fastscroller.updateBubbleColors()
|
||||
media_horizontal_fastscroller.allowBubbleDisplay = config.showInfoBubble
|
||||
media_vertical_fastscroller.allowBubbleDisplay = config.showInfoBubble
|
||||
media_refresh_layout.isEnabled = config.enablePullToRefresh
|
||||
tryloadGallery()
|
||||
invalidateOptionsMenu()
|
||||
|
@ -257,11 +262,13 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, object : MenuItemCompat.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
mIsSearchOpen = true
|
||||
media_refresh_layout.isEnabled = false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
mIsSearchOpen = false
|
||||
media_refresh_layout.isEnabled = config.enablePullToRefresh
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
@ -272,7 +279,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
val filtered = mMedia.filter { it.name.contains(text, true) } as ArrayList
|
||||
filtered.sortBy { !it.name.startsWith(text, true) }
|
||||
runOnUiThread {
|
||||
(media_grid.adapter as? MediaAdapter)?.updateMedia(filtered)
|
||||
getMediaAdapter()?.updateMedia(filtered)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
@ -365,12 +372,14 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
|
||||
private fun showSortingDialog() {
|
||||
ChangeSortingDialog(this, false, !config.showAll, mPath) {
|
||||
mLoadedInitialPhotos = false
|
||||
getMedia()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFilterMediaDialog() {
|
||||
FilterMediaDialog(this) {
|
||||
mLoadedInitialPhotos = false
|
||||
media_refresh_layout.isRefreshing = true
|
||||
getMedia()
|
||||
}
|
||||
|
@ -378,8 +387,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
|
||||
private fun toggleFilenameVisibility() {
|
||||
config.displayFileNames = !config.displayFileNames
|
||||
if (media_grid.adapter != null)
|
||||
getMediaAdapter()?.updateDisplayFilenames(config.displayFileNames)
|
||||
getMediaAdapter()?.updateDisplayFilenames(config.displayFileNames)
|
||||
}
|
||||
|
||||
private fun switchToFolderView() {
|
||||
|
@ -442,7 +450,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) {
|
||||
deleteFile(fileDirItem, true)
|
||||
tryDeleteFileDirItem(fileDirItem, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,16 +460,23 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
}
|
||||
|
||||
mIsGettingMedia = true
|
||||
val media = getCachedMedia(mPath)
|
||||
if (media.isNotEmpty() && !mLoadedInitialPhotos) {
|
||||
gotMedia(media, true)
|
||||
if (!mLoadedInitialPhotos) {
|
||||
getCachedMedia(mPath, mIsGetVideoIntent, mIsGetImageIntent) {
|
||||
if (it.isEmpty()) {
|
||||
runOnUiThread {
|
||||
media_refresh_layout.isRefreshing = true
|
||||
}
|
||||
} else {
|
||||
gotMedia(it, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
media_refresh_layout.isRefreshing = true
|
||||
}
|
||||
|
||||
mLoadedInitialPhotos = true
|
||||
mCurrAsyncTask?.stopFetching()
|
||||
mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetVideoIntent, mIsGetImageIntent, mShowAll) {
|
||||
mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetImageIntent, mIsGetVideoIntent, mShowAll) {
|
||||
gotMedia(it)
|
||||
}
|
||||
mCurrAsyncTask!!.execute()
|
||||
|
@ -470,10 +485,18 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
private fun isDirEmpty(): Boolean {
|
||||
return if (mMedia.size <= 0 && config.filterMedia > 0) {
|
||||
deleteDirectoryIfEmpty()
|
||||
deleteDBDirectory()
|
||||
finish()
|
||||
true
|
||||
} else
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteDBDirectory() {
|
||||
Thread {
|
||||
galleryDB.DirectoryDao().deleteDirPath(mPath)
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun tryToggleTemporarilyShowHidden() {
|
||||
|
@ -487,6 +510,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
}
|
||||
|
||||
private fun toggleTemporarilyShowHidden(show: Boolean) {
|
||||
mLoadedInitialPhotos = false
|
||||
config.temporarilyShowHidden = show
|
||||
getMedia()
|
||||
invalidateOptionsMenu()
|
||||
|
@ -620,61 +644,60 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
|
|||
}
|
||||
|
||||
private fun gotMedia(media: ArrayList<Medium>, isFromCache: Boolean = false) {
|
||||
val mediaToInsert = media.clone() as ArrayList<Medium>
|
||||
Thread {
|
||||
mLatestMediaId = getLatestMediaId()
|
||||
mLatestMediaDateId = getLatestMediaByDateId()
|
||||
if (!isFromCache) {
|
||||
galleryDB.MediumDao().insertAll(mediaToInsert)
|
||||
}
|
||||
}.start()
|
||||
|
||||
mIsGettingMedia = false
|
||||
media_refresh_layout.isRefreshing = false
|
||||
|
||||
media_empty_text_label.beVisibleIf(media.isEmpty() && !isFromCache)
|
||||
media_empty_text.beVisibleIf(media.isEmpty() && !isFromCache)
|
||||
media_grid.beVisibleIf(media_empty_text_label.isGone())
|
||||
|
||||
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)
|
||||
|
||||
checkLastMediaChanged()
|
||||
|
||||
mMedia = media
|
||||
|
||||
runOnUiThread {
|
||||
media_refresh_layout.isRefreshing = false
|
||||
media_empty_text_label.beVisibleIf(media.isEmpty() && !isFromCache)
|
||||
media_empty_text.beVisibleIf(media.isEmpty() && !isFromCache)
|
||||
media_grid.beVisibleIf(media_empty_text_label.isGone())
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
if (!isFromCache) {
|
||||
storeFolder()
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeFolder() {
|
||||
if (!config.temporarilyShowHidden) {
|
||||
Thread {
|
||||
storeFolderItems(mPath, mMedia)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteFiles(fileDirItems: ArrayList<FileDirItem>) {
|
||||
override fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>) {
|
||||
val filtered = fileDirItems.filter { it.path.isImageVideoGif() } as ArrayList
|
||||
deleteFiles(filtered) {
|
||||
if (!it) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
} else if (mMedia.isEmpty()) {
|
||||
return@deleteFiles
|
||||
}
|
||||
|
||||
mMedia.removeAll { filtered.map { it.path }.contains(it.path) }
|
||||
|
||||
Thread {
|
||||
val mediumDao = galleryDB.MediumDao()
|
||||
filtered.forEach {
|
||||
mediumDao.deleteMediumPath(it.path)
|
||||
}
|
||||
}.start()
|
||||
|
||||
if (mMedia.isEmpty()) {
|
||||
deleteDirectoryIfEmpty()
|
||||
deleteDBDirectory()
|
||||
finish()
|
||||
} else {
|
||||
updateStoredDirectories()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun refreshItems() {
|
||||
getMedia()
|
||||
Handler().postDelayed({
|
||||
getMedia()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
override fun selectedPaths(paths: ArrayList<String>) {
|
||||
|
|
|
@ -85,12 +85,12 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
|
|||
val bundle = Bundle()
|
||||
val file = File(mUri.toString())
|
||||
val type = when {
|
||||
file.isImageFast() -> TYPE_IMAGE
|
||||
file.isVideoFast() -> TYPE_VIDEO
|
||||
else -> TYPE_GIF
|
||||
file.isImageFast() -> TYPE_IMAGES
|
||||
file.isVideoFast() -> TYPE_VIDEOS
|
||||
else -> TYPE_GIFS
|
||||
}
|
||||
|
||||
mMedium = Medium(getFilenameFromUri(mUri!!), mUri.toString(), 0, 0, file.length(), type)
|
||||
mMedium = Medium(null, getFilenameFromUri(mUri!!), mUri.toString(), mUri!!.path.getParentPath(), 0, 0, file.length(), type)
|
||||
supportActionBar?.title = mMedium!!.name
|
||||
bundle.putSerializable(MEDIUM, mMedium)
|
||||
|
||||
|
|
|
@ -203,6 +203,11 @@ class SettingsActivity : SimpleActivity() {
|
|||
settings_scroll_horizontally_holder.setOnClickListener {
|
||||
settings_scroll_horizontally.toggle()
|
||||
config.scrollHorizontally = settings_scroll_horizontally.isChecked
|
||||
|
||||
if (config.scrollHorizontally) {
|
||||
config.enablePullToRefresh = false
|
||||
settings_enable_pull_to_refresh.isChecked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,10 @@ import com.simplemobiletools.gallery.fragments.ViewPagerFragment
|
|||
import com.simplemobiletools.gallery.helpers.*
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import kotlinx.android.synthetic.main.activity_medium.*
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, ViewPagerFragment.FragmentListener {
|
||||
|
@ -197,7 +200,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
|||
|
||||
showSystemUI()
|
||||
|
||||
mDirectory = mPath.getParentPath().trimEnd('/')
|
||||
mDirectory = mPath.getParentPath()
|
||||
if (mDirectory.startsWith(OTG_PATH.trimEnd('/'))) {
|
||||
mDirectory += "/"
|
||||
}
|
||||
|
@ -527,11 +530,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
|||
}
|
||||
}
|
||||
|
||||
val newFile = File(newPath)
|
||||
val tmpFile = File(filesDir, ".tmp_${newPath.getFilenameFromPath()}")
|
||||
|
||||
val tmpPath = "$filesDir/.tmp_${newPath.getFilenameFromPath()}"
|
||||
val tmpFileDirItem = FileDirItem(tmpPath, tmpPath.getFilenameFromPath())
|
||||
try {
|
||||
getFileOutputStream(tmpFile.toFileDirItem(applicationContext)) {
|
||||
getFileOutputStream(tmpFileDirItem) {
|
||||
if (it == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return@getFileOutputStream
|
||||
|
@ -539,22 +541,24 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
|||
|
||||
val oldLastModified = getCurrentFile().lastModified()
|
||||
if (oldPath.isJpg()) {
|
||||
copyFile(getCurrentFile(), tmpFile)
|
||||
saveExifRotation(ExifInterface(tmpFile.absolutePath), mRotationDegrees)
|
||||
copyFile(getCurrentPath(), tmpPath)
|
||||
saveExifRotation(ExifInterface(tmpPath), mRotationDegrees)
|
||||
} else {
|
||||
val bitmap = BitmapFactory.decodeFile(oldPath)
|
||||
saveFile(tmpFile, bitmap, it as FileOutputStream)
|
||||
val inputstream = getFileInputStreamSync(oldPath)
|
||||
val bitmap = BitmapFactory.decodeStream(inputstream)
|
||||
saveFile(tmpPath, bitmap, it as FileOutputStream)
|
||||
}
|
||||
|
||||
if (tmpFile.length() > 0 && getDoesFilePathExist(newPath)) {
|
||||
deleteFile(FileDirItem(newPath, newPath.getFilenameFromPath()))
|
||||
if (getDoesFilePathExist(newPath)) {
|
||||
tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath()))
|
||||
}
|
||||
copyFile(tmpFile, newFile)
|
||||
|
||||
copyFile(tmpPath, newPath)
|
||||
scanPath(newPath)
|
||||
toast(R.string.file_saved)
|
||||
|
||||
if (config.keepLastModified) {
|
||||
newFile.setLastModified(oldLastModified)
|
||||
File(newPath).setLastModified(oldLastModified)
|
||||
updateLastModified(newPath, oldLastModified)
|
||||
}
|
||||
|
||||
|
@ -575,41 +579,45 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
|||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
} finally {
|
||||
deleteFile(FileDirItem(tmpFile.absolutePath, tmpFile.absolutePath.getFilenameFromPath()))
|
||||
tryDeleteFileDirItem(tmpFileDirItem)
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
private fun tryRotateByExif(path: String): Boolean {
|
||||
return if (saveImageRotation(path, mRotationDegrees)) {
|
||||
mRotationDegrees = 0
|
||||
invalidateOptionsMenu()
|
||||
toast(R.string.file_saved)
|
||||
true
|
||||
} else {
|
||||
return try {
|
||||
if (saveImageRotation(path, mRotationDegrees)) {
|
||||
mRotationDegrees = 0
|
||||
invalidateOptionsMenu()
|
||||
toast(R.string.file_saved)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyFile(source: File, destination: File) {
|
||||
private fun copyFile(source: String, destination: String) {
|
||||
var inputStream: InputStream? = null
|
||||
var out: OutputStream? = null
|
||||
try {
|
||||
val fileDocument = if (isPathOnSD(destination.absolutePath)) getDocumentFile(destination.parent) else null
|
||||
out = getFileOutputStreamSync(destination.absolutePath, source.getMimeType(), fileDocument)
|
||||
inputStream = FileInputStream(source)
|
||||
inputStream.copyTo(out!!)
|
||||
out = getFileOutputStreamSync(destination, source.getMimeType())
|
||||
inputStream = getFileInputStreamSync(source)
|
||||
inputStream?.copyTo(out!!)
|
||||
} finally {
|
||||
inputStream?.close()
|
||||
out?.close()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveFile(file: File, bitmap: Bitmap, out: FileOutputStream) {
|
||||
private fun saveFile(path: String, bitmap: Bitmap, out: FileOutputStream) {
|
||||
val matrix = Matrix()
|
||||
matrix.postRotate(mRotationDegrees.toFloat())
|
||||
val bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
||||
bmp.compress(file.absolutePath.getCompressionFormat(), 90, out)
|
||||
bmp.compress(path.getCompressionFormat(), 90, out)
|
||||
}
|
||||
|
||||
private fun isShowHiddenFlagNeeded(): Boolean {
|
||||
|
@ -736,7 +744,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
|||
|
||||
private fun deleteConfirmed() {
|
||||
val path = getCurrentMedia()[mPos].path
|
||||
deleteFile(FileDirItem(path, path.getFilenameFromPath())) {
|
||||
tryDeleteFileDirItem(FileDirItem(path, path.getFilenameFromPath())) {
|
||||
refreshViewPager()
|
||||
}
|
||||
}
|
||||
|
@ -752,8 +760,16 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
|||
}
|
||||
|
||||
private fun renameFile() {
|
||||
RenameItemDialog(this, getCurrentPath()) {
|
||||
getCurrentMedia()[mPos].path = it
|
||||
val oldPath = getCurrentPath()
|
||||
RenameItemDialog(this, oldPath) {
|
||||
getCurrentMedia()[mPos].apply {
|
||||
path = it
|
||||
name = it.getFilenameFromPath()
|
||||
}
|
||||
|
||||
Thread {
|
||||
updateDBMediaPath(oldPath, it)
|
||||
}.start()
|
||||
updateActionbarTitle()
|
||||
}
|
||||
}
|
||||
|
@ -814,7 +830,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) {
|
||||
deleteFile(fileDirItem, true)
|
||||
tryDeleteFileDirItem(fileDirItem, true)
|
||||
}
|
||||
|
||||
scanPath(mDirectory)
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.simplemobiletools.commons.dialogs.ConfirmationDialog
|
|||
import com.simplemobiletools.commons.dialogs.PropertiesDialog
|
||||
import com.simplemobiletools.commons.dialogs.RenameItemDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.OTG_PATH
|
||||
import com.simplemobiletools.commons.models.FileDirItem
|
||||
import com.simplemobiletools.commons.views.FastScroller
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
|
@ -19,10 +20,7 @@ import com.simplemobiletools.gallery.R
|
|||
import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
|
||||
import com.simplemobiletools.gallery.dialogs.PickMediumDialog
|
||||
import com.simplemobiletools.gallery.extensions.*
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIF
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGE
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEO
|
||||
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
|
||||
import com.simplemobiletools.gallery.helpers.*
|
||||
import com.simplemobiletools.gallery.models.AlbumCover
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
import kotlinx.android.synthetic.main.directory_item_list.view.*
|
||||
|
@ -155,7 +153,8 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
}
|
||||
|
||||
private fun renameDir() {
|
||||
val sourcePath = dirs[selectedPositions.first()].path
|
||||
val firstDir = dirs[selectedPositions.first()]
|
||||
val sourcePath = firstDir.path
|
||||
val dir = File(sourcePath)
|
||||
if (activity.isAStorageRootFolder(dir.absolutePath)) {
|
||||
activity.toast(R.string.rename_folder_root)
|
||||
|
@ -164,17 +163,13 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
|
||||
RenameItemDialog(activity, dir.absolutePath) {
|
||||
activity.runOnUiThread {
|
||||
if (selectedPositions.isEmpty()) {
|
||||
return@runOnUiThread
|
||||
}
|
||||
|
||||
dirs[selectedPositions.first()].apply {
|
||||
firstDir.apply {
|
||||
path = it
|
||||
name = it.getFilenameFromPath()
|
||||
tmb = File(it, tmb.getFilenameFromPath()).absolutePath
|
||||
}
|
||||
updateDirs(dirs)
|
||||
listener?.updateDirectories(dirs.toList() as ArrayList, false)
|
||||
listener?.updateDirectories(dirs.toList() as ArrayList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,7 +207,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
dirs.forEach {
|
||||
it.name = activity.checkAppendingHidden(it.path, hidden, includedFolders)
|
||||
}
|
||||
listener?.updateDirectories(dirs.toList() as ArrayList, false)
|
||||
listener?.updateDirectories(dirs.toList() as ArrayList)
|
||||
activity.runOnUiThread {
|
||||
updateDirs(dirs)
|
||||
}
|
||||
|
@ -249,7 +244,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
dirs = newDirs
|
||||
finishActMode()
|
||||
fastScroller?.measureRecyclerView()
|
||||
listener?.updateDirectories(newDirs, false)
|
||||
listener?.updateDirectories(newDirs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,9 +279,16 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
|
||||
private fun copyMoveTo(isCopyOperation: Boolean) {
|
||||
val paths = ArrayList<String>()
|
||||
val showHidden = activity.config.shouldShowHidden
|
||||
selectedPositions.forEach {
|
||||
val dir = File(dirs[it].path)
|
||||
paths.addAll(dir.listFiles().filter { !activity.getIsPathDirectory(it.absolutePath) && it.absolutePath.isImageVideoGif() }.map { it.absolutePath })
|
||||
val path = dirs[it].path
|
||||
if (path.startsWith(OTG_PATH)) {
|
||||
paths.addAll(getOTGFilePaths(path, showHidden))
|
||||
} else {
|
||||
File(path).listFiles()?.filter {
|
||||
!activity.getIsPathDirectory(it.absolutePath) && it.isImageVideoGif() && (showHidden || !it.name.startsWith('.'))
|
||||
}?.mapTo(paths, { it.absolutePath })
|
||||
}
|
||||
}
|
||||
|
||||
val fileDirItems = paths.map { FileDirItem(it, it.getFilenameFromPath()) } as ArrayList<FileDirItem>
|
||||
|
@ -297,6 +299,17 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
}
|
||||
}
|
||||
|
||||
private fun getOTGFilePaths(path: String, showHidden: Boolean): ArrayList<String> {
|
||||
val paths = ArrayList<String>()
|
||||
activity.getOTGFolderChildren(path)?.forEach {
|
||||
if (!it.isDirectory && it.name.isImageVideoGif() && (showHidden || !it.name.startsWith('.'))) {
|
||||
val relativePath = it.uri.path.substringAfterLast("${activity.config.OTGPartition}:")
|
||||
paths.add("$OTG_PATH$relativePath")
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun askConfirmDelete() {
|
||||
if (config.skipDeleteConfirmation) {
|
||||
deleteFiles()
|
||||
|
@ -416,14 +429,18 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
dir_path?.text = "${directory.path.substringBeforeLast("/")}/"
|
||||
photo_cnt.text = directory.mediaCnt.toString()
|
||||
val thumbnailType = when {
|
||||
directory.tmb.isImageFast() -> TYPE_IMAGE
|
||||
directory.tmb.isVideoFast() -> TYPE_VIDEO
|
||||
else -> TYPE_GIF
|
||||
directory.tmb.isImageFast() -> TYPE_IMAGES
|
||||
directory.tmb.isVideoFast() -> TYPE_VIDEOS
|
||||
else -> TYPE_GIFS
|
||||
}
|
||||
|
||||
activity.loadImage(thumbnailType, directory.tmb, dir_thumbnail, scrollHorizontally, animateGifs, cropThumbnails)
|
||||
dir_pin.beVisibleIf(pinnedFolders.contains(directory.path))
|
||||
dir_sd_card.beVisibleIf(directory.isOnSDCard)
|
||||
dir_location.beVisibleIf(directory.location != LOCAITON_INTERNAL)
|
||||
if (dir_location.isVisible()) {
|
||||
dir_location.setImageResource(if (directory.location == LOCATION_SD) R.drawable.ic_sd_card else R.drawable.ic_usb)
|
||||
}
|
||||
|
||||
photo_cnt.beVisibleIf(showMediaCount)
|
||||
|
||||
if (isListViewType) {
|
||||
|
@ -431,7 +448,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
dir_path.setTextColor(textColor)
|
||||
photo_cnt.setTextColor(textColor)
|
||||
dir_pin.applyColorFilter(textColor)
|
||||
dir_sd_card.applyColorFilter(textColor)
|
||||
dir_location.applyColorFilter(textColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -443,6 +460,6 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList<Directo
|
|||
|
||||
fun recheckPinnedFolders()
|
||||
|
||||
fun updateDirectories(directories: ArrayList<Directory>, refreshList: Boolean)
|
||||
fun updateDirectories(directories: ArrayList<Directory>)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,12 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
|
|||
}
|
||||
|
||||
private fun renameFile() {
|
||||
RenameItemDialog(activity, getCurrentPath()) {
|
||||
val oldPath = getCurrentPath()
|
||||
RenameItemDialog(activity, oldPath) {
|
||||
Thread {
|
||||
activity.updateDBMediaPath(oldPath, it)
|
||||
}.start()
|
||||
|
||||
activity.runOnUiThread {
|
||||
listener?.refreshItems()
|
||||
finishActMode()
|
||||
|
@ -158,7 +163,6 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
|
|||
|
||||
private fun editFile() {
|
||||
activity.openEditor(getCurrentPath())
|
||||
finishActMode()
|
||||
}
|
||||
|
||||
private fun toggleFileVisibility(hide: Boolean) {
|
||||
|
@ -188,7 +192,8 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
|
|||
val fileDirItems = paths.map { FileDirItem(it, it.getFilenameFromPath()) } as ArrayList
|
||||
activity.tryCopyMoveFilesTo(fileDirItems, isCopyOperation) {
|
||||
config.tempFolderPath = ""
|
||||
activity.applicationContext.updateStoredFolderItems(it)
|
||||
activity.applicationContext.rescanFolderMedia(it)
|
||||
activity.applicationContext.rescanFolderMedia(fileDirItems.first().getParentPath())
|
||||
if (!isCopyOperation) {
|
||||
listener?.refreshItems()
|
||||
}
|
||||
|
@ -236,9 +241,8 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
|
|||
}
|
||||
|
||||
media.removeAll(removeMedia)
|
||||
listener?.deleteFiles(fileDirItems)
|
||||
listener?.tryDeleteFiles(fileDirItems)
|
||||
removeSelectedItems()
|
||||
updateStoredFolderItems()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,14 +252,6 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
|
|||
return selectedMedia
|
||||
}
|
||||
|
||||
private fun updateStoredFolderItems() {
|
||||
Thread {
|
||||
if (media.isNotEmpty()) {
|
||||
activity.applicationContext.storeFolderItems(media.first().path.getParentPath().trimEnd('/'), media as ArrayList<Medium>)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun updateMedia(newMedia: ArrayList<Medium>) {
|
||||
if (newMedia.hashCode() != currentMediaHash) {
|
||||
currentMediaHash = newMedia.hashCode()
|
||||
|
@ -331,7 +327,7 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Medium>,
|
|||
interface MediaOperationsListener {
|
||||
fun refreshItems()
|
||||
|
||||
fun deleteFiles(fileDirItems: ArrayList<FileDirItem>)
|
||||
fun tryDeleteFiles(fileDirItems: ArrayList<FileDirItem>)
|
||||
|
||||
fun selectedPaths(paths: ArrayList<String>)
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
package com.simplemobiletools.gallery.asynctasks
|
||||
|
||||
import android.content.Context
|
||||
import android.os.AsyncTask
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.OTG_PATH
|
||||
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
|
||||
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
|
||||
import com.simplemobiletools.commons.helpers.sumByLong
|
||||
import com.simplemobiletools.gallery.R
|
||||
import com.simplemobiletools.gallery.extensions.checkAppendingHidden
|
||||
import com.simplemobiletools.gallery.extensions.config
|
||||
import com.simplemobiletools.gallery.helpers.MediaFetcher
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import java.io.File
|
||||
|
||||
class GetDirectoriesAsynctask(val context: Context, val isPickVideo: Boolean, val isPickImage: Boolean,
|
||||
val callback: (dirs: ArrayList<Directory>) -> Unit) : AsyncTask<Void, Void, ArrayList<Directory>>() {
|
||||
private val mediaFetcher = MediaFetcher(context)
|
||||
|
||||
override fun doInBackground(vararg params: Void): ArrayList<Directory> {
|
||||
if (!context.hasPermission(PERMISSION_WRITE_STORAGE)) {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
val config = context.config
|
||||
val groupedMedia = mediaFetcher.getMediaByDirectories(isPickVideo, isPickImage)
|
||||
val directories = ArrayList<Directory>()
|
||||
val hidden = context.getString(R.string.hidden)
|
||||
val albumCovers = config.parseAlbumCovers()
|
||||
val hasOTG = context.hasOTGConnected() && context.config.OTGBasePath.isNotEmpty()
|
||||
val includedFolders = config.includedFolders
|
||||
|
||||
for ((path, curMedia) in groupedMedia) {
|
||||
Medium.sorting = config.getFileSorting(path)
|
||||
curMedia.sort()
|
||||
|
||||
val firstItem = curMedia.first()
|
||||
val lastItem = curMedia.last()
|
||||
val parentDir = if (hasOTG && firstItem.path.startsWith(OTG_PATH)) firstItem.path.getParentPath() else File(firstItem.path).parent ?: continue
|
||||
var thumbnail = curMedia.firstOrNull { context.getDoesFilePathExist(it.path) }?.path ?: ""
|
||||
if (thumbnail.startsWith(OTG_PATH)) {
|
||||
thumbnail = thumbnail.getOTGPublicPath(context)
|
||||
}
|
||||
|
||||
albumCovers.forEach {
|
||||
if (it.path == parentDir && context.getDoesFilePathExist(it.tmb)) {
|
||||
thumbnail = it.tmb
|
||||
}
|
||||
}
|
||||
|
||||
val dirName = context.checkAppendingHidden(parentDir, hidden, includedFolders)
|
||||
val lastModified = if (config.directorySorting and SORT_DESCENDING > 0) Math.max(firstItem.modified, lastItem.modified) else Math.min(firstItem.modified, lastItem.modified)
|
||||
val dateTaken = if (config.directorySorting and SORT_DESCENDING > 0) Math.max(firstItem.taken, lastItem.taken) else Math.min(firstItem.taken, lastItem.taken)
|
||||
val size = curMedia.sumByLong { it.size }
|
||||
val directory = Directory(parentDir, thumbnail, dirName, curMedia.size, lastModified, dateTaken, size, context.isPathOnSD(parentDir))
|
||||
directories.add(directory)
|
||||
}
|
||||
|
||||
return directories
|
||||
}
|
||||
|
||||
override fun onPostExecute(dirs: ArrayList<Directory>) {
|
||||
super.onPostExecute(dirs)
|
||||
callback(dirs)
|
||||
}
|
||||
|
||||
fun stopFetching() {
|
||||
mediaFetcher.shouldStop = true
|
||||
cancel(true)
|
||||
}
|
||||
}
|
|
@ -7,18 +7,18 @@ import com.simplemobiletools.gallery.helpers.MediaFetcher
|
|||
import com.simplemobiletools.gallery.models.Medium
|
||||
import java.util.*
|
||||
|
||||
class GetMediaAsynctask(val context: Context, val mPath: String, val isPickVideo: Boolean = false, val isPickImage: Boolean = false,
|
||||
class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage: Boolean = false, val isPickVideo: Boolean = false,
|
||||
val showAll: Boolean, val callback: (media: ArrayList<Medium>) -> Unit) :
|
||||
AsyncTask<Void, Void, ArrayList<Medium>>() {
|
||||
private val mediaFetcher = MediaFetcher(context)
|
||||
|
||||
override fun doInBackground(vararg params: Void): ArrayList<Medium> {
|
||||
return if (showAll) {
|
||||
val mediaMap = mediaFetcher.getMediaByDirectories(isPickVideo, isPickImage)
|
||||
val foldersToScan = mediaFetcher.getFoldersToScan("")
|
||||
val media = ArrayList<Medium>()
|
||||
|
||||
mediaMap.values.forEach {
|
||||
media.addAll(it)
|
||||
for (folder in foldersToScan) {
|
||||
val newMedia = mediaFetcher.getFilesFrom(folder, isPickImage, isPickVideo)
|
||||
media.addAll(newMedia)
|
||||
}
|
||||
|
||||
Medium.sorting = context.config.getFileSorting("")
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package com.simplemobiletools.gallery.databases
|
||||
|
||||
import android.arch.persistence.room.Database
|
||||
import android.arch.persistence.room.Room
|
||||
import android.arch.persistence.room.RoomDatabase
|
||||
import android.content.Context
|
||||
import com.simplemobiletools.gallery.interfaces.DirectoryDao
|
||||
import com.simplemobiletools.gallery.interfaces.MediumDao
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
|
||||
@Database(entities = [(Directory::class), (Medium::class)], version = 2)
|
||||
abstract class GalleryDataBase : RoomDatabase() {
|
||||
|
||||
abstract fun DirectoryDao(): DirectoryDao
|
||||
|
||||
abstract fun MediumDao(): MediumDao
|
||||
|
||||
companion object {
|
||||
private var INSTANCE: GalleryDataBase? = null
|
||||
|
||||
fun getInstance(context: Context): GalleryDataBase {
|
||||
if (INSTANCE == null) {
|
||||
synchronized(GalleryDataBase::class) {
|
||||
INSTANCE = Room.databaseBuilder(context.applicationContext, GalleryDataBase::class.java, "gallery.db")
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
}
|
||||
}
|
||||
return INSTANCE!!
|
||||
}
|
||||
|
||||
fun destroyInstance() {
|
||||
INSTANCE = null
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,8 +29,8 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
|
|||
.setPositiveButton(R.string.ok, this)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.sort_by)
|
||||
}
|
||||
activity.setupDialogStuff(view, this, R.string.sort_by)
|
||||
}
|
||||
|
||||
currSorting = if (isDirectorySorting) config.directorySorting else config.getFileSorting(path)
|
||||
setupSortRadio()
|
||||
|
@ -44,7 +44,6 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
|
|||
currSorting and SORT_BY_PATH != 0 -> sortingRadio.sorting_dialog_radio_path
|
||||
currSorting and SORT_BY_SIZE != 0 -> sortingRadio.sorting_dialog_radio_size
|
||||
currSorting and SORT_BY_DATE_MODIFIED != 0 -> sortingRadio.sorting_dialog_radio_last_modified
|
||||
currSorting and SORT_BY_DATE_TAKEN != 0 -> sortingRadio.sorting_dialog_radio_date_taken
|
||||
else -> sortingRadio.sorting_dialog_radio_name
|
||||
}
|
||||
sortBtn.isChecked = true
|
||||
|
@ -66,8 +65,7 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
|
|||
R.id.sorting_dialog_radio_name -> SORT_BY_NAME
|
||||
R.id.sorting_dialog_radio_path -> SORT_BY_PATH
|
||||
R.id.sorting_dialog_radio_size -> SORT_BY_SIZE
|
||||
R.id.sorting_dialog_radio_last_modified -> SORT_BY_DATE_MODIFIED
|
||||
else -> SORT_BY_DATE_TAKEN
|
||||
else -> SORT_BY_DATE_MODIFIED
|
||||
}
|
||||
|
||||
if (view.sorting_dialog_radio_order.checkedRadioButtonId == R.id.sorting_dialog_radio_descending) {
|
||||
|
|
|
@ -5,9 +5,9 @@ import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
|||
import com.simplemobiletools.commons.extensions.setupDialogStuff
|
||||
import com.simplemobiletools.gallery.R
|
||||
import com.simplemobiletools.gallery.extensions.config
|
||||
import com.simplemobiletools.gallery.helpers.GIFS
|
||||
import com.simplemobiletools.gallery.helpers.IMAGES
|
||||
import com.simplemobiletools.gallery.helpers.VIDEOS
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIFS
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGES
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEOS
|
||||
import kotlinx.android.synthetic.main.dialog_filter_media.view.*
|
||||
|
||||
class FilterMediaDialog(val activity: BaseSimpleActivity, val callback: (result: Int) -> Unit) {
|
||||
|
@ -16,27 +16,27 @@ class FilterMediaDialog(val activity: BaseSimpleActivity, val callback: (result:
|
|||
init {
|
||||
val filterMedia = activity.config.filterMedia
|
||||
view.apply {
|
||||
filter_media_images.isChecked = filterMedia and IMAGES != 0
|
||||
filter_media_videos.isChecked = filterMedia and VIDEOS != 0
|
||||
filter_media_gifs.isChecked = filterMedia and GIFS != 0
|
||||
filter_media_images.isChecked = filterMedia and TYPE_IMAGES != 0
|
||||
filter_media_videos.isChecked = filterMedia and TYPE_VIDEOS != 0
|
||||
filter_media_gifs.isChecked = filterMedia and TYPE_GIFS != 0
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() })
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.filter_media)
|
||||
}
|
||||
activity.setupDialogStuff(view, this, R.string.filter_media)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dialogConfirmed() {
|
||||
var result = 0
|
||||
if (view.filter_media_images.isChecked)
|
||||
result += IMAGES
|
||||
result += TYPE_IMAGES
|
||||
if (view.filter_media_videos.isChecked)
|
||||
result += VIDEOS
|
||||
result += TYPE_VIDEOS
|
||||
if (view.filter_media_gifs.isChecked)
|
||||
result += GIFS
|
||||
result += TYPE_GIFS
|
||||
|
||||
activity.config.filterMedia = result
|
||||
callback(result)
|
||||
|
|
|
@ -11,7 +11,6 @@ import com.simplemobiletools.commons.extensions.toast
|
|||
import com.simplemobiletools.commons.views.MyGridLayoutManager
|
||||
import com.simplemobiletools.gallery.R
|
||||
import com.simplemobiletools.gallery.adapters.DirectoryAdapter
|
||||
import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask
|
||||
import com.simplemobiletools.gallery.extensions.addTempFolderIfNeeded
|
||||
import com.simplemobiletools.gallery.extensions.config
|
||||
import com.simplemobiletools.gallery.extensions.getCachedDirectories
|
||||
|
@ -37,17 +36,16 @@ class PickDirectoryDialog(val activity: BaseSimpleActivity, val sourcePath: Stri
|
|||
.setNegativeButton(R.string.cancel, null)
|
||||
.setNeutralButton(R.string.other_folder, { dialogInterface, i -> showOtherFolder() })
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.select_destination)
|
||||
}
|
||||
activity.setupDialogStuff(view, this, R.string.select_destination)
|
||||
}
|
||||
|
||||
val dirs = activity.getCachedDirectories()
|
||||
if (dirs.isNotEmpty()) {
|
||||
gotDirectories(activity.addTempFolderIfNeeded(dirs))
|
||||
activity.getCachedDirectories {
|
||||
if (it.isNotEmpty()) {
|
||||
activity.runOnUiThread {
|
||||
gotDirectories(activity.addTempFolderIfNeeded(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetDirectoriesAsynctask(activity, false, false) {
|
||||
gotDirectories(activity.addTempFolderIfNeeded(it))
|
||||
}.execute()
|
||||
}
|
||||
|
||||
private fun showOtherFolder() {
|
||||
|
|
|
@ -33,15 +33,19 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
|
|||
.setNegativeButton(R.string.cancel, null)
|
||||
.setNeutralButton(R.string.other_folder, { dialogInterface, i -> showOtherFolder() })
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.select_photo)
|
||||
activity.setupDialogStuff(view, this, R.string.select_photo)
|
||||
}
|
||||
|
||||
activity.getCachedMedia(path) {
|
||||
val media = it.filter { !it.isVideo() } as ArrayList
|
||||
if (media.isNotEmpty()) {
|
||||
activity.runOnUiThread {
|
||||
gotMedia(media)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val media = activity.getCachedMedia(path).filter { !it.isVideo() } as ArrayList
|
||||
if (media.isNotEmpty()) {
|
||||
gotMedia(media)
|
||||
}
|
||||
|
||||
GetMediaAsynctask(activity, path, false, true, false) {
|
||||
GetMediaAsynctask(activity, path, true, false, false) {
|
||||
gotMedia(it)
|
||||
}.execute()
|
||||
}
|
||||
|
|
|
@ -7,15 +7,14 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
|||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.gallery.R
|
||||
import kotlinx.android.synthetic.main.dialog_save_as.view.*
|
||||
import java.io.File
|
||||
|
||||
class SaveAsDialog(val activity: BaseSimpleActivity, val path: String, val appendFilename: Boolean, val callback: (savePath: String) -> Unit) {
|
||||
|
||||
init {
|
||||
var realPath = path.getParentPath().trimEnd('/')
|
||||
var realPath = path.getParentPath()
|
||||
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_save_as, null).apply {
|
||||
save_as_path.text = activity.humanizePath(realPath)
|
||||
save_as_path.text = "${activity.humanizePath(realPath).trimEnd('/')}/"
|
||||
|
||||
val fullName = path.getFilenameFromPath()
|
||||
val dotAt = fullName.lastIndexOf(".")
|
||||
|
@ -60,20 +59,21 @@ class SaveAsDialog(val activity: BaseSimpleActivity, val path: String, val appen
|
|||
return@setOnClickListener
|
||||
}
|
||||
|
||||
val newFile = File(realPath, "$filename.$extension")
|
||||
if (!newFile.name.isAValidFilename()) {
|
||||
val newFilename = "$filename.$extension"
|
||||
val newPath = "${realPath.trimEnd('/')}/$newFilename"
|
||||
if (!newFilename.isAValidFilename()) {
|
||||
activity.toast(R.string.filename_invalid_characters)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
if (newFile.exists()) {
|
||||
val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newFile.name)
|
||||
if (activity.getDoesFilePathExist(newPath)) {
|
||||
val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newFilename)
|
||||
ConfirmationDialog(activity, title) {
|
||||
callback(newFile.absolutePath)
|
||||
callback(newPath)
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
callback(newFile.absolutePath)
|
||||
callback(newPath)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,6 @@ import android.content.Intent
|
|||
import android.provider.MediaStore
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DecodeFormat
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
|
@ -23,13 +15,7 @@ import com.simplemobiletools.gallery.R
|
|||
import com.simplemobiletools.gallery.activities.SimpleActivity
|
||||
import com.simplemobiletools.gallery.dialogs.PickDirectoryDialog
|
||||
import com.simplemobiletools.gallery.helpers.NOMEDIA
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIF
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGE
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEO
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import com.simplemobiletools.gallery.views.MySquareImageView
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
|
@ -148,7 +134,7 @@ fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = nul
|
|||
return
|
||||
}
|
||||
|
||||
deleteFile(file.toFileDirItem(applicationContext)) {
|
||||
tryDeleteFileDirItem(file.toFileDirItem(applicationContext)) {
|
||||
callback?.invoke()
|
||||
}
|
||||
}
|
||||
|
@ -162,36 +148,12 @@ fun BaseSimpleActivity.toggleFileVisibility(oldPath: String, hide: Boolean, call
|
|||
filename.substring(1, filename.length)
|
||||
}
|
||||
|
||||
val newPath = "$path$filename"
|
||||
val newPath = "$path/$filename"
|
||||
renameFile(oldPath, newPath) {
|
||||
callback?.invoke(newPath)
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.loadImage(type: Int, path: String, target: MySquareImageView, horizontalScroll: Boolean, animateGifs: Boolean, cropThumbnails: Boolean) {
|
||||
target.isHorizontalScrolling = horizontalScroll
|
||||
if (type == TYPE_IMAGE || type == TYPE_VIDEO) {
|
||||
if (type == TYPE_IMAGE && path.isPng()) {
|
||||
loadPng(path, target, cropThumbnails)
|
||||
} else {
|
||||
loadJpg(path, target, cropThumbnails)
|
||||
}
|
||||
} else if (type == TYPE_GIF) {
|
||||
try {
|
||||
val gifDrawable = GifDrawable(path)
|
||||
target.setImageDrawable(gifDrawable)
|
||||
if (animateGifs) {
|
||||
gifDrawable.start()
|
||||
} else {
|
||||
gifDrawable.stop()
|
||||
}
|
||||
|
||||
target.scaleType = if (cropThumbnails) ImageView.ScaleType.CENTER_CROP else ImageView.ScaleType.FIT_CENTER
|
||||
} catch (e: Exception) {
|
||||
loadJpg(path, target, cropThumbnails)
|
||||
} catch (e: OutOfMemoryError) {
|
||||
loadJpg(path, target, cropThumbnails)
|
||||
}
|
||||
Thread {
|
||||
updateDBMediaPath(oldPath, newPath)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,49 +169,12 @@ fun BaseSimpleActivity.tryCopyMoveFilesTo(fileDirItems: ArrayList<FileDirItem>,
|
|||
}
|
||||
}
|
||||
|
||||
fun BaseSimpleActivity.addTempFolderIfNeeded(dirs: ArrayList<Directory>): ArrayList<Directory> {
|
||||
val directories = ArrayList<Directory>()
|
||||
val tempFolderPath = config.tempFolderPath
|
||||
if (tempFolderPath.isNotEmpty()) {
|
||||
val newFolder = Directory(tempFolderPath, "", tempFolderPath.getFilenameFromPath(), 0, 0, 0, 0L, isPathOnSD(tempFolderPath))
|
||||
directories.add(newFolder)
|
||||
fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
|
||||
deleteFile(fileDirItem, allowDeleteFolder) {
|
||||
callback?.invoke(it)
|
||||
|
||||
Thread {
|
||||
galleryDB.MediumDao().deleteMediumPath(fileDirItem.path)
|
||||
}.start()
|
||||
}
|
||||
directories.addAll(dirs)
|
||||
return directories
|
||||
}
|
||||
|
||||
fun Activity.loadPng(path: String, target: MySquareImageView, cropThumbnails: Boolean) {
|
||||
val options = RequestOptions()
|
||||
.signature(path.getFileSignature())
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.format(DecodeFormat.PREFER_ARGB_8888)
|
||||
|
||||
val builder = Glide.with(applicationContext)
|
||||
.asBitmap()
|
||||
.load(path)
|
||||
|
||||
if (cropThumbnails) options.centerCrop() else options.fitCenter()
|
||||
builder.apply(options).into(target)
|
||||
}
|
||||
|
||||
fun Activity.loadJpg(path: String, target: MySquareImageView, cropThumbnails: Boolean) {
|
||||
val options = RequestOptions()
|
||||
.signature(path.getFileSignature())
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
|
||||
val builder = Glide.with(applicationContext)
|
||||
.load(path)
|
||||
|
||||
if (cropThumbnails) options.centerCrop() else options.fitCenter()
|
||||
builder.apply(options).transition(DrawableTransitionOptions.withCrossFade()).into(target)
|
||||
}
|
||||
|
||||
fun Activity.getCachedDirectories(): ArrayList<Directory> {
|
||||
val token = object : TypeToken<List<Directory>>() {}.type
|
||||
return Gson().fromJson<ArrayList<Directory>>(config.directories, token) ?: ArrayList(1)
|
||||
}
|
||||
|
||||
fun Activity.getCachedMedia(path: String): ArrayList<Medium> {
|
||||
val token = object : TypeToken<List<Medium>>() {}.type
|
||||
return Gson().fromJson<ArrayList<Medium>>(config.loadFolderMedia(path), token) ?: ArrayList(1)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.simplemobiletools.gallery.extensions
|
||||
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIFS
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGES
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEOS
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
|
||||
fun ArrayList<Medium>.getDirMediaTypes(): Int {
|
||||
var types = 0
|
||||
if (any { it.isImage() }) {
|
||||
types += TYPE_IMAGES
|
||||
}
|
||||
|
||||
if (any { it.isVideo() }) {
|
||||
types += TYPE_VIDEOS
|
||||
}
|
||||
|
||||
if (any { it.isGif() }) {
|
||||
types += TYPE_GIFS
|
||||
}
|
||||
|
||||
return types
|
||||
}
|
|
@ -9,19 +9,24 @@ import android.media.AudioManager
|
|||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.view.WindowManager
|
||||
import com.google.gson.Gson
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DecodeFormat
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.OTG_PATH
|
||||
import com.simplemobiletools.gallery.R
|
||||
import com.simplemobiletools.gallery.activities.SettingsActivity
|
||||
import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask
|
||||
import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
|
||||
import com.simplemobiletools.gallery.helpers.Config
|
||||
import com.simplemobiletools.gallery.helpers.NOMEDIA
|
||||
import com.simplemobiletools.gallery.helpers.SAVE_DIRS_CNT
|
||||
import com.simplemobiletools.gallery.helpers.SAVE_MEDIA_CNT
|
||||
import com.simplemobiletools.gallery.databases.GalleryDataBase
|
||||
import com.simplemobiletools.gallery.helpers.*
|
||||
import com.simplemobiletools.gallery.interfaces.DirectoryDao
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import com.simplemobiletools.gallery.views.MySquareImageView
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import java.io.File
|
||||
|
||||
val Context.portrait get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
@ -65,17 +70,27 @@ fun Context.launchSettings() {
|
|||
|
||||
val Context.config: Config get() = Config.newInstance(applicationContext)
|
||||
|
||||
val Context.galleryDB: GalleryDataBase get() = GalleryDataBase.getInstance(applicationContext)
|
||||
|
||||
fun Context.movePinnedDirectoriesToFront(dirs: ArrayList<Directory>): ArrayList<Directory> {
|
||||
val foundFolders = ArrayList<Directory>()
|
||||
val pinnedFolders = config.pinnedFolders
|
||||
|
||||
dirs.forEach {
|
||||
if (pinnedFolders.contains(it.path))
|
||||
if (pinnedFolders.contains(it.path)) {
|
||||
foundFolders.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
dirs.removeAll(foundFolders)
|
||||
dirs.addAll(0, foundFolders)
|
||||
if (config.tempFolderPath.isNotEmpty()) {
|
||||
val newFolder = dirs.firstOrNull { it.path == config.tempFolderPath }
|
||||
if (newFolder != null) {
|
||||
dirs.remove(newFolder)
|
||||
dirs.add(0, newFolder)
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
|
@ -98,7 +113,6 @@ fun Context.getNoMediaFolders(callback: (folders: ArrayList<String>) -> Unit) {
|
|||
val sortOrder = "${MediaStore.Files.FileColumns.DATE_MODIFIED} DESC"
|
||||
|
||||
var cursor: Cursor? = null
|
||||
|
||||
try {
|
||||
cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)
|
||||
if (cursor?.moveToFirst() == true) {
|
||||
|
@ -118,50 +132,35 @@ fun Context.getNoMediaFolders(callback: (folders: ArrayList<String>) -> Unit) {
|
|||
}.start()
|
||||
}
|
||||
|
||||
fun Context.isPathInMediaStore(path: String): Boolean {
|
||||
if (path.startsWith(OTG_PATH)) {
|
||||
return false
|
||||
}
|
||||
|
||||
val projection = arrayOf(MediaStore.Images.Media.DATE_MODIFIED)
|
||||
val uri = MediaStore.Files.getContentUri("external")
|
||||
val selection = "${MediaStore.MediaColumns.DATA} = ?"
|
||||
val selectionArgs = arrayOf(path)
|
||||
val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
|
||||
cursor?.use {
|
||||
return cursor.moveToFirst()
|
||||
}
|
||||
return false
|
||||
fun Context.rescanFolderMedia(path: String) {
|
||||
Thread {
|
||||
rescanFolderMediaSync(path)
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun Context.updateStoredFolderItems(path: String) {
|
||||
GetMediaAsynctask(this, path, false, false, false) {
|
||||
storeFolderItems(path, it)
|
||||
}.execute()
|
||||
}
|
||||
fun Context.rescanFolderMediaSync(path: String) {
|
||||
getCachedMedia(path) {
|
||||
val cached = it
|
||||
GetMediaAsynctask(applicationContext, path, false, false, false) {
|
||||
Thread {
|
||||
val newMedia = it
|
||||
val mediumDao = galleryDB.MediumDao()
|
||||
mediumDao.insertAll(newMedia)
|
||||
|
||||
fun Context.storeFolderItems(path: String, items: ArrayList<Medium>) {
|
||||
try {
|
||||
val subList = items.subList(0, Math.min(SAVE_MEDIA_CNT, items.size))
|
||||
val json = Gson().toJson(subList)
|
||||
config.saveFolderMedia(path, json)
|
||||
} catch (ignored: Exception) {
|
||||
} catch (ignored: OutOfMemoryError) {
|
||||
cached.forEach {
|
||||
if (!newMedia.contains(it)) {
|
||||
mediumDao.deleteMediumPath(it.path)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}.execute()
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.updateStoredDirectories() {
|
||||
GetDirectoriesAsynctask(this, false, false) {
|
||||
if (!config.temporarilyShowHidden) {
|
||||
storeDirectoryItems(it)
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
fun Context.storeDirectoryItems(items: ArrayList<Directory>) {
|
||||
val subList = items.subList(0, Math.min(SAVE_DIRS_CNT, items.size))
|
||||
config.directories = Gson().toJson(subList)
|
||||
Thread {
|
||||
galleryDB.DirectoryDao().insertAll(items)
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun Context.checkAppendingHidden(path: String, hidden: String, includedFolders: MutableSet<String>): String {
|
||||
|
@ -184,3 +183,150 @@ fun Context.checkAppendingHidden(path: String, hidden: String, includedFolders:
|
|||
dirName
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.loadImage(type: Int, path: String, target: MySquareImageView, horizontalScroll: Boolean, animateGifs: Boolean, cropThumbnails: Boolean) {
|
||||
target.isHorizontalScrolling = horizontalScroll
|
||||
if (type == TYPE_IMAGES || type == TYPE_VIDEOS) {
|
||||
if (type == TYPE_IMAGES && path.isPng()) {
|
||||
loadPng(path, target, cropThumbnails)
|
||||
} else {
|
||||
loadJpg(path, target, cropThumbnails)
|
||||
}
|
||||
} else if (type == TYPE_GIFS) {
|
||||
try {
|
||||
val gifDrawable = GifDrawable(path)
|
||||
target.setImageDrawable(gifDrawable)
|
||||
if (animateGifs) {
|
||||
gifDrawable.start()
|
||||
} else {
|
||||
gifDrawable.stop()
|
||||
}
|
||||
|
||||
target.scaleType = if (cropThumbnails) ImageView.ScaleType.CENTER_CROP else ImageView.ScaleType.FIT_CENTER
|
||||
} catch (e: Exception) {
|
||||
loadJpg(path, target, cropThumbnails)
|
||||
} catch (e: OutOfMemoryError) {
|
||||
loadJpg(path, target, cropThumbnails)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.addTempFolderIfNeeded(dirs: ArrayList<Directory>): ArrayList<Directory> {
|
||||
val directories = ArrayList<Directory>()
|
||||
val tempFolderPath = config.tempFolderPath
|
||||
if (tempFolderPath.isNotEmpty()) {
|
||||
val newFolder = Directory(null, tempFolderPath, "", tempFolderPath.getFilenameFromPath(), 0, 0, 0, 0L, getPathLocation(tempFolderPath), 0)
|
||||
directories.add(newFolder)
|
||||
}
|
||||
directories.addAll(dirs)
|
||||
return directories
|
||||
}
|
||||
|
||||
fun Context.getPathLocation(path: String): Int {
|
||||
return when {
|
||||
isPathOnSD(path) -> LOCATION_SD
|
||||
path.startsWith(OTG_PATH) -> LOCATION_OTG
|
||||
else -> LOCAITON_INTERNAL
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.loadPng(path: String, target: MySquareImageView, cropThumbnails: Boolean) {
|
||||
val options = RequestOptions()
|
||||
.signature(path.getFileSignature())
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.format(DecodeFormat.PREFER_ARGB_8888)
|
||||
|
||||
val builder = Glide.with(applicationContext)
|
||||
.asBitmap()
|
||||
.load(path)
|
||||
|
||||
if (cropThumbnails) options.centerCrop() else options.fitCenter()
|
||||
builder.apply(options).into(target)
|
||||
}
|
||||
|
||||
fun Context.loadJpg(path: String, target: MySquareImageView, cropThumbnails: Boolean) {
|
||||
val options = RequestOptions()
|
||||
.signature(path.getFileSignature())
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
|
||||
val builder = Glide.with(applicationContext)
|
||||
.load(path)
|
||||
|
||||
if (cropThumbnails) options.centerCrop() else options.fitCenter()
|
||||
builder.apply(options).transition(DrawableTransitionOptions.withCrossFade()).into(target)
|
||||
}
|
||||
|
||||
fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, callback: (ArrayList<Directory>) -> Unit) {
|
||||
Thread {
|
||||
val directoryDao = galleryDB.DirectoryDao()
|
||||
val directories = directoryDao.getAll() as ArrayList<Directory>
|
||||
val shouldShowHidden = config.shouldShowHidden
|
||||
val excludedPaths = config.excludedFolders
|
||||
val includedPaths = config.includedFolders
|
||||
var filteredDirectories = directories.filter { it.path.shouldFolderBeVisible(excludedPaths, includedPaths, shouldShowHidden) } as ArrayList<Directory>
|
||||
val filterMedia = config.filterMedia
|
||||
|
||||
filteredDirectories = (when {
|
||||
getVideosOnly -> filteredDirectories.filter { it.types and TYPE_VIDEOS != 0 }
|
||||
getImagesOnly -> filteredDirectories.filter { it.types and TYPE_IMAGES != 0 }
|
||||
else -> filteredDirectories.filter {
|
||||
(filterMedia and TYPE_IMAGES != 0 && it.types and TYPE_IMAGES != 0) ||
|
||||
(filterMedia and TYPE_VIDEOS != 0 && it.types and TYPE_VIDEOS != 0) ||
|
||||
(filterMedia and TYPE_GIFS != 0 && it.types and TYPE_GIFS != 0)
|
||||
}
|
||||
}) as ArrayList<Directory>
|
||||
|
||||
callback(filteredDirectories.distinctBy { it.path.toLowerCase() } as ArrayList<Directory>)
|
||||
|
||||
removeInvalidDBDirectories(directories, directoryDao)
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, callback: (ArrayList<Medium>) -> Unit) {
|
||||
Thread {
|
||||
val mediumDao = galleryDB.MediumDao()
|
||||
val media = (if (path == "/") mediumDao.getAll() else mediumDao.getMediaFromPath(path)) as ArrayList<Medium>
|
||||
val shouldShowHidden = config.shouldShowHidden
|
||||
var filteredMedia = media
|
||||
if (!shouldShowHidden) {
|
||||
filteredMedia = media.filter { !it.name.startsWith('.') } as ArrayList<Medium>
|
||||
}
|
||||
|
||||
val filterMedia = config.filterMedia
|
||||
filteredMedia = (when {
|
||||
getVideosOnly -> filteredMedia.filter { it.type == TYPE_VIDEOS }
|
||||
getImagesOnly -> filteredMedia.filter { it.type == TYPE_IMAGES }
|
||||
else -> filteredMedia.filter {
|
||||
(filterMedia and TYPE_IMAGES != 0 && it.type == TYPE_IMAGES) ||
|
||||
(filterMedia and TYPE_VIDEOS != 0 && it.type == TYPE_VIDEOS) ||
|
||||
(filterMedia and TYPE_GIFS != 0 && it.type == TYPE_GIFS)
|
||||
}
|
||||
}) as ArrayList<Medium>
|
||||
|
||||
callback(filteredMedia)
|
||||
media.filter { !getDoesFilePathExist(it.path) }.forEach {
|
||||
mediumDao.deleteMediumPath(it.path)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun Context.removeInvalidDBDirectories(dirs: ArrayList<Directory>? = null, directoryDao: DirectoryDao = galleryDB.DirectoryDao()) {
|
||||
val dirsToCheck = dirs ?: directoryDao.getAll()
|
||||
dirsToCheck.filter { !getDoesFilePathExist(it.path) && it.path != config.tempFolderPath }.forEach {
|
||||
directoryDao.deleteDirPath(it.path)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.updateDBMediaPath(oldPath: String, newPath: String) {
|
||||
val newFilename = newPath.getFilenameFromPath()
|
||||
val newParentPath = newPath.getParentPath()
|
||||
galleryDB.MediumDao().updateMedium(oldPath, newParentPath, newFilename, newPath)
|
||||
}
|
||||
|
||||
fun Context.updateDBDirectory(directory: Directory) {
|
||||
galleryDB.DirectoryDao().updateDirectory(directory.path, directory.tmb, directory.mediaCnt, directory.modified, directory.taken, directory.size, directory.types)
|
||||
}
|
||||
|
||||
fun Context.getOTGFolderChildren(path: String) = getDocumentFile(path)?.listFiles()
|
||||
|
||||
fun Context.getOTGFolderChildrenNames(path: String) = getOTGFolderChildren(path)?.map { it.name }?.toList()
|
||||
|
|
|
@ -5,9 +5,30 @@ import java.io.File
|
|||
|
||||
fun String.getFileSignature(): ObjectKey {
|
||||
val file = File(this)
|
||||
return ObjectKey("${file.name}${file.lastModified()}")
|
||||
return ObjectKey("${file.absolutePath}${file.lastModified()}")
|
||||
}
|
||||
|
||||
fun String.isThisOrParentIncluded(includedPaths: MutableSet<String>) = includedPaths.any { startsWith(it, true) }
|
||||
|
||||
fun String.isThisOrParentExcluded(excludedPaths: MutableSet<String>) = excludedPaths.any { startsWith(it, true) }
|
||||
|
||||
fun String.shouldFolderBeVisible(excludedPaths: MutableSet<String>, includedPaths: MutableSet<String>, showHidden: Boolean): Boolean {
|
||||
val file = File(this)
|
||||
return if (isEmpty()) {
|
||||
false
|
||||
} else if (!showHidden && file.containsNoMedia()) {
|
||||
false
|
||||
} else if (isThisOrParentIncluded(includedPaths)) {
|
||||
true
|
||||
} else if (isThisOrParentExcluded(excludedPaths)) {
|
||||
false
|
||||
} else if (!showHidden && file.isDirectory && file.canonicalFile == file.absoluteFile) {
|
||||
var containsNoMediaOrDot = file.containsNoMedia() || contains("/.")
|
||||
if (!containsNoMediaOrDot) {
|
||||
containsNoMediaOrDot = file.doesThisOrParentHaveNoMedia()
|
||||
}
|
||||
!containsNoMediaOrDot
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@ class PhotoFragment : ViewPagerFragment() {
|
|||
} else {
|
||||
val options = RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.transform(GlideRotateTransformation(context!!, degrees))
|
||||
.transform(GlideRotateTransformation(degrees))
|
||||
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.google.gson.Gson
|
|||
import com.google.gson.reflect.TypeToken
|
||||
import com.simplemobiletools.commons.helpers.BaseConfig
|
||||
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
|
||||
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_TAKEN
|
||||
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
|
||||
import com.simplemobiletools.gallery.R
|
||||
import com.simplemobiletools.gallery.models.AlbumCover
|
||||
|
@ -17,7 +18,13 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
}
|
||||
|
||||
var directorySorting: Int
|
||||
get() = prefs.getInt(DIRECTORY_SORT_ORDER, SORT_BY_DATE_MODIFIED or SORT_DESCENDING)
|
||||
get(): Int {
|
||||
var sort = prefs.getInt(DIRECTORY_SORT_ORDER, SORT_BY_DATE_MODIFIED or SORT_DESCENDING)
|
||||
if (sort and SORT_BY_DATE_TAKEN != 0) {
|
||||
sort = sort - SORT_BY_DATE_TAKEN + SORT_BY_DATE_MODIFIED
|
||||
}
|
||||
return sort
|
||||
}
|
||||
set(order) = prefs.edit().putInt(DIRECTORY_SORT_ORDER, order).apply()
|
||||
|
||||
fun saveFileSorting(path: String, value: Int) {
|
||||
|
@ -28,7 +35,13 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getFileSorting(path: String) = prefs.getInt(SORT_FOLDER_PREFIX + path.toLowerCase(), sorting)
|
||||
fun getFileSorting(path: String): Int {
|
||||
var sort = prefs.getInt(SORT_FOLDER_PREFIX + path.toLowerCase(), sorting)
|
||||
if (sort and SORT_BY_DATE_TAKEN != 0) {
|
||||
sort = sort - SORT_BY_DATE_TAKEN + SORT_BY_DATE_MODIFIED
|
||||
}
|
||||
return sort
|
||||
}
|
||||
|
||||
fun removeFileSorting(path: String) {
|
||||
prefs.edit().remove(SORT_FOLDER_PREFIX + path.toLowerCase()).apply()
|
||||
|
@ -118,18 +131,6 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
get() = prefs.getStringSet(INCLUDED_FOLDERS, HashSet<String>())
|
||||
set(includedFolders) = prefs.edit().remove(INCLUDED_FOLDERS).putStringSet(INCLUDED_FOLDERS, includedFolders).apply()
|
||||
|
||||
fun saveFolderMedia(path: String, json: String) {
|
||||
prefs.edit().putString(SAVE_FOLDER_PREFIX + path, json).apply()
|
||||
}
|
||||
|
||||
fun loadFolderMedia(path: String) = prefs.getString(SAVE_FOLDER_PREFIX + path, "")
|
||||
|
||||
fun saveFolderMediaShort(path: String, value: String) {
|
||||
prefs.edit().putString(SAVE_FOLDER_SHORT_PREFIX + path, value).apply()
|
||||
}
|
||||
|
||||
fun loadFolderMediaShort(path: String) = prefs.getString(SAVE_FOLDER_SHORT_PREFIX + path, "")
|
||||
|
||||
var autoplayVideos: Boolean
|
||||
get() = prefs.getBoolean(AUTOPLAY_VIDEOS, false)
|
||||
set(autoplay) = prefs.edit().putBoolean(AUTOPLAY_VIDEOS, autoplay).apply()
|
||||
|
@ -163,7 +164,7 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
set(darkBackground) = prefs.edit().putBoolean(DARK_BACKGROUND, darkBackground).apply()
|
||||
|
||||
var filterMedia: Int
|
||||
get() = prefs.getInt(FILTER_MEDIA, IMAGES or VIDEOS or GIFS)
|
||||
get() = prefs.getInt(FILTER_MEDIA, TYPE_IMAGES or TYPE_VIDEOS or TYPE_GIFS)
|
||||
set(filterMedia) = prefs.edit().putInt(FILTER_MEDIA, filterMedia).apply()
|
||||
|
||||
var dirColumnCnt: Int
|
||||
|
@ -226,10 +227,6 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
private fun getDefaultMediaColumnCount() = context.resources.getInteger(if (scrollHorizontally) R.integer.media_columns_horizontal_scroll
|
||||
else R.integer.media_columns_vertical_scroll)
|
||||
|
||||
var directories: String
|
||||
get() = prefs.getString(DIRECTORIES, "")
|
||||
set(directories) = prefs.edit().putString(DIRECTORIES, directories).apply()
|
||||
|
||||
var albumCovers: String
|
||||
get() = prefs.getString(ALBUM_COVERS, "")
|
||||
set(albumCovers) = prefs.edit().putString(ALBUM_COVERS, albumCovers).apply()
|
||||
|
@ -328,6 +325,10 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
set(wasNewAppShown) = prefs.edit().putBoolean(WAS_NEW_APP_SHOWN, wasNewAppShown).apply()
|
||||
|
||||
var lastFilepickerPath: String
|
||||
get() = prefs.getString(TEMP_FOLDER_PATH, "")
|
||||
set(tempFolderPath) = prefs.edit().putString(TEMP_FOLDER_PATH, tempFolderPath).apply()
|
||||
get() = prefs.getString(LAST_FILEPICKER_PATH, "")
|
||||
set(lastFilepickerPath) = prefs.edit().putString(LAST_FILEPICKER_PATH, lastFilepickerPath).apply()
|
||||
|
||||
var wasOTGHandled: Boolean
|
||||
get() = prefs.getBoolean(WAS_OTG_HANDLED, false)
|
||||
set(wasOTGHandled) = prefs.edit().putBoolean(WAS_OTG_HANDLED, wasOTGHandled).apply()
|
||||
}
|
||||
|
|
|
@ -25,8 +25,6 @@ const val MEDIA_LANDSCAPE_COLUMN_CNT = "media_landscape_column_cnt"
|
|||
const val MEDIA_HORIZONTAL_COLUMN_CNT = "media_horizontal_column_cnt"
|
||||
const val MEDIA_LANDSCAPE_HORIZONTAL_COLUMN_CNT = "media_landscape_horizontal_column_cnt"
|
||||
const val SHOW_ALL = "show_all" // display images and videos from all folders together
|
||||
const val SAVE_FOLDER_PREFIX = "folder2_"
|
||||
const val SAVE_FOLDER_SHORT_PREFIX = "folder_short_"
|
||||
const val HIDE_FOLDER_TOOLTIP_SHOWN = "hide_folder_tooltip_shown"
|
||||
const val EXCLUDED_FOLDERS = "excluded_folders"
|
||||
const val INCLUDED_FOLDERS = "included_folders"
|
||||
|
@ -49,6 +47,7 @@ const val REPLACE_ZOOMABLE_IMAGES = "replace_zoomable_images"
|
|||
const val DO_EXTRA_CHECK = "do_extra_check"
|
||||
const val WAS_NEW_APP_SHOWN = "was_new_app_shown_clock"
|
||||
const val LAST_FILEPICKER_PATH = "last_filepicker_path"
|
||||
const val WAS_OTG_HANDLED = "was_otg_handled"
|
||||
|
||||
// slideshow
|
||||
const val SLIDESHOW_INTERVAL = "slideshow_interval"
|
||||
|
@ -67,8 +66,6 @@ const val MAX_COLUMN_COUNT = 20
|
|||
const val SHOW_TEMP_HIDDEN_DURATION = 600000L
|
||||
const val CLICK_MAX_DURATION = 150
|
||||
const val DRAG_THRESHOLD = 8
|
||||
const val SAVE_DIRS_CNT = 60
|
||||
const val SAVE_MEDIA_CNT = 80
|
||||
|
||||
const val DIRECTORY = "directory"
|
||||
const val MEDIUM = "medium"
|
||||
|
@ -77,7 +74,6 @@ const val GET_IMAGE_INTENT = "get_image_intent"
|
|||
const val GET_VIDEO_INTENT = "get_video_intent"
|
||||
const val GET_ANY_INTENT = "get_any_intent"
|
||||
const val SET_WALLPAPER_INTENT = "set_wallpaper_intent"
|
||||
const val DIRECTORIES = "directories2"
|
||||
const val IS_VIEW_INTENT = "is_view_intent"
|
||||
const val PICKED_PATHS = "picked_paths"
|
||||
|
||||
|
@ -86,11 +82,6 @@ const val ROTATE_BY_SYSTEM_SETTING = 0
|
|||
const val ROTATE_BY_DEVICE_ROTATION = 1
|
||||
const val ROTATE_BY_ASPECT_RATIO = 2
|
||||
|
||||
// filter media
|
||||
const val IMAGES = 1
|
||||
const val VIDEOS = 2
|
||||
const val GIFS = 4
|
||||
|
||||
// view types
|
||||
const val VIEW_TYPE_GRID = 1
|
||||
const val VIEW_TYPE_LIST = 2
|
||||
|
@ -109,6 +100,10 @@ const val EXT_ARTIST = 512
|
|||
const val EXT_ALBUM = 1024
|
||||
|
||||
// media types
|
||||
const val TYPE_IMAGE = 1
|
||||
const val TYPE_VIDEO = 2
|
||||
const val TYPE_GIF = 3
|
||||
const val TYPE_IMAGES = 1
|
||||
const val TYPE_VIDEOS = 2
|
||||
const val TYPE_GIFS = 4
|
||||
|
||||
const val LOCAITON_INTERNAL = 1
|
||||
const val LOCATION_SD = 2
|
||||
const val LOCATION_OTG = 3
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
package com.simplemobiletools.gallery.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.ExifInterface
|
||||
import android.net.Uri
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
|
||||
import com.simplemobiletools.gallery.activities.ViewPagerActivity
|
||||
import com.simplemobiletools.gallery.extensions.getFileSignature
|
||||
|
||||
class GlideDecoder : ImageDecoder {
|
||||
override fun decode(context: Context, uri: Uri): Bitmap {
|
||||
val exif = android.media.ExifInterface(uri.path)
|
||||
val orientation = exif.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, android.media.ExifInterface.ORIENTATION_NORMAL)
|
||||
|
||||
val targetWidth = if (ViewPagerActivity.screenWidth == 0) Target.SIZE_ORIGINAL else ViewPagerActivity.screenWidth
|
||||
val targetHeight = if (ViewPagerActivity.screenHeight == 0) Target.SIZE_ORIGINAL else ViewPagerActivity.screenHeight
|
||||
|
||||
ViewPagerActivity.wasDecodedByGlide = true
|
||||
val options = RequestOptions()
|
||||
.signature(uri.path.getFileSignature())
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.override(targetWidth, targetHeight)
|
||||
|
||||
val degrees = getRotationDegrees(orientation)
|
||||
if (degrees != 0) {
|
||||
options.transform(GlideRotateTransformation(context, getRotationDegrees(orientation)))
|
||||
}
|
||||
|
||||
val drawable = Glide.with(context)
|
||||
.load(uri)
|
||||
.apply(options)
|
||||
.submit()
|
||||
.get()
|
||||
|
||||
return drawableToBitmap(drawable)
|
||||
}
|
||||
|
||||
private fun drawableToBitmap(drawable: Drawable): Bitmap {
|
||||
if (drawable is BitmapDrawable && drawable.bitmap != null) {
|
||||
return drawable.bitmap
|
||||
}
|
||||
|
||||
val bitmap = if (drawable.intrinsicWidth <= 0 || drawable.intrinsicHeight <= 0) {
|
||||
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||
} else {
|
||||
Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
|
||||
val canvas = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
// rotating backwards intentionally, as SubsamplingScaleImageView will rotate it properly at displaying
|
||||
private fun getRotationDegrees(orientation: Int) = when (orientation) {
|
||||
ExifInterface.ORIENTATION_ROTATE_270 -> 90
|
||||
ExifInterface.ORIENTATION_ROTATE_180 -> 180
|
||||
ExifInterface.ORIENTATION_ROTATE_90 -> 270
|
||||
else -> 0
|
||||
}
|
||||
}
|
|
@ -1,13 +1,12 @@
|
|||
package com.simplemobiletools.gallery.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Matrix
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||
import java.security.MessageDigest
|
||||
|
||||
class GlideRotateTransformation(context: Context, val rotateRotationAngle: Int) : BitmapTransformation(context) {
|
||||
class GlideRotateTransformation(val rotateRotationAngle: Int) : BitmapTransformation() {
|
||||
override fun transform(pool: BitmapPool, bitmap: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
|
||||
if (rotateRotationAngle % 360 == 0)
|
||||
return bitmap
|
||||
|
|
|
@ -8,45 +8,48 @@ import com.simplemobiletools.commons.extensions.*
|
|||
import com.simplemobiletools.commons.helpers.OTG_PATH
|
||||
import com.simplemobiletools.commons.helpers.photoExtensions
|
||||
import com.simplemobiletools.commons.helpers.videoExtensions
|
||||
import com.simplemobiletools.gallery.extensions.*
|
||||
import com.simplemobiletools.gallery.extensions.config
|
||||
import com.simplemobiletools.gallery.extensions.getOTGFolderChildren
|
||||
import com.simplemobiletools.gallery.extensions.shouldFolderBeVisible
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import java.io.File
|
||||
import java.util.LinkedHashMap
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.collections.set
|
||||
|
||||
class MediaFetcher(val context: Context) {
|
||||
var shouldStop = false
|
||||
|
||||
fun getMediaByDirectories(isPickVideo: Boolean, isPickImage: Boolean): HashMap<String, ArrayList<Medium>> {
|
||||
val media = getFilesFrom("", isPickImage, isPickVideo)
|
||||
return groupDirectories(media)
|
||||
}
|
||||
|
||||
fun getFilesFrom(curPath: String, isPickImage: Boolean, isPickVideo: Boolean): ArrayList<Medium> {
|
||||
val filterMedia = context.config.filterMedia
|
||||
if (filterMedia == 0) {
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
val curMedia = ArrayList<Medium>()
|
||||
if (curPath.startsWith(OTG_PATH)) {
|
||||
val curMedia = ArrayList<Medium>()
|
||||
getMediaOnOTG(curPath, curMedia, isPickImage, isPickVideo, filterMedia)
|
||||
return curMedia
|
||||
val newMedia = getMediaOnOTG(curPath, isPickImage, isPickVideo, filterMedia)
|
||||
curMedia.addAll(newMedia)
|
||||
} else {
|
||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||
val uri = MediaStore.Files.getContentUri("external")
|
||||
val newMedia = fetchFolderContent(curPath, isPickImage, isPickVideo, filterMedia)
|
||||
curMedia.addAll(newMedia)
|
||||
}
|
||||
|
||||
val selection = "${getSelectionQuery(curPath, filterMedia)} ${MediaStore.Images.ImageColumns.BUCKET_ID} IS NOT NULL) GROUP BY (${MediaStore.Images.ImageColumns.BUCKET_ID}"
|
||||
val selectionArgs = getSelectionArgsQuery(curPath, filterMedia).toTypedArray()
|
||||
Medium.sorting = context.config.getFileSorting(curPath)
|
||||
curMedia.sort()
|
||||
return curMedia
|
||||
}
|
||||
|
||||
return try {
|
||||
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
parseCursor(context, cursor, isPickImage, isPickVideo, curPath, filterMedia)
|
||||
} catch (e: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
fun getFoldersToScan(path: String): ArrayList<String> {
|
||||
val filterMedia = context.config.filterMedia
|
||||
val projection = arrayOf(MediaStore.Images.Media.DATA)
|
||||
val uri = MediaStore.Files.getContentUri("external")
|
||||
|
||||
val selection = "${getSelectionQuery(path, filterMedia)} ${MediaStore.Images.ImageColumns.BUCKET_ID} IS NOT NULL) GROUP BY (${MediaStore.Images.ImageColumns.BUCKET_ID}"
|
||||
val selectionArgs = getSelectionArgsQuery(path, filterMedia).toTypedArray()
|
||||
|
||||
return try {
|
||||
val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
parseCursor(cursor, path)
|
||||
} catch (e: Exception) {
|
||||
ArrayList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,19 +60,19 @@ class MediaFetcher(val context: Context) {
|
|||
}
|
||||
|
||||
query.append("(")
|
||||
if (filterMedia and IMAGES != 0) {
|
||||
if (filterMedia and TYPE_IMAGES != 0) {
|
||||
photoExtensions.forEach {
|
||||
query.append("${MediaStore.Images.Media.DATA} LIKE ? OR ")
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMedia and VIDEOS != 0) {
|
||||
if (filterMedia and TYPE_VIDEOS != 0) {
|
||||
videoExtensions.forEach {
|
||||
query.append("${MediaStore.Images.Media.DATA} LIKE ? OR ")
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMedia and GIFS != 0) {
|
||||
if (filterMedia and TYPE_GIFS != 0) {
|
||||
query.append("${MediaStore.Images.Media.DATA} LIKE ?")
|
||||
}
|
||||
|
||||
|
@ -85,29 +88,29 @@ class MediaFetcher(val context: Context) {
|
|||
args.add("$path/%/%")
|
||||
}
|
||||
|
||||
if (filterMedia and IMAGES != 0) {
|
||||
if (filterMedia and TYPE_IMAGES != 0) {
|
||||
photoExtensions.forEach {
|
||||
args.add("%$it")
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMedia and VIDEOS != 0) {
|
||||
if (filterMedia and TYPE_VIDEOS != 0) {
|
||||
videoExtensions.forEach {
|
||||
args.add("%$it")
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMedia and GIFS != 0) {
|
||||
if (filterMedia and TYPE_GIFS != 0) {
|
||||
args.add("%.gif")
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
private fun parseCursor(context: Context, cursor: Cursor, isPickImage: Boolean, isPickVideo: Boolean, curPath: String, filterMedia: Int): ArrayList<Medium> {
|
||||
private fun parseCursor(cursor: Cursor, curPath: String): ArrayList<String> {
|
||||
val config = context.config
|
||||
val includedFolders = config.includedFolders
|
||||
val foldersToScan = HashSet<String>()
|
||||
var foldersToScan = ArrayList<String>()
|
||||
|
||||
cursor.use {
|
||||
if (cursor.moveToFirst()) {
|
||||
|
@ -129,81 +132,47 @@ class MediaFetcher(val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
val curMedia = ArrayList<Medium>()
|
||||
val showHidden = config.shouldShowHidden
|
||||
val excludedFolders = config.excludedFolders
|
||||
foldersToScan.filter { shouldFolderBeVisible(it, excludedFolders, includedFolders, showHidden) }.toList().forEach {
|
||||
fetchFolderContent(it, curMedia, isPickImage, isPickVideo, filterMedia)
|
||||
foldersToScan = foldersToScan.filter { it.shouldFolderBeVisible(excludedFolders, includedFolders, showHidden) } as ArrayList<String>
|
||||
if (config.isThirdPartyIntent && curPath.isNotEmpty()) {
|
||||
foldersToScan.add(curPath)
|
||||
}
|
||||
|
||||
if (config.isThirdPartyIntent && curPath.isNotEmpty() && curMedia.isEmpty()) {
|
||||
getMediaInFolder(curPath, curMedia, isPickImage, isPickVideo, filterMedia)
|
||||
}
|
||||
|
||||
Medium.sorting = config.getFileSorting(curPath)
|
||||
curMedia.sort()
|
||||
|
||||
return curMedia
|
||||
return foldersToScan.distinctBy { it.toLowerCase() } as ArrayList<String>
|
||||
}
|
||||
|
||||
private fun addFolder(curFolders: HashSet<String>, folder: String) {
|
||||
private fun addFolder(curFolders: ArrayList<String>, folder: String) {
|
||||
curFolders.add(folder)
|
||||
val files = File(folder).listFiles() ?: return
|
||||
for (file in files) {
|
||||
if (file.isDirectory) {
|
||||
addFolder(curFolders, file.absolutePath)
|
||||
if (folder.startsWith(OTG_PATH)) {
|
||||
val files = context.getOTGFolderChildren(folder) ?: return
|
||||
for (file in files) {
|
||||
if (file.isDirectory) {
|
||||
val relativePath = file.uri.path.substringAfterLast("${context.config.OTGPartition}:")
|
||||
addFolder(curFolders, "$OTG_PATH$relativePath")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchFolderContent(path: String, curMedia: ArrayList<Medium>, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int) {
|
||||
if (path.startsWith(OTG_PATH)) {
|
||||
getMediaOnOTG(path, curMedia, isPickImage, isPickVideo, filterMedia)
|
||||
} else {
|
||||
getMediaInFolder(path, curMedia, isPickImage, isPickVideo, filterMedia)
|
||||
val files = File(folder).listFiles() ?: return
|
||||
for (file in files) {
|
||||
if (file.isDirectory) {
|
||||
addFolder(curFolders, file.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun groupDirectories(media: ArrayList<Medium>): HashMap<String, ArrayList<Medium>> {
|
||||
val directories = LinkedHashMap<String, ArrayList<Medium>>()
|
||||
val hasOTG = context.hasOTGConnected() && context.config.OTGBasePath.isNotEmpty()
|
||||
for (medium in media) {
|
||||
if (shouldStop) {
|
||||
break
|
||||
}
|
||||
|
||||
val parentDir = (if (hasOTG && medium.path.startsWith(OTG_PATH)) medium.path.getParentPath().toLowerCase() else File(medium.path).parent?.toLowerCase())
|
||||
?: continue
|
||||
if (directories.containsKey(parentDir)) {
|
||||
directories[parentDir]!!.add(medium)
|
||||
} else {
|
||||
directories[parentDir] = arrayListOf(medium)
|
||||
}
|
||||
}
|
||||
return directories
|
||||
}
|
||||
|
||||
private fun shouldFolderBeVisible(path: String, excludedPaths: MutableSet<String>, includedPaths: MutableSet<String>, showHidden: Boolean): Boolean {
|
||||
val file = File(path)
|
||||
return if (path.isEmpty()) {
|
||||
false
|
||||
} else if (path.isThisOrParentIncluded(includedPaths)) {
|
||||
true
|
||||
} else if (path.isThisOrParentExcluded(excludedPaths)) {
|
||||
false
|
||||
} else if (!showHidden && file.isDirectory && file.canonicalFile == file.absoluteFile) {
|
||||
var containsNoMediaOrDot = file.containsNoMedia() || path.contains("/.")
|
||||
if (!containsNoMediaOrDot) {
|
||||
containsNoMediaOrDot = file.doesThisOrParentHaveNoMedia()
|
||||
}
|
||||
!containsNoMediaOrDot
|
||||
private fun fetchFolderContent(path: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int): ArrayList<Medium> {
|
||||
return if (path.startsWith(OTG_PATH)) {
|
||||
getMediaOnOTG(path, isPickImage, isPickVideo, filterMedia)
|
||||
} else {
|
||||
true
|
||||
getMediaInFolder(path, isPickImage, isPickVideo, filterMedia)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMediaInFolder(folder: String, curMedia: ArrayList<Medium>, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int) {
|
||||
val files = File(folder).listFiles() ?: return
|
||||
private fun getMediaInFolder(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int): ArrayList<Medium> {
|
||||
val media = ArrayList<Medium>()
|
||||
val files = File(folder).listFiles() ?: return media
|
||||
val doExtraCheck = context.config.doExtraCheck
|
||||
val showHidden = context.config.shouldShowHidden
|
||||
|
||||
|
@ -220,13 +189,13 @@ class MediaFetcher(val context: Context) {
|
|||
if (!isImage && !isVideo && !isGif)
|
||||
continue
|
||||
|
||||
if (isVideo && (isPickImage || filterMedia and VIDEOS == 0))
|
||||
if (isVideo && (isPickImage || filterMedia and TYPE_VIDEOS == 0))
|
||||
continue
|
||||
|
||||
if (isImage && (isPickVideo || filterMedia and IMAGES == 0))
|
||||
if (isImage && (isPickVideo || filterMedia and TYPE_IMAGES == 0))
|
||||
continue
|
||||
|
||||
if (isGif && filterMedia and GIFS == 0)
|
||||
if (isGif && filterMedia and TYPE_GIFS == 0)
|
||||
continue
|
||||
|
||||
if (!showHidden && filename.startsWith('.'))
|
||||
|
@ -240,18 +209,20 @@ class MediaFetcher(val context: Context) {
|
|||
val dateModified = file.lastModified()
|
||||
|
||||
val type = when {
|
||||
isImage -> TYPE_IMAGE
|
||||
isVideo -> TYPE_VIDEO
|
||||
else -> TYPE_GIF
|
||||
isImage -> TYPE_IMAGES
|
||||
isVideo -> TYPE_VIDEOS
|
||||
else -> TYPE_GIFS
|
||||
}
|
||||
|
||||
val medium = Medium(filename, file.absolutePath, dateModified, dateTaken, size, type)
|
||||
curMedia.add(medium)
|
||||
val medium = Medium(null, filename, file.absolutePath, folder, dateModified, dateTaken, size, type)
|
||||
media.add(medium)
|
||||
}
|
||||
return media
|
||||
}
|
||||
|
||||
private fun getMediaOnOTG(folder: String, curMedia: ArrayList<Medium>, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int) {
|
||||
val files = context.getDocumentFile(folder)?.listFiles() ?: return
|
||||
private fun getMediaOnOTG(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int): ArrayList<Medium> {
|
||||
val media = ArrayList<Medium>()
|
||||
val files = context.getDocumentFile(folder)?.listFiles() ?: return media
|
||||
val doExtraCheck = context.config.doExtraCheck
|
||||
val showHidden = context.config.shouldShowHidden
|
||||
|
||||
|
@ -260,7 +231,7 @@ class MediaFetcher(val context: Context) {
|
|||
break
|
||||
}
|
||||
|
||||
val filename = file.name
|
||||
val filename = file.name ?: continue
|
||||
val isImage = filename.isImageFast()
|
||||
val isVideo = if (isImage) false else filename.isVideoFast()
|
||||
val isGif = if (isImage || isVideo) false else filename.isGif()
|
||||
|
@ -268,13 +239,13 @@ class MediaFetcher(val context: Context) {
|
|||
if (!isImage && !isVideo && !isGif)
|
||||
continue
|
||||
|
||||
if (isVideo && (isPickImage || filterMedia and VIDEOS == 0))
|
||||
if (isVideo && (isPickImage || filterMedia and TYPE_VIDEOS == 0))
|
||||
continue
|
||||
|
||||
if (isImage && (isPickVideo || filterMedia and IMAGES == 0))
|
||||
if (isImage && (isPickVideo || filterMedia and TYPE_IMAGES == 0))
|
||||
continue
|
||||
|
||||
if (isGif && filterMedia and GIFS == 0)
|
||||
if (isGif && filterMedia and TYPE_GIFS == 0)
|
||||
continue
|
||||
|
||||
if (!showHidden && filename.startsWith('.'))
|
||||
|
@ -288,14 +259,16 @@ class MediaFetcher(val context: Context) {
|
|||
val dateModified = file.lastModified()
|
||||
|
||||
val type = when {
|
||||
isImage -> TYPE_IMAGE
|
||||
isVideo -> TYPE_VIDEO
|
||||
else -> TYPE_GIF
|
||||
isImage -> TYPE_IMAGES
|
||||
isVideo -> TYPE_VIDEOS
|
||||
else -> TYPE_GIFS
|
||||
}
|
||||
|
||||
val path = Uri.decode(file.uri.toString().replaceFirst("${context.config.OTGBasePath}%3A", OTG_PATH))
|
||||
val medium = Medium(filename, path, dateModified, dateTaken, size, type)
|
||||
curMedia.add(medium)
|
||||
val path = Uri.decode(file.uri.toString().replaceFirst("${context.config.OTGTreeUri}/document/${context.config.OTGPartition}%3A", OTG_PATH))
|
||||
val medium = Medium(null, filename, path, folder, dateModified, dateTaken, size, type)
|
||||
media.add(medium)
|
||||
}
|
||||
|
||||
return media
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package com.simplemobiletools.gallery.interfaces
|
||||
|
||||
import android.arch.persistence.room.Dao
|
||||
import android.arch.persistence.room.Insert
|
||||
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
||||
import android.arch.persistence.room.Query
|
||||
import com.simplemobiletools.gallery.models.Directory
|
||||
|
||||
@Dao
|
||||
interface DirectoryDao {
|
||||
@Query("SELECT path, thumbnail, filename, media_count, last_modified, date_taken, size, location, media_types FROM directories")
|
||||
fun getAll(): List<Directory>
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
fun insert(directory: Directory)
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
fun insertAll(directories: List<Directory>)
|
||||
|
||||
@Query("DELETE FROM directories WHERE path = :path")
|
||||
fun deleteDirPath(path: String)
|
||||
|
||||
@Query("UPDATE OR REPLACE directories SET thumbnail = :thumbnail, media_count = :mediaCnt, last_modified = :lastModified, date_taken = :dateTaken, size = :size, media_types = :mediaTypes WHERE path = :path")
|
||||
fun updateDirectory(path: String, thumbnail: String, mediaCnt: Int, lastModified: Long, dateTaken: Long, size: Long, mediaTypes: Int)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.simplemobiletools.gallery.interfaces
|
||||
|
||||
import android.arch.persistence.room.Dao
|
||||
import android.arch.persistence.room.Insert
|
||||
import android.arch.persistence.room.OnConflictStrategy.REPLACE
|
||||
import android.arch.persistence.room.Query
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
|
||||
@Dao
|
||||
interface MediumDao {
|
||||
@Query("SELECT * FROM media")
|
||||
fun getAll(): List<Medium>
|
||||
|
||||
@Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type FROM media WHERE parent_path = :path")
|
||||
fun getMediaFromPath(path: String): List<Medium>
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
fun insert(medium: Medium)
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
fun insertAll(media: List<Medium>)
|
||||
|
||||
@Query("DELETE FROM media WHERE full_path = :path")
|
||||
fun deleteMediumPath(path: String)
|
||||
|
||||
@Query("UPDATE OR REPLACE media SET filename = :newFilename, full_path = :newFullPath, parent_path = :newParentPath WHERE full_path = :oldPath")
|
||||
fun updateMedium(oldPath: String, newParentPath: String, newFilename: String, newFullPath: String)
|
||||
}
|
|
@ -1,14 +1,29 @@
|
|||
package com.simplemobiletools.gallery.models
|
||||
|
||||
import android.arch.persistence.room.ColumnInfo
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.Index
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import com.simplemobiletools.commons.extensions.formatDate
|
||||
import com.simplemobiletools.commons.extensions.formatSize
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import java.io.Serializable
|
||||
|
||||
data class Directory(var path: String, var tmb: String, var name: String, var mediaCnt: Int, val modified: Long, val taken: Long,
|
||||
val size: Long, val isOnSDCard: Boolean) : Serializable, Comparable<Directory> {
|
||||
@Entity(tableName = "directories", indices = [Index(value = "path", unique = true)])
|
||||
data class Directory(
|
||||
@PrimaryKey(autoGenerate = true) var id: Long?,
|
||||
@ColumnInfo(name = "path") var path: String,
|
||||
@ColumnInfo(name = "thumbnail") var tmb: String,
|
||||
@ColumnInfo(name = "filename") var name: String,
|
||||
@ColumnInfo(name = "media_count") var mediaCnt: Int,
|
||||
@ColumnInfo(name = "last_modified") var modified: Long,
|
||||
@ColumnInfo(name = "date_taken") var taken: Long,
|
||||
@ColumnInfo(name = "size") var size: Long,
|
||||
@ColumnInfo(name = "location") val location: Int,
|
||||
@ColumnInfo(name = "media_types") var types: Int) : Serializable, Comparable<Directory> {
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID = -6553345863555455L
|
||||
private const val serialVersionUID = -6553345863555455L
|
||||
var sorting: Int = 0
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +1,42 @@
|
|||
package com.simplemobiletools.gallery.models
|
||||
|
||||
import android.arch.persistence.room.ColumnInfo
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.Index
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import com.simplemobiletools.commons.extensions.formatDate
|
||||
import com.simplemobiletools.commons.extensions.formatSize
|
||||
import com.simplemobiletools.commons.extensions.getMimeType
|
||||
import com.simplemobiletools.commons.extensions.isDng
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIF
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGE
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEO
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIFS
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGES
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEOS
|
||||
import java.io.Serializable
|
||||
|
||||
data class Medium(var name: String, var path: String, val modified: Long, val taken: Long, val size: Long, val type: Int) : Serializable, Comparable<Medium> {
|
||||
@Entity(tableName = "media", indices = [(Index(value = "full_path", unique = true))])
|
||||
data class Medium(
|
||||
@PrimaryKey(autoGenerate = true) var id: Long?,
|
||||
@ColumnInfo(name = "filename") var name: String,
|
||||
@ColumnInfo(name = "full_path") var path: String,
|
||||
@ColumnInfo(name = "parent_path") var parentPath: String,
|
||||
@ColumnInfo(name = "last_modified") val modified: Long,
|
||||
@ColumnInfo(name = "date_taken") val taken: Long,
|
||||
@ColumnInfo(name = "size") val size: Long,
|
||||
@ColumnInfo(name = "type") val type: Int) : Serializable, Comparable<Medium> {
|
||||
|
||||
companion object {
|
||||
private val serialVersionUID = -6553149366975455L
|
||||
private const val serialVersionUID = -6553149366975455L
|
||||
var sorting: Int = 0
|
||||
}
|
||||
|
||||
fun isGif() = type == TYPE_GIF
|
||||
fun isGif() = type == TYPE_GIFS
|
||||
|
||||
fun isImage() = type == TYPE_IMAGE
|
||||
fun isImage() = type == TYPE_IMAGES
|
||||
|
||||
fun isVideo() = type == TYPE_VIDEO
|
||||
fun isVideo() = type == TYPE_VIDEOS
|
||||
|
||||
fun isDng() = path.isDng()
|
||||
|
||||
fun getMimeType() = path.getMimeType()
|
||||
|
||||
override fun compareTo(other: Medium): Int {
|
||||
var result: Int
|
||||
when {
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
package com.simplemobiletools.gallery.receivers
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.google.gson.Gson
|
||||
import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask
|
||||
import com.simplemobiletools.gallery.extensions.config
|
||||
|
||||
class InstallReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
GetDirectoriesAsynctask(context, false, false) {
|
||||
context.config.directories = Gson().toJson(it)
|
||||
}.execute()
|
||||
}
|
||||
}
|
|
@ -3,14 +3,32 @@ package com.simplemobiletools.gallery.receivers
|
|||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.google.gson.Gson
|
||||
import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask
|
||||
import com.simplemobiletools.gallery.extensions.config
|
||||
import com.simplemobiletools.commons.extensions.getFilenameFromPath
|
||||
import com.simplemobiletools.commons.extensions.getParentPath
|
||||
import com.simplemobiletools.commons.extensions.isImageFast
|
||||
import com.simplemobiletools.commons.extensions.isVideoFast
|
||||
import com.simplemobiletools.commons.helpers.REFRESH_PATH
|
||||
import com.simplemobiletools.gallery.extensions.galleryDB
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_GIFS
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_IMAGES
|
||||
import com.simplemobiletools.gallery.helpers.TYPE_VIDEOS
|
||||
import com.simplemobiletools.gallery.models.Medium
|
||||
import java.io.File
|
||||
|
||||
class RefreshMediaReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
GetDirectoriesAsynctask(context, false, false) {
|
||||
context.config.directories = Gson().toJson(it)
|
||||
}.execute()
|
||||
val path = intent.getStringExtra(REFRESH_PATH) ?: return
|
||||
|
||||
Thread {
|
||||
val medium = Medium(null, path.getFilenameFromPath(), path, path.getParentPath(), System.currentTimeMillis(), System.currentTimeMillis(),
|
||||
File(path).length(), getFileType(path))
|
||||
context.galleryDB.MediumDao().insert(medium)
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun getFileType(path: String) = when {
|
||||
path.isImageFast() -> TYPE_IMAGES
|
||||
path.isVideoFast() -> TYPE_VIDEOS
|
||||
else -> TYPE_GIFS
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,14 +52,6 @@
|
|||
android:paddingTop="@dimen/medium_margin"
|
||||
android:text="@string/last_modified"/>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyCompatRadioButton
|
||||
android:id="@+id/sorting_dialog_radio_date_taken"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/medium_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:text="@string/date_taken"/>
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<include
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dir_sd_card"
|
||||
android:id="@+id/dir_location"
|
||||
android:layout_width="@dimen/sd_card_icon_size"
|
||||
android:layout_height="@dimen/sd_card_icon_size"
|
||||
android:layout_alignParentBottom="true"
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
android:paddingBottom="@dimen/tiny_margin">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dir_sd_card"
|
||||
android:id="@+id/dir_location"
|
||||
android:layout_width="@dimen/sd_card_icon_size"
|
||||
android:layout_height="@dimen/sd_card_icon_size"
|
||||
android:paddingBottom="@dimen/small_margin"
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Simple Gallery</string>
|
||||
<string name="app_launcher_name">Galleri</string>
|
||||
<string name="edit">Rediger</string>
|
||||
<string name="open_camera">Åbn kamera</string>
|
||||
<string name="hidden">(skjult)</string>
|
||||
<string name="excluded">(ekskluderet)</string>
|
||||
<string name="pin_folder">Pin folder</string>
|
||||
<string name="unpin_folder">Unpin folder</string>
|
||||
<string name="pin_to_the_top">Pin to the top</string>
|
||||
<string name="show_all">Vis indholdet af alle mappert</string>
|
||||
<string name="all_folders">Alle mapper</string>
|
||||
<string name="folder_view">Skift til mappevisning</string>
|
||||
<string name="other_folder">Anden mappe</string>
|
||||
<string name="show_on_map">Vis på kort</string>
|
||||
<string name="unknown_location">Ukendt placering</string>
|
||||
<string name="increase_column_count">Flere kolonner</string>
|
||||
<string name="reduce_column_count">Færre kolonner</string>
|
||||
<string name="change_cover_image">Change cover image</string>
|
||||
<string name="select_photo">Select photo</string>
|
||||
<string name="use_default">Use default</string>
|
||||
<string name="volume">Volume</string>
|
||||
<string name="brightness">Brightness</string>
|
||||
<string name="lock_orientation">Lås orientering</string>
|
||||
<string name="unlock_orientation">Lås orientering op</string>
|
||||
|
||||
<!-- Filter -->
|
||||
<string name="filter_media">Filtrer medier</string>
|
||||
<string name="images">Billeder</string>
|
||||
<string name="videos">Videoer</string>
|
||||
<string name="gifs">GIF\'er</string>
|
||||
<string name="no_media_with_filters">Der blev ikke fundet nogen filer med det valgte filter.</string>
|
||||
<string name="change_filters_underlined"><u>Skift filter</u></string>
|
||||
|
||||
<!-- Hide / Exclude -->
|
||||
<string name="hide_folder_description">Denne funktion skjuler mappen og dens eventueller undermapper ved at oprette en \'.nomedia\'-fil i den. Du kan se dem ved at klikke på \'Vis skjulte\' i indstillingerne. Fortsæt?</string>
|
||||
<string name="exclude">Ekskluder</string>
|
||||
<string name="excluded_folders">Ekskluderede mapper</string>
|
||||
<string name="manage_excluded_folders">Administrer ekskluderede mapper</string>
|
||||
<string name="exclude_folder_description">Dette vil kun ekskludere de valgte mapper (og deres undermapper) fra Simple Gallery. Du kan administrere ekskluderede mapper i indstillingerne.</string>
|
||||
<string name="exclude_folder_parent">Ekskluder en overliggende mappe i stedet?</string>
|
||||
<string name="excluded_activity_placeholder">Ekskludering af mapper vil skjule dem og eventuelle undermapper for Simple Gallery, de vil stadig være synlige for andre apps.\n\nHvis du også vil skjule dem for andre apps, skal du bruge funktionen Skjul.</string>
|
||||
<string name="remove_all">Fjern alle</string>
|
||||
<string name="remove_all_description">Fjern alle fra listen med ekskluderede mapper? Det vil ikke slette mapperne.</string>
|
||||
<string name="hidden_folders">Skjulte mapper</string>
|
||||
<string name="manage_hidden_folders">Administrer skjulte mapper</string>
|
||||
<string name="hidden_folders_placeholder">Det ser ikke ud til at du har nogen skjulte mapper med en \".nomedia\"-fil.</string>
|
||||
|
||||
<!-- Include folders -->
|
||||
<string name="include_folders">Inkluderede mapper</string>
|
||||
<string name="manage_included_folders">Administrer inkluderede mapper</string>
|
||||
<string name="add_folder">Tilføj mappe</string>
|
||||
<string name="included_activity_placeholder">Hvis du har mapper med mediefiler som appen ikke har fundet, kan du manuelt tilføje dem her.\n\nDet vil ikke ekskludere andre mapper.</string>
|
||||
|
||||
<!-- Resizing -->
|
||||
<string name="resize">Skaler</string>
|
||||
<string name="resize_and_save">Resize selection and save</string>
|
||||
<string name="width">Width</string>
|
||||
<string name="height">Height</string>
|
||||
<string name="keep_aspect_ratio">Keep aspect ratio</string>
|
||||
<string name="invalid_values">Please enter a valid resolution</string>
|
||||
|
||||
<!-- Editor -->
|
||||
<string name="editor">Editor</string>
|
||||
<string name="save">Gem</string>
|
||||
<string name="rotate">Roter</string>
|
||||
<string name="path">Sti</string>
|
||||
<string name="invalid_image_path">Ugyldig sti til billede</string>
|
||||
<string name="image_editing_failed">Redigering af billede mislykkedes</string>
|
||||
<string name="edit_image_with">Rediger billede med:</string>
|
||||
<string name="no_editor_found">Der blev ikke fundet en editor til billedbehandling</string>
|
||||
<string name="unknown_file_location">Ukendt filplacering</string>
|
||||
<string name="error_saving_file">Kunne ikke overskrive kildefilen</string>
|
||||
<string name="rotate_left">Roter mod venstre</string>
|
||||
<string name="rotate_right">Roter mod højre</string>
|
||||
<string name="rotate_one_eighty">Roter 180º</string>
|
||||
<string name="flip">Spejlvend</string>
|
||||
<string name="flip_horizontally">Spejlvend vandret</string>
|
||||
<string name="flip_vertically">Spejlvend lodret</string>
|
||||
<string name="edit_with">Rediger med</string>
|
||||
|
||||
<!-- Set wallpaper -->
|
||||
<string name="simple_wallpaper">Simple Wallpaper</string>
|
||||
<string name="set_as_wallpaper">Sæt som baggrundsbillede</string>
|
||||
<string name="set_as_wallpaper_failed">Det mislykkedes at sætte billedet som baggrund</string>
|
||||
<string name="set_as_wallpaper_with">Sæt som baggrundsbillede med:</string>
|
||||
<string name="setting_wallpaper">Sætter baggrundsbillede…</string>
|
||||
<string name="wallpaper_set_successfully">Sat som baggrundsbillede</string>
|
||||
<string name="portrait_aspect_ratio">Stående billedformat</string>
|
||||
<string name="landscape_aspect_ratio">Liggende billedformat</string>
|
||||
<string name="home_screen">Hjemmeskærm</string>
|
||||
<string name="lock_screen">Låseskærm</string>
|
||||
<string name="home_and_lock_screen">Hjemme- og låseskærm</string>
|
||||
|
||||
<!-- Slideshow -->
|
||||
<string name="slideshow">Slideshow</string>
|
||||
<string name="interval">Frekvens (sekunder):</string>
|
||||
<string name="include_photos">Inkluder billeder</string>
|
||||
<string name="include_videos">Inkluder videoer</string>
|
||||
<string name="include_gifs">Inkluder GIF\'er</string>
|
||||
<string name="random_order">Tilfældig rækkefølge</string>
|
||||
<string name="use_fade">Use fade animations</string>
|
||||
<string name="move_backwards">Kør baglæns</string>
|
||||
<string name="loop_slideshow">Loop slideshow</string>
|
||||
<string name="slideshow_ended">Slideshowet endte</string>
|
||||
<string name="no_media_for_slideshow">Der blev ikke funket nogen mediefiler til slideshowet</string>
|
||||
|
||||
<!-- View types -->
|
||||
<string name="change_view_type">Skift visning</string>
|
||||
<string name="grid">Gitter</string>
|
||||
<string name="list">Liste</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="autoplay_videos">Afspil automatisk videoer</string>
|
||||
<string name="toggle_filename">Toggle filename visibility</string>
|
||||
<string name="loop_videos">Kør videoer i sløjfe</string>
|
||||
<string name="animate_gifs">Animér GIF\'er i miniaturer</string>
|
||||
<string name="max_brightness">Maksimal lysstyrke ved fuldskærmsvisning af medier</string>
|
||||
<string name="crop_thumbnails">Beskær miniaturer til kvadrater</string>
|
||||
<string name="screen_rotation_by">Roter fuldskærmsmedier efter</string>
|
||||
<string name="screen_rotation_system_setting">Systemindstilling</string>
|
||||
<string name="screen_rotation_device_rotation">Enhedens orientering</string>
|
||||
<string name="screen_rotation_aspect_ratio">Billedformat</string>
|
||||
<string name="black_background_at_fullscreen">Sort baggrund og statuslinje ved medievisning i fuldskærm</string>
|
||||
<string name="scroll_thumbnails_horizontally">Scroll miniaturer vandret</string>
|
||||
<string name="hide_system_ui_at_fullscreen">Skjul automatisk systemets brugerflade ved fuldskærmsvisning af medier</string>
|
||||
<string name="delete_empty_folders">Slet tomme mapper efter sletning af deres indhold</string>
|
||||
<string name="allow_photo_gestures">Tillad kontrol af lysstyrke på billeder med lodrette bevægelser</string>
|
||||
<string name="allow_video_gestures">Tillad kontrol af videolyd og lysstyrke med lodrette bevægelser</string>
|
||||
<string name="show_media_count">Vis antal filer i hver mappe i oversigten</string>
|
||||
<string name="replace_share_with_rotate">Erstat Del med Roter i fuldskærmsmenuen</string>
|
||||
<string name="show_extended_details">Vis udvidede oplysninger over medier i fuldskærm</string>
|
||||
<string name="manage_extended_details">Manage extended details</string>
|
||||
<string name="one_finger_zoom">Tillad zoom med en finger når medier er i fuldskærm</string>
|
||||
<string name="allow_instant_change">Tillad skift af medie ved klik på skærmens sider</string>
|
||||
<string name="replace_zoomable_images">RErstat stærkt zoombare billeder med nogle i bedre kvalitet</string>
|
||||
<string name="hide_extended_details">Skjul udvidede oplysninger når statuslinjen er skjult</string>
|
||||
<string name="do_extra_check">Tjek en ekstra gang for at undgå visning af ugyldige filer</string>
|
||||
|
||||
<!-- Setting sections -->
|
||||
<string name="thumbnails">Thumbnails</string>
|
||||
<string name="fullscreen_media">Fullscreen media</string>
|
||||
<string name="extended_details">Extended details</string>
|
||||
|
||||
<!-- FAQ -->
|
||||
<string name="faq_1_title">How can I make Simple Gallery the default device gallery?</string>
|
||||
<string name="faq_1_text">First you have to find the currently default gallery in the Apps section of your device settings, look for a button that says something like \"Open by default\", click on it, then select \"Clear defaults\".
|
||||
The next time you will try opening an image or video you should see an app picker, where you can select Simple Gallery and make it the default app.</string>
|
||||
<string name="faq_2_title">I locked the app with a password, but I forgot it. What can I do?</string>
|
||||
<string name="faq_2_text">You can solve it in 2 ways. You can either reinstall the app, or find the app in your device settings and select \"Clear data\". It will reset all your settings, it will not remove any media files.</string>
|
||||
<string name="faq_3_title">How can I make an album always appear at the top?</string>
|
||||
<string name="faq_3_text">You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.</string>
|
||||
<string name="faq_4_title">How can I fast-forward videos?</string>
|
||||
<string name="faq_4_text">You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.</string>
|
||||
<string name="faq_5_title">What is the difference between hiding and excluding a folder?</string>
|
||||
<string name="faq_5_text">Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.</string>
|
||||
<string name="faq_6_title">Why do folders with music cover art or stickers show up?</string>
|
||||
<string name="faq_6_text">It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.</string>
|
||||
<string name="faq_7_title">A folder with images isn\'t showing up, what can I do?</string>
|
||||
<string name="faq_7_text">That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.</string>
|
||||
<string name="faq_8_title">What if I want just a few particular folders visible?</string>
|
||||
<string name="faq_8_text">Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
|
||||
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.</string>
|
||||
<string name="faq_9_title">Fullscreen images have weird artifacts, can I somehow improve the quality?</string>
|
||||
<string name="faq_9_text">Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.</string>
|
||||
<string name="faq_10_title">Can I crop images with this app?</string>
|
||||
<string name="faq_10_text">Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
<string name="app_short_description">A gallery for viewing photos and videos without ads.</string>
|
||||
<string name="app_long_description">
|
||||
A simple tool usable for viewing photos and videos. Items can be sorted by date, size, name both ascending or descending, photos can be zoomed in. Media files are shown in multiple columns depending on the size of the display, you can change the column count by pinch gestures. They can be renamed, shared, deleted, copied, moved. Images can also be cropped, rotated, flipped or set as Wallpaper directly from the app.
|
||||
|
||||
The Gallery is also offered for third party usage for previewing images / videos, adding attachments at email clients etc. It\'s perfect for everyday usage.
|
||||
|
||||
The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
|
||||
|
||||
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
|
||||
|
||||
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
|
||||
</string>
|
||||
|
||||
<!--
|
||||
Haven't found some strings? There's more at
|
||||
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
|
||||
-->
|
||||
</resources>
|
|
@ -5,7 +5,7 @@
|
|||
<string name="edit">Modifica</string>
|
||||
<string name="open_camera">Apri fotocamera</string>
|
||||
<string name="hidden">(nascosta)</string>
|
||||
<string name="excluded">(excluded)</string>
|
||||
<string name="excluded">(esclusa)</string>
|
||||
<string name="pin_folder">Blocca cartella</string>
|
||||
<string name="unpin_folder">Sblocca cartella</string>
|
||||
<string name="pin_to_the_top">Fissa in alto</string>
|
||||
|
@ -165,7 +165,7 @@
|
|||
<string name="faq_9_title">Le immagini a schermo intero hanno strani artefatti, posso migliorarne la qualità in qualche modo?</string>
|
||||
<string name="faq_9_text">Sì, c\'è un\'opzione nelle impostazioni che dice \"Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità\", puoi usare quella. Ciò migliorerà la qualità delle immagini, ma saranno sfuocate quando proverai a ingrandirle troppo.</string>
|
||||
<string name="faq_10_title">Posso ritagliare le immagini con questa app?</string>
|
||||
<string name="faq_10_text">Sì, puoi ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Puoi usare l\'editor sia premendo a lungo la miniatura di un'immagine e selezionando Modifica, o selezionando Modifica mentre la vedi a schermo intero.</string>
|
||||
<string name="faq_10_text">Sì, puoi ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Puoi usare l\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, o selezionando Modifica mentre la vedi a schermo intero.</string>
|
||||
|
||||
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
|
||||
<!-- Short description has to have less than 80 chars -->
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<string name="change_filters_underlined"><u>Pakeisti filtrus</u></string>
|
||||
|
||||
<!-- Hide / Exclude -->
|
||||
<string name="hide_folder_description">Ši funkcija slepia aplanką, pridedant į jį \ '. Nomedia \' bylą, jis taip pat slėps visus subaplankus. Galite juos peržiūrėti, perjunkite parinktį "Rodyti paslėptus elementus \" skiltyje Nustatymai. Tęsti?</string>
|
||||
<string name="hide_folder_description">Ši funkcija slepia aplanką, pridedant į jį \'. Nomedia \' bylą, jis taip pat slėps visus subaplankus. Galite juos peržiūrėti, perjunkite parinktį "Rodyti paslėptus elementus \" skiltyje Nustatymai. Tęsti?</string>
|
||||
<string name="exclude">Išskirti</string>
|
||||
<string name="excluded_folders">Išskirti aplankai</string>
|
||||
<string name="manage_excluded_folders">Tvarkyti išskirtus aplankus</string>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<string name="remove_all_description">Pašalinti visus aplankus iš išskirtųjų sąrašo? Tai neištrins aplankų.</string>
|
||||
<string name="hidden_folders">Paslėpti aplankai</string>
|
||||
<string name="manage_hidden_folders">Tvarkyti paslėptus aplankus</string>
|
||||
<string name="hidden_folders_placeholder">Atrodo, kad neturite jokių aplankų, paslėptų \ ". Nomedia \" bylos.</string>
|
||||
<string name="hidden_folders_placeholder">Atrodo, kad neturite jokių aplankų, paslėptų \". Nomedia \" bylos.</string>
|
||||
|
||||
<!-- Include folders -->
|
||||
<string name="include_folders">Įtraukti aplankai</string>
|
|
@ -5,7 +5,7 @@
|
|||
<string name="edit">Rediger</string>
|
||||
<string name="open_camera">Åpne kamera</string>
|
||||
<string name="hidden">(skjult)</string>
|
||||
<string name="excluded">(excluded)</string>
|
||||
<string name="excluded">(ekskludert)</string>
|
||||
<string name="pin_folder">Fest mappe</string>
|
||||
<string name="unpin_folder">Løsne mappe</string>
|
||||
<string name="pin_to_the_top">Fest til toppen</string>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<string name="edit">Editar</string>
|
||||
<string name="open_camera">Abrir câmara</string>
|
||||
<string name="hidden">(oculta)</string>
|
||||
<string name="excluded">(excluded)</string>
|
||||
<string name="excluded">(excluída)</string>
|
||||
<string name="pin_folder">Fixar pasta</string>
|
||||
<string name="unpin_folder">Desafixar pasta</string>
|
||||
<string name="pin_to_the_top">Fixar no topo</string>
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<string name="app_name">Simple Gallery</string>
|
||||
<string name="app_launcher_name">Galleri</string>
|
||||
<string name="edit">Redigera</string>
|
||||
<string name="open_camera">Starta kameran</string>
|
||||
<string name="open_camera">Öppna kameran</string>
|
||||
<string name="hidden">(dold)</string>
|
||||
<string name="excluded">(excluded)</string>
|
||||
<string name="excluded">(utesluten)</string>
|
||||
<string name="pin_folder">Fäst mapp</string>
|
||||
<string name="unpin_folder">Lossa mapp</string>
|
||||
<string name="pin_to_the_top">Fäst högst upp</string>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.31'
|
||||
ext.kotlin_version = '1.2.40'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
|
@ -9,7 +9,7 @@ buildscript {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.0'
|
||||
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Loading…
Reference in New Issue