Merge pull request #1 from SimpleMobileTools/master

update fork
This commit is contained in:
solokot 2017-10-03 16:37:55 +03:00 committed by GitHub
commit 7f34fa0537
59 changed files with 1508 additions and 716 deletions

View file

@ -1,6 +1,41 @@
Changelog Changelog
========== ==========
Version 2.15.1 *(2017-10-01)*
----------------------------
* Updated commons library with minor fixes
Version 2.15.0 *(2017-10-01)*
----------------------------
* Added fingerprint to hidden item protection
* Added a new List view type
* Fixed an issue with some hidden items being shown at "Show all folders content"
* Fixed typing in color hex codes manually with some keyboards
* Do not autosave rotated images in any case
* Tons of other performance, stability and UX improvements
Version 2.14.4 *(2017-09-18)*
----------------------------
* Revert to the old way of loading fullscreen images to avoid issues on Android 7+
Version 2.14.3 *(2017-09-17)*
----------------------------
* Removed some error toast messages after delete, or if image loading failed
* Fixed some visual glitches at horizontal scrolling
* Disable pull-to-refresh at horizontal scrolling
* Many other smaller bugfixes and improvements
Version 2.14.2 *(2017-09-11)*
----------------------------
* Fixing some glitches with fullscreen images
* Add an extra check to avoid displaying non-existing media
* Fix opening media from third party intents
Version 2.14.1 *(2017-09-07)* Version 2.14.1 *(2017-09-07)*
---------------------------- ----------------------------

208
LICENSE
View file

