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
==========
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)*
----------------------------

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");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
https://www.apache.org/licenses/LICENSE-2.0
1. Definitions.
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.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
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"
minSdkVersion 16
targetSdkVersion 23
versionCode 129
versionName "2.14.1"
versionCode 134
versionName "2.15.1"
}
signingConfigs {
@ -37,7 +37,7 @@ android {
}
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.theartofdev.edmodo:android-image-cropper:2.4.0'
compile 'com.bignerdranch.android:recyclerview-multiselect:0.2'
@ -52,7 +52,7 @@ dependencies {
}
buildscript {
ext.kotlin_version = '1.1.4-3'
ext.kotlin_version = '1.1.51'
repositories {
mavenCentral()
}

View file

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

View file

@ -2,10 +2,10 @@ package com.simplemobiletools.gallery.activities
import android.Manifest
import android.app.Activity
import android.content.ClipData
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.AsyncTask
import android.os.Build
import android.os.Bundle
import android.os.Handler
@ -19,7 +19,11 @@ import android.widget.FrameLayout
import com.google.gson.Gson
import com.simplemobiletools.commons.dialogs.CreateNewFolderDialog
import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.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.views.MyScalableRecyclerView
import com.simplemobiletools.gallery.BuildConfig
@ -54,10 +58,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private var mStoredAnimateGifs = true
private var mStoredCropThumbnails = true
private var mStoredScrollHorizontally = true
private var mStoredTextColor = 0
private var mLoadedInitialPhotos = false
private var mLastMediaModified = 0
private var mLatestMediaId = 0L
private var mLastMediaHandler = Handler()
private var mCurrAsyncTask: GetDirectoriesAsynctask? = null
override fun onCreate(savedInstanceState: Bundle?) {
@ -79,6 +83,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
storeStoragePaths()
checkWhatsNewDialog()
@ -92,8 +97,8 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
menuInflater.inflate(R.menu.menu_main_intent, menu)
} else {
menuInflater.inflate(R.menu.menu_main, menu)
menu.findItem(R.id.increase_column_count).isVisible = config.dirColumnCnt < 10
menu.findItem(R.id.reduce_column_count).isVisible = config.dirColumnCnt > 1
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.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt > 1
}
menu.findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
menu.findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
@ -106,6 +111,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
R.id.filter -> showFilterMediaDialog()
R.id.open_camera -> launchCamera()
R.id.show_all -> showAllMedia()
R.id.change_view_type -> changeViewType()
R.id.temporarily_show_hidden -> tryToggleTemporarilyShowHidden()
R.id.stop_showing_hidden -> tryToggleTemporarilyShowHidden()
R.id.create_new_folder -> createNewFolder()
@ -130,13 +136,17 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
}
if (mStoredScrollHorizontally != config.scrollHorizontally) {
directories_grid.adapter?.let {
(it as DirectoryAdapter).scrollVertically = !config.scrollHorizontally
it.notifyDataSetChanged()
(directories_grid.adapter as? DirectoryAdapter)?.apply {
scrollVertically = config.viewTypeFolders == VIEW_TYPE_LIST || !config.scrollHorizontally
notifyDataSetChanged()
}
setupScrollDirection()
}
if (mStoredTextColor != config.textColor) {
(directories_grid.adapter as? DirectoryAdapter)?.updateTextColor(config.textColor)
}
tryloadGallery()
invalidateOptionsMenu()
directories_empty_text_label.setTextColor(config.textColor)
@ -145,15 +155,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
override fun onPause() {
super.onPause()
mCurrAsyncTask?.shouldStop = true
storeDirectories()
directories_refresh_layout.isRefreshing = false
mIsGettingDirs = false
mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
directories_grid.listener = null
mLastMediaHandler.removeCallbacksAndMessages(null)
mCurrAsyncTask?.stopFetching()
}
override fun onDestroy() {
@ -178,6 +189,7 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
showAllMedia()
else
getDirectories()
setupLayoutManager()
checkIfColorChanged()
} else {
@ -216,12 +228,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
mCurrAsyncTask = GetDirectoriesAsynctask(applicationContext, mIsPickVideoIntent || mIsGetVideoContentIntent, mIsPickImageIntent || mIsGetImageContentIntent) {
gotDirectories(addTempFolderIfNeeded(it), false)
}
mCurrAsyncTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
mCurrAsyncTask!!.execute()
}
private fun showSortingDialog() {
ChangeSortingDialog(this, true, false) {
if (config.directorySorting and SORT_BY_DATE_MODIFIED > 0 || config.directorySorting and SORT_BY_DATE_TAKEN > 0) {
getDirectories()
} else {
gotDirectories(mDirs, true)
}
}
}
@ -236,10 +252,29 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
config.showAll = true
Intent(this, MediaActivity::class.java).apply {
putExtra(DIRECTORY, "/")
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()
}
}
private fun tryToggleTemporarilyShowHidden() {
if (config.temporarilyShowHidden) {
@ -278,6 +313,13 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
private fun getRecyclerAdapter() = (directories_grid.adapter as DirectoryAdapter)
private fun setupLayoutManager() {
if (config.viewTypeFolders == VIEW_TYPE_GRID)
setupGridLayoutManager()
else
setupListLayoutManager()
}
private fun setupGridLayoutManager() {
val layoutManager = directories_grid.layoutManager as GridLayoutManager
if (config.scrollHorizontally) {
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() {
FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) {
CreateNewFolderDialog(this, it) {
@ -366,12 +418,33 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_MEDIA && resultData?.data != null) {
Intent().apply {
if (requestCode == PICK_MEDIA && resultData != null) {
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 uri = Uri.fromFile(File(path))
if (mIsGetImageContentIntent || mIsGetVideoContentIntent || mIsGetAnyContentIntent) {
if (intent.extras?.containsKey(MediaStore.EXTRA_OUTPUT) == true) {
resultIntent.data = uri
resultIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}
setResult(Activity.RESULT_OK, resultIntent)
finish()
} else if (requestCode == PICK_WALLPAPER) {
setResult(Activity.RESULT_OK)
finish()
}
}
super.onActivityResult(requestCode, resultCode, resultData)
}
private fun fillExtraOutput(resultData: Intent) {
val path = resultData.data.path
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
@ -384,31 +457,37 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
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)
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))
}
finish()
} else if (requestCode == PICK_WALLPAPER) {
setResult(Activity.RESULT_OK)
finish()
resultIntent.clipData = clipData
}
}
super.onActivityResult(requestCode, resultCode, resultData)
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) {
Intent(this, MediaActivity::class.java).apply {
putExtra(DIRECTORY, path)
handleMediaIntent(this)
}
}
private fun handleMediaIntent(intent: Intent) {
intent.apply {
if (mIsSetWallpaperIntent) {
putExtra(SET_WALLPAPER_INTENT, true)
startActivityForResult(this, PICK_WALLPAPER)
@ -416,13 +495,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
putExtra(GET_IMAGE_INTENT, mIsPickImageIntent || mIsGetImageContentIntent)
putExtra(GET_VIDEO_INTENT, mIsPickVideoIntent || mIsGetVideoContentIntent)
putExtra(GET_ANY_INTENT, mIsGetAnyContentIntent)
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false))
startActivityForResult(this, PICK_MEDIA)
}
}
}
private fun gotDirectories(dirs: ArrayList<Directory>, isFromCache: Boolean) {
mLastMediaModified = getLastMediaModified()
private fun gotDirectories(newDirs: ArrayList<Directory>, isFromCache: Boolean) {
val dirs = getSortedDirectories(newDirs)
mLatestMediaId = getLatestMediaId()
directories_refresh_layout.isRefreshing = false
mIsGettingDirs = false
@ -430,8 +512,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
directories_empty_text.beVisibleIf(dirs.isEmpty() && !isFromCache)
checkLastMediaChanged()
if (dirs.hashCode() == mDirs.hashCode())
if (dirs.hashCode() == mDirs.hashCode()) {
return
}
mDirs = dirs
@ -462,13 +545,16 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
}
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.beGoneIf(config.scrollHorizontally)
directories_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
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)
} else {
directories_vertical_fastscroller.setViews(directories_grid, directories_refresh_layout)
@ -482,9 +568,9 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
mLastMediaHandler.removeCallbacksAndMessages(null)
mLastMediaHandler.postDelayed({
Thread({
val lastModified = getLastMediaModified()
if (mLastMediaModified != lastModified) {
mLastMediaModified = lastModified
val mediaId = getLatestMediaId()
if (mLatestMediaId != mediaId) {
mLatestMediaId = mediaId
runOnUiThread {
getDirectories()
}
@ -503,6 +589,10 @@ class MainActivity : SimpleActivity(), DirectoryAdapter.DirOperationsListener {
directories_grid.setDragSelectActive(position)
}
override fun recheckPinnedFolders() {
gotDirectories(movePinnedDirectoriesToFront(mDirs), true)
}
private fun checkWhatsNewDialog() {
arrayListOf<Release>().apply {
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(125, R.string.release_125))
add(Release(127, R.string.release_127))
add(Release(133, R.string.release_133))
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.reflect.TypeToken
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.MyScalableRecyclerView
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.adapters.MediaAdapter
@ -36,8 +38,7 @@ import java.io.File
import java.io.IOException
class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private val TAG = MediaActivity::class.java.simpleName
private val SAVE_MEDIA_CNT = 40
private val SAVE_MEDIA_CNT = 100
private val LAST_MEDIA_CHECK_PERIOD = 3000L
private var mPath = ""
@ -45,14 +46,17 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private var mIsGetVideoIntent = false
private var mIsGetAnyIntent = false
private var mIsGettingMedia = false
private var mAllowPickingMultiple = false
private var mShowAll = false
private var mLoadedInitialPhotos = false
private var mStoredAnimateGifs = true
private var mStoredCropThumbnails = true
private var mStoredScrollHorizontally = true
private var mStoredTextColor = 0
private var mLastDrawnHashCode = 0
private var mLastMediaModified = 0
private var mLatestMediaId = 0L
private var mLastMediaHandler = Handler()
private var mCurrAsyncTask: GetMediaAsynctask? = null
companion object {
var mMedia = ArrayList<Medium>()
@ -65,6 +69,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mIsGetImageIntent = getBooleanExtra(GET_IMAGE_INTENT, false)
mIsGetVideoIntent = getBooleanExtra(GET_VIDEO_INTENT, false)
mIsGetAnyIntent = getBooleanExtra(GET_ANY_INTENT, false)
mAllowPickingMultiple = getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
}
media_refresh_layout.setOnRefreshListener({ getMedia() })
@ -72,6 +77,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
mShowAll = config.showAll
if (mShowAll)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
@ -92,13 +98,17 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
}
if (mStoredScrollHorizontally != config.scrollHorizontally) {
media_grid.adapter?.let {
(it as MediaAdapter).scrollVertically = !config.scrollHorizontally
it.notifyDataSetChanged()
(media_grid.adapter as? MediaAdapter)?.apply {
scrollVertically = config.viewTypeFiles == VIEW_TYPE_LIST || !config.scrollHorizontally
notifyDataSetChanged()
}
setupScrollDirection()
}
if (mStoredTextColor != config.textColor) {
(media_grid.adapter as? MediaAdapter)?.updateTextColor(config.textColor)
}
tryloadGallery()
invalidateOptionsMenu()
media_empty_text_label.setTextColor(config.textColor)
@ -112,8 +122,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mStoredAnimateGifs = config.animateGifs
mStoredCropThumbnails = config.cropThumbnails
mStoredScrollHorizontally = config.scrollHorizontally
mStoredTextColor = config.textColor
media_grid.listener = null
mLastMediaHandler.removeCallbacksAndMessages(null)
mCurrAsyncTask?.stopFetching()
}
override fun onDestroy() {
@ -149,7 +161,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
val currAdapter = media_grid.adapter
if (currAdapter == null) {
media_grid.adapter = MediaAdapter(this, mMedia, this, mIsGetAnyIntent) {
media_grid.adapter = MediaAdapter(this, mMedia, this, mIsGetAnyIntent, mAllowPickingMultiple) {
itemClicked(it.path)
}
} else {
@ -159,13 +171,16 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
}
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.beGoneIf(config.scrollHorizontally)
media_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
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)
} else {
media_vertical_fastscroller.setViews(media_grid, media_refresh_layout)
@ -179,9 +194,9 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mLastMediaHandler.removeCallbacksAndMessages(null)
mLastMediaHandler.postDelayed({
Thread({
val lastModified = getLastMediaModified()
if (mLastMediaModified != lastModified) {
mLastMediaModified = lastModified
val mediaId = getLatestMediaId()
if (mLatestMediaId != mediaId) {
mLatestMediaId = mediaId
runOnUiThread {
getMedia()
}
@ -207,8 +222,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
findItem(R.id.increase_column_count).isVisible = config.mediaColumnCnt < 10
findItem(R.id.reduce_column_count).isVisible = config.mediaColumnCnt > 1
findItem(R.id.increase_column_count).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID && config.mediaColumnCnt < 10
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
@ -221,6 +238,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
R.id.toggle_filename -> toggleFilenameVisibility()
R.id.open_camera -> launchCamera()
R.id.folder_view -> switchToFolderView()
R.id.change_view_type -> changeViewType()
R.id.hide_folder -> tryHideFolder()
R.id.unhide_folder -> unhideFolder()
R.id.exclude_folder -> tryExcludeFolder()
@ -260,6 +278,20 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
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() {
if (config.wasHideFolderTooltipShown) {
hideFolder()
@ -309,7 +341,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
mIsGettingMedia = true
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) {
gotMedia(media, true)
} else {
@ -317,9 +349,10 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
}
mLoadedInitialPhotos = true
GetMediaAsynctask(applicationContext, mPath, mIsGetVideoIntent, mIsGetImageIntent, mShowAll) {
mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetVideoIntent, mIsGetImageIntent, mShowAll) {
gotMedia(it)
}.execute()
}
mCurrAsyncTask!!.execute()
}
private fun isDirEmpty(): Boolean {
@ -350,6 +383,13 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
private fun getRecyclerAdapter() = (media_grid.adapter as MediaAdapter)
private fun setupLayoutManager() {
if (config.viewTypeFiles == VIEW_TYPE_GRID)
setupGridLayoutManager()
else
setupListLayoutManager()
}
private fun setupGridLayoutManager() {
val layoutManager = media_grid.layoutManager as GridLayoutManager
if (config.scrollHorizontally) {
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() {
config.mediaColumnCnt = ++(media_grid.layoutManager as GridLayoutManager).spanCount
invalidateOptionsMenu()
@ -461,7 +511,7 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
}
private fun gotMedia(media: ArrayList<Medium>, isFromCache: Boolean = false) {
mLastMediaModified = getLastMediaModified()
mLatestMediaId = getLatestMediaId()
mIsGettingMedia = false
media_refresh_layout.isRefreshing = false
@ -513,4 +563,12 @@ class MediaActivity : SimpleActivity(), MediaAdapter.MediaOperationsListener {
override fun itemLongClicked(position: Int) {
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.VideoFragment
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.MEDIUM
import com.simplemobiletools.gallery.models.Medium
@ -31,6 +32,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private val STORAGE_PERMISSION = 1
private var mMedium: Medium? = null
private var mIsFullScreen = false
private var mIsFromGallery = false
private var mFragment: ViewPagerFragment? = null
lateinit var mUri: Uri
@ -52,6 +54,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private fun checkIntent(savedInstanceState: Bundle? = null) {
mUri = intent.data ?: return
mIsFromGallery = intent.getBooleanExtra(IS_FROM_GALLERY, false)
if (mUri.scheme == "file") {
scanPath(mUri.path) {}
@ -126,6 +129,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private fun sendViewPagerIntent(path: String) {
Intent(this, ViewPagerActivity::class.java).apply {
putExtra(IS_VIEW_INTENT, true)
putExtra(IS_FROM_GALLERY, mIsFromGallery)
putExtra(MEDIUM, path)
startActivity(this)
}

View file

@ -8,6 +8,7 @@ import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.dialogs.SecurityDialog
import com.simplemobiletools.commons.extensions.handleHiddenFolderPasswordProtection
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.models.RadioItem
import com.simplemobiletools.gallery.R
@ -169,7 +170,9 @@ class SettingsActivity : SimpleActivity() {
config.protectionType = type
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) {
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 dragOffset = dragPosition - oldDragPosition
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() {
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
getCurrentFragment()?.let {
(it as? PhotoFragment)?.rotateImageViewBy(mRotationDegrees)
@ -474,7 +450,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
Thread({
toast(R.string.saving)
val selectedFile = File(it)
val tmpFile = File(selectedFile.parent, "tmp_${it.getFilenameFromPath()}")
val tmpFile = File(selectedFile.parent, ".tmp_${it.getFilenameFromPath()}")
try {
val bitmap = BitmapFactory.decodeFile(currPath)
getFileOutputStream(tmpFile) {
@ -497,7 +473,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
toast(R.string.out_of_memory_error)
deleteFile(tmpFile) {}
} catch (e: Exception) {
toast(R.string.unknown_error_occurred)
showErrorToast(e)
deleteFile(tmpFile) {}
}
}).start()
@ -510,11 +486,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
val bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
bmp.compress(file.getCompressionFormat(), 90, out)
out.flush()
toast(R.string.file_saved)
out.close()
toast(R.string.file_saved)
mRotationDegrees = 0f
invalidateOptionsMenu()
}
private fun isShowHiddenFlagNeeded(): Boolean {
val file = File(mPath)
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.PickMediumDialog
import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
import com.simplemobiletools.gallery.models.AlbumCover
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.util.*
@ -31,11 +32,13 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
val multiSelector = MultiSelector()
val config = activity.config
val isListViewType = config.viewTypeFolders == VIEW_TYPE_LIST
var actMode: ActionMode? = null
var itemViews = SparseArray<View>()
val selectedPositions = HashSet<Int>()
var primaryColor = config.primaryColor
var textColor = config.textColor
var pinnedFolders = config.pinnedFolders
var scrollVertically = !config.scrollHorizontally
@ -56,12 +59,12 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
updateTitle(selectedPositions.size)
}
fun updateTitle(cnt: Int) {
private fun updateTitle(cnt: Int) {
actMode?.title = "$cnt / ${dirs.size}"
actMode?.invalidate()
}
val adapterListener = object : MyAdapterListener {
private val adapterListener = object : MyAdapterListener {
override fun toggleItemSelectionAdapter(select: Boolean, position: Int) {
toggleItemSelection(select, position)
}
@ -69,7 +72,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
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 {
when (item.itemId) {
R.id.cab_properties -> showProperties()
@ -119,7 +122,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun checkHideBtnVisibility(menu: Menu) {
var hiddenCnt = 0
var unhiddenCnt = 0
selectedPositions.map { dirs.getOrNull(it)?.path }.filterNotNull().forEach {
selectedPositions.mapNotNull { dirs.getOrNull(it)?.path }.forEach {
if (File(it).containsNoMedia())
hiddenCnt++
else
@ -134,7 +137,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
val pinnedFolders = config.pinnedFolders
var pinnedCnt = 0
var unpinnedCnt = 0
selectedPositions.map { dirs.getOrNull(it)?.path }.filterNotNull().forEach {
selectedPositions.mapNotNull { dirs.getOrNull(it)?.path }.forEach {
if (pinnedFolders.contains(it))
pinnedCnt++
else
@ -218,7 +221,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
config.removePinnedFolders(getSelectedPaths())
pinnedFolders = config.pinnedFolders
listener?.refreshItems()
listener?.recheckPinnedFolders()
notifyDataSetChanged()
actMode?.finish()
}
@ -242,7 +245,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun selectAll() {
val cnt = dirs.size
for (i in 0..cnt - 1) {
for (i in 0 until cnt) {
selectedPositions.add(i)
notifyItemChanged(i)
}
@ -286,13 +289,9 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
listener?.tryDeleteFolders(folders)
val newItems = SparseArray<View>()
var curIndex = 0
for (i in 0..itemViews.size() - 1) {
if (itemViews[i] != null) {
newItems.put(curIndex, itemViews[i])
curIndex++
}
}
(0 until itemViews.size())
.filter { itemViews[it] != null }
.forEachIndexed { curIndex, i -> newItems.put(curIndex, itemViews[i]) }
itemViews = newItems
}
@ -330,13 +329,14 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
}
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)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
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)
holder.itemView.tag = holder
}
@ -351,6 +351,12 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
fun updateDirs(newDirs: ArrayList<Directory>) {
dirs = newDirs
notifyDataSetChanged()
actMode?.finish()
}
fun updateTextColor(textColor: Int) {
this.textColor = textColor
notifyDataSetChanged()
}
fun selectItem(pos: Int) {
@ -369,7 +375,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
toggleItemSelection(true, i)
if (min > -1 && min < to) {
(min..to - 1).filter { it != from }
(min until to).filter { it != from }
.forEach { toggleItemSelection(false, it) }
}
if (max > -1) {
@ -386,7 +392,7 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
}
if (min > -1) {
for (i in min..from - 1)
for (i in min until from)
toggleItemSelection(false, i)
}
}
@ -395,46 +401,51 @@ class DirectoryAdapter(val activity: SimpleActivity, var dirs: MutableList<Direc
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)) :
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 {
dir_name.text = directory.name
dir_path?.text = "${directory.path.substringBeforeLast("/")}/"
photo_cnt.text = directory.mediaCnt.toString()
activity.loadImage(directory.tmb, dir_thumbnail, scrollVertically)
dir_pin.beVisibleIf(isPinned)
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) }
setOnLongClickListener { if (isPickIntent) viewClicked(directory) else viewLongClicked(); true }
}
return itemView
}
fun viewClicked(directory: Directory) {
private fun viewClicked(directory: Directory) {
if (multiSelector.isSelectable) {
val isSelected = adapterListener.getSelectedPositions().contains(layoutPosition)
adapterListener.toggleItemSelectionAdapter(!isSelected, layoutPosition)
val isSelected = adapterListener.getSelectedPositions().contains(adapterPosition)
adapterListener.toggleItemSelectionAdapter(!isSelected, adapterPosition)
} else {
itemClick(directory)
}
}
fun viewLongClicked() {
private fun viewLongClicked() {
if (listener != null) {
if (!multiSelector.isSelectable) {
activity.startSupportActionMode(multiSelectorCallback)
adapterListener.toggleItemSelectionAdapter(true, layoutPosition)
adapterListener.toggleItemSelectionAdapter(true, adapterPosition)
}
listener.itemLongClicked(layoutPosition)
listener.itemLongClicked(adapterPosition)
}
}
fun stopLoad() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed)
return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed)
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 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.activities.SimpleActivity
import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
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.util.*
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 config = activity.config
val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST
var actMode: ActionMode? = null
var itemViews = SparseArray<View>()
val selectedPositions = HashSet<Int>()
var primaryColor = config.primaryColor
var textColor = config.textColor
var displayFilenames = config.displayFileNames
var scrollVertically = !config.scrollHorizontally
@ -53,12 +56,12 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
updateTitle(selectedPositions.size)
}
fun updateTitle(cnt: Int) {
private fun updateTitle(cnt: Int) {
actMode?.title = "$cnt / ${media.size}"
actMode?.invalidate()
}
val adapterListener = object : MyAdapterListener {
private val adapterListener = object : MyAdapterListener {
override fun toggleItemSelectionAdapter(select: Boolean, position: Int) {
toggleItemSelection(select, position)
}
@ -66,9 +69,10 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
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 {
when (item.itemId) {
R.id.cab_confirm_selection -> confirmSelection()
R.id.cab_properties -> showProperties()
R.id.cab_rename -> renameFile()
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 {
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_confirm_selection).isVisible = isPickIntent && allowMultiplePicks && selectedPositions.size > 0
checkHideBtnVisibility(menu)
@ -112,7 +117,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun checkHideBtnVisibility(menu: Menu) {
var hiddenCnt = 0
var unhiddenCnt = 0
selectedPositions.map { media.getOrNull(it) }.filterNotNull().forEach {
selectedPositions.mapNotNull { media.getOrNull(it) }.forEach {
if (it.name.startsWith('.'))
hiddenCnt++
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() {
if (selectedPositions.size <= 1) {
PropertiesDialog(activity, media[selectedPositions.first()].path, config.shouldShowHidden)
@ -184,7 +194,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun selectAll() {
val cnt = media.size
for (i in 0..cnt - 1) {
for (i in 0 until cnt) {
selectedPositions.add(i)
multiSelector.setSelected(i, 0, true)
notifyItemChanged(i)
@ -227,13 +237,9 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
listener?.deleteFiles(files)
val newItems = SparseArray<View>()
var curIndex = 0
for (i in 0..itemViews.size() - 1) {
if (itemViews[i] != null) {
newItems.put(curIndex, itemViews[i])
curIndex++
}
}
(0 until itemViews.size())
.filter { itemViews[it] != null }
.forEachIndexed { curIndex, i -> newItems.put(curIndex, itemViews[i]) }
itemViews = newItems
}
@ -246,12 +252,13 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent?.context).inflate(R.layout.photo_video_item, parent, false)
return ViewHolder(view, adapterListener, activity, multiSelectorMode, multiSelector, listener, isPickIntent, itemClick)
val layoutType = if (isListViewType) R.layout.photo_video_item_list else R.layout.photo_video_item_grid
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) {
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)
holder.itemView.tag = holder
}
@ -266,6 +273,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
fun updateMedia(newMedia: ArrayList<Medium>) {
media = newMedia
notifyDataSetChanged()
actMode?.finish()
}
fun updateDisplayFilenames(display: Boolean) {
@ -273,6 +281,11 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
notifyDataSetChanged()
}
fun updateTextColor(textColor: Int) {
this.textColor = textColor
notifyDataSetChanged()
}
fun selectItem(pos: Int) {
toggleItemSelection(true, pos)
}
@ -289,7 +302,7 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
toggleItemSelection(true, i)
if (min > -1 && min < to) {
(min..to - 1).filter { it != from }
(min until to).filter { it != from }
.forEach { toggleItemSelection(false, it) }
}
if (max > -1) {
@ -306,52 +319,56 @@ class MediaAdapter(val activity: SimpleActivity, var media: MutableList<Medium>,
}
if (min > -1) {
for (i in min..from - 1)
for (i in min until from)
toggleItemSelection(false, i)
}
}
}
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()) {
fun bindView(medium: Medium, displayFilenames: Boolean, scrollVertically: Boolean): View {
fun bindView(medium: Medium, displayFilenames: Boolean, scrollVertically: Boolean, isListViewType: Boolean, textColor: Int): View {
itemView.apply {
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
activity.loadImage(medium.path, medium_thumbnail, scrollVertically)
if (isListViewType) {
photo_name.setTextColor(textColor)
play_outline.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)
}
setOnClickListener { viewClicked(medium) }
setOnLongClickListener { if (isPickIntent) viewClicked(medium) else viewLongClicked(); true }
setOnLongClickListener { if (allowMultiplePicks) viewLongClicked() else viewClicked(medium); true }
}
return itemView
}
fun viewClicked(medium: Medium) {
private fun viewClicked(medium: Medium) {
if (multiSelector.isSelectable) {
val isSelected = adapterListener.getSelectedPositions().contains(layoutPosition)
adapterListener.toggleItemSelectionAdapter(!isSelected, layoutPosition)
val isSelected = adapterListener.getSelectedPositions().contains(adapterPosition)
adapterListener.toggleItemSelectionAdapter(!isSelected, adapterPosition)
} else {
itemClick(medium)
}
}
fun viewLongClicked() {
private fun viewLongClicked() {
if (listener != null) {
if (!multiSelector.isSelectable) {
activity.startSupportActionMode(multiSelectorCallback)
adapterListener.toggleItemSelectionAdapter(true, layoutPosition)
adapterListener.toggleItemSelectionAdapter(true, adapterPosition)
}
listener.itemLongClicked(layoutPosition)
listener.itemLongClicked(adapterPosition)
}
}
fun stopLoad() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed)
return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed)
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 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.internalStoragePath
import com.simplemobiletools.commons.extensions.sdCardPath
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.extensions.config
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.Medium
import java.io.File
import java.util.*
class GetDirectoriesAsynctask(val context: Context, val isPickVideo: Boolean, val isPickImage: Boolean,
val callback: (dirs: ArrayList<Directory>) -> Unit) : AsyncTask<Void, Void, ArrayList<Directory>>() {
var config = context.config
var shouldStop = false
val showHidden = config.shouldShowHidden
val mediaFetcher = MediaFetcher(context)
override fun doInBackground(vararg params: Void): ArrayList<Directory> {
if (!context.hasWriteStoragePermission())
return ArrayList()
val media = context.getFilesFrom("", isPickImage, isPickVideo)
val excludedPaths = config.excludedFolders
val includedPaths = config.includedFolders
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 config = context.config
val groupedMedia = mediaFetcher.getMediaByDirectories(isPickVideo, isPickImage)
val directories = ArrayList<Directory>()
val hidden = context.resources.getString(R.string.hidden)
val directories = LinkedHashMap<String, Directory>()
for ((name, path, isVideo, dateModified, dateTaken, size) in media) {
if (shouldStop)
cancel(true)
val albumCovers = config.parseAlbumCovers()
for ((path, curMedia) in groupedMedia) {
Medium.sorting = config.getFileSorting(path)
curMedia.sort()
val parentDir = File(path).parent ?: continue
if (directories.containsKey(parentDir.toLowerCase())) {
val directory = directories[parentDir.toLowerCase()]!!
val newImageCnt = directory.mediaCnt + 1
directory.mediaCnt = newImageCnt
directory.addSize(size)
} else {
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
val firstItem = curMedia.first()
val lastItem = curMedia.last()
val parentDir = File(firstItem.path).parent
var thumbnail = firstItem.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
}
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>) {
super.onPostExecute(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.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 java.util.*
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) :
AsyncTask<Void, Void, ArrayList<Medium>>() {
val mediaFetcher = MediaFetcher(context)
override fun doInBackground(vararg params: Void): ArrayList<Medium> {
val path = if (showAll) "" else mPath
return context.getFilesFrom(path, isPickImage, isPickVideo)
return if (showAll) {
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>) {
super.onPostExecute(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.extensions.config
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 kotlinx.android.synthetic.main.dialog_directory_picker.view.*
@ -22,11 +24,12 @@ class PickDirectoryDialog(val activity: SimpleActivity, val sourcePath: String,
var dialog: AlertDialog
var shownDirectories: ArrayList<Directory> = ArrayList()
var view: View = LayoutInflater.from(activity).inflate(R.layout.dialog_directory_picker, null)
var isGridViewType = activity.config.viewTypeFolders == VIEW_TYPE_GRID
init {
(view.directories_grid.layoutManager as GridLayoutManager).apply {
orientation = if (activity.config.scrollHorizontally) GridLayoutManager.HORIZONTAL else GridLayoutManager.VERTICAL
spanCount = activity.config.dirColumnCnt
orientation = if (activity.config.scrollHorizontally && isGridViewType) GridLayoutManager.HORIZONTAL else GridLayoutManager.VERTICAL
spanCount = if (isGridViewType) activity.config.dirColumnCnt else 1
}
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
FilePickerDialog(activity, sourcePath, false, showHidden, true) {
callback(it)
}
}
private fun gotDirectories(directories: ArrayList<Directory>) {
if (directories.hashCode() == shownDirectories.hashCode())
private fun gotDirectories(newDirs: ArrayList<Directory>) {
val dirs = activity.getSortedDirectories(newDirs)
if (dirs.hashCode() == shownDirectories.hashCode())
return
shownDirectories = directories
val adapter = DirectoryAdapter(activity, directories, null, true) {
shownDirectories = dirs
val adapter = DirectoryAdapter(activity, dirs, null, true) {
if (it.path.trimEnd('/') == sourcePath) {
activity.toast(R.string.source_and_destination_same)
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 {
directories_grid.adapter = adapter

View file

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

View file

@ -24,6 +24,7 @@ import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.gallery.BuildConfig
import com.simplemobiletools.gallery.R
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.REQUEST_EDIT_IMAGE
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)
val chooser = Intent.createChooser(this, getString(R.string.set_as))
if (resolveActivity(packageManager) != null) {
success = if (resolveActivity(packageManager) != null) {
startActivityForResult(chooser, REQUEST_SET_AS)
success = true
true
} else {
if (showToast) {
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
setDataAndType(uri, file.getMimeType())
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
putExtra(IS_FROM_GALLERY, true)
if (resolveActivity(packageManager) != null) {
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) {
val path = oldFile.parent
var filename = oldFile.name
if (hide) {
filename = ".${filename.trimStart('.')}"
filename = if (hide) {
".${filename.trimStart('.')}"
} else {
filename = filename.substring(1, filename.length)
filename.substring(1, filename.length)
}
val newFile = File(path, filename)
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.res.Configuration
import android.database.Cursor
import android.graphics.Point
import android.media.AudioManager
import android.net.Uri
import android.os.Build
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 android.view.WindowManager
import com.simplemobiletools.commons.extensions.humanizePath
import com.simplemobiletools.gallery.activities.SettingsActivity
import com.simplemobiletools.gallery.helpers.*
import com.simplemobiletools.gallery.models.Medium
import java.io.File
import com.simplemobiletools.gallery.helpers.Config
import com.simplemobiletools.gallery.models.Directory
val Context.portrait get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
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? {
var cursor: Cursor? = null
@ -47,213 +71,24 @@ fun Context.launchSettings() {
val Context.config: Config get() = Config.newInstance(this)
fun Context.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 = 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/%/%")
fun Context.movePinnedDirectoriesToFront(dirs: ArrayList<Directory>): ArrayList<Directory> {
val foundFolders = ArrayList<Directory>()
val pinnedFolders = config.pinnedFolders
return try {
val cur = contentResolver.query(uri, projection, selection, selectionArgs, getSortingForFolder(curPath))
parseCursor(this, cur, isPickImage, isPickVideo, curPath)
} catch (e: Exception) {
ArrayList()
dirs.forEach {
if (pinnedFolders.contains(it.path))
foundFolders.add(it)
}
dirs.removeAll(foundFolders)
dirs.addAll(0, foundFolders)
return dirs
}
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 = 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
@Suppress("UNCHECKED_CAST")
fun Context.getSortedDirectories(source: ArrayList<Directory>): ArrayList<Directory> {
Directory.sorting = config.directorySorting
val dirs = source.clone() as ArrayList<Directory>
dirs.sort()
return movePinnedDirectoriesToFront(dirs)
}

View file

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

View file

@ -20,10 +20,7 @@ import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.gallery.R
import com.simplemobiletools.gallery.activities.ViewPagerActivity
import com.simplemobiletools.gallery.extensions.audioManager
import com.simplemobiletools.gallery.extensions.config
import com.simplemobiletools.gallery.extensions.getNavBarHeight
import com.simplemobiletools.gallery.extensions.hasNavBar
import com.simplemobiletools.gallery.extensions.*
import com.simplemobiletools.gallery.helpers.MEDIUM
import com.simplemobiletools.gallery.models.Medium
import kotlinx.android.synthetic.main.pager_video_item.view.*
@ -168,7 +165,9 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mLastTouchY = event.y
}
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()
}
}
@ -204,7 +203,9 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
mLastTouchY = event.y
}
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()
}
mTouchDownBrightness = mTempBrightness
@ -273,6 +274,7 @@ class VideoFragment : ViewPagerFragment(), SurfaceHolder.Callback, SeekBar.OnSee
bottom += height
} else {
right += height
bottom += context.navigationBarHeight
}
mTimeHolder!!.setPadding(left, top, right, bottom)
}

View file

@ -28,17 +28,17 @@ class Config(context: Context) : BaseConfig(context) {
if (path.isEmpty()) {
fileSorting = value
} 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) {
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
get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false)
@ -280,4 +280,12 @@ class Config(context: Context) : BaseConfig(context) {
var tempFolderPath: String
get() = prefs.getString(TEMP_FOLDER_PATH, "")
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 ALLOW_VIDEO_GESTURES = "allow_video_gestures"
val TEMP_FOLDER_PATH = "temp_folder_path"
val VIEW_TYPE_FOLDERS = "view_type_folders"
val VIEW_TYPE_FILES = "view_type_files"
// slideshow
val SLIDESHOW_INTERVAL = "slideshow_interval"
@ -60,6 +62,8 @@ val GET_ANY_INTENT = "get_any_intent"
val SET_WALLPAPER_INTENT = "set_wallpaper_intent"
val DIRECTORIES = "directories2"
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_SET_AS = 2
@ -77,3 +81,7 @@ val ORIENT_LANDSCAPE_RIGHT = 2
val IMAGES = 1
val VIDEOS = 2
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.net.Uri
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
@ -26,11 +25,13 @@ class GlideDecoder : ImageDecoder {
val options = RequestOptions()
.signature(uri.path.getFileSignature())
.format(DecodeFormat.PREFER_ARGB_8888)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.transform(GlideRotateTransformation(context, getRotationDegrees(orientation)))
.override(targetWidth, targetHeight)
val degrees = getRotationDegrees(orientation)
if (degrees != 0f)
options.transform(GlideRotateTransformation(context, getRotationDegrees(orientation)))
val drawable = Glide.with(context)
.load(uri)
.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 {
var result: Int
if (sorting and SORT_BY_NAME != 0) {
result = AlphanumComparator().compare(name.toLowerCase(), other.name.toLowerCase())
} else if (sorting and SORT_BY_SIZE != 0) {
result = if (size == other.size)
0
else if (size > other.size)
1
else
-1
} else if (sorting and SORT_BY_DATE_MODIFIED != 0) {
result = if (modified == other.modified)
0
else if (modified > other.modified)
1
else
-1
} else {
result = if (taken == other.taken)
0
else if (taken > other.taken)
1
else
-1
when {
sorting and SORT_BY_NAME != 0 -> result = AlphanumericComparator().compare(name.toLowerCase(), other.name.toLowerCase())
sorting and SORT_BY_SIZE != 0 -> result = when {
size == other.size -> 0
size > other.size -> 1
else -> -1
}
sorting and SORT_BY_DATE_MODIFIED != 0 -> result = when {
modified == other.modified -> 0
modified > other.modified -> 1
else -> -1
}
else -> result = when {
taken == other.taken -> 0
taken > other.taken -> 1
else -> -1
}
}
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 {
var result: Int
if (sorting and SORT_BY_NAME != 0) {
result = AlphanumComparator().compare(name.toLowerCase(), other.name.toLowerCase())
} else if (sorting and SORT_BY_SIZE != 0) {
result = if (size == other.size)
0
else if (size > other.size)
1
else
-1
} else if (sorting and SORT_BY_DATE_MODIFIED != 0) {
result = if (modified == other.modified)
0
else if (modified > other.modified)
1
else
-1
} else {
result = if (taken == other.taken)
0
else if (taken > other.taken)
1
else
-1
when {
sorting and SORT_BY_NAME != 0 -> result = AlphanumericComparator().compare(name.toLowerCase(), other.name.toLowerCase())
sorting and SORT_BY_SIZE != 0 -> result = when {
size == other.size -> 0
size > other.size -> 1
else -> -1
}
sorting and SORT_BY_DATE_MODIFIED != 0 -> result = when {
modified == other.modified -> 0
modified > other.modified -> 1
else -> -1
}
else -> result = when {
taken == other.taken -> 0
taken > other.taken -> 1
else -> -1
}
}
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)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val spec = if (isVerticalScrolling) measuredWidth else measuredHeight
setMeasuredDimension(spec, spec)
val spec = if (isVerticalScrolling) widthMeasureSpec else heightMeasureSpec
super.onMeasure(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
android:id="@+id/directories_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:scrollbars="none"
app:layoutManager="android.support.v7.widget.GridLayoutManager"
app:spanCount="@integer/directory_columns_vertical_scroll"/>

View file

@ -39,7 +39,9 @@
android:id="@+id/dir_shadow_holder"
android:layout_width="match_parent"
android:layout_height="@dimen/tmb_shadow_height"
android:layout_alignLeft="@+id/dir_bottom_holder"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/dir_bottom_holder"
android:background="@drawable/gradient_background"/>
<LinearLayout
@ -80,8 +82,7 @@
android:layout_width="@dimen/sd_card_icon_size"
android:layout_height="@dimen/sd_card_icon_size"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignRight="@+id/dir_bottom_holder"
android:alpha="0.8"
android:paddingBottom="@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"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
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
android:id="@+id/cab_delete"
android:icon="@drawable/ic_delete"

View file

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

View file

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

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</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 -->
<string name="show_hidden_media">Show hidden media</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="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 -->
<string name="show_hidden_media">Zobrazit skryté média</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="increase_column_count">Kacheln verkleinern</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="use_default">Standard</string>
<string name="set_as">Festlegen als</string>
@ -27,12 +27,12 @@
<string name="brightness">Helligkeit</string>
<!-- Filter -->
<string name="filter_media">Filter media</string>
<string name="images">Images</string>
<string name="filter_media">Medienfilter</string>
<string name="images">Bilder</string>
<string name="videos">Videos</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="change_filters_underlined"><u>Change filters</u></string>
<string name="no_media_with_filters">Keine Medien für die ausgewählten Filter gefunden</string>
<string name="change_filters_underlined"><u>Filter ändern</u></string>
<!-- 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>
@ -94,14 +94,19 @@
<string name="interval">Intervall (Sekunden):</string>
<string name="include_photos">Bilder 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="use_fade">Übergänge animieren</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="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 -->
<string name="show_hidden_media">Versteckte Ordner zeigen</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="scroll_thumbnails_horizontally">Kacheln horizontal scrollen</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="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string>
<string name="delete_empty_folders">Nach Löschen leere Ordner löschen</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>
<!-- 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="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 -->
<string name="show_hidden_media">Mostrar carpetas ocultas</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="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 -->
<string name="show_hidden_media">Afficher les dossiers cachés</string>
<string name="autoplay_videos">Lecture automatique des vidéos</string>
@ -125,13 +130,13 @@
<!-- 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_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.
Cette application est juste l\'une des applications d\'une plus grande suite.
Vous pouvez trouver les autres sur http://www.simplemobiletools.com
Cette application est juste l\'une des applications d\'une plus grande suite. Vous pouvez trouver les autres sur http://www.simplemobiletools.com
</string>
<!--

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</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 -->
<string name="show_hidden_media">Show hidden media</string>
<string name="autoplay_videos">Play videos automatically</string>

View file

@ -27,12 +27,12 @@
<string name="brightness">Luminosità</string>
<!-- Filter -->
<string name="filter_media">Filter media</string>
<string name="images">Images</string>
<string name="videos">Videos</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="change_filters_underlined"><u>Change filters</u></string>
<string name="filter_media">Filtra i media</string>
<string name="images">Immagini</string>
<string name="videos">Video</string>
<string name="gifs">GIF</string>
<string name="no_media_with_filters">Nessun file trovato per il filtro selezionato.</string>
<string name="change_filters_underlined"><u>Cambia filtro</u></string>
<!-- 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>
@ -98,10 +98,15 @@
<string name="random_order">Ordine sparso</string>
<string name="use_fade">Usa animazioni a dissolvenza</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="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 -->
<string name="show_hidden_media">Mostra cartelle nascoste</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="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="delete_empty_folders">Delete empty folders after deleting their content</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string>
<string name="delete_empty_folders">Elimina cartelle vuote dopo averne eliminato il contenuto</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>
<!-- 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="edit">編集</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="hidden">(非表示)</string>
<string name="pin_folder">Pin folder</string>
<string name="unpin_folder">Unpin folder</string>
<string name="show_all">Show all folders content</string>
<string name="all_folders">All media</string>
<string name="folder_view">Switch to folder view</string>
<string name="other_folder">Other folder</string>
<string name="show_on_map">Show on map</string>
<string name="unknown_location">Unknown location</string>
<string name="no_map_application">No application with maps has been found</string>
<string name="no_camera_app_found">No Camera app has been found</string>
<string name="increase_column_count">Increase column count</string>
<string name="reduce_column_count">Reduce column count</string>
<string name="change_cover_image">Change cover image</string>
<string name="select_photo">Select photo</string>
<string name="use_default">Use default</string>
<string name="set_as">Set as</string>
<string name="volume">Volume</string>
<string name="brightness">Brightness</string>
<string name="pin_folder">フォルダーをピン留めする</string>
<string name="unpin_folder">フォルダーのピン留めを外す</string>
<string name="show_all">全てを表示</string>
<string name="all_folders">すべてのフォルダー</string>
<string name="folder_view">フォルダーを選択する</string>
<string name="other_folder">その他のフォルダー</string>
<string name="show_on_map">地図で表示</string>
<string name="unknown_location">位置情報がありません</string>
<string name="no_map_application">地図アプリが見つかりません</string>
<string name="no_camera_app_found">カメラアプリが見つかりません</string>
<string name="increase_column_count">列数を増やす</string>
<string name="reduce_column_count">列数を減らす</string>
<string name="change_cover_image">カバー画像を変更</string>
<string name="select_photo">写真を選択</string>
<string name="use_default">デフォルトに戻す</string>
<string name="set_as">他で使う</string>
<string name="volume">音量</string>
<string name="brightness">明るさ</string>
<!-- Filter -->
<string name="filter_media">Filter media</string>
<string name="images">Images</string>
<string name="videos">Videos</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="change_filters_underlined"><u>Change filters</u></string>
<string name="filter_media">表示メディア種</string>
<string name="images">画像</string>
<string name="videos">ビデオ</string>
<string name="gifs">GIF</string>
<string name="no_media_with_filters">絞り込み条件に該当するメディアがありません。</string>
<string name="change_filters_underlined"><u>絞り込み条件を変更</u></string>
<!-- 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="exclude">Exclude</string>
<string name="excluded_folders">Excluded folders</string>
<string name="manage_excluded_folders">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_parent">Exclude a parent instead?</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="remove_all">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="hide_folder_description">対象のフォルダーに「.nomedia」というファイルを作成し、フォルダーを非表示にします。そのフォルダー以下のすべてのサブフォルダーも、同様に非表示となります。非表示となったフォルダーを見るには、「設定」の中にある「非表示のフォルダーを表示」オプションを切り替えてください。このフォルダーを非表示にしますか</string>
<string name="exclude">除外する</string>
<string name="excluded_folders">除外フォルダー</string>
<string name="manage_excluded_folders">除外フォルダーを管理</string>
<string name="exclude_folder_description">選択したフォルダーとそのサブフォルダーを、Simple Galleyの一覧から除外します。除外したフォルダーは「設定」で管理できます。</string>
<string name="exclude_folder_parent">親フォルダーを選択して除外することもできます。</string>
<string name="excluded_activity_placeholder">フォルダーを除外すると、サブフォルダーも含めSimple Galleyの一覧から除外します。他のアプリでは引き続き表示されます。\\n\\n他のアプリでも非表示にしたい場合は、「非表示」機能を使用してください。</string>
<string name="remove_all">すべて解除</string>
<string name="remove_all_description">除外するフォルダーの登録をすべて解除しますか? フォルダー自体は削除されません。</string>
<!-- Include folders -->
<string name="include_folders">Included folders</string>
<string name="manage_included_folders">Manage included folders</string>
<string name="add_folder">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="include_folders">追加フォルダー</string>
<string name="manage_included_folders">追加フォルダーを管理</string>
<string name="add_folder">フォルダーを追加</string>
<string name="included_activity_placeholder">メディアを含んでいるフォルダーがアプリから認識されていない場合は、手動で追加できます。</string>
<!-- Resizing -->
<string name="resize">Resize</string>
<string name="resize_and_save">Resize selection and save</string>
<string name="width">Width</string>
<string name="height">Height</string>
<string name="keep_aspect_ratio">Keep aspect ratio</string>
<string name="invalid_values">Please enter a valid resolution</string>
<string name="resize">リサイズ</string>
<string name="resize_and_save">選択領域をリサイズして保存</string>
<string name="width"></string>
<string name="height">高さ</string>
<string name="keep_aspect_ratio">縦横比を固定</string>
<string name="invalid_values">解像度を正しく入力してください</string>
<!-- Editor -->
<string name="editor">エディター</string>
<string name="save">保存</string>
<string name="rotate">回転</string>
<string name="path">Path</string>
<string name="path">パス</string>
<string name="invalid_image_path">無効な画像パス</string>
<string name="image_editing_failed">画像の編集に失敗しました</string>
<string name="edit_image_with">画像を編集:</string>
<string name="no_editor_found">画像エディターが見つかりません</string>
<string name="unknown_file_location">ファイルの場所が不明です</string>
<string name="error_saving_file">元のファイルを上書きできません</string>
<string name="rotate_left">Rotate left</string>
<string name="rotate_right">Rotate right</string>
<string name="rotate_one_eighty">Rotate by 180º</string>
<string name="flip">Flip</string>
<string name="flip_horizontally">Flip horizontally</string>
<string name="flip_vertically">Flip vertically</string>
<string name="edit_with">Edit with</string>
<string name="rotate_left">左に回転</string>
<string name="rotate_right">右に回転</string>
<string name="rotate_one_eighty">180º回転</string>
<string name="flip">反転</string>
<string name="flip_horizontally">水平方向に反転</string>
<string name="flip_vertically">垂直方向に反転</string>
<string name="edit_with">他のアプリで編集</string>
<!-- Set wallpaper -->
<string name="simple_wallpaper">シンプル壁紙</string>
<string name="set_as_wallpaper">壁紙として設定</string>
<string name="set_as_wallpaper_failed">壁紙としての設定に失敗しました</string>
<string name="set_as_wallpaper_with">壁紙として設定:</string>
<string name="set_as_wallpaper">壁紙設定</string>
<string name="set_as_wallpaper_failed">壁紙の設定に失敗しました</string>
<string name="set_as_wallpaper_with">壁紙設定:</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="portrait_aspect_ratio">Portrait aspect ratio</string>
<string name="landscape_aspect_ratio">Landscape aspect ratio</string>
<string name="portrait_aspect_ratio">縦向きの縦横比</string>
<string name="landscape_aspect_ratio">横向きの縦横比</string>
<!-- Slideshow -->
<string name="slideshow">Slideshow</string>
<string name="interval">Interval (seconds):</string>
<string name="include_photos">Include photos</string>
<string name="include_videos">Include videos</string>
<string name="include_gifs">Include GIFs</string>
<string name="random_order">Random order</string>
<string name="use_fade">Use fade animations</string>
<string name="move_backwards">Move backwards</string>
<string name="loop_slideshow">Loop slideshow</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="slideshow">スライドショー</string>
<string name="interval">間隔 (秒):</string>
<string name="include_photos">写真を含める</string>
<string name="include_videos">ビデオを含める</string>
<string name="include_gifs">GIFを含める</string>
<string name="random_order">ランダムな順序</string>
<string name="use_fade">フェードアニメーションを使用する</string>
<string name="move_backwards">逆方向に進む</string>
<string name="loop_slideshow">スライドショーをリピート再生する</string>
<string name="slideshow_ended">スライドショーが終了しました</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 -->
<string name="show_hidden_media">非表示フォルダーを表示</string>
<string name="autoplay_videos">自動的にビデオを再生</string>
<string name="autoplay_videos">ビデオを自動再生する</string>
<string name="toggle_filename">ファイル名の表示を切り替え</string>
<string name="loop_videos">Loop videos</string>
<string name="animate_gifs">Animate GIFs at thumbnails</string>
<string name="max_brightness">Max brightness when viewing media</string>
<string name="crop_thumbnails">Crop thumbnails into squares</string>
<string name="screen_rotation_by">Rotate fullscreen media by</string>
<string name="screen_rotation_system_setting">System setting</string>
<string name="screen_rotation_device_rotation">Device rotation</string>
<string name="screen_rotation_aspect_ratio">Aspect ratio</string>
<string name="dark_background_at_fullscreen">Dark background at fullscreen media</string>
<string name="scroll_thumbnails_horizontally">Scroll thumbnails horizontally</string>
<string name="hide_system_ui_at_fullscreen">Automatically hide system UI at fullscreen media</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string>
<string name="replace_share_with_rotate">Replace Share with Rotate at fullscreen menu</string>
<string name="loop_videos">ビデオをリピート再生する</string>
<string name="animate_gifs">GIF画像のサムネイルをアニメーション表示する</string>
<string name="max_brightness">メディア再生時に明るさを最大にする</string>
<string name="crop_thumbnails">サムネイルを正方形に切り取る</string>
<string name="screen_rotation_by">メディア再生時のフルスクリーン表示切り替え</string>
<string name="screen_rotation_system_setting">システム設定に従う</string>
<string name="screen_rotation_device_rotation">端末の向きに従う</string>
<string name="screen_rotation_aspect_ratio">メディアの縦横比に従う</string>
<string name="dark_background_at_fullscreen">黒背景でフルスクリーン表示</string>
<string name="scroll_thumbnails_horizontally">サムネイル画面を横方向にスクロール</string>
<string name="hide_system_ui_at_fullscreen">フルスクリーン時にシステムUIを非表示にする</string>
<string name="delete_empty_folders">メディアの削除後にフォルダーが空になった場合、そのフォルダーを削除する</string>
<string name="allow_video_gestures">ビデオ再生中に、音量と明るさを縦方向のジェスチャーで変更する</string>
<string name="replace_share_with_rotate">フルスクリーンメニューの「共有」を「回転」に置き換える</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->

View file

@ -57,7 +57,7 @@
<string name="width">Szerokość</string>
<string name="height">Wysokość</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 -->
<string name="editor">Edycja</string>
@ -98,10 +98,15 @@
<string name="random_order">Losowa kolejność</string>
<string name="use_fade">Używaj płynnych przejść</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="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 -->
<string name="show_hidden_media">Pokazuj ukryte foldery</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="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 -->
<string name="show_hidden_media">Mostrar pastas ocultas</string>
<string name="autoplay_videos">Reproduzir vídeos automaticamente</string>

View file

@ -31,7 +31,7 @@
<string name="images">Imagens</string>
<string name="videos">Vídeos</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>
<!-- Hide / Exclude -->
@ -98,10 +98,15 @@
<string name="random_order">Ordem aleatória</string>
<string name="use_fade">Usar animações</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="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 -->
<string name="show_hidden_media">Mostrar pastas ocultas</string>
<string name="autoplay_videos">Reproduzir vídeos automaticamente</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">Слайдшоу завершилось</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 -->
<string name="show_hidden_media">Показать скрытые папки</string>
<string name="autoplay_videos">Воспроизводить видео автоматически</string>

View file

@ -102,6 +102,11 @@
<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>
<!-- View types -->
<string name="change_view_type">Zmeniť typ zobrazenia</string>
<string name="grid">Mriežka</string>
<string name="list">Zoznam</string>
<!-- Settings -->
<string name="show_hidden_media">Zobraziť skryté médiá</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="use_default">Använd standard</string>
<string name="set_as">Ange som</string>
<string name="volume">Volume</string>
<string name="brightness">Brightness</string>
<string name="volume">Volym</string>
<string name="brightness">Ljusstyrka</string>
<!-- Filter -->
<string name="filter_media">Filter media</string>
<string name="images">Images</string>
<string name="videos">Videos</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="change_filters_underlined"><u>Change filters</u></string>
<string name="filter_media">Filtrera media</string>
<string name="images">Bilder</string>
<string name="videos">Videor</string>
<string name="gifs">GIF-bilder</string>
<string name="no_media_with_filters">Inga mediefiler hittades med valda filter.</string>
<string name="change_filters_underlined"><u>Ändra filter</u></string>
<!-- 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>
@ -49,7 +49,7 @@
<string name="include_folders">Inkluderade mappar</string>
<string name="manage_included_folders">Hantera inkluderade mappar</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 -->
<string name="resize">Ändra storlek</string>
@ -90,17 +90,22 @@
<string name="landscape_aspect_ratio">Liggande bildförhållande</string>
<!-- Slideshow -->
<string name="slideshow">Slideshow</string>
<string name="interval">Interval (seconds):</string>
<string name="include_photos">Include photos</string>
<string name="include_videos">Include videos</string>
<string name="include_gifs">Include GIFs</string>
<string name="random_order">Random order</string>
<string name="use_fade">Use fade animations</string>
<string name="move_backwards">Move backwards</string>
<string name="loop_slideshow">Loop slideshow</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="slideshow">Bildspel</string>
<string name="interval">Intervall (sekunder):</string>
<string name="include_photos">Inkludera foton</string>
<string name="include_videos">Inkludera videor</string>
<string name="include_gifs">Inkludera GIF-bilder</string>
<string name="random_order">Spela upp i slumpmässig ordning</string>
<string name="use_fade">Använd toningsanimationer</string>
<string name="move_backwards">Spela upp i omvänd ordning</string>
<string name="loop_slideshow">Spela upp i en slinga</string>
<string name="slideshow_ended">Bildspelet har avslutats</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 -->
<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="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="delete_empty_folders">Delete empty folders after deleting their content</string>
<string name="allow_video_gestures">Allow controlling video volume and brightness with vertical gestures</string>
<string name="replace_share_with_rotate">Replace Share with Rotate at fullscreen menu</string>
<string name="delete_empty_folders">Ta bort tomma mappar när deras innehåll tas bort</string>
<string name="allow_video_gestures">Tillåt styrning av videovolym och videoljusstyrka med vertikala gester</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 -->
<!-- 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_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.

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">The slideshow ended</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 -->
<string name="show_hidden_media">Gizli klasörleri göster</string>
<string name="autoplay_videos">Videoları otomatik olarak oynat</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">幻灯片结束</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 -->
<string name="show_hidden_media">显示所有</string>
<string name="autoplay_videos">自动播放</string>

View file

@ -102,6 +102,11 @@
<string name="slideshow_ended">投影片結束</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 -->
<string name="show_hidden_media">顯示隱藏的媒體檔案</string>
<string name="autoplay_videos">自動播放影片</string>

View file

@ -3,9 +3,11 @@
<dimen name="dir_tmb_size">150dp</dimen>
<dimen name="medium_tmb_size">100dp</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="play_outline_size_big">150dp</dimen>
<dimen name="timer_padding">24dp</dimen>
<dimen name="tmb_shadow_height">50dp</dimen>
<dimen name="video_side_slider_width">150dp</dimen>
<dimen name="list_view_folder_thumbnail_size">72dp</dimen>
</resources>

View file

@ -2,6 +2,10 @@
<resources>
<!-- Release notes -->
<string name="release_133">
Added fingerprint to hidden item protection\n
Added a new List view type
</string>
<string name="release_127">
Added a switch for disabling video gestures\n
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="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 -->
<string name="show_hidden_media">Show hidden media</string>
<string name="autoplay_videos">Play videos automatically</string>