@ -1,13 +1,201 @@
Copyright 2016 SimpleMobileTools Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Licensed under the Apache License, Version 2.0 (the "License"); TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0 1. Definitions.
Unless required by applicable law or agreed to in writing, software "License" shall mean the terms and conditions for use, reproduction,
distributed under the License is distributed on an "AS IS" BASIS, and distribution as defined by Sections 1 through 9 of this document.
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and "Licensor" shall mean the copyright owner or entity authorized by
limitations under the License. the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 SimpleMobileTools
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -10,8 +10,8 @@ android {
applicationId "com.simplemobiletools.gallery" applicationId "com.simplemobiletools.gallery"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 23 targetSdkVersion 23
versionCode 129 versionCode 134
versionName "2.14.1" versionName "2.15.1"
} }
signingConfigs { signingConfigs {
@ -37,7 +37,7 @@ android {
} }
dependencies { dependencies {
compile 'com.simplemobiletools:commons:2.27.9' compile 'com.simplemobiletools:commons:2.29.1'
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0' compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.6.0'
compile 'com.theartofdev.edmodo:android-image-cropper:2.4.0' compile 'com.theartofdev.edmodo:android-image-cropper:2.4.0'
compile 'com.bignerdranch.android:recyclerview-multiselect:0.2' compile 'com.bignerdranch.android:recyclerview-multiselect:0.2'
@ -52,7 +52,7 @@ dependencies {
} }
buildscript { buildscript {
ext.kotlin_version = '1.1.4-3' ext.kotlin_version = '1.1.51'
repositories { repositories {
mavenCentral() mavenCentral()
} }

View file

@ -1,12 +1,14 @@
package com.simplemobiletools.gallery package com.simplemobiletools.gallery
import android.app.Application import android.app.Application
import com.github.ajalt.reprint.core.Reprint
import com.squareup.leakcanary.LeakCanary import com.squareup.leakcanary.LeakCanary
class App : Application() { class App : Application() {
val USE_LEAK_CANARY = false val USE_LEAK_CANARY = false
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Reprint.initialize(this)
if (USE_LEAK_CANARY) { if (USE_LEAK_CANARY) {
if (LeakCanary.isInAnalyzerProcess(this)) { if (LeakCanary.isInAnalyzerProcess(this)) {
return return

View file

@ -2,10 +2,10 @@ package com.simplemobiletools.gallery.activities
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.AsyncTask
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -19,7 +19,11 @@ import android.widget.FrameLayout
import com.google.gson.Gson import com.google.gson.Gson
import com.simplemobiletools.commons.dialogs.CreateNewFolderDialog import com.simplemobiletools.commons.dialogs.CreateNewFolderDialog
import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_TAKEN
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.models.Release import com.simplemobiletools.commons.models.Release
import com.simplemobiletools.commons.views.MyScalableRecyclerView import com.simplemobiletools.commons.views.MyScalableRecyclerView
import com.simplemobiletools.gallery.BuildConfig import com.simplemobiletools.gallery.BuildConfig
@ -54,10 +58,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private var mStoredAnimateGifs = true private var mStoredAnimateGifs = true
private var mStoredCropThumbnails = true private var mStoredCropThumbnails = true
private var mStoredScrollHorizontally = true private var mStoredScrollHorizontally = true
private var mStoredTextColor = 0
private var mLoadedInitialPhotos = false private var mLoadedInitialPhotos = false
private var mLastMediaModified = 0 private var mLatestMediaId = 0L
private var mLastMediaHandler = Handler() private var mLastMediaHandler = Handler()
private var mCurrAsyncTask: GetDirectoriesAsynctask? = null private var mCurrAsyncTask: GetDirectoriesAsynctask? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -79,6 +83,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
mStoredAnimateGifs = config.animateGifs mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
storeStoragePaths() storeStoragePaths()
checkWhatsNewDialog() checkWhatsNewDialog()
@ -92,8 +97,8 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
menuInflater.inflate(R.menu.menu_main_intent, menu) menuInflater.inflate(R.menu.menu_main_intent, menu)
} else { } else {
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
menu.findItem(R.id.increase_column_count).isVisible = config.dirColumnCnt < 10 menu.findItem(R.id.increase_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt < 10
menu.findItem(R.id.reduce_column_count).isVisible = config.dirColumnCnt > 1 menu.findItem(R.id.reduce_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt > 1
} }
menu.findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden menu.findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
menu.findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden menu.findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
@ -106,6 +111,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
R.id.filter -> showFilterMediaDialog() R.id.filter -> showFilterMediaDialog()
R.id.open_camera -> launchCamera() R.id.open_camera -> launchCamera()
R.id.show_all -> showAllMedia() R.id.show_all -> showAllMedia()
R.id.change_view_type -> changeViewType()
R.id.temporarily_show_hidden -> tryToggleTemporarilyShowHidden() R.id.temporarily_show_hidden -> tryToggleTemporarilyShowHidden()
R.id.stop_showing_hidden -> tryToggleTemporarilyShowHidden() R.id.stop_showing_hidden -> tryToggleTemporarilyShowHidden()
R.id.create_new_folder -> createNewFolder() R.id.create_new_folder -> createNewFolder()
@ -130,13 +136,17 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
if (mStoredScrollHorizontally != config.scrollHorizontally) { if (mStoredScrollHorizontally != config.scrollHorizontally) {
directories_grid.adapter?.let { (directories_grid.adapter as? DirectoryAdapter)?.apply {
(it as DirectoryAdapter).scrollVertically = !config.scrollHorizontally scrollVertically = config.viewTypeFolders == VIEW_TYPE_LIST || !config.scrollHorizontally
it.notifyDataSetChanged() notifyDataSetChanged()
} }
setupScrollDirection() setupScrollDirection()
} }
if (mStoredTextColor != config.textColor) {
(directories_grid.adapter as? DirectoryAdapter)?.updateTextColor(config.textColor)
}
tryloadGallery() tryloadGallery()
invalidateOptionsMenu() invalidateOptionsMenu()
directories_empty_text_label.setTextColor(config.textColor) directories_empty_text_label.setTextColor(config.textColor)
@ -145,15 +155,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
mCurrAsyncTask?.shouldStop = true
storeDirectories() storeDirectories()
directories_refresh_layout.isRefreshing = false directories_refresh_layout.isRefreshing = false
mIsGettingDirs = false mIsGettingDirs = false
mStoredAnimateGifs = config.animateGifs mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
directories_grid.listener = null directories_grid.listener = null
mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.removeCallbacksAndMessages(null)
mCurrAsyncTask?.stopFetching()
} }
override fun onDestroy() { override fun onDestroy() {
@ -178,6 +189,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
showAllMedia() showAllMedia()
else else
getDirectories() getDirectories()
setupLayoutManager() setupLayoutManager()
checkIfColorChanged() checkIfColorChanged()
} else { } else {
@ -216,12 +228,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
mCurrAsyncTask = GetDirectoriesAsynctask(applicationContext, mIsPickVideoIntent || mIsGetVideoContentIntent, mIsPickImageIntent || mIsGetImageContentIntent) { mCurrAsyncTask = GetDirectoriesAsynctask(applicationContext, mIsPickVideoIntent || mIsGetVideoContentIntent, mIsPickImageIntent || mIsGetImageContentIntent) {
gotDirectories(addTempFolderIfNeeded(it), false) gotDirectories(addTempFolderIfNeeded(it), false)
} }
mCurrAsyncTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) mCurrAsyncTask!!.execute()
} }
private fun showSortingDialog() { private fun showSortingDialog() {
ChangeSortingDialog(this, true, false) { ChangeSortingDialog(this, true, false) {
getDirectories() if (config.directorySorting and SORT_BY_DATE_MODIFIED > 0 || config.directorySorting and SORT_BY_DATE_TAKEN > 0) {
getDirectories()
} else {
gotDirectories(mDirs, true)
}
} }
} }
@ -236,9 +252,28 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
config.showAll = true config.showAll = true
Intent(this, MediaActivity::class.java).apply { Intent(this, MediaActivity::class.java).apply {
putExtra(DIRECTORY, "/") putExtra(DIRECTORY, "/")
startActivity(this)
if (mIsThirdPartyIntent) {
handleMediaIntent(this)
} else {
startActivity(this)
finish()
}
}
}
private fun changeViewType() {
val items = arrayListOf(
RadioItem(VIEW_TYPE_GRID, getString(R.string.grid)),
RadioItem(VIEW_TYPE_LIST, getString(R.string.list)))
RadioGroupDialog(this, items, config.viewTypeFolders) {
config.viewTypeFolders = it as Int
invalidateOptionsMenu()
setupLayoutManager()
directories_grid.adapter = null
setupAdapter()
} }
finish()
} }
private fun tryToggleTemporarilyShowHidden() { private fun tryToggleTemporarilyShowHidden() {
@ -278,6 +313,13 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun getRecyclerAdapter() = (directories_grid.adapter as DirectoryAdapter) private fun getRecyclerAdapter() = (directories_grid.adapter as DirectoryAdapter)
private fun setupLayoutManager() { private fun setupLayoutManager() {
if (config.viewTypeFolders == VIEW_TYPE_GRID)
setupGridLayoutManager()
else
setupListLayoutManager()
}
private fun setupGridLayoutManager() {
val layoutManager = directories_grid.layoutManager as GridLayoutManager val layoutManager = directories_grid.layoutManager as GridLayoutManager
if (config.scrollHorizontally) { if (config.scrollHorizontally) {
layoutManager.orientation = GridLayoutManager.HORIZONTAL layoutManager.orientation = GridLayoutManager.HORIZONTAL
@ -315,6 +357,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
} }
private fun setupListLayoutManager() {
directories_grid.isDragSelectionEnabled = true
directories_grid.isZoomingEnabled = false
val layoutManager = directories_grid.layoutManager as GridLayoutManager
layoutManager.spanCount = 1
layoutManager.orientation = GridLayoutManager.VERTICAL
directories_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
private fun createNewFolder() { private fun createNewFolder() {
FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) { FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) {
CreateNewFolderDialog(this, it) { CreateNewFolderDialog(this, it) {
@ -366,36 +418,22 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_MEDIA && resultData?.data != null) { if (requestCode == PICK_MEDIA && resultData != null) {
Intent().apply { val resultIntent = Intent()
if (mIsGetImageContentIntent || mIsGetVideoContentIntent || mIsGetAnyContentIntent) {
when {
intent.extras?.containsKey(MediaStore.EXTRA_OUTPUT) == true -> fillExtraOutput(resultData)
resultData.extras?.containsKey(PICKED_PATHS) == true -> fillPickedPaths(resultData, resultIntent)
else -> fillIntentPath(resultData, resultIntent)
}
} else if ((mIsPickImageIntent || mIsPickVideoIntent)) {
val path = resultData.data.path val path = resultData.data.path
val uri = Uri.fromFile(File(path)) val uri = Uri.fromFile(File(path))
if (mIsGetImageContentIntent || mIsGetVideoContentIntent || mIsGetAnyContentIntent) { resultIntent.data = uri
if (intent.extras?.containsKey(MediaStore.EXTRA_OUTPUT) == true) { resultIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val output = intent.extras.get(MediaStore.EXTRA_OUTPUT) as Uri
inputStream = FileInputStream(File(path))
outputStream = contentResolver.openOutputStream(output)
inputStream.copyTo(outputStream)
} catch (ignored: FileNotFoundException) {
} finally {
inputStream?.close()
outputStream?.close()
}
} else {
val type = File(path).getMimeType("image/jpeg")
setDataAndTypeAndNormalize(uri, type)
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
}
} else if (mIsPickImageIntent || mIsPickVideoIntent) {
data = uri
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}
setResult(Activity.RESULT_OK, this)
} }
setResult(Activity.RESULT_OK, resultIntent)
finish() finish()
} else if (requestCode == PICK_WALLPAPER) { } else if (requestCode == PICK_WALLPAPER) {
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
@ -405,10 +443,51 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
super.onActivityResult(requestCode, resultCode, resultData) super.onActivityResult(requestCode, resultCode, resultData)
} }
private fun fillExtraOutput(resultData: Intent) {
val path = resultData.data.path
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val output = intent.extras.get(MediaStore.EXTRA_OUTPUT) as Uri
inputStream = FileInputStream(File(path))
outputStream = contentResolver.openOutputStream(output)
inputStream.copyTo(outputStream)
} catch (ignored: FileNotFoundException) {
} finally {
inputStream?.close()
outputStream?.close()
}
}
private fun fillPickedPaths(resultData: Intent, resultIntent: Intent) {
val paths = resultData.extras.getStringArrayList(PICKED_PATHS)
val uris = paths.map { Uri.fromFile(File(it)) } as ArrayList
val clipData = ClipData("Attachment", arrayOf("image/*", "video/*"), ClipData.Item(uris.removeAt(0)))
uris.forEach {
clipData.addItem(ClipData.Item(it))
}
resultIntent.clipData = clipData
}
private fun fillIntentPath(resultData: Intent, resultIntent: Intent) {
val path = resultData.data.path
val uri = Uri.fromFile(File(path))
val type = File(path).getMimeType("image/jpeg")
resultIntent.setDataAndTypeAndNormalize(uri, type)
resultIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
}
private fun itemClicked(path: String) { private fun itemClicked(path: String) {
Intent(this, MediaActivity::class.java).apply { Intent(this, MediaActivity::class.java).apply {
putExtra(DIRECTORY, path) putExtra(DIRECTORY, path)
handleMediaIntent(this)
}
}
private fun handleMediaIntent(intent: Intent) {
intent.apply {
if (mIsSetWallpaperIntent) { if (mIsSetWallpaperIntent) {
putExtra(SET_WALLPAPER_INTENT, true) putExtra(SET_WALLPAPER_INTENT, true)
startActivityForResult(this, PICK_WALLPAPER) startActivityForResult(this, PICK_WALLPAPER)
@ -416,13 +495,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
putExtra(GET_IMAGE_INTENT, mIsPickImageIntent || mIsGetImageContentIntent) putExtra(GET_IMAGE_INTENT, mIsPickImageIntent || mIsGetImageContentIntent)
putExtra(GET_VIDEO_INTENT, mIsPickVideoIntent || mIsGetVideoContentIntent) putExtra(GET_VIDEO_INTENT, mIsPickVideoIntent || mIsGetVideoContentIntent)
putExtra(GET_ANY_INTENT, mIsGetAnyContentIntent) putExtra(GET_ANY_INTENT, mIsGetAnyContentIntent)
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false))
startActivityForResult(this, PICK_MEDIA) startActivityForResult(this, PICK_MEDIA)
} }
} }
} }
private fun gotDirectories(dirs: ArrayList<Directory>, isFromCache: Boolean) { private fun gotDirectories(newDirs: ArrayList<Directory>, isFromCache: Boolean) {
mLastMediaModified = getLastMediaModified() val dirs = getSortedDirectories(newDirs)
mLatestMediaId = getLatestMediaId()
directories_refresh_layout.isRefreshing = false directories_refresh_layout.isRefreshing = false
mIsGettingDirs = false mIsGettingDirs = false
@ -430,8 +512,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
directories_empty_text.beVisibleIf(dirs.isEmpty() && !isFromCache) directories_empty_text.beVisibleIf(dirs.isEmpty() && !isFromCache)
checkLastMediaChanged() checkLastMediaChanged()
if (dirs.hashCode() == mDirs.hashCode()) if (dirs.hashCode() == mDirs.hashCode()) {
return return
}
mDirs = dirs mDirs = dirs
@ -462,13 +545,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
} }
private fun setupScrollDirection() { private fun setupScrollDirection() {
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID
directories_refresh_layout.isEnabled = !config.scrollHorizontally
directories_vertical_fastscroller.isHorizontal = false directories_vertical_fastscroller.isHorizontal = false
directories_vertical_fastscroller.beGoneIf(config.scrollHorizontally) directories_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
directories_horizontal_fastscroller.isHorizontal = true directories_horizontal_fastscroller.isHorizontal = true
directories_horizontal_fastscroller.beVisibleIf(config.scrollHorizontally) directories_horizontal_fastscroller.beVisibleIf(allowHorizontalScroll)
if (config.scrollHorizontally) { if (allowHorizontalScroll) {
directories_horizontal_fastscroller.setViews(directories_grid, directories_refresh_layout) directories_horizontal_fastscroller.setViews(directories_grid, directories_refresh_layout)
} else { } else {
directories_vertical_fastscroller.setViews(directories_grid, directories_refresh_layout) directories_vertical_fastscroller.setViews(directories_grid, directories_refresh_layout)
@ -482,9 +568,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.removeCallbacksAndMessages(null)
mLastMediaHandler.postDelayed({ mLastMediaHandler.postDelayed({
Thread({ Thread({
val lastModified = getLastMediaModified() val mediaId = getLatestMediaId()
if (mLastMediaModified != lastModified) { if (mLatestMediaId != mediaId) {
mLastMediaModified = lastModified mLatestMediaId = mediaId
runOnUiThread { runOnUiThread {
getDirectories() getDirectories()
} }
@ -503,6 +589,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
directories_grid.setDragSelectActive(position) directories_grid.setDragSelectActive(position)
} }
override fun recheckPinnedFolders() {
gotDirectories(movePinnedDirectoriesToFront(mDirs), true)
}
private fun checkWhatsNewDialog() { private fun checkWhatsNewDialog() {
arrayListOf<Release>().apply { arrayListOf<Release>().apply {
add(Release(46, R.string.release_46)) add(Release(46, R.string.release_46))
@ -540,6 +630,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
add(Release(123, R.string.release_123)) add(Release(123, R.string.release_123))
add(Release(125, R.string.release_125)) add(Release(125, R.string.release_125))
add(Release(127, R.string.release_127)) add(Release(127, R.string.release_127))
add(Release(133, R.string.release_133))
checkWhatsNew(this, BuildConfig.VERSION_CODE) checkWhatsNew(this, BuildConfig.VERSION_CODE)
} }
} }

View file

@ -20,7 +20,9 @@ import com.bumptech.glide.request.transition.Transition
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.MyScalableRecyclerView import com.simplemobiletools.commons.views.MyScalableRecyclerView
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.adapters.MediaAdapter import com.simplemobiletools.gallery.adapters.MediaAdapter
@ -36,8 +38,7 @@ import java.io.File
import java.io.IOException import java.io.IOException
class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener { class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private val TAG = MediaActivity::class.java.simpleName private val SAVE_MEDIA_CNT = 100
private val SAVE_MEDIA_CNT = 40
private val LAST_MEDIA_CHECK_PERIOD = 3000L private val LAST_MEDIA_CHECK_PERIOD = 3000L
private var mPath = "" private var mPath = ""
@ -45,14 +46,17 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private var mIsGetVideoIntent = false private var mIsGetVideoIntent = false
private var mIsGetAnyIntent = false private var mIsGetAnyIntent = false
private var mIsGettingMedia = false private var mIsGettingMedia = false
private var mAllowPickingMultiple = false
private var mShowAll = false private var mShowAll = false
private var mLoadedInitialPhotos = false private var mLoadedInitialPhotos = false
private var mStoredAnimateGifs = true private var mStoredAnimateGifs = true
private var mStoredCropThumbnails = true private var mStoredCropThumbnails = true
private var mStoredScrollHorizontally = true private var mStoredScrollHorizontally = true
private var mStoredTextColor = 0
private var mLastDrawnHashCode = 0 private var mLastDrawnHashCode = 0
private var mLastMediaModified = 0 private var mLatestMediaId = 0L
private var mLastMediaHandler = Handler() private var mLastMediaHandler = Handler()
private var mCurrAsyncTask: GetMediaAsynctask? = null
companion object { companion object {
var mMedia = ArrayList<Medium>() var mMedia = ArrayList<Medium>()
@ -65,6 +69,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mIsGetImageIntent = getBooleanExtra(GET_IMAGE_INTENT, false) mIsGetImageIntent = getBooleanExtra(GET_IMAGE_INTENT, false)
mIsGetVideoIntent = getBooleanExtra(GET_VIDEO_INTENT, false) mIsGetVideoIntent = getBooleanExtra(GET_VIDEO_INTENT, false)
mIsGetAnyIntent = getBooleanExtra(GET_ANY_INTENT, false) mIsGetAnyIntent = getBooleanExtra(GET_ANY_INTENT, false)
mAllowPickingMultiple = getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
} }
media_refresh_layout.setOnRefreshListener({ getMedia() }) media_refresh_layout.setOnRefreshListener({ getMedia() })
@ -72,6 +77,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mStoredAnimateGifs = config.animateGifs mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
mShowAll = config.showAll mShowAll = config.showAll
if (mShowAll) if (mShowAll)
supportActionBar?.setDisplayHomeAsUpEnabled(false) supportActionBar?.setDisplayHomeAsUpEnabled(false)
@ -92,13 +98,17 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
if (mStoredScrollHorizontally != config.scrollHorizontally) { if (mStoredScrollHorizontally != config.scrollHorizontally) {
media_grid.adapter?.let { (media_grid.adapter as? MediaAdapter)?.apply {
(it as MediaAdapter).scrollVertically = !config.scrollHorizontally scrollVertically = config.viewTypeFiles == VIEW_TYPE_LIST || !config.scrollHorizontally
it.notifyDataSetChanged() notifyDataSetChanged()
} }
setupScrollDirection() setupScrollDirection()
} }
if (mStoredTextColor != config.textColor) {
(media_grid.adapter as? MediaAdapter)?.updateTextColor(config.textColor)
}
tryloadGallery() tryloadGallery()
invalidateOptionsMenu() invalidateOptionsMenu()
media_empty_text_label.setTextColor(config.textColor) media_empty_text_label.setTextColor(config.textColor)
@ -112,8 +122,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mStoredAnimateGifs = config.animateGifs mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
media_grid.listener = null media_grid.listener = null
mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.removeCallbacksAndMessages(null)
mCurrAsyncTask?.stopFetching()
} }
override fun onDestroy() { override fun onDestroy() {
@ -149,7 +161,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
val currAdapter = media_grid.adapter val currAdapter = media_grid.adapter
if (currAdapter == null) { if (currAdapter == null) {
media_grid.adapter = MediaAdapter(this, mMedia, this, mIsGetAnyIntent) { media_grid.adapter = MediaAdapter(this, mMedia, this, mIsGetAnyIntent, mAllowPickingMultiple) {
itemClicked(it.path) itemClicked(it.path)
} }
} else { } else {
@ -159,13 +171,16 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
private fun setupScrollDirection() { private fun setupScrollDirection() {
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
media_refresh_layout.isEnabled = !config.scrollHorizontally
media_vertical_fastscroller.isHorizontal = false media_vertical_fastscroller.isHorizontal = false
media_vertical_fastscroller.beGoneIf(config.scrollHorizontally) media_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
media_horizontal_fastscroller.isHorizontal = true media_horizontal_fastscroller.isHorizontal = true
media_horizontal_fastscroller.beVisibleIf(config.scrollHorizontally) media_horizontal_fastscroller.beVisibleIf(allowHorizontalScroll)
if (config.scrollHorizontally) { if (allowHorizontalScroll) {
media_horizontal_fastscroller.setViews(media_grid, media_refresh_layout) media_horizontal_fastscroller.setViews(media_grid, media_refresh_layout)
} else { } else {
media_vertical_fastscroller.setViews(media_grid, media_refresh_layout) media_vertical_fastscroller.setViews(media_grid, media_refresh_layout)
@ -179,9 +194,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mLastMediaHandler.removeCallbacksAndMessages(null) mLastMediaHandler.removeCallbacksAndMessages(null)
mLastMediaHandler.postDelayed({ mLastMediaHandler.postDelayed({
Thread({ Thread({
val lastModified = getLastMediaModified() val mediaId = getLatestMediaId()
if (mLastMediaModified != lastModified) { if (mLatestMediaId != mediaId) {
mLastMediaModified = lastModified mLatestMediaId = mediaId
runOnUiThread { runOnUiThread {
getMedia() getMedia()
} }
@ -207,8 +222,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
findItem(R.id.increase_column_count).isVisible = config.mediaColumnCnt < 10 findItem(R.id.increase_column_count).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID && config.mediaColumnCnt < 10
findItem(R.id.reduce_column_count).isVisible = config.mediaColumnCnt > 1 findItem(R.id.reduce_column_count).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID && config.mediaColumnCnt > 1
findItem(R.id.toggle_filename).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID
} }
return true return true
@ -221,6 +238,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
R.id.toggle_filename -> toggleFilenameVisibility() R.id.toggle_filename -> toggleFilenameVisibility()
R.id.open_camera -> launchCamera() R.id.open_camera -> launchCamera()
R.id.folder_view -> switchToFolderView() R.id.folder_view -> switchToFolderView()
R.id.change_view_type -> changeViewType()
R.id.hide_folder -> tryHideFolder() R.id.hide_folder -> tryHideFolder()
R.id.unhide_folder -> unhideFolder() R.id.unhide_folder -> unhideFolder()
R.id.exclude_folder -> tryExcludeFolder() R.id.exclude_folder -> tryExcludeFolder()
@ -260,6 +278,20 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
finish() finish()
} }
private fun changeViewType() {
val items = arrayListOf(
RadioItem(VIEW_TYPE_GRID, getString(R.string.grid)),
RadioItem(VIEW_TYPE_LIST, getString(R.string.list)))
RadioGroupDialog(this, items, config.viewTypeFiles) {
config.viewTypeFiles = it as Int
invalidateOptionsMenu()
setupLayoutManager()
media_grid.adapter = null
setupAdapter()
}
}
private fun tryHideFolder() { private fun tryHideFolder() {
if (config.wasHideFolderTooltipShown) { if (config.wasHideFolderTooltipShown) {
hideFolder() hideFolder()
@ -309,7 +341,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mIsGettingMedia = true mIsGettingMedia = true
val token = object : TypeToken<List<Medium>>() {}.type val token = object : TypeToken<List<Medium>>() {}.type
val media = Gson().fromJson<ArrayList<Medium>>(config.loadFolderMedia(mPath), token) ?: ArrayList<Medium>(1) val media = Gson().fromJson<ArrayList<Medium>>(config.loadFolderMedia(mPath), token) ?: ArrayList(1)
if (media.isNotEmpty() && !mLoadedInitialPhotos) { if (media.isNotEmpty() && !mLoadedInitialPhotos) {
gotMedia(media, true) gotMedia(media, true)
} else { } else {
@ -317,9 +349,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
mLoadedInitialPhotos = true mLoadedInitialPhotos = true
GetMediaAsynctask(applicationContext, mPath, mIsGetVideoIntent, mIsGetImageIntent, mShowAll) { mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetVideoIntent, mIsGetImageIntent, mShowAll) {
gotMedia(it) gotMedia(it)
}.execute() }
mCurrAsyncTask!!.execute()
} }
private fun isDirEmpty(): Boolean { private fun isDirEmpty(): Boolean {
@ -350,6 +383,13 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private fun getRecyclerAdapter() = (media_grid.adapter as MediaAdapter) private fun getRecyclerAdapter() = (media_grid.adapter as MediaAdapter)
private fun setupLayoutManager() { private fun setupLayoutManager() {
if (config.viewTypeFiles == VIEW_TYPE_GRID)
setupGridLayoutManager()
else
setupListLayoutManager()
}
private fun setupGridLayoutManager() {
val layoutManager = media_grid.layoutManager as GridLayoutManager val layoutManager = media_grid.layoutManager as GridLayoutManager
if (config.scrollHorizontally) { if (config.scrollHorizontally) {
layoutManager.orientation = GridLayoutManager.HORIZONTAL layoutManager.orientation = GridLayoutManager.HORIZONTAL
@ -387,6 +427,16 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
} }
private fun setupListLayoutManager() {
media_grid.isDragSelectionEnabled = true
media_grid.isZoomingEnabled = false
val layoutManager = media_grid.layoutManager as GridLayoutManager
layoutManager.spanCount = 1
layoutManager.orientation = GridLayoutManager.VERTICAL
media_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
private fun increaseColumnCount() { private fun increaseColumnCount() {
config.mediaColumnCnt = ++(media_grid.layoutManager as GridLayoutManager).spanCount config.mediaColumnCnt = ++(media_grid.layoutManager as GridLayoutManager).spanCount
invalidateOptionsMenu() invalidateOptionsMenu()
@ -461,7 +511,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
} }
private fun gotMedia(media: ArrayList<Medium>, isFromCache: Boolean = false) { private fun gotMedia(media: ArrayList<Medium>, isFromCache: Boolean = false) {
mLastMediaModified = getLastMediaModified() mLatestMediaId = getLatestMediaId()
mIsGettingMedia = false mIsGettingMedia = false
media_refresh_layout.isRefreshing = false media_refresh_layout.isRefreshing = false
@ -513,4 +563,12 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
override fun itemLongClicked(position: Int) { override fun itemLongClicked(position: Int) {
media_grid.setDragSelectActive(position) media_grid.setDragSelectActive(position)
} }
override fun selectedPaths(paths: ArrayList<String>) {
Intent().apply {
putExtra(PICKED_PATHS, paths)
setResult(Activity.RESULT_OK, this)
}
finish()
}
} }

View file

@ -21,6 +21,7 @@ import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.fragments.PhotoFragment import com.simplemobiletools.gallery.fragments.PhotoFragment
import com.simplemobiletools.gallery.fragments.VideoFragment import com.simplemobiletools.gallery.fragments.VideoFragment
import com.simplemobiletools.gallery.fragments.ViewPagerFragment import com.simplemobiletools.gallery.fragments.ViewPagerFragment
import com.simplemobiletools.gallery.helpers.IS_FROM_GALLERY
import com.simplemobiletools.gallery.helpers.IS_VIEW_INTENT import com.simplemobiletools.gallery.helpers.IS_VIEW_INTENT
import com.simplemobiletools.gallery.helpers.MEDIUM import com.simplemobiletools.gallery.helpers.MEDIUM
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
@ -31,6 +32,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private val STORAGE_PERMISSION = 1 private val STORAGE_PERMISSION = 1
private var mMedium: Medium? = null private var mMedium: Medium? = null
private var mIsFullScreen = false private var mIsFullScreen = false
private var mIsFromGallery = false
private var mFragment: ViewPagerFragment? = null private var mFragment: ViewPagerFragment? = null
lateinit var mUri: Uri lateinit var mUri: Uri
@ -52,6 +54,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private fun checkIntent(savedInstanceState: Bundle? = null) { private fun checkIntent(savedInstanceState: Bundle? = null) {
mUri = intent.data ?: return mUri = intent.data ?: return
mIsFromGallery = intent.getBooleanExtra(IS_FROM_GALLERY, false)
if (mUri.scheme == "file") { if (mUri.scheme == "file") {
scanPath(mUri.path) {} scanPath(mUri.path) {}
@ -126,6 +129,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private fun sendViewPagerIntent(path: String) { private fun sendViewPagerIntent(path: String) {
Intent(this, ViewPagerActivity::class.java).apply { Intent(this, ViewPagerActivity::class.java).apply {
putExtra(IS_VIEW_INTENT, true) putExtra(IS_VIEW_INTENT, true)
putExtra(IS_FROM_GALLERY, mIsFromGallery)
putExtra(MEDIUM, path) putExtra(MEDIUM, path)
startActivity(this) startActivity(this)
} }

View file

@ -8,6 +8,7 @@ import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.dialogs.SecurityDialog import com.simplemobiletools.commons.dialogs.SecurityDialog
import com.simplemobiletools.commons.extensions.handleHiddenFolderPasswordProtection import com.simplemobiletools.commons.extensions.handleHiddenFolderPasswordProtection
import com.simplemobiletools.commons.extensions.updateTextColors import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.commons.helpers.PROTECTION_FINGERPRINT
import com.simplemobiletools.commons.helpers.SHOW_ALL_TABS import com.simplemobiletools.commons.helpers.SHOW_ALL_TABS
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
@ -169,7 +170,9 @@ class SettingsActivity : SimpleActivity() {
config.protectionType = type config.protectionType = type
if (config.isPasswordProtectionOn) { if (config.isPasswordProtectionOn) {
ConfirmationDialog(this, "", R.string.protection_setup_successfully, R.string.ok, 0) { } val confirmationTextId = if (config.protectionType == PROTECTION_FINGERPRINT)
R.string.fingerprint_setup_successfully else R.string.protection_setup_successfully
ConfirmationDialog(this, "", confirmationTextId, R.string.ok, 0) { }
} }
} }
} }

View file

@ -161,6 +161,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
if (config.isThirdPartyIntent) { if (config.isThirdPartyIntent) {
config.isThirdPartyIntent = false config.isThirdPartyIntent = false
if (intent.extras == null || !intent.getBooleanExtra(IS_FROM_GALLERY, false)) {
mMedia.clear()
}
} }
} }
@ -326,7 +330,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
val dragPosition = animation.animatedValue as Int val dragPosition = animation.animatedValue as Int
val dragOffset = dragPosition - oldDragPosition val dragOffset = dragPosition - oldDragPosition
oldDragPosition = dragPosition oldDragPosition = dragPosition
view_pager.fakeDragBy(dragOffset * (if (forward) 1f else -1f)) view_pager?.fakeDragBy(dragOffset * (if (forward) 1f else -1f))
} }
}) })
@ -433,34 +437,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
} }
private fun rotateImage() { private fun rotateImage() {
val currentMedium = getCurrentMedium() ?: return
if (currentMedium.isJpg() && !isPathOnSD(currentMedium.path)) {
rotateByExif()
} else {
rotateByDegrees()
}
}
private fun rotateByExif() {
val exif = ExifInterface(getCurrentPath())
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
val newOrientation = getNewOrientation(orientation)
exif.setAttribute(ExifInterface.TAG_ORIENTATION, newOrientation)
exif.saveAttributes()
File(getCurrentPath()).setLastModified(System.currentTimeMillis())
(getCurrentFragment() as? PhotoFragment)?.refreshBitmap()
}
private fun getNewOrientation(rotation: Int): String {
return when (rotation) {
ExifInterface.ORIENTATION_ROTATE_90 -> ExifInterface.ORIENTATION_ROTATE_180
ExifInterface.ORIENTATION_ROTATE_180 -> ExifInterface.ORIENTATION_ROTATE_270
ExifInterface.ORIENTATION_ROTATE_270 -> ExifInterface.ORIENTATION_NORMAL
else -> ExifInterface.ORIENTATION_ROTATE_90
}.toString()
}
private fun rotateByDegrees() {
mRotationDegrees = (mRotationDegrees + 90) % 360 mRotationDegrees = (mRotationDegrees + 90) % 360
getCurrentFragment()?.let { getCurrentFragment()?.let {
(it as? PhotoFragment)?.rotateImageViewBy(mRotationDegrees) (it as? PhotoFragment)?.rotateImageViewBy(mRotationDegrees)
@ -474,7 +450,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
Thread({ Thread({
toast(R.string.saving) toast(R.string.saving)
val selectedFile = File(it) val selectedFile = File(it)
val tmpFile = File(selectedFile.parent, "tmp_${it.getFilenameFromPath()}") val tmpFile = File(selectedFile.parent, ".tmp_${it.getFilenameFromPath()}")
try { try {
val bitmap = BitmapFactory.decodeFile(currPath) val bitmap = BitmapFactory.decodeFile(currPath)
getFileOutputStream(tmpFile) { getFileOutputStream(tmpFile) {
@ -497,7 +473,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
toast(R.string.out_of_memory_error) toast(R.string.out_of_memory_error)
deleteFile(tmpFile) {} deleteFile(tmpFile) {}
} catch (e: Exception) { } catch (e: Exception) {
toast(R.string.unknown_error_occurred) showErrorToast(e)
deleteFile(tmpFile) {} deleteFile(tmpFile) {}
} }
}).start() }).start()
@ -510,11 +486,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
val bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) val bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
bmp.compress(file.getCompressionFormat(), 90, out) bmp.compress(file.getCompressionFormat(), 90, out)
out.flush() out.flush()
toast(R.string.file_saved)
out.close() out.close()
toast(R.string.file_saved)
mRotationDegrees = 0f
invalidateOptionsMenu()
} }
private fun isShowHiddenFlagNeeded(): Boolean { private fun isShowHiddenFlagNeeded(): Boolean {
val file = File(mPath) val file = File(mPath)
if (file.isHidden) if (file.isHidden)

View file

@ -20,9 +20,10 @@ import com.simplemobiletools.gallery.activities.SimpleActivity
import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
import com.simplemobiletools.gallery.dialogs.PickMediumDialog import com.simplemobiletools.gallery.dialogs.PickMediumDialog
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
import com.simplemobiletools.gallery.models.AlbumCover import com.simplemobiletools.gallery.models.AlbumCover
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import kotlinx.android.synthetic.main.directory_item.view.* import kotlinx.android.synthetic.main.directory_item_list.view.*
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -31,11 +32,13 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
val multiSelector = MultiSelector() val multiSelector = MultiSelector()
val config = activity.config val config = activity.config
val isListViewType = config.viewTypeFolders == VIEW_TYPE_LIST
var actMode: ActionMode? = null var actMode: ActionMode? = null
var itemViews = SparseArray<View>() var itemViews = SparseArray<View>()
val selectedPositions = HashSet<Int>() val selectedPositions = HashSet<Int>()
var primaryColor = config.primaryColor var primaryColor = config.primaryColor
var textColor = config.textColor
var pinnedFolders = config.pinnedFolders var pinnedFolders = config.pinnedFolders
var scrollVertically = !config.scrollHorizontally var scrollVertically = !config.scrollHorizontally
@ -56,12 +59,12 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
updateTitle(selectedPositions.size) updateTitle(selectedPositions.size)
} }
fun updateTitle(cnt: Int) { private fun updateTitle(cnt: Int) {
actMode?.title = "$cnt / ${dirs.size}" actMode?.title = "$cnt / ${dirs.size}"
actMode?.invalidate() actMode?.invalidate()
} }
val adapterListener = object : MyAdapterListener { private val adapterListener = object : MyAdapterListener {
override fun toggleItemSelectionAdapter(select: Boolean, position: Int) { override fun toggleItemSelectionAdapter(select: Boolean, position: Int) {
toggleItemSelection(select, position) toggleItemSelection(select, position)
} }
@ -69,7 +72,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
override fun getSelectedPositions(): HashSet<Int> = selectedPositions override fun getSelectedPositions(): HashSet<Int> = selectedPositions
} }
val multiSelectorMode = object : ModalMultiSelectorCallback(multiSelector) { private val multiSelectorMode = object : ModalMultiSelectorCallback(multiSelector) {
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.cab_properties -> showProperties() R.id.cab_properties -> showProperties()
@ -119,7 +122,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun checkHideBtnVisibility(menu: Menu) { fun checkHideBtnVisibility(menu: Menu) {
var hiddenCnt = 0 var hiddenCnt = 0
var unhiddenCnt = 0 var unhiddenCnt = 0
selectedPositions.map { dirs.getOrNull(it)?.path }.filterNotNull().forEach { selectedPositions.mapNotNull { dirs.getOrNull(it)?.path }.forEach {
if (File(it).containsNoMedia()) if (File(it).containsNoMedia())
hiddenCnt++ hiddenCnt++
else else
@ -134,7 +137,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
val pinnedFolders = config.pinnedFolders val pinnedFolders = config.pinnedFolders
var pinnedCnt = 0 var pinnedCnt = 0
var unpinnedCnt = 0 var unpinnedCnt = 0
selectedPositions.map { dirs.getOrNull(it)?.path }.filterNotNull().forEach { selectedPositions.mapNotNull { dirs.getOrNull(it)?.path }.forEach {
if (pinnedFolders.contains(it)) if (pinnedFolders.contains(it))
pinnedCnt++ pinnedCnt++
else else
@ -218,7 +221,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
config.removePinnedFolders(getSelectedPaths()) config.removePinnedFolders(getSelectedPaths())
pinnedFolders = config.pinnedFolders pinnedFolders = config.pinnedFolders
listener?.refreshItems() listener?.recheckPinnedFolders()
notifyDataSetChanged() notifyDataSetChanged()
actMode?.finish() actMode?.finish()
} }
@ -242,7 +245,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun selectAll() { fun selectAll() {
val cnt = dirs.size val cnt = dirs.size
for (i in 0..cnt - 1) { for (i in 0 until cnt) {
selectedPositions.add(i) selectedPositions.add(i)
notifyItemChanged(i) notifyItemChanged(i)
} }
@ -286,13 +289,9 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
listener?.tryDeleteFolders(folders) listener?.tryDeleteFolders(folders)
val newItems = SparseArray<View>() val newItems = SparseArray<View>()
var curIndex = 0 (0 until itemViews.size())
for (i in 0..itemViews.size() - 1) { .filter { itemViews[it] != null }
if (itemViews[i] != null) { .forEachIndexed { curIndex, i -> newItems.put(curIndex, itemViews[i]) }
newItems.put(curIndex, itemViews[i])
curIndex++
}
}
itemViews = newItems itemViews = newItems
} }
@ -330,13 +329,14 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
} }
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent?.context).inflate(R.layout.directory_item, parent, false) val layoutType = if (isListViewType) R.layout.directory_item_list else R.layout.directory_item_grid
val view = LayoutInflater.from(parent?.context).inflate(layoutType, parent, false)
return ViewHolder(view, adapterListener, activity, multiSelectorMode, multiSelector, listener, isPickIntent, itemClick) return ViewHolder(view, adapterListener, activity, multiSelectorMode, multiSelector, listener, isPickIntent, itemClick)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val dir = dirs[position] val dir = dirs[position]
itemViews.put(position, holder.bindView(dir, pinnedFolders.contains(dir.path), scrollVertically)) itemViews.put(position, holder.bindView(dir, pinnedFolders.contains(dir.path), scrollVertically, isListViewType, textColor))
toggleItemSelection(selectedPositions.contains(position), position) toggleItemSelection(selectedPositions.contains(position), position)
holder.itemView.tag = holder holder.itemView.tag = holder
} }
@ -351,6 +351,12 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun updateDirs(newDirs: ArrayList<Directory>) { fun updateDirs(newDirs: ArrayList<Directory>) {
dirs = newDirs dirs = newDirs
notifyDataSetChanged() notifyDataSetChanged()
actMode?.finish()
}
fun updateTextColor(textColor: Int) {
this.textColor = textColor
notifyDataSetChanged()
} }
fun selectItem(pos: Int) { fun selectItem(pos: Int) {
@ -369,7 +375,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
toggleItemSelection(true, i) toggleItemSelection(true, i)
if (min > -1 && min < to) { if (min > -1 && min < to) {
(min..to - 1).filter { it != from } (min until to).filter { it != from }
.forEach { toggleItemSelection(false, it) } .forEach { toggleItemSelection(false, it) }
} }
if (max > -1) { if (max > -1) {
@ -386,7 +392,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
} }
if (min > -1) { if (min > -1) {
for (i in min..from - 1) for (i in min until from)
toggleItemSelection(false, i) toggleItemSelection(false, i)
} }
} }
@ -395,47 +401,52 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
class ViewHolder(val view: View, val adapterListener: MyAdapterListener, val activity: SimpleActivity, val multiSelectorCallback: ModalMultiSelectorCallback, class ViewHolder(val view: View, val adapterListener: MyAdapterListener, val activity: SimpleActivity, val multiSelectorCallback: ModalMultiSelectorCallback,
val multiSelector: MultiSelector, val listener: DirOperationsListener?, val isPickIntent: Boolean, val itemClick: (Directory) -> (Unit)) : val multiSelector: MultiSelector, val listener: DirOperationsListener?, val isPickIntent: Boolean, val itemClick: (Directory) -> (Unit)) :
SwappingHolder(view, MultiSelector()) { SwappingHolder(view, MultiSelector()) {
fun bindView(directory: Directory, isPinned: Boolean, scrollVertically: Boolean): View { fun bindView(directory: Directory, isPinned: Boolean, scrollVertically: Boolean, isListView: Boolean, textColor: Int): View {
itemView.apply { itemView.apply {
dir_name.text = directory.name dir_name.text = directory.name
dir_path?.text = "${directory.path.substringBeforeLast("/")}/"
photo_cnt.text = directory.mediaCnt.toString() photo_cnt.text = directory.mediaCnt.toString()
activity.loadImage(directory.tmb, dir_thumbnail, scrollVertically) activity.loadImage(directory.tmb, dir_thumbnail, scrollVertically)
dir_pin.beVisibleIf(isPinned) dir_pin.beVisibleIf(isPinned)
dir_sd_card.beVisibleIf(activity.isPathOnSD(directory.path)) dir_sd_card.beVisibleIf(activity.isPathOnSD(directory.path))
if (isListView) {
dir_name.setTextColor(textColor)
dir_path.setTextColor(textColor)
photo_cnt.setTextColor(textColor)
dir_pin.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
dir_sd_card.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
}
setOnClickListener { viewClicked(directory) } setOnClickListener { viewClicked(directory) }
setOnLongClickListener { if (isPickIntent) viewClicked(directory) else viewLongClicked(); true } setOnLongClickListener { if (isPickIntent) viewClicked(directory) else viewLongClicked(); true }
} }
return itemView return itemView
} }
fun viewClicked(directory: Directory) { private fun viewClicked(directory: Directory) {
if (multiSelector.isSelectable) { if (multiSelector.isSelectable) {
val isSelected = adapterListener.getSelectedPositions().contains(layoutPosition) val isSelected = adapterListener.getSelectedPositions().contains(adapterPosition)
adapterListener.toggleItemSelectionAdapter(!isSelected, layoutPosition) adapterListener.toggleItemSelectionAdapter(!isSelected, adapterPosition)
} else { } else {
itemClick(directory) itemClick(directory)
} }
} }
fun viewLongClicked() { private fun viewLongClicked() {
if (listener != null) { if (listener != null) {
if (!multiSelector.isSelectable) { if (!multiSelector.isSelectable) {
activity.startSupportActionMode(multiSelectorCallback) activity.startSupportActionMode(multiSelectorCallback)
adapterListener.toggleItemSelectionAdapter(true, layoutPosition) adapterListener.toggleItemSelectionAdapter(true, adapterPosition)
} }
listener.itemLongClicked(layoutPosition) listener.itemLongClicked(adapterPosition)
} }
} }
fun stopLoad() { fun stopLoad() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed)
return Glide.with(activity).clear(view.dir_thumbnail)
Glide.with(activity).clear(view.dir_thumbnail)
} }
} }
@ -451,5 +462,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun tryDeleteFolders(folders: ArrayList<File>) fun tryDeleteFolders(folders: ArrayList<File>)
fun itemLongClicked(position: Int) fun itemLongClicked(position: Int)
fun recheckPinnedFolders()
} }
} }

View file

@ -18,21 +18,24 @@ import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.SimpleActivity import com.simplemobiletools.gallery.activities.SimpleActivity
import com.simplemobiletools.gallery.extensions.* import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import kotlinx.android.synthetic.main.photo_video_item.view.* import kotlinx.android.synthetic.main.photo_video_item_grid.view.*
import java.io.File import java.io.File
import java.util.* import java.util.*
class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>, val listener: MediaOperationsListener?, val isPickIntent: Boolean, class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>, val listener: MediaOperationsListener?, val isPickIntent: Boolean,
val itemClick: (Medium) -> Unit) : RecyclerView.Adapter<MediaAdapter.ViewHolder>() { val allowMultiplePicks: Boolean, val itemClick: (Medium) -> Unit) : RecyclerView.Adapter<MediaAdapter.ViewHolder>() {
val multiSelector = MultiSelector() val multiSelector = MultiSelector()
val config = activity.config val config = activity.config
val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST
var actMode: ActionMode? = null var actMode: ActionMode? = null
var itemViews = SparseArray<View>() var itemViews = SparseArray<View>()
val selectedPositions = HashSet<Int>() val selectedPositions = HashSet<Int>()
var primaryColor = config.primaryColor var primaryColor = config.primaryColor
var textColor = config.textColor
var displayFilenames = config.displayFileNames var displayFilenames = config.displayFileNames
var scrollVertically = !config.scrollHorizontally var scrollVertically = !config.scrollHorizontally
@ -53,12 +56,12 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
updateTitle(selectedPositions.size) updateTitle(selectedPositions.size)
} }
fun updateTitle(cnt: Int) { private fun updateTitle(cnt: Int) {
actMode?.title = "$cnt / ${media.size}" actMode?.title = "$cnt / ${media.size}"
actMode?.invalidate() actMode?.invalidate()
} }
val adapterListener = object : MyAdapterListener { private val adapterListener = object : MyAdapterListener {
override fun toggleItemSelectionAdapter(select: Boolean, position: Int) { override fun toggleItemSelectionAdapter(select: Boolean, position: Int) {
toggleItemSelection(select, position) toggleItemSelection(select, position)
} }
@ -66,9 +69,10 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
override fun getSelectedPositions(): HashSet<Int> = selectedPositions override fun getSelectedPositions(): HashSet<Int> = selectedPositions
} }
val multiSelectorMode = object : ModalMultiSelectorCallback(multiSelector) { private val multiSelectorMode = object : ModalMultiSelectorCallback(multiSelector) {
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.cab_confirm_selection -> confirmSelection()
R.id.cab_properties -> showProperties() R.id.cab_properties -> showProperties()
R.id.cab_rename -> renameFile() R.id.cab_rename -> renameFile()
R.id.cab_edit -> editFile() R.id.cab_edit -> editFile()
@ -94,6 +98,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
override fun onPrepareActionMode(actionMode: ActionMode?, menu: Menu): Boolean { override fun onPrepareActionMode(actionMode: ActionMode?, menu: Menu): Boolean {
menu.findItem(R.id.cab_rename).isVisible = selectedPositions.size <= 1 menu.findItem(R.id.cab_rename).isVisible = selectedPositions.size <= 1
menu.findItem(R.id.cab_edit).isVisible = selectedPositions.size == 1 && media.size > selectedPositions.first() && media[selectedPositions.first()].isImage() menu.findItem(R.id.cab_edit).isVisible = selectedPositions.size == 1 && media.size > selectedPositions.first() && media[selectedPositions.first()].isImage()
menu.findItem(R.id.cab_confirm_selection).isVisible = isPickIntent && allowMultiplePicks && selectedPositions.size > 0
checkHideBtnVisibility(menu) checkHideBtnVisibility(menu)
@ -112,7 +117,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun checkHideBtnVisibility(menu: Menu) { fun checkHideBtnVisibility(menu: Menu) {
var hiddenCnt = 0 var hiddenCnt = 0
var unhiddenCnt = 0 var unhiddenCnt = 0
selectedPositions.map { media.getOrNull(it) }.filterNotNull().forEach { selectedPositions.mapNotNull { media.getOrNull(it) }.forEach {
if (it.name.startsWith('.')) if (it.name.startsWith('.'))
hiddenCnt++ hiddenCnt++
else else
@ -124,6 +129,11 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
} }
} }
private fun confirmSelection() {
val paths = getSelectedMedia().map { it.path } as ArrayList<String>
listener?.selectedPaths(paths)
}
private fun showProperties() { private fun showProperties() {
if (selectedPositions.size <= 1) { if (selectedPositions.size <= 1) {
PropertiesDialog(activity, media[selectedPositions.first()].path, config.shouldShowHidden) PropertiesDialog(activity, media[selectedPositions.first()].path, config.shouldShowHidden)
@ -184,7 +194,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun selectAll() { fun selectAll() {
val cnt = media.size val cnt = media.size
for (i in 0..cnt - 1) { for (i in 0 until cnt) {
selectedPositions.add(i) selectedPositions.add(i)
multiSelector.setSelected(i, 0, true) multiSelector.setSelected(i, 0, true)
notifyItemChanged(i) notifyItemChanged(i)
@ -227,13 +237,9 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
listener?.deleteFiles(files) listener?.deleteFiles(files)
val newItems = SparseArray<View>() val newItems = SparseArray<View>()
var curIndex = 0 (0 until itemViews.size())
for (i in 0..itemViews.size() - 1) { .filter { itemViews[it] != null }
if (itemViews[i] != null) { .forEachIndexed { curIndex, i -> newItems.put(curIndex, itemViews[i]) }
newItems.put(curIndex, itemViews[i])
curIndex++
}
}
itemViews = newItems itemViews = newItems
} }
@ -246,12 +252,13 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
} }
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent?.context).inflate(R.layout.photo_video_item, parent, false) val layoutType = if (isListViewType) R.layout.photo_video_item_list else R.layout.photo_video_item_grid
return ViewHolder(view, adapterListener, activity, multiSelectorMode, multiSelector, listener, isPickIntent, itemClick) val view = LayoutInflater.from(parent?.context).inflate(layoutType, parent, false)
return ViewHolder(view, adapterListener, activity, multiSelectorMode, multiSelector, listener, allowMultiplePicks || !isPickIntent, itemClick)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
itemViews.put(position, holder.bindView(media[position], displayFilenames, scrollVertically)) itemViews.put(position, holder.bindView(media[position], displayFilenames, scrollVertically, isListViewType, textColor))
toggleItemSelection(selectedPositions.contains(position), position) toggleItemSelection(selectedPositions.contains(position), position)
holder.itemView.tag = holder holder.itemView.tag = holder
} }
@ -266,6 +273,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun updateMedia(newMedia: ArrayList<Medium>) { fun updateMedia(newMedia: ArrayList<Medium>) {
media = newMedia media = newMedia
notifyDataSetChanged() notifyDataSetChanged()
actMode?.finish()
} }
fun updateDisplayFilenames(display: Boolean) { fun updateDisplayFilenames(display: Boolean) {
@ -273,6 +281,11 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
notifyDataSetChanged() notifyDataSetChanged()
} }
fun updateTextColor(textColor: Int) {
this.textColor = textColor
notifyDataSetChanged()
}
fun selectItem(pos: Int) { fun selectItem(pos: Int) {
toggleItemSelection(true, pos) toggleItemSelection(true, pos)
} }
@ -289,7 +302,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
toggleItemSelection(true, i) toggleItemSelection(true, i)
if (min > -1 && min < to) { if (min > -1 && min < to) {
(min..to - 1).filter { it != from } (min until to).filter { it != from }
.forEach { toggleItemSelection(false, it) } .forEach { toggleItemSelection(false, it) }
} }
if (max > -1) { if (max > -1) {
@ -306,53 +319,57 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
} }
if (min > -1) { if (min > -1) {
for (i in min..from - 1) for (i in min until from)
toggleItemSelection(false, i) toggleItemSelection(false, i)
} }
} }
} }
class ViewHolder(val view: View, val adapterListener: MyAdapterListener, val activity: SimpleActivity, val multiSelectorCallback: ModalMultiSelectorCallback, class ViewHolder(val view: View, val adapterListener: MyAdapterListener, val activity: SimpleActivity, val multiSelectorCallback: ModalMultiSelectorCallback,
val multiSelector: MultiSelector, val listener: MediaOperationsListener?, val isPickIntent: Boolean, val itemClick: (Medium) -> (Unit)) : val multiSelector: MultiSelector, val listener: MediaOperationsListener?, val allowMultiplePicks: Boolean,
val itemClick: (Medium) -> (Unit)) :
SwappingHolder(view, MultiSelector()) { SwappingHolder(view, MultiSelector()) {
fun bindView(medium: Medium, displayFilenames: Boolean, scrollVertically: Boolean): View { fun bindView(medium: Medium, displayFilenames: Boolean, scrollVertically: Boolean, isListViewType: Boolean, textColor: Int): View {
itemView.apply { itemView.apply {
play_outline.visibility = if (medium.video) View.VISIBLE else View.GONE play_outline.visibility = if (medium.video) View.VISIBLE else View.GONE
photo_name.beVisibleIf(displayFilenames) photo_name.beVisibleIf(displayFilenames || isListViewType)
photo_name.text = medium.name photo_name.text = medium.name
activity.loadImage(medium.path, medium_thumbnail, scrollVertically) activity.loadImage(medium.path, medium_thumbnail, scrollVertically)
if (isListViewType) {
photo_name.setTextColor(textColor)
play_outline.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
}
setOnClickListener { viewClicked(medium) } setOnClickListener { viewClicked(medium) }
setOnLongClickListener { if (isPickIntent) viewClicked(medium) else viewLongClicked(); true } setOnLongClickListener { if (allowMultiplePicks) viewLongClicked() else viewClicked(medium); true }
} }
return itemView return itemView
} }
fun viewClicked(medium: Medium) { private fun viewClicked(medium: Medium) {
if (multiSelector.isSelectable) { if (multiSelector.isSelectable) {
val isSelected = adapterListener.getSelectedPositions().contains(layoutPosition) val isSelected = adapterListener.getSelectedPositions().contains(adapterPosition)
adapterListener.toggleItemSelectionAdapter(!isSelected, layoutPosition) adapterListener.toggleItemSelectionAdapter(!isSelected, adapterPosition)
} else { } else {
itemClick(medium) itemClick(medium)
} }
} }
fun viewLongClicked() { private fun viewLongClicked() {
if (listener != null) { if (listener != null) {
if (!multiSelector.isSelectable) { if (!multiSelector.isSelectable) {
activity.startSupportActionMode(multiSelectorCallback) activity.startSupportActionMode(multiSelectorCallback)
adapterListener.toggleItemSelectionAdapter(true, layoutPosition) adapterListener.toggleItemSelectionAdapter(true, adapterPosition)
} }
listener.itemLongClicked(layoutPosition) listener.itemLongClicked(adapterPosition)
} }
} }
fun stopLoad() { fun stopLoad() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed)
return Glide.with(activity).clear(view.medium_thumbnail)
Glide.with(activity).clear(view.medium_thumbnail)
} }
} }
@ -368,5 +385,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun deleteFiles(files: ArrayList<File>) fun deleteFiles(files: ArrayList<File>)
fun itemLongClicked(position: Int) fun itemLongClicked(position: Int)
fun selectedPaths(paths: ArrayList<String>)
} }
} }

View file

@ -6,125 +6,70 @@ import com.simplemobiletools.commons.extensions.getFilenameFromPath
import com.simplemobiletools.commons.extensions.hasWriteStoragePermission import com.simplemobiletools.commons.extensions.hasWriteStoragePermission
import com.simplemobiletools.commons.extensions.internalStoragePath import com.simplemobiletools.commons.extensions.internalStoragePath
import com.simplemobiletools.commons.extensions.sdCardPath import com.simplemobiletools.commons.extensions.sdCardPath
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.containsNoMedia import com.simplemobiletools.gallery.extensions.containsNoMedia
import com.simplemobiletools.gallery.extensions.getFilesFrom import com.simplemobiletools.gallery.extensions.sumByLong
import com.simplemobiletools.gallery.helpers.MediaFetcher
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import java.io.File import java.io.File
import java.util.*
class GetDirectoriesAsynctask(val context: Context, val isPickVideo: Boolean, val isPickImage: Boolean, class GetDirectoriesAsynctask(val context: Context, val isPickVideo: Boolean, val isPickImage: Boolean,
val callback: (dirs: ArrayList<Directory>) -> Unit) : AsyncTask<Void, Void, ArrayList<Directory>>() { val callback: (dirs: ArrayList<Directory>) -> Unit) : AsyncTask<Void, Void, ArrayList<Directory>>() {
var config = context.config val mediaFetcher = MediaFetcher(context)
var shouldStop = false
val showHidden = config.shouldShowHidden
override fun doInBackground(vararg params: Void): ArrayList<Directory> { override fun doInBackground(vararg params: Void): ArrayList<Directory> {
if (!context.hasWriteStoragePermission()) if (!context.hasWriteStoragePermission())
return ArrayList() return ArrayList()
val media = context.getFilesFrom("", isPickImage, isPickVideo) val config = context.config
val excludedPaths = config.excludedFolders val groupedMedia = mediaFetcher.getMediaByDirectories(isPickVideo, isPickImage)
val includedPaths = config.includedFolders val directories = ArrayList<Directory>()
val directories = groupDirectories(media)
val dirs = ArrayList(directories.values
.filter { File(it.path).exists() })
.filter { shouldFolderBeVisible(it.path, excludedPaths, includedPaths) } as ArrayList<Directory>
Directory.sorting = config.directorySorting
dirs.sort()
return movePinnedToFront(dirs)
}
private fun groupDirectories(media: ArrayList<Medium>): Map<String, Directory> {
val albumCovers = config.parseAlbumCovers()
val hidden = context.resources.getString(R.string.hidden) val hidden = context.resources.getString(R.string.hidden)
val directories = LinkedHashMap<String, Directory>() val albumCovers = config.parseAlbumCovers()
for ((name, path, isVideo, dateModified, dateTaken, size) in media) { for ((path, curMedia) in groupedMedia) {
if (shouldStop) Medium.sorting = config.getFileSorting(path)
cancel(true) curMedia.sort()
val parentDir = File(path).parent ?: continue val firstItem = curMedia.first()
if (directories.containsKey(parentDir.toLowerCase())) { val lastItem = curMedia.last()
val directory = directories[parentDir.toLowerCase()]!! val parentDir = File(firstItem.path).parent
val newImageCnt = directory.mediaCnt + 1 var thumbnail = firstItem.path
directory.mediaCnt = newImageCnt albumCovers.forEach {
directory.addSize(size) if (it.path == parentDir && File(it.tmb).exists()) {
} else { thumbnail = it.tmb
var dirName = parentDir.getFilenameFromPath()
if (parentDir == context.internalStoragePath) {
dirName = context.getString(R.string.internal)
} else if (parentDir == context.sdCardPath) {
dirName = context.getString(R.string.sd_card)
} }
if (File(parentDir).containsNoMedia()) {
dirName += " $hidden"
if (!showHidden)
continue
}
var thumbnail = path
albumCovers.forEach {
if (it.path == parentDir && File(it.tmb).exists()) {
thumbnail = it.tmb
}
}
val directory = Directory(parentDir, thumbnail, dirName, 1, dateModified, dateTaken, size)
directories.put(parentDir.toLowerCase(), directory)
} }
var dirName = when (parentDir) {
context.internalStoragePath -> context.getString(R.string.internal)
context.sdCardPath -> context.getString(R.string.sd_card)
else -> parentDir.getFilenameFromPath()
}
if (File(parentDir).containsNoMedia()) {
dirName += " $hidden"
}
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)
directories.add(directory)
} }
return directories return directories
} }
private fun shouldFolderBeVisible(path: String, excludedPaths: MutableSet<String>, includedPaths: MutableSet<String>): Boolean {
val file = File(path)
return if (includedPaths.contains(path)) {
true
} else if (isThisOrParentExcluded(path, excludedPaths, includedPaths)) {
false
} else if (!config.shouldShowHidden && file.isDirectory && file.canonicalFile == file.absoluteFile) {
var containsNoMediaOrDot = file.containsNoMedia() || path.contains("/.")
if (!containsNoMediaOrDot) {
containsNoMediaOrDot = checkParentHasNoMedia(file.parentFile)
}
!containsNoMediaOrDot
} else {
true
}
}
private fun checkParentHasNoMedia(file: File): Boolean {
var curFile = file
while (true) {
if (curFile.containsNoMedia()) {
return true
}
curFile = curFile.parentFile
if (curFile.absolutePath == "/")
break
}
return false
}
private fun isThisOrParentExcluded(path: String, excludedPaths: MutableSet<String>, includedPaths: MutableSet<String>) =
includedPaths.none { path.startsWith(it) } && excludedPaths.any { path.startsWith(it) }
private fun movePinnedToFront(dirs: ArrayList<Directory>): ArrayList<Directory> {
val foundFolders = ArrayList<Directory>()
val pinnedFolders = config.pinnedFolders
dirs.forEach { if (pinnedFolders.contains(it.path)) foundFolders.add(it) }
dirs.removeAll(foundFolders)
dirs.addAll(0, foundFolders)
return dirs
}
override fun onPostExecute(dirs: ArrayList<Directory>) { override fun onPostExecute(dirs: ArrayList<Directory>) {
super.onPostExecute(dirs) super.onPostExecute(dirs)
callback(dirs) callback(dirs)
} }
fun stopFetching() {
mediaFetcher.shouldStop = true
cancel(true)
}
} }

View file

@ -2,21 +2,39 @@ package com.simplemobiletools.gallery.asynctasks
import android.content.Context import android.content.Context
import android.os.AsyncTask import android.os.AsyncTask
import com.simplemobiletools.gallery.extensions.getFilesFrom import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.helpers.MediaFetcher
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import java.util.* 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 isPickVideo: Boolean = false, val isPickImage: Boolean = false,
val showAll: Boolean, val callback: (media: ArrayList<Medium>) -> Unit) : val showAll: Boolean, val callback: (media: ArrayList<Medium>) -> Unit) :
AsyncTask<Void, Void, ArrayList<Medium>>() { AsyncTask<Void, Void, ArrayList<Medium>>() {
val mediaFetcher = MediaFetcher(context)
override fun doInBackground(vararg params: Void): ArrayList<Medium> { override fun doInBackground(vararg params: Void): ArrayList<Medium> {
val path = if (showAll) "" else mPath return if (showAll) {
return context.getFilesFrom(path, isPickImage, isPickVideo) val mediaMap = mediaFetcher.getMediaByDirectories(isPickVideo, isPickImage)
val media = ArrayList<Medium>()
for ((path, curMedia) in mediaMap) {
media.addAll(curMedia)
}
Medium.sorting = context.config.getFileSorting("")
media.sort()
media
} else {
mediaFetcher.getFilesFrom(mPath, isPickImage, isPickVideo)
}
} }
override fun onPostExecute(media: ArrayList<Medium>) { override fun onPostExecute(media: ArrayList<Medium>) {
super.onPostExecute(media) super.onPostExecute(media)
callback(media) callback(media)
} }
fun stopFetching() {
mediaFetcher.shouldStop = true
cancel(true)
}
} }

View file

@ -15,6 +15,8 @@ import com.simplemobiletools.gallery.adapters.DirectoryAdapter
import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask import com.simplemobiletools.gallery.asynctasks.GetDirectoriesAsynctask
import com.simplemobiletools.gallery.extensions.config import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getCachedDirectories import com.simplemobiletools.gallery.extensions.getCachedDirectories
import com.simplemobiletools.gallery.extensions.getSortedDirectories
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID
import com.simplemobiletools.gallery.models.Directory import com.simplemobiletools.gallery.models.Directory
import kotlinx.android.synthetic.main.dialog_directory_picker.view.* import kotlinx.android.synthetic.main.dialog_directory_picker.view.*
@ -22,11 +24,12 @@ class PickDirectoryDialog(val activity: SimpleActivity, val sourcePath: String,
var dialog: AlertDialog var dialog: AlertDialog
var shownDirectories: ArrayList<Directory> = ArrayList() var shownDirectories: ArrayList<Directory> = ArrayList()
var view: View = LayoutInflater.from(activity).inflate(R.layout.dialog_directory_picker, null) var view: View = LayoutInflater.from(activity).inflate(R.layout.dialog_directory_picker, null)
var isGridViewType = activity.config.viewTypeFolders == VIEW_TYPE_GRID
init { init {
(view.directories_grid.layoutManager as GridLayoutManager).apply { (view.directories_grid.layoutManager as GridLayoutManager).apply {
orientation = if (activity.config.scrollHorizontally) GridLayoutManager.HORIZONTAL else GridLayoutManager.VERTICAL orientation = if (activity.config.scrollHorizontally && isGridViewType) GridLayoutManager.HORIZONTAL else GridLayoutManager.VERTICAL
spanCount = activity.config.dirColumnCnt spanCount = if (isGridViewType) activity.config.dirColumnCnt else 1
} }
dialog = AlertDialog.Builder(activity) dialog = AlertDialog.Builder(activity)
@ -47,19 +50,20 @@ class PickDirectoryDialog(val activity: SimpleActivity, val sourcePath: String,
} }
} }
fun showOtherFolder() { private fun showOtherFolder() {
val showHidden = activity.config.shouldShowHidden val showHidden = activity.config.shouldShowHidden
FilePickerDialog(activity, sourcePath, false, showHidden, true) { FilePickerDialog(activity, sourcePath, false, showHidden, true) {
callback(it) callback(it)
} }
} }
private fun gotDirectories(directories: ArrayList<Directory>) { private fun gotDirectories(newDirs: ArrayList<Directory>) {
if (directories.hashCode() == shownDirectories.hashCode()) val dirs = activity.getSortedDirectories(newDirs)
if (dirs.hashCode() == shownDirectories.hashCode())
return return
shownDirectories = directories shownDirectories = dirs
val adapter = DirectoryAdapter(activity, directories, null, true) { val adapter = DirectoryAdapter(activity, dirs, null, true) {
if (it.path.trimEnd('/') == sourcePath) { if (it.path.trimEnd('/') == sourcePath) {
activity.toast(R.string.source_and_destination_same) activity.toast(R.string.source_and_destination_same)
return@DirectoryAdapter return@DirectoryAdapter
@ -69,7 +73,7 @@ class PickDirectoryDialog(val activity: SimpleActivity, val sourcePath: String,
} }
} }
val scrollHorizontally = activity.config.scrollHorizontally val scrollHorizontally = activity.config.scrollHorizontally && isGridViewType
view.apply { view.apply {
directories_grid.adapter = adapter directories_grid.adapter = adapter

View file

@ -47,7 +47,7 @@ class PickMediumDialog(val activity: SimpleActivity, val path: String, val callb
return return
shownMedia = media shownMedia = media
val adapter = MediaAdapter(activity, media, null, true) { val adapter = MediaAdapter(activity, media, null, true, false) {
callback(it.path) callback(it.path)
dialog.dismiss() dialog.dismiss()
} }

View file

@ -24,6 +24,7 @@ import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.gallery.BuildConfig import com.simplemobiletools.gallery.BuildConfig
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.SimpleActivity import com.simplemobiletools.gallery.activities.SimpleActivity
import com.simplemobiletools.gallery.helpers.IS_FROM_GALLERY
import com.simplemobiletools.gallery.helpers.NOMEDIA import com.simplemobiletools.gallery.helpers.NOMEDIA
import com.simplemobiletools.gallery.helpers.REQUEST_EDIT_IMAGE import com.simplemobiletools.gallery.helpers.REQUEST_EDIT_IMAGE
import com.simplemobiletools.gallery.helpers.REQUEST_SET_AS import com.simplemobiletools.gallery.helpers.REQUEST_SET_AS
@ -91,14 +92,14 @@ fun Activity.setAs(uri: Uri, file: File, showToast: Boolean = true): Boolean {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val chooser = Intent.createChooser(this, getString(R.string.set_as)) val chooser = Intent.createChooser(this, getString(R.string.set_as))
if (resolveActivity(packageManager) != null) { success = if (resolveActivity(packageManager) != null) {
startActivityForResult(chooser, REQUEST_SET_AS) startActivityForResult(chooser, REQUEST_SET_AS)
success = true true
} else { } else {
if (showToast) { if (showToast) {
toast(R.string.no_capable_app_found) toast(R.string.no_capable_app_found)
} }
success = false false
} }
} }
@ -130,6 +131,7 @@ fun Activity.openWith(file: File, forceChooser: Boolean = true) {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
setDataAndType(uri, file.getMimeType()) setDataAndType(uri, file.getMimeType())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
putExtra(IS_FROM_GALLERY, true)
if (resolveActivity(packageManager) != null) { if (resolveActivity(packageManager) != null) {
val chooser = Intent.createChooser(this, getString(R.string.open_with)) val chooser = Intent.createChooser(this, getString(R.string.open_with))
@ -250,10 +252,10 @@ fun SimpleActivity.removeNoMedia(path: String, callback: () -> Unit) {
fun SimpleActivity.toggleFileVisibility(oldFile: File, hide: Boolean, callback: (newFile: File) -> Unit) { fun SimpleActivity.toggleFileVisibility(oldFile: File, hide: Boolean, callback: (newFile: File) -> Unit) {
val path = oldFile.parent val path = oldFile.parent
var filename = oldFile.name var filename = oldFile.name
if (hide) { filename = if (hide) {
filename = ".${filename.trimStart('.')}" ".${filename.trimStart('.')}"
} else { } else {
filename = filename.substring(1, filename.length) filename.substring(1, filename.length)
} }
val newFile = File(path, filename) val newFile = File(path, filename)
renameFile(oldFile, newFile) { renameFile(oldFile, newFile) {

View file

@ -0,0 +1,5 @@
package com.simplemobiletools.gallery.extensions
import java.util.*
fun <E> ArrayList<E>.sumByLong(selector: (E) -> Long) = map { selector(it) }.sum()

View file

@ -4,21 +4,45 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.database.Cursor import android.database.Cursor
import android.graphics.Point
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.provider.MediaStore import android.provider.MediaStore
import com.simplemobiletools.commons.extensions.* import android.view.WindowManager
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED import com.simplemobiletools.commons.extensions.humanizePath
import com.simplemobiletools.commons.helpers.SORT_BY_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_SIZE
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.gallery.activities.SettingsActivity import com.simplemobiletools.gallery.activities.SettingsActivity
import com.simplemobiletools.gallery.helpers.* import com.simplemobiletools.gallery.helpers.Config
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Directory
import java.io.File
val Context.portrait get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT val Context.portrait get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
val Context.audioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager val Context.audioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val Context.windowManager: WindowManager get() = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val Context.navigationBarRight: Boolean get() = usableScreenSize.x < realScreenSize.x
val Context.navigationBarBottom: Boolean get() = usableScreenSize.y < realScreenSize.y
val Context.navigationBarHeight: Int get() = if (navigationBarBottom) navigationBarSize.y else 0
internal val Context.navigationBarSize: Point
get() = when {
navigationBarRight -> Point(realScreenSize.x - usableScreenSize.x, usableScreenSize.y)
navigationBarBottom -> Point(usableScreenSize.x, realScreenSize.y - usableScreenSize.y)
else -> Point()
}
val Context.usableScreenSize: Point
get() {
val size = Point()
windowManager.defaultDisplay.getSize(size)
return size
}
val Context.realScreenSize: Point
get() {
val size = Point()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
windowManager.defaultDisplay.getRealSize(size)
return size
}
fun Context.getRealPathFromURI(uri: Uri): String? { fun Context.getRealPathFromURI(uri: Uri): String? {
var cursor: Cursor? = null var cursor: Cursor? = null
@ -47,213 +71,24 @@ fun Context.launchSettings() {
val Context.config: Config get() = Config.newInstance(this) val Context.config: Config get() = Config.newInstance(this)
fun Context.getFilesFrom(curPath: String, isPickImage: Boolean, isPickVideo: Boolean): ArrayList<Medium> { fun Context.movePinnedDirectoriesToFront(dirs: ArrayList<Directory>): ArrayList<Directory> {
val projection = arrayOf(MediaStore.Images.Media._ID, val foundFolders = ArrayList<Directory>()
MediaStore.Images.Media.DISPLAY_NAME, val pinnedFolders = config.pinnedFolders
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.DATE_MODIFIED,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.SIZE)
val uri = MediaStore.Files.getContentUri("external")
val selection = if (curPath.isEmpty()) null else "(${MediaStore.Images.Media.DATA} LIKE ? AND ${MediaStore.Images.Media.DATA} NOT LIKE ?)"
val selectionArgs = if (curPath.isEmpty()) null else arrayOf("$curPath/%", "$curPath/%/%")
return try { dirs.forEach {
val cur = contentResolver.query(uri, projection, selection, selectionArgs, getSortingForFolder(curPath)) if (pinnedFolders.contains(it.path))
parseCursor(this, cur, isPickImage, isPickVideo, curPath) foundFolders.add(it)
} catch (e: Exception) {
ArrayList()
} }
dirs.removeAll(foundFolders)
dirs.addAll(0, foundFolders)
return dirs
} }
private fun parseCursor(context: Context, cur: Cursor, isPickImage: Boolean, isPickVideo: Boolean, curPath: String): ArrayList<Medium> { @Suppress("UNCHECKED_CAST")
val curMedia = ArrayList<Medium>() fun Context.getSortedDirectories(source: ArrayList<Directory>): ArrayList<Directory> {
val config = context.config Directory.sorting = config.directorySorting
val filterMedia = config.filterMedia val dirs = source.clone() as ArrayList<Directory>
val showHidden = config.shouldShowHidden dirs.sort()
val includedFolders = config.includedFolders.map { "${it.trimEnd('/')}/" } return movePinnedDirectoriesToFront(dirs)
val excludedFolders = config.excludedFolders.map { "${it.trimEnd('/')}/" }
val noMediaFolders = context.getNoMediaFolders()
val isThirdPartyIntent = config.isThirdPartyIntent
cur.use {
if (cur.moveToFirst()) {
do {
try {
val path = cur.getStringValue(MediaStore.Images.Media.DATA)
var size = cur.getLongValue(MediaStore.Images.Media.SIZE)
if (size == 0L) {
size = File(path).length()
}
if (size <= 0L) {
continue
}
var filename = cur.getStringValue(MediaStore.Images.Media.DISPLAY_NAME) ?: ""
if (filename.isEmpty())
filename = path.getFilenameFromPath()
val isImage = filename.isImageFast()
val isVideo = if (isImage) false else filename.isVideoFast()
val isGif = if (isImage || isVideo) false else filename.isGif()
if (!isImage && !isVideo && !isGif)
continue
if (isVideo && (isPickImage || filterMedia and VIDEOS == 0))
continue
if (isImage && (isPickVideo || filterMedia and IMAGES == 0))
continue
if (isGif && filterMedia and GIFS == 0)
continue
if (!showHidden && filename.startsWith('.'))
continue
var isExcluded = false
excludedFolders.forEach {
if (path.startsWith(it)) {
isExcluded = true
includedFolders.forEach {
if (path.startsWith(it)) {
isExcluded = false
}
}
}
}
if (!isExcluded && !showHidden) {
noMediaFolders.forEach {
if (path.startsWith(it)) {
isExcluded = true
}
}
}
if (!isExcluded && !showHidden && path.contains("/.")) {
isExcluded = true
}
if (!isExcluded || isThirdPartyIntent) {
val dateTaken = cur.getLongValue(MediaStore.Images.Media.DATE_TAKEN)
val dateModified = cur.getIntValue(MediaStore.Images.Media.DATE_MODIFIED) * 1000L
val medium = Medium(filename, path, isVideo, dateModified, dateTaken, size)
curMedia.add(medium)
}
} catch (e: Exception) {
continue
}
} while (cur.moveToNext())
}
}
config.includedFolders.filter { it.isNotEmpty() && (curPath.isEmpty() || it == curPath) }.forEach {
getMediaInFolder(it, curMedia, isPickImage, isPickVideo, filterMedia)
}
if (isThirdPartyIntent && curPath.isNotEmpty() && curMedia.isEmpty()) {
getMediaInFolder(curPath, curMedia, isPickImage, isPickVideo, filterMedia)
}
Medium.sorting = config.getFileSorting(curPath)
curMedia.sort()
return curMedia
}
private fun getMediaInFolder(folder: String, curMedia: ArrayList<Medium>, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int) {
val files = File(folder).listFiles() ?: return
for (file in files) {
val size = file.length()
if (size <= 0L) {
continue
}
val filename = file.name
val isImage = filename.isImageFast()
val isVideo = if (isImage) false else filename.isVideoFast()
val isGif = if (isImage || isVideo) false else filename.isGif()
if (!isImage && !isVideo)
continue
if (isVideo && (isPickImage || filterMedia and VIDEOS == 0))
continue
if (isImage && (isPickVideo || filterMedia and IMAGES == 0))
continue
if (isGif && filterMedia and GIFS == 0)
continue
val dateTaken = file.lastModified()
val dateModified = file.lastModified()
val medium = Medium(filename, file.absolutePath, isVideo, dateModified, dateTaken, size)
val isAlreadyAdded = curMedia.any { it.path == file.absolutePath }
if (!isAlreadyAdded)
curMedia.add(medium)
}
}
fun Context.getSortingForFolder(path: String): String {
val sorting = config.getFileSorting(path)
val sortValue = when {
sorting and SORT_BY_NAME > 0 -> MediaStore.Images.Media.DISPLAY_NAME
sorting and SORT_BY_SIZE > 0 -> MediaStore.Images.Media.SIZE
sorting and SORT_BY_DATE_MODIFIED > 0 -> MediaStore.Images.Media.DATE_MODIFIED
else -> MediaStore.Images.Media.DATE_TAKEN
}
return if (sorting and SORT_DESCENDING > 0)
"$sortValue DESC"
else
"$sortValue ASC"
}
fun Context.getNoMediaFolders(): ArrayList<String> {
val folders = ArrayList<String>()
val noMediaCondition = "${MediaStore.Files.FileColumns.MEDIA_TYPE} = ${MediaStore.Files.FileColumns.MEDIA_TYPE_NONE}"
val uri = MediaStore.Files.getContentUri("external")
val columns = arrayOf(MediaStore.Files.FileColumns.DATA)
val where = "$noMediaCondition AND ${MediaStore.Files.FileColumns.TITLE} LIKE ?"
val args = arrayOf("%$NOMEDIA%")
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, columns, where, args, null)
if (cursor?.moveToFirst() == true) {
do {
val path = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)) ?: continue
val noMediaFile = File(path)
if (noMediaFile.exists())
folders.add("${noMediaFile.parent}/")
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
}
return folders
}
fun Context.getLastMediaModified(): Int {
val max = "max"
val uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(MediaStore.Images.Media._ID, "MAX(${MediaStore.Images.Media.DATE_MODIFIED}) AS $max")
var cursor: Cursor? = null
try {
cursor = contentResolver.query(uri, projection, null, null, null)
if (cursor?.moveToFirst() == true) {
return cursor.getIntValue(max)
}
} finally {
cursor?.close()
}
return 0
} }

View file

@ -33,7 +33,6 @@ import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getFileSignature import com.simplemobiletools.gallery.extensions.getFileSignature
import com.simplemobiletools.gallery.extensions.getRealPathFromURI import com.simplemobiletools.gallery.extensions.getRealPathFromURI
import com.simplemobiletools.gallery.extensions.portrait import com.simplemobiletools.gallery.extensions.portrait
import com.simplemobiletools.gallery.helpers.GlideDecoder
import com.simplemobiletools.gallery.helpers.GlideRotateTransformation import com.simplemobiletools.gallery.helpers.GlideRotateTransformation
import com.simplemobiletools.gallery.helpers.MEDIUM import com.simplemobiletools.gallery.helpers.MEDIUM
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
@ -200,8 +199,7 @@ class PhotoFragment : ViewPagerFragment() {
private fun addZoomableView() { private fun addZoomableView() {
if ((medium.isImage()) && isFragmentVisible && view.subsampling_view.visibility == View.GONE) { if ((medium.isImage()) && isFragmentVisible && view.subsampling_view.visibility == View.GONE) {
view.subsampling_view.apply { view.subsampling_view.apply {
setBitmapDecoderClass(GlideDecoder::class.java) //setBitmapDecoderClass(GlideDecoder::class.java) // causing random crashes on Android 7+
setMaxTileSize(10000)
maxScale = 10f maxScale = 10f
beVisible() beVisible()
setImage(ImageSource.uri(medium.path)) setImage(ImageSource.uri(medium.path))
@ -221,7 +219,7 @@ class PhotoFragment : ViewPagerFragment() {
override fun onPreviewReleased() { override fun onPreviewReleased() {
} }
override fun onImageLoadError(e: Exception?) { override fun onImageLoadError(e: Exception) {
background = ColorDrawable(Color.TRANSPARENT) background = ColorDrawable(Color.TRANSPARENT)
beGone() beGone()
} }
@ -255,11 +253,6 @@ class PhotoFragment : ViewPagerFragment() {
} }
} }
fun refreshBitmap() {
view.subsampling_view.beGone()
loadBitmap()
}
fun rotateImageViewBy(degrees: Float) { fun rotateImageViewBy(degrees: Float) {
view.subsampling_view.beGone() view.subsampling_view.beGone()
loadBitmap(degrees) loadBitmap(degrees)

View file

@ -20,10 +20,7 @@ import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.extensions.updateTextColors import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.gallery.R import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.ViewPagerActivity import com.simplemobiletools.gallery.activities.ViewPagerActivity
import com.simplemobiletools.gallery.extensions.audioManager import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getNavBarHeight
import com.simplemobiletools.gallery.extensions.hasNavBar
import com.simplemobiletools.gallery.helpers.MEDIUM import com.simplemobiletools.gallery.helpers.MEDIUM
import com.simplemobiletools.gallery.models.Medium import com.simplemobiletools.gallery.models.Medium
import kotlinx.android.synthetic.main.pager_video_item.view.* import kotlinx.android.synthetic.main.pager_video_item.view.*
@ -168,7 +165,9 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mLastTouchY = event.y mLastTouchY = event.y
} }
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION) { val diffX = Math.abs(event.x - mTouchDownX)
val diffY = Math.abs(event.y - mTouchDownY)
if (System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION && diffX < 20 && diffY < 20) {
mView.video_holder.performClick() mView.video_holder.performClick()
} }
} }
@ -204,7 +203,9 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mLastTouchY = event.y mLastTouchY = event.y
} }
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION) { val diffX = Math.abs(event.x - mTouchDownX)
val diffY = Math.abs(event.y - mTouchDownY)
if (System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION && diffX < 20 && diffY < 20) {
mView.video_holder.performClick() mView.video_holder.performClick()
} }
mTouchDownBrightness = mTempBrightness mTouchDownBrightness = mTempBrightness
@ -273,6 +274,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
bottom += height bottom += height
} else { } else {
right += height right += height
bottom += context.navigationBarHeight
} }
mTimeHolder!!.setPadding(left, top, right, bottom) mTimeHolder!!.setPadding(left, top, right, bottom)
} }

View file

@ -28,17 +28,17 @@ class Config(context: Context) : BaseConfig(context) {
if (path.isEmpty()) { if (path.isEmpty()) {
fileSorting = value fileSorting = value
} else { } else {
prefs.edit().putInt(SORT_FOLDER_PREFIX + path, value).apply() prefs.edit().putInt(SORT_FOLDER_PREFIX + path.toLowerCase(), value).apply()
} }
} }
fun getFileSorting(path: String) = prefs.getInt(SORT_FOLDER_PREFIX + path, fileSorting) fun getFileSorting(path: String) = prefs.getInt(SORT_FOLDER_PREFIX + path.toLowerCase(), fileSorting)
fun removeFileSorting(path: String) { fun removeFileSorting(path: String) {
prefs.edit().remove(SORT_FOLDER_PREFIX + path).apply() prefs.edit().remove(SORT_FOLDER_PREFIX + path.toLowerCase()).apply()
} }
fun hasCustomSorting(path: String) = prefs.contains(SORT_FOLDER_PREFIX + path) fun hasCustomSorting(path: String) = prefs.contains(SORT_FOLDER_PREFIX + path.toLowerCase())
var wasHideFolderTooltipShown: Boolean var wasHideFolderTooltipShown: Boolean
get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false) get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false)
@ -280,4 +280,12 @@ class Config(context: Context) : BaseConfig(context) {
var tempFolderPath: String var tempFolderPath: String
get() = prefs.getString(TEMP_FOLDER_PATH, "") get() = prefs.getString(TEMP_FOLDER_PATH, "")
set(tempFolderPath) = prefs.edit().putString(TEMP_FOLDER_PATH, tempFolderPath).apply() set(tempFolderPath) = prefs.edit().putString(TEMP_FOLDER_PATH, tempFolderPath).apply()
var viewTypeFolders: Int
get() = prefs.getInt(VIEW_TYPE_FOLDERS, VIEW_TYPE_GRID)
set(viewTypeFolders) = prefs.edit().putInt(VIEW_TYPE_FOLDERS, viewTypeFolders).apply()
var viewTypeFiles: Int
get() = prefs.getInt(VIEW_TYPE_FILES, VIEW_TYPE_GRID)
set(viewTypeFiles) = prefs.edit().putInt(VIEW_TYPE_FILES, viewTypeFiles).apply()
} }

View file

@ -37,6 +37,8 @@ val REPLACE_SHARE_WITH_ROTATE = "replace_share_with_rotate"
val DELETE_EMPTY_FOLDERS = "delete_empty_folders" val DELETE_EMPTY_FOLDERS = "delete_empty_folders"
val ALLOW_VIDEO_GESTURES = "allow_video_gestures" val ALLOW_VIDEO_GESTURES = "allow_video_gestures"
val TEMP_FOLDER_PATH = "temp_folder_path" val TEMP_FOLDER_PATH = "temp_folder_path"
val VIEW_TYPE_FOLDERS = "view_type_folders"
val VIEW_TYPE_FILES = "view_type_files"
// slideshow // slideshow
val SLIDESHOW_INTERVAL = "slideshow_interval" val SLIDESHOW_INTERVAL = "slideshow_interval"
@ -60,6 +62,8 @@ val GET_ANY_INTENT = "get_any_intent"
val SET_WALLPAPER_INTENT = "set_wallpaper_intent" val SET_WALLPAPER_INTENT = "set_wallpaper_intent"
val DIRECTORIES = "directories2" val DIRECTORIES = "directories2"
val IS_VIEW_INTENT = "is_view_intent" val IS_VIEW_INTENT = "is_view_intent"
val IS_FROM_GALLERY = "is_from_gallery"
val PICKED_PATHS = "picked_paths"
val REQUEST_EDIT_IMAGE = 1 val REQUEST_EDIT_IMAGE = 1
val REQUEST_SET_AS = 2 val REQUEST_SET_AS = 2
@ -77,3 +81,7 @@ val ORIENT_LANDSCAPE_RIGHT = 2
val IMAGES = 1 val IMAGES = 1
val VIDEOS = 2 val VIDEOS = 2
val GIFS = 4 val GIFS = 4
// view types
val VIEW_TYPE_GRID = 1
val VIEW_TYPE_LIST = 2

View file

@ -8,7 +8,6 @@ import android.graphics.drawable.Drawable
import android.media.ExifInterface import android.media.ExifInterface
import android.net.Uri import android.net.Uri
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
@ -26,11 +25,13 @@ class GlideDecoder : ImageDecoder {
val options = RequestOptions() val options = RequestOptions()
.signature(uri.path.getFileSignature()) .signature(uri.path.getFileSignature())
.format(DecodeFormat.PREFER_ARGB_8888)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE) .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.transform(GlideRotateTransformation(context, getRotationDegrees(orientation)))
.override(targetWidth, targetHeight) .override(targetWidth, targetHeight)
val degrees = getRotationDegrees(orientation)
if (degrees != 0f)
options.transform(GlideRotateTransformation(context, getRotationDegrees(orientation)))
val drawable = Glide.with(context) val drawable = Glide.with(context)
.load(uri) .load(uri)
.apply(options) .apply(options)

View file

@ -0,0 +1,321 @@
package com.simplemobiletools.gallery.helpers
import android.content.Context
import android.database.Cursor
import android.provider.MediaStore
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_BY_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_SIZE
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.containsNoMedia
import com.simplemobiletools.gallery.models.Medium
import java.io.File
import java.util.LinkedHashMap
import kotlin.collections.ArrayList
import kotlin.collections.component1
import kotlin.collections.component2
class MediaFetcher(val context: Context) {
var shouldStop = false
fun getMediaByDirectories(isPickVideo: Boolean, isPickImage: Boolean): HashMap<String, ArrayList<Medium>> {
val media = getFilesFrom("", isPickImage, isPickVideo)
val excludedPaths = context.config.excludedFolders
val includedPaths = context.config.includedFolders
val showHidden = context.config.shouldShowHidden
val directories = groupDirectories(media)
val removePaths = ArrayList<String>()
for ((path, curMedia) in directories) {
// make sure the path has uppercase letters wherever appropriate
val groupPath = File(curMedia.first().path).parent
if (!File(groupPath).exists() || !shouldFolderBeVisible(groupPath, excludedPaths, includedPaths, showHidden)) {
removePaths.add(groupPath.toLowerCase())
}
}
removePaths.forEach {
directories.remove(it)
}
return directories
}
fun getFilesFrom(curPath: String, isPickImage: Boolean, isPickVideo: Boolean): ArrayList<Medium> {
val projection = arrayOf(MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.DATE_MODIFIED,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.SIZE)
val uri = MediaStore.Files.getContentUri("external")
val selection = getSelectionQuery(curPath)
val selectionArgs = getSelectionArgsQuery(curPath)
return try {
val cur = context.contentResolver.query(uri, projection, selection, selectionArgs, getSortingForFolder(curPath))
parseCursor(context, cur, isPickImage, isPickVideo, curPath)
} catch (e: Exception) {
ArrayList()
}
}
private fun getSelectionQuery(path: String): String {
val dataQuery = "${MediaStore.Images.Media.DATA} LIKE ?"
return if (path.isEmpty()) {
var query = "($dataQuery)"
if (context.hasExternalSDCard()) {
query += " OR ($dataQuery)"
}
query
} else {
"($dataQuery AND ${MediaStore.Images.Media.DATA} NOT LIKE ?)"
}
}
private fun getSelectionArgsQuery(path: String): Array<String> {
return if (path.isEmpty()) {
if (context.hasExternalSDCard()) arrayOf("${context.internalStoragePath}/%", "${context.sdCardPath}/%") else arrayOf("${context.internalStoragePath}/%")
} else {
arrayOf("$path/%", "$path/%/%")
}
}
private fun parseCursor(context: Context, cur: Cursor, isPickImage: Boolean, isPickVideo: Boolean, curPath: String): ArrayList<Medium> {
val curMedia = ArrayList<Medium>()
val config = context.config
val filterMedia = config.filterMedia
val showHidden = config.shouldShowHidden
val includedFolders = config.includedFolders.map { "${it.trimEnd('/')}/" }
val excludedFolders = config.excludedFolders.map { "${it.trimEnd('/')}/" }
val noMediaFolders = getNoMediaFolders()
val isThirdPartyIntent = config.isThirdPartyIntent
cur.use {
if (cur.moveToFirst()) {
do {
try {
if (shouldStop)
break
val path = cur.getStringValue(MediaStore.Images.Media.DATA)
var filename = cur.getStringValue(MediaStore.Images.Media.DISPLAY_NAME) ?: ""
if (filename.isEmpty())
filename = path.getFilenameFromPath()
val isImage = filename.isImageFast()
val isVideo = if (isImage) false else filename.isVideoFast()
val isGif = if (isImage || isVideo) false else filename.isGif()
if (!isImage && !isVideo && !isGif)
continue
if (isVideo && (isPickImage || filterMedia and VIDEOS == 0))
continue
if (isImage && (isPickVideo || filterMedia and IMAGES == 0))
continue
if (isGif && filterMedia and GIFS == 0)
continue
if (!showHidden && filename.startsWith('.'))
continue
var size = cur.getLongValue(MediaStore.Images.Media.SIZE)
val file = File(path)
if (size == 0L) {
size = file.length()
}
if (size <= 0L)
continue
var isExcluded = false
excludedFolders.forEach {
if (path.startsWith(it)) {
isExcluded = true
includedFolders.forEach {
if (path.startsWith(it)) {
isExcluded = false
}
}
}
}
if (!isExcluded && !showHidden) {
noMediaFolders.forEach {
if (path.startsWith(it)) {
isExcluded = true
}
}
}
if (!isExcluded && !showHidden && path.contains("/.")) {
isExcluded = true
}
if (!isExcluded || isThirdPartyIntent) {
if (!file.exists())
continue
val dateTaken = cur.getLongValue(MediaStore.Images.Media.DATE_TAKEN)
val dateModified = cur.getIntValue(MediaStore.Images.Media.DATE_MODIFIED) * 1000L
val medium = Medium(filename, path, isVideo, dateModified, dateTaken, size)
curMedia.add(medium)
}
} catch (e: Exception) {
continue
}
} while (cur.moveToNext())
}
}
config.includedFolders.filter { it.isNotEmpty() && (curPath.isEmpty() || it == curPath) }.forEach {
getMediaInFolder(it, curMedia, isPickImage, isPickVideo, filterMedia)
}
if (isThirdPartyIntent && curPath.isNotEmpty() && curMedia.isEmpty()) {
getMediaInFolder(curPath, curMedia, isPickImage, isPickVideo, filterMedia)
}
Medium.sorting = config.getFileSorting(curPath)
curMedia.sort()
return curMedia
}
private fun groupDirectories(media: ArrayList<Medium>): HashMap<String, ArrayList<Medium>> {
val directories = LinkedHashMap<String, ArrayList<Medium>>()
for (medium in media) {
if (shouldStop)
break
val parentDir = File(medium.path).parent?.toLowerCase() ?: continue
if (directories.containsKey(parentDir)) {
directories[parentDir]!!.add(medium)
} else {
directories.put(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 (includedPaths.contains(path)) {
true
} else if (isThisOrParentExcluded(path, excludedPaths, includedPaths)) {
false
} else if (!showHidden && file.isDirectory && file.canonicalFile == file.absoluteFile) {
var containsNoMediaOrDot = file.containsNoMedia() || path.contains("/.")
if (!containsNoMediaOrDot) {
containsNoMediaOrDot = checkParentHasNoMedia(file.parentFile)
}
!containsNoMediaOrDot
} else {
true
}
}
private fun checkParentHasNoMedia(file: File): Boolean {
var curFile = file
while (true) {
if (curFile.containsNoMedia()) {
return true
}
curFile = curFile.parentFile
if (curFile.absolutePath == "/")
break
}
return false
}
private fun isThisOrParentExcluded(path: String, excludedPaths: MutableSet<String>, includedPaths: MutableSet<String>) =
includedPaths.none { path.startsWith(it) } && excludedPaths.any { path.startsWith(it) }
private fun getMediaInFolder(folder: String, curMedia: ArrayList<Medium>, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int) {
val files = File(folder).listFiles() ?: return
for (file in files) {
if (shouldStop)
break
val filename = file.name
val isImage = filename.isImageFast()
val isVideo = if (isImage) false else filename.isVideoFast()
val isGif = if (isImage || isVideo) false else filename.isGif()
if (!isImage && !isVideo)
continue
if (isVideo && (isPickImage || filterMedia and VIDEOS == 0))
continue
if (isImage && (isPickVideo || filterMedia and IMAGES == 0))
continue
if (isGif && filterMedia and GIFS == 0)
continue
val size = file.length()
if (size <= 0L)
continue
val dateTaken = file.lastModified()
val dateModified = file.lastModified()
val medium = Medium(filename, file.absolutePath, isVideo, dateModified, dateTaken, size)
val isAlreadyAdded = curMedia.any { it.path == file.absolutePath }
if (!isAlreadyAdded) {
curMedia.add(medium)
context.scanPath(file.absolutePath) {}
}
}
}
private fun getSortingForFolder(path: String): String {
val sorting = context.config.getFileSorting(path)
val sortValue = when {
sorting and SORT_BY_NAME > 0 -> MediaStore.Images.Media.DISPLAY_NAME
sorting and SORT_BY_SIZE > 0 -> MediaStore.Images.Media.SIZE
sorting and SORT_BY_DATE_MODIFIED > 0 -> MediaStore.Images.Media.DATE_MODIFIED
else -> MediaStore.Images.Media.DATE_TAKEN
}
return if (sorting and SORT_DESCENDING > 0)
"$sortValue DESC"
else
"$sortValue ASC"
}
private fun getNoMediaFolders(): ArrayList<String> {
val folders = ArrayList<String>()
val noMediaCondition = "${MediaStore.Files.FileColumns.MEDIA_TYPE} = ${MediaStore.Files.FileColumns.MEDIA_TYPE_NONE}"
val uri = MediaStore.Files.getContentUri("external")
val columns = arrayOf(MediaStore.Files.FileColumns.DATA)
val where = "$noMediaCondition AND ${MediaStore.Files.FileColumns.TITLE} LIKE ?"
val args = arrayOf("%$NOMEDIA%")
var cursor: Cursor? = null
try {
cursor = context.contentResolver.query(uri, columns, where, args, null)
if (cursor?.moveToFirst() == true) {
do {
val path = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)) ?: continue
val noMediaFile = File(path)
if (noMediaFile.exists())
folders.add("${noMediaFile.parent}/")
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
}
return folders
}
}

View file

@ -16,29 +16,23 @@ data class Directory(val path: String, val tmb: String, val name: String, var me
override fun compareTo(other: Directory): Int { override fun compareTo(other: Directory): Int {
var result: Int var result: Int
if (sorting and SORT_BY_NAME != 0) { when {
result = AlphanumComparator().compare(name.toLowerCase(), other.name.toLowerCase()) sorting and SORT_BY_NAME != 0 -> result = AlphanumericComparator().compare(name.toLowerCase(), other.name.toLowerCase())
} else if (sorting and SORT_BY_SIZE != 0) { sorting and SORT_BY_SIZE != 0 -> result = when {
result = if (size == other.size) size == other.size -> 0
0 size > other.size -> 1
else if (size > other.size) else -> -1
1 }
else sorting and SORT_BY_DATE_MODIFIED != 0 -> result = when {
-1 modified == other.modified -> 0
} else if (sorting and SORT_BY_DATE_MODIFIED != 0) { modified > other.modified -> 1
result = if (modified == other.modified) else -> -1
0 }
else if (modified > other.modified) else -> result = when {
1 taken == other.taken -> 0
else taken > other.taken -> 1
-1 else -> -1
} else { }
result = if (taken == other.taken)
0
else if (taken > other.taken)
1
else
-1
} }
if (sorting and SORT_DESCENDING != 0) { if (sorting and SORT_DESCENDING != 0) {

View file

@ -25,29 +25,23 @@ data class Medium(var name: String, var path: String, val video: Boolean, val mo
override fun compareTo(other: Medium): Int { override fun compareTo(other: Medium): Int {
var result: Int var result: Int
if (sorting and SORT_BY_NAME != 0) { when {
result = AlphanumComparator().compare(name.toLowerCase(), other.name.toLowerCase()) sorting and SORT_BY_NAME != 0 -> result = AlphanumericComparator().compare(name.toLowerCase(), other.name.toLowerCase())
} else if (sorting and SORT_BY_SIZE != 0) { sorting and SORT_BY_SIZE != 0 -> result = when {
result = if (size == other.size) size == other.size -> 0
0 size > other.size -> 1
else if (size > other.size) else -> -1
1 }
else sorting and SORT_BY_DATE_MODIFIED != 0 -> result = when {
-1 modified == other.modified -> 0
} else if (sorting and SORT_BY_DATE_MODIFIED != 0) { modified > other.modified -> 1
result = if (modified == other.modified) else -> -1
0 }
else if (modified > other.modified) else -> result = when {
1 taken == other.taken -> 0
else taken > other.taken -> 1
-1 else -> -1
} else { }
result = if (taken == other.taken)
0
else if (taken > other.taken)
1
else
-1
} }
if (sorting and SORT_DESCENDING != 0) { if (sorting and SORT_DESCENDING != 0) {

View file

@ -14,8 +14,7 @@ class MySquareImageView : ImageView {
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec) val spec = if (isVerticalScrolling) widthMeasureSpec else heightMeasureSpec
val spec = if (isVerticalScrolling) measuredWidth else measuredHeight super.onMeasure(spec, spec)
setMeasuredDimension(spec, spec)
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -10,7 +10,7 @@
<com.simplemobiletools.commons.views.MyScalableRecyclerView <com.simplemobiletools.commons.views.MyScalableRecyclerView
android:id="@+id/directories_grid" android:id="@+id/directories_grid"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:scrollbars="none" android:scrollbars="none"
app:layoutManager="android.support.v7.widget.GridLayoutManager" app:layoutManager="android.support.v7.widget.GridLayoutManager"
app:spanCount="@integer/directory_columns_vertical_scroll"/> app:spanCount="@integer/directory_columns_vertical_scroll"/>

View file

@ -39,7 +39,9 @@
android:id="@+id/dir_shadow_holder" android:id="@+id/dir_shadow_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/tmb_shadow_height" android:layout_height="@dimen/tmb_shadow_height"
android:layout_alignLeft="@+id/dir_bottom_holder"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/dir_bottom_holder"
android:background="@drawable/gradient_background"/> android:background="@drawable/gradient_background"/>
<LinearLayout <LinearLayout
@ -80,8 +82,7 @@
android:layout_width="@dimen/sd_card_icon_size" android:layout_width="@dimen/sd_card_icon_size"
android:layout_height="@dimen/sd_card_icon_size" android:layout_height="@dimen/sd_card_icon_size"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true" android:layout_alignRight="@+id/dir_bottom_holder"
android:layout_alignParentRight="true"
android:alpha="0.8" android:alpha="0.8"
android:paddingBottom="@dimen/small_margin" android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin" android:paddingEnd="@dimen/small_margin"

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dir_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/small_margin">
<com.simplemobiletools.gallery.views.MySquareImageView
android:id="@+id/dir_thumbnail"
android:layout_width="@dimen/list_view_folder_thumbnail_size"
android:layout_height="@dimen/list_view_folder_thumbnail_size"/>
<ImageView
android:id="@+id/dir_check"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_margin="@dimen/small_margin"
android:background="@drawable/circle_background"
android:padding="@dimen/tiny_margin"
android:src="@drawable/ic_check"/>
<TextView
android:id="@+id/dir_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/dir_thumbnail"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:textColor="@android:color/white"
android:textSize="@dimen/bigger_text_size"/>
<TextView
android:id="@+id/dir_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/dir_name"
android:layout_below="@+id/dir_name"
android:layout_marginRight="@dimen/activity_margin"
android:alpha="0.4"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="6dp"
android:textColor="@android:color/white"
android:textSize="@dimen/smaller_text_size"/>
<TextView
android:id="@+id/photo_cnt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/dir_name"
android:layout_toRightOf="@+id/dir_name"
android:alpha="0.4"
android:textColor="@android:color/white"
android:textSize="@dimen/smaller_text_size"/>
<LinearLayout
android:id="@+id/dir_icon_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/small_margin"
android:gravity="end"
android:orientation="horizontal"
android:paddingBottom="@dimen/tiny_margin">
<ImageView
android:id="@+id/dir_sd_card"
android:layout_width="@dimen/sd_card_icon_size"
android:layout_height="@dimen/sd_card_icon_size"
android:paddingBottom="@dimen/small_margin"
android:src="@drawable/ic_sd_card"
android:visibility="gone"/>
<ImageView
android:id="@+id/dir_pin"
android:layout_width="@dimen/sd_card_icon_size"
android:layout_height="@dimen/sd_card_icon_size"
android:paddingBottom="@dimen/small_margin"
android:src="@drawable/ic_pin"
android:visibility="gone"/>
</LinearLayout>
<TextView
android:id="@+id/dir_list_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignBottom="@+id/dir_thumbnail"
android:layout_marginTop="2dp"
android:layout_toRightOf="@+id/dir_thumbnail"
android:background="@drawable/divider"/>
</RelativeLayout>

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/media_item_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/small_margin">
<com.simplemobiletools.gallery.views.MySquareImageView
android:id="@+id/medium_thumbnail"
android:layout_width="@dimen/list_view_folder_thumbnail_size"
android:layout_height="@dimen/list_view_folder_thumbnail_size"/>
<ImageView
android:id="@+id/medium_check"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_margin="@dimen/small_margin"
android:background="@drawable/circle_background"
android:padding="@dimen/tiny_margin"
android:src="@drawable/ic_check"/>
<TextView
android:id="@+id/photo_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@+id/medium_thumbnail"
android:layout_alignTop="@+id/medium_thumbnail"
android:layout_toRightOf="@+id/medium_thumbnail"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="3"
android:paddingLeft="6dp"
android:paddingRight="@dimen/normal_margin"
android:textColor="@android:color/white"
android:textSize="@dimen/bigger_text_size"/>
<ImageView
android:id="@+id/play_outline"
android:layout_width="@dimen/play_outline_icon_size"
android:layout_height="@dimen/play_outline_icon_size"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/small_margin"
android:paddingBottom="6dp"
android:src="@drawable/img_play_outline_empty"
android:visibility="gone"/>
<TextView
android:id="@+id/dir_list_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignBottom="@+id/medium_thumbnail"
android:layout_marginTop="2dp"
android:layout_toRightOf="@+id/medium_thumbnail"
android:background="@drawable/divider"/>
</RelativeLayout>

View file

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/cab_confirm_selection"
android:icon="@drawable/ic_check"
android:title="@string/confirm_selection"
app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/cab_delete" android:id="@+id/cab_delete"
android:icon="@drawable/ic_delete" android:icon="@drawable/ic_delete"

View file

@ -16,6 +16,10 @@
android:icon="@drawable/ic_filter" android:icon="@drawable/ic_filter"
android:title="@string/filter_media" android:title="@string/filter_media"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/change_view_type"
android:title="@string/change_view_type"
app:showAsAction="never"/>
<item <item
android:id="@+id/show_all" android:id="@+id/show_all"
android:title="@string/show_all" android:title="@string/show_all"

View file

@ -25,6 +25,10 @@
android:icon="@drawable/ic_camera" android:icon="@drawable/ic_camera"
android:title="@string/open_camera" android:title="@string/open_camera"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item
android:id="@+id/change_view_type"
android:title="@string/change_view_type"
app:showAsAction="never"/>
<item <item
android:id="@+id/hide_folder" android:id="@+id/hide_folder"
android:title="@string/hide_folder" android:title="@string/hide_folder"

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Show hidden media</string> <string name="show_hidden_media">Show hidden media</string>
<string name="autoplay_videos">Play videos automatically</string> <string name="autoplay_videos">Play videos automatically</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Zobrazit skryté média</string> <string name="show_hidden_media">Zobrazit skryté média</string>
<string name="autoplay_videos">Automaticky přehrávat videa</string> <string name="autoplay_videos">Automaticky přehrávat videa</string>

View file

@ -19,7 +19,7 @@
<string name="no_camera_app_found">Keine Kamera-App gefunden</string> <string name="no_camera_app_found">Keine Kamera-App gefunden</string>
<string name="increase_column_count">Kacheln verkleinern</string> <string name="increase_column_count">Kacheln verkleinern</string>
<string name="reduce_column_count">Kacheln vergrößern</string> <string name="reduce_column_count">Kacheln vergrößern</string>
<string name="change_cover_image">Cover-Bild ändern</string> <string name="change_cover_image">Coverbild ändern</string>
<string name="select_photo">Auswählen</string> <string name="select_photo">Auswählen</string>
<string name="use_default">Standard</string> <string name="use_default">Standard</string>
<string name="set_as">Festlegen als</string> <string name="set_as">Festlegen als</string>
@ -27,12 +27,12 @@
<string name="brightness">Helligkeit</string> <string name="brightness">Helligkeit</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter media</string> <string name="filter_media">Medienfilter</string>
<string name="images">Images</string> <string name="images">Bilder</string>
<string name="videos">Videos</string> <string name="videos">Videos</string>
<string name="gifs">GIFs</string> <string name="gifs">GIFs</string>
<string name="no_media_with_filters">No media files have been found with the selected filters.</string> <string name="no_media_with_filters">Keine Medien für die ausgewählten Filter gefunden</string>
<string name="change_filters_underlined"><u>Change filters</u></string> <string name="change_filters_underlined"><u>Filter ändern</u></string>
<!-- Hide / Exclude --> <!-- Hide / Exclude -->
<string name="hide_folder_description">Diese Funktion versteckt ausgewählte Ordner (auch für andere Apps), indem dort im Dateisystem eine \'.nomedia\'-Datei abgelegt wird. Dadurch werden auch deren Unterordner versteckt. Solche Ordner werden nur gezeigt, wenn die Einstellung \'Versteckte Ordner zeigen\' aktiv ist (auch andere Apps bieten üblicherweise eine solche Option). Fortfahren?</string> <string name="hide_folder_description">Diese Funktion versteckt ausgewählte Ordner (auch für andere Apps), indem dort im Dateisystem eine \'.nomedia\'-Datei abgelegt wird. Dadurch werden auch deren Unterordner versteckt. Solche Ordner werden nur gezeigt, wenn die Einstellung \'Versteckte Ordner zeigen\' aktiv ist (auch andere Apps bieten üblicherweise eine solche Option). Fortfahren?</string>
@ -94,14 +94,19 @@
<string name="interval">Intervall (Sekunden):</string> <string name="interval">Intervall (Sekunden):</string>
<string name="include_photos">Bilder verwenden</string> <string name="include_photos">Bilder verwenden</string>
<string name="include_videos">Videos verwenden</string> <string name="include_videos">Videos verwenden</string>
<string name="include_gifs">GIF verwenden</string> <string name="include_gifs">GIFs verwenden</string>
<string name="random_order">Zufällige Reihenfolge</string> <string name="random_order">Zufällige Reihenfolge</string>
<string name="use_fade">Übergänge animieren</string> <string name="use_fade">Übergänge animieren</string>
<string name="move_backwards">Rückwärts abspielen</string> <string name="move_backwards">Rückwärts abspielen</string>
<string name="loop_slideshow">Loop slideshow</string> <string name="loop_slideshow">Endlos abspielen</string>
<string name="slideshow_ended">Diashow beendet</string> <string name="slideshow_ended">Diashow beendet</string>
<string name="no_media_for_slideshow">Keine Medien für Diashow gefunden</string> <string name="no_media_for_slideshow">Keine Medien für Diashow gefunden</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Versteckte Ordner zeigen</string> <string name="show_hidden_media">Versteckte Ordner zeigen</string>
<string name="autoplay_videos">Videos automatisch abspielen</string> <string name="autoplay_videos">Videos automatisch abspielen</string>
@ -117,8 +122,8 @@
<string name="dark_background_at_fullscreen">Schwarzer Hintergrund im Vollbild</string> <string name="dark_background_at_fullscreen">Schwarzer Hintergrund im Vollbild</string>
<string name="scroll_thumbnails_horizontally">Kacheln horizontal scrollen</string> <string name="scroll_thumbnails_horizontally">Kacheln horizontal scrollen</string>
<string name="hide_system_ui_at_fullscreen">Systemleisten ausblenden im Vollbild</string> <string name="hide_system_ui_at_fullscreen">Systemleisten ausblenden im Vollbild</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string> <string name="delete_empty_folders">Nach Löschen leere Ordner löschen</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string> <string name="allow_video_gestures">Gesten für Videolautstärke/Helligkeit</string>
<string name="replace_share_with_rotate">Teilen/Drehen im Vollbild-Menü vertauschen</string> <string name="replace_share_with_rotate">Teilen/Drehen im Vollbild-Menü vertauschen</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Mostrar carpetas ocultas</string> <string name="show_hidden_media">Mostrar carpetas ocultas</string>
<string name="autoplay_videos">Reproducir vídeos automáticamente</string> <string name="autoplay_videos">Reproducir vídeos automáticamente</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">Diaporama terminé</string> <string name="slideshow_ended">Diaporama terminé</string>
<string name="no_media_for_slideshow">Aucun média trouvé pour le diaporama</string> <string name="no_media_for_slideshow">Aucun média trouvé pour le diaporama</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Afficher les dossiers cachés</string> <string name="show_hidden_media">Afficher les dossiers cachés</string>
<string name="autoplay_videos">Lecture automatique des vidéos</string> <string name="autoplay_videos">Lecture automatique des vidéos</string>
@ -125,13 +130,13 @@
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Un album pour visionner photos et vidéos sans publicité.</string> <string name="app_short_description">Un album pour visionner photos et vidéos sans publicité.</string>
<string name="app_long_description"> <string name="app_long_description">
Un simple outil pour visionner les photos et les vidéos. Elles peuvent être triées par dates, tailles, noms dans les deux sens (alphabétique comme désalphabétique), il est possible de zoomer sur les photos. Les fichiers sont affichés sur de multiple colonnes en fonction de la taille de l\'écran, vous pouvez changer le nombre de colonnes par pincement. Elles peuvent être renommées, partagées, supprimées, copiées et déplacées. Les images peuvent en plus être tournées, rognées ou être définies comme fond d\'écran directement depuis l\'application. La galerie est aussi offerte pour l\'utiliser comme une tierce partie pour de la prévisualisation des images/vidéos, joindre aux clients mail etc. C\'est parfait pour un usage au quotidien. Un simple outil pour visionner les photos et les vidéos. Elles peuvent être triées par dates, tailles, noms dans les deux sens (alphabétique comme désalphabétique), il est possible de zoomer sur les photos. Les fichiers sont affichés sur de multiple colonnes en fonction de la taille de l\'écran, vous pouvez changer le nombre de colonnes par pincement. Elles peuvent être renommées, partagées, supprimées, copiées et déplacées. Les images peuvent en plus être tournées, rognées ou être définies comme fond d\'écran directement depuis l\'application.
La galerie est aussi offerte pour l\'utiliser comme une tierce partie pour de la prévisualisation des images/vidéos, joindre aux clients mail etc. C\'est parfait pour un usage au quotidien.
L\'application ne contient ni de publicité ni d\'autorisation inutile. Elle est totalement OpenSource et est aussi fournie avec un thème sombre. L\'application ne contient ni de publicité ni d\'autorisation inutile. Elle est totalement OpenSource et est aussi fournie avec un thème sombre.
Cette application est juste l\'une des applications d\'une plus grande suite. Cette application est juste l\'une des applications d\'une plus grande suite. Vous pouvez trouver les autres sur http://www.simplemobiletools.com
Vous pouvez trouver les autres sur http://www.simplemobiletools.com
</string> </string>
<!-- <!--

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Show hidden media</string> <string name="show_hidden_media">Show hidden media</string>
<string name="autoplay_videos">Play videos automatically</string> <string name="autoplay_videos">Play videos automatically</string>

View file

@ -27,12 +27,12 @@
<string name="brightness">Luminosità</string> <string name="brightness">Luminosità</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter media</string> <string name="filter_media">Filtra i media</string>
<string name="images">Images</string> <string name="images">Immagini</string>
<string name="videos">Videos</string> <string name="videos">Video</string>
<string name="gifs">GIFs</string> <string name="gifs">GIF</string>
<string name="no_media_with_filters">No media files have been found with the selected filters.</string> <string name="no_media_with_filters">Nessun file trovato per il filtro selezionato.</string>
<string name="change_filters_underlined"><u>Change filters</u></string> <string name="change_filters_underlined"><u>Cambia filtro</u></string>
<!-- Hide / Exclude --> <!-- Hide / Exclude -->
<string name="hide_folder_description">Questa funzione nasconde la cartella aggiungendo un file \'.nomedia\' all\'interno, nasconderà anche tutte le sottocartelle. Puoi vederle attivando l\'opzione \'Mostra cartelle nascoste\' nelle impostazioni. Continuare?</string> <string name="hide_folder_description">Questa funzione nasconde la cartella aggiungendo un file \'.nomedia\' all\'interno, nasconderà anche tutte le sottocartelle. Puoi vederle attivando l\'opzione \'Mostra cartelle nascoste\' nelle impostazioni. Continuare?</string>
@ -98,10 +98,15 @@
<string name="random_order">Ordine sparso</string> <string name="random_order">Ordine sparso</string>
<string name="use_fade">Usa animazioni a dissolvenza</string> <string name="use_fade">Usa animazioni a dissolvenza</string>
<string name="move_backwards">Scorri al contrario</string> <string name="move_backwards">Scorri al contrario</string>
<string name="loop_slideshow">Loop slideshow</string> <string name="loop_slideshow">Ripeti presentazione</string>
<string name="slideshow_ended">La presentazione è terminata</string> <string name="slideshow_ended">La presentazione è terminata</string>
<string name="no_media_for_slideshow">Nessun media trovato per la presentazione</string> <string name="no_media_for_slideshow">Nessun media trovato per la presentazione</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Mostra cartelle nascoste</string> <string name="show_hidden_media">Mostra cartelle nascoste</string>
<string name="autoplay_videos">Riproduci video automaticamente</string> <string name="autoplay_videos">Riproduci video automaticamente</string>
@ -117,8 +122,8 @@
<string name="dark_background_at_fullscreen">Sfondo scuro a schermo intero</string> <string name="dark_background_at_fullscreen">Sfondo scuro a schermo intero</string>
<string name="scroll_thumbnails_horizontally">Scorri miniature orizzontalmente</string> <string name="scroll_thumbnails_horizontally">Scorri miniature orizzontalmente</string>
<string name="hide_system_ui_at_fullscreen">Nascondi UI di sistema con media a schermo intero</string> <string name="hide_system_ui_at_fullscreen">Nascondi UI di sistema con media a schermo intero</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string> <string name="delete_empty_folders">Elimina cartelle vuote dopo averne eliminato il contenuto</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string> <string name="allow_video_gestures">Gestisci il volume e la luminosità dei video con gesti verticali</string>
<string name="replace_share_with_rotate">Sostituisci Condividi con Ruota a schermo intero</string> <string name="replace_share_with_rotate">Sostituisci Condividi con Ruota a schermo intero</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->

View file

@ -4,122 +4,127 @@
<string name="app_launcher_name">ギャラリー</string> <string name="app_launcher_name">ギャラリー</string>
<string name="edit">編集</string> <string name="edit">編集</string>
<string name="open_camera">カメラを開く</string> <string name="open_camera">カメラを開く</string>
<string name="open_with">&#8230;で開く</string> <string name="open_with">別のアプリで開く</string>
<string name="no_app_found">有効なアプリが見つかりません</string> <string name="no_app_found">有効なアプリが見つかりません</string>
<string name="hidden">(非表示)</string> <string name="hidden">(非表示)</string>
<string name="pin_folder">Pin folder</string> <string name="pin_folder">フォルダーをピン留めする</string>
<string name="unpin_folder">Unpin folder</string> <string name="unpin_folder">フォルダーのピン留めを外す</string>
<string name="show_all">Show all folders content</string> <string name="show_all">全てを表示</string>
<string name="all_folders">All media</string> <string name="all_folders">すべてのフォルダー</string>
<string name="folder_view">Switch to folder view</string> <string name="folder_view">フォルダーを選択する</string>
<string name="other_folder">Other folder</string> <string name="other_folder">その他のフォルダー</string>
<string name="show_on_map">Show on map</string> <string name="show_on_map">地図で表示</string>
<string name="unknown_location">Unknown location</string> <string name="unknown_location">位置情報がありません</string>
<string name="no_map_application">No application with maps has been found</string> <string name="no_map_application">地図アプリが見つかりません</string>
<string name="no_camera_app_found">No Camera app has been found</string> <string name="no_camera_app_found">カメラアプリが見つかりません</string>
<string name="increase_column_count">Increase column count</string> <string name="increase_column_count">列数を増やす</string>
<string name="reduce_column_count">Reduce column count</string> <string name="reduce_column_count">列数を減らす</string>
<string name="change_cover_image">Change cover image</string> <string name="change_cover_image">カバー画像を変更</string>
<string name="select_photo">Select photo</string> <string name="select_photo">写真を選択</string>
<string name="use_default">Use default</string> <string name="use_default">デフォルトに戻す</string>
<string name="set_as">Set as</string> <string name="set_as">他で使う</string>
<string name="volume">Volume</string> <string name="volume">音量</string>
<string name="brightness">Brightness</string> <string name="brightness">明るさ</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter media</string> <string name="filter_media">表示メディア種</string>
<string name="images">Images</string> <string name="images">画像</string>
<string name="videos">Videos</string> <string name="videos">ビデオ</string>
<string name="gifs">GIFs</string> <string name="gifs">GIF</string>
<string name="no_media_with_filters">No media files have been found with the selected filters.</string> <string name="no_media_with_filters">絞り込み条件に該当するメディアがありません。</string>
<string name="change_filters_underlined"><u>Change filters</u></string> <string name="change_filters_underlined"><u>絞り込み条件を変更</u></string>
<!-- Hide / Exclude --> <!-- Hide / Exclude -->
<string name="hide_folder_description">This function hides the folder by adding a \'.nomedia\' file into it, it will hide all subfolders too. You can see them by toggling the \'Show hidden folders\' option in Settings. Continue?</string> <string name="hide_folder_description">対象のフォルダーに「.nomedia」というファイルを作成し、フォルダーを非表示にします。そのフォルダー以下のすべてのサブフォルダーも、同様に非表示となります。非表示となったフォルダーを見るには、「設定」の中にある「非表示のフォルダーを表示」オプションを切り替えてください。このフォルダーを非表示にしますか</string>
<string name="exclude">Exclude</string> <string name="exclude">除外する</string>
<string name="excluded_folders">Excluded folders</string> <string name="excluded_folders">除外フォルダー</string>
<string name="manage_excluded_folders">Manage excluded folders</string> <string name="manage_excluded_folders">除外フォルダーを管理</string>
<string name="exclude_folder_description">This will exclude the selection together with its subfolders from Simple Gallery only. You can manage excluded folders in Settings.</string> <string name="exclude_folder_description">選択したフォルダーとそのサブフォルダーを、Simple Galleyの一覧から除外します。除外したフォルダーは「設定」で管理できます。</string>
<string name="exclude_folder_parent">Exclude a parent instead?</string> <string name="exclude_folder_parent">親フォルダーを選択して除外することもできます。</string>
<string name="excluded_activity_placeholder">Excluding folders will make them together with their subfolders hidden just in Simple Gallery, they will still be visible in other applications.\\n\\nIf you want to hide them from other apps too, use the Hide function.</string> <string name="excluded_activity_placeholder">フォルダーを除外すると、サブフォルダーも含めSimple Galleyの一覧から除外します。他のアプリでは引き続き表示されます。\\n\\n他のアプリでも非表示にしたい場合は、「非表示」機能を使用してください。</string>
<string name="remove_all">Remove all</string> <string name="remove_all">すべて解除</string>
<string name="remove_all_description">Remove all folders from the list of excluded? This will not delete the folders.</string> <string name="remove_all_description">除外するフォルダーの登録をすべて解除しますか? フォルダー自体は削除されません。</string>
<!-- Include folders --> <!-- Include folders -->
<string name="include_folders">Included folders</string> <string name="include_folders">追加フォルダー</string>
<string name="manage_included_folders">Manage included folders</string> <string name="manage_included_folders">追加フォルダーを管理</string>
<string name="add_folder">Add folder</string> <string name="add_folder">フォルダーを追加</string>
<string name="included_activity_placeholder">If you have some folders which contain media, but were not recognized by the app, you can add them manually here.</string> <string name="included_activity_placeholder">メディアを含んでいるフォルダーがアプリから認識されていない場合は、手動で追加できます。</string>
<!-- Resizing --> <!-- Resizing -->
<string name="resize">Resize</string> <string name="resize">リサイズ</string>
<string name="resize_and_save">Resize selection and save</string> <string name="resize_and_save">選択領域をリサイズして保存</string>
<string name="width">Width</string> <string name="width"></string>
<string name="height">Height</string> <string name="height">高さ</string>
<string name="keep_aspect_ratio">Keep aspect ratio</string> <string name="keep_aspect_ratio">縦横比を固定</string>
<string name="invalid_values">Please enter a valid resolution</string> <string name="invalid_values">解像度を正しく入力してください</string>
<!-- Editor --> <!-- Editor -->
<string name="editor">エディター</string> <string name="editor">エディター</string>
<string name="save">保存</string> <string name="save">保存</string>
<string name="rotate">回転</string> <string name="rotate">回転</string>
<string name="path">Path</string> <string name="path">パス</string>
<string name="invalid_image_path">無効な画像パス</string> <string name="invalid_image_path">無効な画像パス</string>
<string name="image_editing_failed">画像の編集に失敗しました</string> <string name="image_editing_failed">画像の編集に失敗しました</string>
<string name="edit_image_with">画像を編集:</string> <string name="edit_image_with">画像を編集:</string>
<string name="no_editor_found">画像エディターが見つかりません</string> <string name="no_editor_found">画像エディターが見つかりません</string>
<string name="unknown_file_location">ファイルの場所が不明です</string> <string name="unknown_file_location">ファイルの場所が不明です</string>
<string name="error_saving_file">元のファイルを上書きできません</string> <string name="error_saving_file">元のファイルを上書きできません</string>
<string name="rotate_left">Rotate left</string> <string name="rotate_left">左に回転</string>
<string name="rotate_right">Rotate right</string> <string name="rotate_right">右に回転</string>
<string name="rotate_one_eighty">Rotate by 180º</string> <string name="rotate_one_eighty">180º回転</string>
<string name="flip">Flip</string> <string name="flip">反転</string>
<string name="flip_horizontally">Flip horizontally</string> <string name="flip_horizontally">水平方向に反転</string>
<string name="flip_vertically">Flip vertically</string> <string name="flip_vertically">垂直方向に反転</string>
<string name="edit_with">Edit with</string> <string name="edit_with">他のアプリで編集</string>
<!-- Set wallpaper --> <!-- Set wallpaper -->
<string name="simple_wallpaper">シンプル壁紙</string> <string name="simple_wallpaper">シンプル壁紙</string>
<string name="set_as_wallpaper">壁紙として設定</string> <string name="set_as_wallpaper">壁紙設定</string>
<string name="set_as_wallpaper_failed">壁紙としての設定に失敗しました</string> <string name="set_as_wallpaper_failed">壁紙の設定に失敗しました</string>
<string name="set_as_wallpaper_with">壁紙として設定:</string> <string name="set_as_wallpaper_with">壁紙設定:</string>
<string name="no_capable_app_found">対応できるアプリが見つかりません</string> <string name="no_capable_app_found">対応できるアプリが見つかりません</string>
<string name="setting_wallpaper">壁紙の設定&#8230;</string> <string name="setting_wallpaper">壁紙に設定中&#8230;</string>
<string name="wallpaper_set_successfully">壁紙を正常に設定しました</string> <string name="wallpaper_set_successfully">壁紙を正常に設定しました</string>
<string name="portrait_aspect_ratio">Portrait aspect ratio</string> <string name="portrait_aspect_ratio">縦向きの縦横比</string>
<string name="landscape_aspect_ratio">Landscape aspect ratio</string> <string name="landscape_aspect_ratio">横向きの縦横比</string>
<!-- Slideshow --> <!-- Slideshow -->
<string name="slideshow">Slideshow</string> <string name="slideshow">スライドショー</string>
<string name="interval">Interval (seconds):</string> <string name="interval">間隔 (秒):</string>
<string name="include_photos">Include photos</string> <string name="include_photos">写真を含める</string>
<string name="include_videos">Include videos</string> <string name="include_videos">ビデオを含める</string>
<string name="include_gifs">Include GIFs</string> <string name="include_gifs">GIFを含める</string>
<string name="random_order">Random order</string> <string name="random_order">ランダムな順序</string>
<string name="use_fade">Use fade animations</string> <string name="use_fade">フェードアニメーションを使用する</string>
<string name="move_backwards">Move backwards</string> <string name="move_backwards">逆方向に進む</string>
<string name="loop_slideshow">Loop slideshow</string> <string name="loop_slideshow">スライドショーをリピート再生する</string>
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">スライドショーが終了しました</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">スライドショーに表示するメディアがありません</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">非表示フォルダーを表示</string> <string name="show_hidden_media">非表示フォルダーを表示</string>
<string name="autoplay_videos">自動的にビデオを再生</string> <string name="autoplay_videos">ビデオを自動再生する</string>
<string name="toggle_filename">ファイル名の表示を切り替え</string> <string name="toggle_filename">ファイル名の表示を切り替え</string>
<string name="loop_videos">Loop videos</string> <string name="loop_videos">ビデオをリピート再生する</string>
<string name="animate_gifs">Animate GIFs at thumbnails</string> <string name="animate_gifs">GIF画像のサムネイルをアニメーション表示する</string>
<string name="max_brightness">Max brightness when viewing media</string> <string name="max_brightness">メディア再生時に明るさを最大にする</string>
<string name="crop_thumbnails">Crop thumbnails into squares</string> <string name="crop_thumbnails">サムネイルを正方形に切り取る</string>
<string name="screen_rotation_by">Rotate fullscreen media by</string> <string name="screen_rotation_by">メディア再生時のフルスクリーン表示切り替え</string>
<string name="screen_rotation_system_setting">System setting</string> <string name="screen_rotation_system_setting">システム設定に従う</string>
<string name="screen_rotation_device_rotation">Device rotation</string> <string name="screen_rotation_device_rotation">端末の向きに従う</string>
<string name="screen_rotation_aspect_ratio">Aspect ratio</string> <string name="screen_rotation_aspect_ratio">メディアの縦横比に従う</string>
<string name="dark_background_at_fullscreen">Dark background at fullscreen media</string> <string name="dark_background_at_fullscreen">黒背景でフルスクリーン表示</string>
<string name="scroll_thumbnails_horizontally">Scroll thumbnails horizontally</string> <string name="scroll_thumbnails_horizontally">サムネイル画面を横方向にスクロール</string>
<string name="hide_system_ui_at_fullscreen">Automatically hide system UI at fullscreen media</string> <string name="hide_system_ui_at_fullscreen">フルスクリーン時にシステムUIを非表示にする</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string> <string name="delete_empty_folders">メディアの削除後にフォルダーが空になった場合、そのフォルダーを削除する</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string> <string name="allow_video_gestures">ビデオ再生中に、音量と明るさを縦方向のジェスチャーで変更する</string>
<string name="replace_share_with_rotate">Replace Share with Rotate at fullscreen menu</string> <string name="replace_share_with_rotate">フルスクリーンメニューの「共有」を「回転」に置き換える</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View file

@ -57,7 +57,7 @@
<string name="width">Szerokość</string> <string name="width">Szerokość</string>
<string name="height">Wysokość</string> <string name="height">Wysokość</string>
<string name="keep_aspect_ratio">Zachowaj proporcje</string> <string name="keep_aspect_ratio">Zachowaj proporcje</string>
<string name="invalid_values">Wpisz poprawną rozdzielczość</string>    <string name="invalid_values">Podaj poprawną rozdzielczość</string>
<!-- Editor --> <!-- Editor -->
<string name="editor">Edycja</string> <string name="editor">Edycja</string>
@ -98,10 +98,15 @@
<string name="random_order">Losowa kolejność</string> <string name="random_order">Losowa kolejność</string>
<string name="use_fade">Używaj płynnych przejść</string> <string name="use_fade">Używaj płynnych przejść</string>
   <string name="move_backwards">Odwrotna kolejność</string>    <string name="move_backwards">Odwrotna kolejność</string>
<string name="loop_slideshow">Loop slideshow</string>    <string name="loop_slideshow">Zapętlaj</string>
<string name="slideshow_ended">Pokaz slajdów zakończony</string> <string name="slideshow_ended">Pokaz slajdów zakończony</string>
<string name="no_media_for_slideshow">Nie znalazłem multimediów do pokazu slajdów</string> <string name="no_media_for_slideshow">Nie znalazłem multimediów do pokazu slajdów</string>
<!-- View types -->
   <string name="change_view_type">Zmień typ widoku</string>
   <string name="grid">Siatka</string>
   <string name="list">Lista</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Pokazuj ukryte foldery</string> <string name="show_hidden_media">Pokazuj ukryte foldery</string>
<string name="autoplay_videos">Odtwarzaj filmy automatycznie</string> <string name="autoplay_videos">Odtwarzaj filmy automatycznie</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Mostrar pastas ocultas</string> <string name="show_hidden_media">Mostrar pastas ocultas</string>
<string name="autoplay_videos">Reproduzir vídeos automaticamente</string> <string name="autoplay_videos">Reproduzir vídeos automaticamente</string>

View file

@ -31,7 +31,7 @@
<string name="images">Imagens</string> <string name="images">Imagens</string>
<string name="videos">Vídeos</string> <string name="videos">Vídeos</string>
<string name="gifs">GIFs</string> <string name="gifs">GIFs</string>
<string name="no_media_with_filters">N foram encontrados ficheiros que cumpram os requisitos.</string> <string name="no_media_with_filters">Não foram encontrados ficheiros que cumpram os requisitos.</string>
<string name="change_filters_underlined"><u>Alterar filtros</u></string> <string name="change_filters_underlined"><u>Alterar filtros</u></string>
<!-- Hide / Exclude --> <!-- Hide / Exclude -->
@ -98,10 +98,15 @@
<string name="random_order">Ordem aleatória</string> <string name="random_order">Ordem aleatória</string>
<string name="use_fade">Usar animações</string> <string name="use_fade">Usar animações</string>
<string name="move_backwards">Mover para trás</string> <string name="move_backwards">Mover para trás</string>
<string name="loop_slideshow">Loop slideshow</string> <string name="loop_slideshow">Apresentação em ciclo</string>
<string name="slideshow_ended">Apresentação terminada</string> <string name="slideshow_ended">Apresentação terminada</string>
<string name="no_media_for_slideshow">Não foram encontrados ficheiros para a apresentação</string> <string name="no_media_for_slideshow">Não foram encontrados ficheiros para a apresentação</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Mostrar pastas ocultas</string> <string name="show_hidden_media">Mostrar pastas ocultas</string>
<string name="autoplay_videos">Reproduzir vídeos automaticamente</string> <string name="autoplay_videos">Reproduzir vídeos automaticamente</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">Слайдшоу завершилось</string> <string name="slideshow_ended">Слайдшоу завершилось</string>
<string name="no_media_for_slideshow">Никаких медиафайлов для слайдшоу не было найдено.</string> <string name="no_media_for_slideshow">Никаких медиафайлов для слайдшоу не было найдено.</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Показать скрытые папки</string> <string name="show_hidden_media">Показать скрытые папки</string>
<string name="autoplay_videos">Воспроизводить видео автоматически</string> <string name="autoplay_videos">Воспроизводить видео автоматически</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">Prezentácia skončila</string> <string name="slideshow_ended">Prezentácia skončila</string>
<string name="no_media_for_slideshow">Pre prezentáciu sa nenašli žiadne vhodné súbory</string> <string name="no_media_for_slideshow">Pre prezentáciu sa nenašli žiadne vhodné súbory</string>
<!-- View types -->
<string name="change_view_type">Zmeniť typ zobrazenia</string>
<string name="grid">Mriežka</string>
<string name="list">Zoznam</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Zobraziť skryté médiá</string> <string name="show_hidden_media">Zobraziť skryté médiá</string>
<string name="autoplay_videos">Spúšťať videá automaticky</string> <string name="autoplay_videos">Spúšťať videá automaticky</string>

View file

@ -23,16 +23,16 @@
<string name="select_photo">Välj foto</string> <string name="select_photo">Välj foto</string>
<string name="use_default">Använd standard</string> <string name="use_default">Använd standard</string>
<string name="set_as">Ange som</string> <string name="set_as">Ange som</string>
<string name="volume">Volume</string> <string name="volume">Volym</string>
<string name="brightness">Brightness</string> <string name="brightness">Ljusstyrka</string>
<!-- Filter --> <!-- Filter -->
<string name="filter_media">Filter media</string> <string name="filter_media">Filtrera media</string>
<string name="images">Images</string> <string name="images">Bilder</string>
<string name="videos">Videos</string> <string name="videos">Videor</string>
<string name="gifs">GIFs</string> <string name="gifs">GIF-bilder</string>
<string name="no_media_with_filters">No media files have been found with the selected filters.</string> <string name="no_media_with_filters">Inga mediefiler hittades med valda filter.</string>
<string name="change_filters_underlined"><u>Change filters</u></string> <string name="change_filters_underlined"><u>Ändra filter</u></string>
<!-- Hide / Exclude --> <!-- Hide / Exclude -->
<string name="hide_folder_description">Denna funktion döljer mappen och alla dess undermappar genom att lägga till en \'.nomedia\'-fil i den. Du kan se dem genom att växla \'Visa dolda mappar\'-alternativet i Inställningar. Vill du fortsätta?</string> <string name="hide_folder_description">Denna funktion döljer mappen och alla dess undermappar genom att lägga till en \'.nomedia\'-fil i den. Du kan se dem genom att växla \'Visa dolda mappar\'-alternativet i Inställningar. Vill du fortsätta?</string>
@ -49,7 +49,7 @@
<string name="include_folders">Inkluderade mappar</string> <string name="include_folders">Inkluderade mappar</string>
<string name="manage_included_folders">Hantera inkluderade mappar</string> <string name="manage_included_folders">Hantera inkluderade mappar</string>
<string name="add_folder">Lägg till mapp</string> <string name="add_folder">Lägg till mapp</string>
<string name="included_activity_placeholder">Om du har vissa mappar som innehåller media men som inte känns igen av appen kan du lägga till dem manuellt här.</string> <string name="included_activity_placeholder">Om du har vissa mappar som innehåller media men som inte känns igen av appen, kan du lägga till dem manuellt här.</string>
<!-- Resizing --> <!-- Resizing -->
<string name="resize">Ändra storlek</string> <string name="resize">Ändra storlek</string>
@ -90,17 +90,22 @@
<string name="landscape_aspect_ratio">Liggande bildförhållande</string> <string name="landscape_aspect_ratio">Liggande bildförhållande</string>
<!-- Slideshow --> <!-- Slideshow -->
<string name="slideshow">Slideshow</string> <string name="slideshow">Bildspel</string>
<string name="interval">Interval (seconds):</string> <string name="interval">Intervall (sekunder):</string>
<string name="include_photos">Include photos</string> <string name="include_photos">Inkludera foton</string>
<string name="include_videos">Include videos</string> <string name="include_videos">Inkludera videor</string>
<string name="include_gifs">Include GIFs</string> <string name="include_gifs">Inkludera GIF-bilder</string>
<string name="random_order">Random order</string> <string name="random_order">Spela upp i slumpmässig ordning</string>
<string name="use_fade">Use fade animations</string> <string name="use_fade">Använd toningsanimationer</string>
<string name="move_backwards">Move backwards</string> <string name="move_backwards">Spela upp i omvänd ordning</string>
<string name="loop_slideshow">Loop slideshow</string> <string name="loop_slideshow">Spela upp i en slinga</string>
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">Bildspelet har avslutats</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">Ingen media hittades för bildspelet</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Visa dolda mappar</string> <string name="show_hidden_media">Visa dolda mappar</string>
@ -117,17 +122,17 @@
<string name="dark_background_at_fullscreen">Mörk bakgrund när media visas i helskärmsläge</string> <string name="dark_background_at_fullscreen">Mörk bakgrund när media visas i helskärmsläge</string>
<string name="scroll_thumbnails_horizontally">Rulla horisontellt genom miniatyrer</string> <string name="scroll_thumbnails_horizontally">Rulla horisontellt genom miniatyrer</string>
<string name="hide_system_ui_at_fullscreen">Dölj systemanvändargränssnittet automatiskt när media visas i helskärmsläge</string> <string name="hide_system_ui_at_fullscreen">Dölj systemanvändargränssnittet automatiskt när media visas i helskärmsläge</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string> <string name="delete_empty_folders">Ta bort tomma mappar när deras innehåll tas bort</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string> <string name="allow_video_gestures">Tillåt styrning av videovolym och videoljusstyrka med vertikala gester</string>
<string name="replace_share_with_rotate">Replace Share with Rotate at fullscreen menu</string> <string name="replace_share_with_rotate">Ersätt Dela med Rotera i helskärmsmenyn</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Ett Galleri för att visa bilder och videos utan en massa reklam.</string> <string name="app_short_description">Ett Galleri för att visa bilder och videos utan en massa reklam.</string>
<string name="app_long_description"> <string name="app_long_description">
Ett enkelt verktyg för att visa bilder och vdeos. Objekten kan sorteras efter datum, storlek, namn både stigande och fallande, bilder kan zoomas in. Mediafiler visas i flera kolumner beroende av skärmens storlek, du kan ändra antalet kolumner genom en nyp-rörelse. De går att döpa om, dela, ta bort, kopiera, flytta. Bilder kan också beskäras, roteras och anges som bakgrundsbild direkt från appen. Ett enkelt verktyg för att visa bilder och videos. Objekten kan sorteras efter datum, storlek, namn både stigande och fallande, bilder kan zoomas in. Mediafiler visas i flera kolumner beroende av skärmens storlek, du kan ändra antalet kolumner genom en nyp-rörelse. De går att döpa om, dela, ta bort, kopiera, flytta. Bilder kan också beskäras, roteras och anges som bakgrundsbild direkt från appen.
Galleriet kan också användas av tredjeparts för förhandsgranskning av bilder / videos, bifoga bilagor i e-postklienter etc. Den är perfekt för det dagliga användandet. Galleriet kan också användas av tredjepartsappar för förhandsgranskning av bilder / videos, bifoga bilagor i e-postklienter etc. Den är perfekt för det dagliga användandet.
Innehåller ingen reklam eller onödiga behörigheter. Det är helt och hållet opensource, innehåller anpassningsbara färger. Innehåller ingen reklam eller onödiga behörigheter. Det är helt och hållet opensource, innehåller anpassningsbara färger.

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Gizli klasörleri göster</string> <string name="show_hidden_media">Gizli klasörleri göster</string>
<string name="autoplay_videos">Videoları otomatik olarak oynat</string> <string name="autoplay_videos">Videoları otomatik olarak oynat</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">幻灯片结束</string> <string name="slideshow_ended">幻灯片结束</string>
<string name="no_media_for_slideshow">未发现可用媒体</string> <string name="no_media_for_slideshow">未发现可用媒体</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">显示所有</string> <string name="show_hidden_media">显示所有</string>
<string name="autoplay_videos">自动播放</string> <string name="autoplay_videos">自动播放</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">投影片結束</string> <string name="slideshow_ended">投影片結束</string>
<string name="no_media_for_slideshow">找不到投影片的媒體檔案</string> <string name="no_media_for_slideshow">找不到投影片的媒體檔案</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">顯示隱藏的媒體檔案</string> <string name="show_hidden_media">顯示隱藏的媒體檔案</string>
<string name="autoplay_videos">自動播放影片</string> <string name="autoplay_videos">自動播放影片</string>

View file

@ -3,9 +3,11 @@
<dimen name="dir_tmb_size">150dp</dimen> <dimen name="dir_tmb_size">150dp</dimen>
<dimen name="medium_tmb_size">100dp</dimen> <dimen name="medium_tmb_size">100dp</dimen>
<dimen name="sd_card_icon_size">20dp</dimen> <dimen name="sd_card_icon_size">20dp</dimen>
<dimen name="play_outline_icon_size">22dp</dimen>
<dimen name="selection_check_size">26dp</dimen> <dimen name="selection_check_size">26dp</dimen>
<dimen name="play_outline_size_big">150dp</dimen> <dimen name="play_outline_size_big">150dp</dimen>
<dimen name="timer_padding">24dp</dimen> <dimen name="timer_padding">24dp</dimen>
<dimen name="tmb_shadow_height">50dp</dimen> <dimen name="tmb_shadow_height">50dp</dimen>
<dimen name="video_side_slider_width">150dp</dimen> <dimen name="video_side_slider_width">150dp</dimen>
<dimen name="list_view_folder_thumbnail_size">72dp</dimen>
</resources> </resources>

View file

@ -2,6 +2,10 @@
<resources> <resources>
<!-- Release notes --> <!-- Release notes -->
<string name="release_133">
Added fingerprint to hidden item protection\n
Added a new List view type
</string>
<string name="release_127"> <string name="release_127">
Added a switch for disabling video gestures\n Added a switch for disabling video gestures\n
Added a switch for deleting empty folders after deleting content Added a switch for deleting empty folders after deleting content

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</string> <string name="slideshow_ended">The slideshow ended</string>
<string name="no_media_for_slideshow">No media for the slideshow have been found</string> <string name="no_media_for_slideshow">No media for the slideshow have been found</string>
<!-- View types -->
<string name="change_view_type">Change view type</string>
<string name="grid">Grid</string>
<string name="list">List</string>
<!-- Settings --> <!-- Settings -->
<string name="show_hidden_media">Show hidden media</string> <string name="show_hidden_media">Show hidden media</string>
<string name="autoplay_videos">Play videos automatically</string> <string name="autoplay_videos">Play videos automatically</string>