diff --git a/.gitignore b/.gitignore
index 7051e766d..660f31f5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*.iml
+*.aab
.gradle
/local.properties
/gradle.properties
@@ -6,5 +7,5 @@
.DS_Store
/build
/captures
-release.keystore
-signing.properties
+keystore.jks
+keystore.properties
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1340a8d7..17ab5339f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,226 @@
Changelog
==========
+Version 6.5.0 *(2019-02-07)*
+----------------------------
+
+ * Allow rotating fullscreen images with gestures, if "Allow deep zooming images" option is enabled
+ * Zoom out videos and gifs after device rotation
+
+Version 6.4.1 *(2019-01-29)*
+----------------------------
+
+ * Fixed some crashes related to zoomable videos
+ * Disable the Close Down gesture at GIFs and videos, if they are zoomed in
+
+Version 6.4.0 *(2019-01-29)*
+----------------------------
+
+ * Implemented export/importing for app settings and other preferences, like sorting
+ * Allow hiding Notch on fullscreen view on Android 9+
+ * Some gif/video zoom related improvements
+ * Autosave images zoomed at the fullscreen view
+ * Many other UX and stability improvements
+
+Version 6.3.2 *(2019-01-23)*
+----------------------------
+
+ * Fixed some fullscreen image and gif issues related to zooming
+ * Show directly included folders even if they contain a .nomedia file
+
+Version 6.3.1 *(2019-01-22)*
+----------------------------
+
+ * Fixed fullscreen images crashing when the app was installed on an SD card
+ * A couple other fullscreen image viewer improvements
+ * Allow batch rotating only images, ignore other file types
+
+Version 6.3.0 *(2019-01-17)*
+----------------------------
+
+ * Allow zooming GIFs and videos
+ * Allow sharing images directly from the editor
+ * Allow drawing in the editor
+ * If a folder is directly excluded, make it a higher priority than some included parent folder
+ * Added batch rotating from the thumbnails view
+ * Many other smaller improvements
+
+Version 6.2.2 *(2019-01-10)*
+----------------------------
+
+ * Reverted to the old way of playing videos, opening them on a separate screen can be enabled in the app settings
+ * Added some memory related improvements at displaying fullscreen images
+ * Allow showing videos in slideshows
+
+Version 6.2.1 *(2019-01-08)*
+----------------------------
+
+ * Fixed some menu buttons at the video player activity
+ * Added buttons to the videoplayer for going to the previous/next item
+ * Allow pressing play/pause at the video player at fullscreen mode
+ * Properly retain exif values after editing a file, when overwriting the source file
+
+Version 6.2.0 *(2019-01-04)*
+----------------------------
+
+ * Rewrote video playback, use a separate screen + added fast-forwarding with horizontal swiping
+ * Added optional 1:1 pixel ratio zooming with two double taps at fullscreen view
+ * Allow adding Copy at the fullscreen bottom actions
+ * Always include images at slideshows, never videos
+ * Fixed scanning of some predefined folders for images
+ * Some other stability/performance/translation improvements
+
+Version 6.1.3 *(2018-12-26)*
+----------------------------
+
+ * Fixed a glitch at zooming fullscreen images with double tap
+ * Hide favorite items from hidden folders, if showing hidden items is disabled
+
+Version 6.1.2 *(2018-12-24)*
+----------------------------
+
+ * Done a few performance improvements here and there
+ * Allow changing view type individually per folder
+ * Merry Christmas!
+
+Version 6.1.1 *(2018-12-18)*
+----------------------------
+
+ * Fixing some crashes
+
+Version 6.1.0 *(2018-12-17)*
+----------------------------
+
+ * Added an initial widget implementation for creating homescreen folder shortcuts
+ * Added optional grouping of direct subfolders, as a check at the "Change view type" dialog
+ * Added an option to set custom crop aspect ratio at the editor
+ * Save exif data at edited files on Android 7+
+ * Handle only Mass Storage USB devices, ignore the rest
+ * Many other smaller UX/stability/performance improvements
+
+Version 6.0.4 *(2018-12-04)*
+----------------------------
+
+ * Limit automatic spam folder exclusion to the "/Android/data" folder
+
+Version 6.0.3 *(2018-12-02)*
+----------------------------
+
+ * Added multiple predefined aspect ratios at the Editor + remember the last used ratio
+ * Fix some issue with deleted items not appearing in the Recycle Bin, causing the app to take up too much space
+ * At delete/copy/move operations on folders apply them only on the visible files, take filters/hiding into account
+ * Do not exclude whole Data folder by default, be smarter about filtering out spam folders
+ * Added support for Sony RAW ".arw" files
+ * Optimize video duration fetching at thumbnails
+
+Version 6.0.2 *(2018-11-19)*
+----------------------------
+
+ * Adding a crashfix related to showing video duration
+
+Version 6.0.1 *(2018-11-19)*
+----------------------------
+
+ * Added optional displaying video duration on thumbnails
+ * Fixed keeping last_modified value at copy/move in some cases
+ * Exclude the Data folder by default
+ * Many translation, UX and stability improvements
+
+Version 6.0.0 *(2018-11-04)*
+----------------------------
+
+ * Initial Pro version
+
+Version 5.1.4 *(2018-11-28)*
+----------------------------
+
+ * Make sure the "Upgrade to Pro" popup isn't shown at first launch
+ * This version of the app is no longer maintained, please upgrade to the Pro version. You can find the Upgrade button at the top of the app Settings.
+
+Version 5.1.3 *(2018-11-04)*
+----------------------------
+
+ * Adding an option to store last video playback position (by mathevs)
+ * Adding a "Keep both" conflict resolution at copy/move (by Doubl3MM)
+ * Improved panoramic video detection
+ * Remove some glitches related to third party file opening
+ * Do not exclude the Data folder by default
+ * Removed the "Avoid showing Whats New at app startup" option
+
+Version 5.1.2 *(2018-10-30)*
+----------------------------
+
+ * Added a new option for password protecting file deletion/move
+ * Improved panorama video detection
+ * Improved the opening of media files without file extension
+ * Disabled move operation on Recycle bin items, use Restore
+ * Fixed handling of some third party image picker intents
+ * Fixed slideshow looping and a couple other UX glitches
+ * Improved the stability of retrieving cached files
+ * Hi
+
+Version 5.1.1 *(2018-10-23)*
+----------------------------
+
+ * Fixing the inability to delete SD card files
+
+Version 5.1.0 *(2018-10-23)*
+----------------------------
+
+ * Added support for panorama videos
+ * Added an extra check to avoid trying deleting files without write permission
+ * Added an initial implementation of renaming multiple items at once
+ * Added some performance improvements at item de/selection
+ * Allow enabling hidden item visibility at the copy/move destination picker
+ * Allow closing fullscreen view with swipe down gestures (can be disabled in settings)
+ * Fixed a glitch with Favorite items getting unselected every day
+ * Fixed exposure time displayed at long exposure photos
+ * Fixed fullscreen images being sometimes totally zoomed in after device rotation
+ * Fixed slideshow direction
+ * Made loading initial fullscreen view quicker and fullscreen toggling more reliable
+ * Not sure what else, nobody reads this anyway
+
+Version 5.0.1 *(2018-10-17)*
+----------------------------
+
+ * Adding some crashfixes
+
+Version 5.0.0 *(2018-10-17)*
+----------------------------
+
+ * Increased the minimal required Android OS version to 5 (Lollipop)
+ * Rewrote file selection for more robustness
+ * Added a new option for showing the Recycle Bin as the last folder
+ * Added Search for searching folders by names
+ * Replaced the G+ button with Reddit
+ * Couple smaller glitch fixes and improvements
+
+Version 4.6.5 *(2018-10-02)*
+----------------------------
+
+ * Added notch support for Android 9
+ * Allow faster video seeking by dragging a finger at the bottom seekbar
+ * Use a different way of displaying fullscreen GIFs
+ * Added a new toggle for trying to show the best possible image quality
+ * Keep Favorite items marked after moving
+ * Fixed some glitches related to toggling fullscreen mode
+ * Many other smaller improvements
+
+Version 4.6.4 *(2018-09-22)*
+----------------------------
+
+ * Fixed lag at zooming fullscreen images on some devices
+
+Version 4.6.3 *(2018-09-21)*
+----------------------------
+
+ * Improved zooming performance at fullscreen view
+ * Fixed showing conflict resolution dialog at Move
+ * Fixed selection check icons at horizontal scrolling
+ * Fixed displaying some fullscreen images, where file path contained percentage sign or hashtag
+ * Optimized many database operations
+ * Fixed many other smaller issues
+
Version 4.6.2 *(2018-09-05)*
----------------------------
diff --git a/README.md b/README.md
index b804ef6d6..6b7f501ad 100644
--- a/README.md
+++ b/README.md
@@ -4,21 +4,39 @@
A gallery for viewing photos and videos.
-A simple tool usable for viewing photos and videos. Items can be sorted by date, size, name both ascending or descending, photos can be zoomed in. Media files are shown in multiple columns depending on the size of the display, you can change the column count by pinch gestures. They can be renamed, shared, deleted, copied, moved. Images can also be cropped, rotated, flipped or set as Wallpaper directly from the app.
+A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
-The Gallery is also offered for third party usage for previewing images / videos, adding attachments at email clients etc. It's perfect for everyday usage.
+It is open source, contains no ads or unnecessary permissions.
-Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
+Let's list some of its features worth mentioning:
+1. Search
+2. Slideshow
+3. Notch support
+4. Pinning folders to the top
+5. Filtering media files by type
+6. Recycle bin for easy file recovery
+7. Fullscreen view orientation locking
+8. Marking favorite files for easy access
+9. Quick fullscreen media closing with down gesture
+10. An editor for modifying images and applying filters
+11. Password protection for protecting hidden items or the whole app
+12. Changing the thumbnail column count with gestures or menu buttons
+13. Customizable bottom actions at the fullscreen view for quick access
+14. Showing extended details over fullscreen media with desired file properties
+15. Several different ways of sorting or grouping items, both ascending and descending
+16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
-This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com
+The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
-
-
+This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
+
+
+
-
+
License
diff --git a/app/build.gradle b/app/build.gradle
index d955bc467..4b8e30b97 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,22 +3,31 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
+def keystorePropertiesFile = rootProject.file("keystore.properties")
+def keystoreProperties = new Properties()
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
android {
compileSdkVersion 28
- buildToolsVersion "28.0.2"
+ buildToolsVersion "28.0.3"
defaultConfig {
- applicationId "com.simplemobiletools.gallery"
- minSdkVersion 16
+ applicationId "com.simplemobiletools.gallery.pro"
+ minSdkVersion 21
targetSdkVersion 28
- versionCode 198
- versionName "4.6.2"
+ versionCode 228
+ versionName "6.5.0"
multiDexEnabled true
setProperty("archivesBaseName", "gallery")
}
signingConfigs {
- release
+ release {
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ }
}
buildTypes {
@@ -40,49 +49,36 @@ android {
checkReleaseBuilds false
abortOnError false
}
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ packagingOptions {
+ exclude 'META-INF/library_release.kotlin_module'
+ }
}
dependencies {
- implementation 'com.simplemobiletools:commons:4.7.14'
- implementation 'com.theartofdev.edmodo:android-image-cropper:2.7.0'
- implementation 'com.android.support:multidex:1.0.3'
+ implementation 'com.simplemobiletools:commons:5.7.6'
+ implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
+ implementation 'androidx.multidex:multidex:2.0.1'
implementation 'it.sephiroth.android.exif:library:1.0.1'
- implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- implementation 'com.google.android.exoplayer:exoplayer-core:2.8.4'
- implementation 'com.google.vr:sdk-panowidget:1.150.0'
+ implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.16'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
+ implementation 'com.google.android.exoplayer:exoplayer-core:2.9.2'
+ implementation 'com.google.vr:sdk-panowidget:1.180.0'
+ implementation 'com.google.vr:sdk-videowidget:1.180.0'
implementation 'org.apache.sanselan:sanselan:0.97-incubator'
implementation 'info.androidhive:imagefilters:1.0.7'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation 'com.caverock:androidsvg-aar:1.3'
+ implementation 'com.github.tibbi:gestureviews:bd0a8e67a1'
+ implementation 'com.github.tibbi:subsampling-scale-image-view:2fe77ff361'
kapt 'com.github.bumptech.glide:compiler:4.8.0' // keep it here too, not just in Commons, else loading SVGs wont work
- kapt "android.arch.persistence.room:compiler:1.1.1"
- implementation "android.arch.persistence.room:runtime:1.1.1"
- annotationProcessor "android.arch.persistence.room:compiler:1.1.1"
-
- //implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.9.0'
- implementation 'com.github.tibbi:subsampling-scale-image-view:v3.10.0-fork'
-
- // implementation 'com.github.chrisbanes:PhotoView:2.1.4'
- implementation 'com.github.tibbi:PhotoView:2.1.4-fork'
-}
-
-Properties props = new Properties()
-def propFile = new File('signing.properties')
-if (propFile.canRead()) {
- props.load(new FileInputStream(propFile))
-
- if (props != null && props.containsKey('STORE_FILE') && props.containsKey('KEY_ALIAS') && props.containsKey('PASSWORD')) {
- android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
- android.signingConfigs.release.storePassword = props['PASSWORD']
- android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
- android.signingConfigs.release.keyPassword = props['PASSWORD']
- } else {
- println 'signing.properties found but some entries are missing'
- android.buildTypes.release.signingConfig = null
- }
-} else {
- println 'signing.properties not found'
- android.buildTypes.release.signingConfig = null
+ kapt 'androidx.room:room-compiler:2.0.0'
+ implementation 'androidx.room:room-runtime:2.0.0'
+ annotationProcessor 'androidx.room:room-compiler:2.0.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e58d5e3a1..8b41fb872 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,7 +2,7 @@
@@ -32,6 +32,14 @@
android:name=".activities.MainActivity"
android:resizeableActivity="true">
+
+
+
+
+
+
@@ -115,7 +123,17 @@
android:configChanges="orientation|keyboardHidden|screenSize"/>
+
+
+
+
@@ -202,8 +220,17 @@
+
+
+
+
+
+
@@ -220,6 +247,18 @@
+
+
+
+
+
+
+
+
10 && !isThankYouInstalled())
- settings_purchase_thank_you_holder.setOnClickListener {
- launchPurchaseThankYouIntent()
- }
- }
-
- private fun setupCustomizeColors() {
- settings_customize_colors_holder.setOnClickListener {
- startCustomizationActivity()
- }
- }
-
- private fun setupUseEnglish() {
- settings_use_english_holder.beVisibleIf(config.wasUseEnglishToggled || Locale.getDefault().language != "en")
- settings_use_english.isChecked = config.useEnglish
- settings_use_english_holder.setOnClickListener {
- settings_use_english.toggle()
- config.useEnglish = settings_use_english.isChecked
- System.exit(0)
- }
- }
-
- private fun setupAvoidWhatsNew() {
- settings_avoid_whats_new.isChecked = config.avoidWhatsNew
- settings_avoid_whats_new_holder.setOnClickListener {
- settings_avoid_whats_new.toggle()
- config.avoidWhatsNew = settings_avoid_whats_new.isChecked
- }
- }
-
- private fun setupManageIncludedFolders() {
- settings_manage_included_folders_holder.setOnClickListener {
- startActivity(Intent(this, IncludedFoldersActivity::class.java))
- }
- }
-
- private fun setupManageExcludedFolders() {
- settings_manage_excluded_folders_holder.setOnClickListener {
- startActivity(Intent(this, ExcludedFoldersActivity::class.java))
- }
- }
-
- private fun setupManageHiddenFolders() {
- settings_manage_hidden_folders_holder.setOnClickListener {
- handleHiddenFolderPasswordProtection {
- startActivity(Intent(this, HiddenFoldersActivity::class.java))
- }
- }
- }
-
- private fun setupShowHiddenItems() {
- settings_show_hidden_items.isChecked = config.showHiddenMedia
- settings_show_hidden_items_holder.setOnClickListener {
- if (config.showHiddenMedia) {
- toggleHiddenItems()
- } else {
- handleHiddenFolderPasswordProtection {
- toggleHiddenItems()
- }
- }
- }
- }
-
- private fun toggleHiddenItems() {
- settings_show_hidden_items.toggle()
- config.showHiddenMedia = settings_show_hidden_items.isChecked
- }
-
- private fun setupDoExtraCheck() {
- settings_do_extra_check.isChecked = config.doExtraCheck
- settings_do_extra_check_holder.setOnClickListener {
- settings_do_extra_check.toggle()
- config.doExtraCheck = settings_do_extra_check.isChecked
- }
- }
-
- private fun setupAutoplayVideos() {
- settings_autoplay_videos.isChecked = config.autoplayVideos
- settings_autoplay_videos_holder.setOnClickListener {
- settings_autoplay_videos.toggle()
- config.autoplayVideos = settings_autoplay_videos.isChecked
- }
- }
-
- private fun setupLoopVideos() {
- settings_loop_videos.isChecked = config.loopVideos
- settings_loop_videos_holder.setOnClickListener {
- settings_loop_videos.toggle()
- config.loopVideos = settings_loop_videos.isChecked
- }
- }
-
- private fun setupAnimateGifs() {
- settings_animate_gifs.isChecked = config.animateGifs
- settings_animate_gifs_holder.setOnClickListener {
- settings_animate_gifs.toggle()
- config.animateGifs = settings_animate_gifs.isChecked
- }
- }
-
- private fun setupMaxBrightness() {
- settings_max_brightness.isChecked = config.maxBrightness
- settings_max_brightness_holder.setOnClickListener {
- settings_max_brightness.toggle()
- config.maxBrightness = settings_max_brightness.isChecked
- }
- }
-
- private fun setupCropThumbnails() {
- settings_crop_thumbnails.isChecked = config.cropThumbnails
- settings_crop_thumbnails_holder.setOnClickListener {
- settings_crop_thumbnails.toggle()
- config.cropThumbnails = settings_crop_thumbnails.isChecked
- }
- }
-
- private fun setupDarkBackground() {
- settings_black_background.isChecked = config.blackBackground
- settings_black_background_holder.setOnClickListener {
- settings_black_background.toggle()
- config.blackBackground = settings_black_background.isChecked
- }
- }
-
- private fun setupScrollHorizontally() {
- settings_scroll_horizontally.isChecked = config.scrollHorizontally
- settings_scroll_horizontally_holder.setOnClickListener {
- settings_scroll_horizontally.toggle()
- config.scrollHorizontally = settings_scroll_horizontally.isChecked
-
- if (config.scrollHorizontally) {
- config.enablePullToRefresh = false
- settings_enable_pull_to_refresh.isChecked = false
- }
- }
- }
-
- private fun setupHideSystemUI() {
- settings_hide_system_ui.isChecked = config.hideSystemUI
- settings_hide_system_ui_holder.setOnClickListener {
- settings_hide_system_ui.toggle()
- config.hideSystemUI = settings_hide_system_ui.isChecked
- }
- }
-
- private fun setupPasswordProtection() {
- settings_password_protection.isChecked = config.isPasswordProtectionOn
- settings_password_protection_holder.setOnClickListener {
- val tabToShow = if (config.isPasswordProtectionOn) config.protectionType else SHOW_ALL_TABS
- SecurityDialog(this, config.passwordHash, tabToShow) { hash, type, success ->
- if (success) {
- val hasPasswordProtection = config.isPasswordProtectionOn
- settings_password_protection.isChecked = !hasPasswordProtection
- config.isPasswordProtectionOn = !hasPasswordProtection
- config.passwordHash = if (hasPasswordProtection) "" else hash
- config.protectionType = type
-
- if (config.isPasswordProtectionOn) {
- 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) { }
- }
- }
- }
- }
- }
-
- private fun setupAppPasswordProtection() {
- settings_app_password_protection.isChecked = config.appPasswordProtectionOn
- settings_app_password_protection_holder.setOnClickListener {
- val tabToShow = if (config.appPasswordProtectionOn) config.appProtectionType else SHOW_ALL_TABS
- SecurityDialog(this, config.appPasswordHash, tabToShow) { hash, type, success ->
- if (success) {
- val hasPasswordProtection = config.appPasswordProtectionOn
- settings_app_password_protection.isChecked = !hasPasswordProtection
- config.appPasswordProtectionOn = !hasPasswordProtection
- config.appPasswordHash = if (hasPasswordProtection) "" else hash
- config.appProtectionType = type
-
- if (config.appPasswordProtectionOn) {
- val confirmationTextId = if (config.appProtectionType == PROTECTION_FINGERPRINT)
- R.string.fingerprint_setup_successfully else R.string.protection_setup_successfully
- ConfirmationDialog(this, "", confirmationTextId, R.string.ok, 0) { }
- }
- }
- }
- }
- }
-
- private fun setupDeleteEmptyFolders() {
- settings_delete_empty_folders.isChecked = config.deleteEmptyFolders
- settings_delete_empty_folders_holder.setOnClickListener {
- settings_delete_empty_folders.toggle()
- config.deleteEmptyFolders = settings_delete_empty_folders.isChecked
- }
- }
-
- private fun setupAllowPhotoGestures() {
- settings_allow_photo_gestures.isChecked = config.allowPhotoGestures
- settings_allow_photo_gestures_holder.setOnClickListener {
- settings_allow_photo_gestures.toggle()
- config.allowPhotoGestures = settings_allow_photo_gestures.isChecked
- }
- }
-
- private fun setupAllowVideoGestures() {
- settings_allow_video_gestures.isChecked = config.allowVideoGestures
- settings_allow_video_gestures_holder.setOnClickListener {
- settings_allow_video_gestures.toggle()
- config.allowVideoGestures = settings_allow_video_gestures.isChecked
- }
- }
-
- private fun setupShowMediaCount() {
- settings_show_media_count.isChecked = config.showMediaCount
- settings_show_media_count_holder.setOnClickListener {
- settings_show_media_count.toggle()
- config.showMediaCount = settings_show_media_count.isChecked
- }
- }
-
- private fun setupKeepLastModified() {
- settings_keep_last_modified.isChecked = config.keepLastModified
- settings_keep_last_modified_holder.setOnClickListener {
- settings_keep_last_modified.toggle()
- config.keepLastModified = settings_keep_last_modified.isChecked
- }
- }
-
- private fun setupShowInfoBubble() {
- settings_show_info_bubble.isChecked = config.showInfoBubble
- settings_show_info_bubble_holder.setOnClickListener {
- settings_show_info_bubble.toggle()
- config.showInfoBubble = settings_show_info_bubble.isChecked
- }
- }
-
- private fun setupEnablePullToRefresh() {
- settings_enable_pull_to_refresh.isChecked = config.enablePullToRefresh
- settings_enable_pull_to_refresh_holder.setOnClickListener {
- settings_enable_pull_to_refresh.toggle()
- config.enablePullToRefresh = settings_enable_pull_to_refresh.isChecked
- }
- }
-
- private fun setupAllowZoomingImages() {
- settings_allow_zooming_images.isChecked = config.allowZoomingImages
- settings_allow_zooming_images_holder.setOnClickListener {
- settings_allow_zooming_images.toggle()
- config.allowZoomingImages = settings_allow_zooming_images.isChecked
- settings_one_finger_zoom_holder.beVisibleIf(config.allowZoomingImages)
- }
- }
-
- private fun setupOneFingerZoom() {
- settings_one_finger_zoom.isChecked = config.oneFingerZoom
- settings_one_finger_zoom_holder.setOnClickListener {
- settings_one_finger_zoom.toggle()
- config.oneFingerZoom = settings_one_finger_zoom.isChecked
- }
- }
-
- private fun setupAllowInstantChange() {
- settings_allow_instant_change.isChecked = config.allowInstantChange
- settings_allow_instant_change_holder.setOnClickListener {
- settings_allow_instant_change.toggle()
- config.allowInstantChange = settings_allow_instant_change.isChecked
- }
- }
-
- private fun setupShowExtendedDetails() {
- settings_show_extended_details.isChecked = config.showExtendedDetails
- settings_show_extended_details_holder.setOnClickListener {
- settings_show_extended_details.toggle()
- config.showExtendedDetails = settings_show_extended_details.isChecked
- settings_manage_extended_details_holder.beVisibleIf(config.showExtendedDetails)
- settings_hide_extended_details_holder.beVisibleIf(config.showExtendedDetails)
- }
- }
-
- private fun setupHideExtendedDetails() {
- settings_hide_extended_details_holder.beVisibleIf(config.showExtendedDetails)
- settings_hide_extended_details.isChecked = config.hideExtendedDetails
- settings_hide_extended_details_holder.setOnClickListener {
- settings_hide_extended_details.toggle()
- config.hideExtendedDetails = settings_hide_extended_details.isChecked
- }
- }
-
- private fun setupManageExtendedDetails() {
- settings_manage_extended_details_holder.beVisibleIf(config.showExtendedDetails)
- settings_manage_extended_details_holder.setOnClickListener {
- ManageExtendedDetailsDialog(this) {
- if (config.extendedDetails == 0) {
- settings_show_extended_details_holder.callOnClick()
- }
- }
- }
- }
-
- private fun setupSkipDeleteConfirmation() {
- settings_skip_delete_confirmation.isChecked = config.skipDeleteConfirmation
- settings_skip_delete_confirmation_holder.setOnClickListener {
- settings_skip_delete_confirmation.toggle()
- config.skipDeleteConfirmation = settings_skip_delete_confirmation.isChecked
- }
- }
-
- private fun setupScreenRotation() {
- settings_screen_rotation.text = getScreenRotationText()
- settings_screen_rotation_holder.setOnClickListener {
- val items = arrayListOf(
- RadioItem(ROTATE_BY_SYSTEM_SETTING, res.getString(R.string.screen_rotation_system_setting)),
- RadioItem(ROTATE_BY_DEVICE_ROTATION, res.getString(R.string.screen_rotation_device_rotation)),
- RadioItem(ROTATE_BY_ASPECT_RATIO, res.getString(R.string.screen_rotation_aspect_ratio)))
-
- RadioGroupDialog(this@SettingsActivity, items, config.screenRotation) {
- config.screenRotation = it as Int
- settings_screen_rotation.text = getScreenRotationText()
- }
- }
- }
-
- private fun getScreenRotationText() = getString(when (config.screenRotation) {
- ROTATE_BY_SYSTEM_SETTING -> R.string.screen_rotation_system_setting
- ROTATE_BY_DEVICE_ROTATION -> R.string.screen_rotation_device_rotation
- else -> R.string.screen_rotation_aspect_ratio
- })
-
- private fun setupBottomActions() {
- settings_bottom_actions.isChecked = config.bottomActions
- settings_bottom_actions_holder.setOnClickListener {
- settings_bottom_actions.toggle()
- config.bottomActions = settings_bottom_actions.isChecked
- settings_manage_bottom_actions_holder.beVisibleIf(config.bottomActions)
- }
- }
-
- private fun setupManageBottomActions() {
- settings_manage_bottom_actions_holder.beVisibleIf(config.bottomActions)
- settings_manage_bottom_actions_holder.setOnClickListener {
- ManageBottomActionsDialog(this) {
- if (config.visibleBottomActions == 0) {
- settings_bottom_actions_holder.callOnClick()
- config.bottomActions = false
- config.visibleBottomActions = DEFAULT_BOTTOM_ACTIONS
- }
- }
- }
- }
-
- private fun setupUseRecycleBin() {
- settings_empty_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
- settings_show_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
- settings_use_recycle_bin.isChecked = config.useRecycleBin
- settings_use_recycle_bin_holder.setOnClickListener {
- settings_use_recycle_bin.toggle()
- config.useRecycleBin = settings_use_recycle_bin.isChecked
- settings_empty_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
- }
- }
-
- private fun setupShowRecycleBin() {
- settings_show_recycle_bin.isChecked = config.showRecycleBinAtFolders
- settings_show_recycle_bin_holder.setOnClickListener {
- settings_show_recycle_bin.toggle()
- config.showRecycleBinAtFolders = settings_show_recycle_bin.isChecked
- }
- }
-
- private fun setupEmptyRecycleBin() {
- Thread {
- mRecycleBinContentSize = galleryDB.MediumDao().getDeletedMedia().sumByLong { it.size }
- runOnUiThread {
- settings_empty_recycle_bin_size.text = mRecycleBinContentSize.formatSize()
- }
- }.start()
-
- settings_empty_recycle_bin_holder.setOnClickListener {
- if (mRecycleBinContentSize == 0L) {
- toast(R.string.recycle_bin_empty)
- } else {
- showRecycleBinEmptyingDialog {
- emptyTheRecycleBin()
- mRecycleBinContentSize = 0L
- settings_empty_recycle_bin_size.text = 0L.formatSize()
- }
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/databases/GalleryDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/databases/GalleryDatabase.kt
deleted file mode 100644
index a787d3a4d..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/databases/GalleryDatabase.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.simplemobiletools.gallery.databases
-
-import android.arch.persistence.room.Database
-import android.arch.persistence.room.Room
-import android.arch.persistence.room.RoomDatabase
-import android.content.Context
-import com.simplemobiletools.gallery.interfaces.DirectoryDao
-import com.simplemobiletools.gallery.interfaces.MediumDao
-import com.simplemobiletools.gallery.models.Directory
-import com.simplemobiletools.gallery.models.Medium
-
-@Database(entities = [(Directory::class), (Medium::class)], version = 4)
-abstract class GalleryDatabase : RoomDatabase() {
-
- abstract fun DirectoryDao(): DirectoryDao
-
- abstract fun MediumDao(): MediumDao
-
- companion object {
- private var db: GalleryDatabase? = null
-
- fun getInstance(context: Context): GalleryDatabase {
- if (db == null) {
- synchronized(GalleryDatabase::class) {
- if (db == null) {
- db = Room.databaseBuilder(context.applicationContext, GalleryDatabase::class.java, "gallery.db")
- .fallbackToDestructiveMigration()
- .build()
- db!!.openHelper.setWriteAheadLoggingEnabled(true)
- }
- }
- }
- return db!!
- }
-
- fun destroyInstance() {
- db = null
- }
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickDirectoryDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickDirectoryDialog.kt
deleted file mode 100644
index 653041574..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickDirectoryDialog.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.simplemobiletools.gallery.dialogs
-
-import android.support.v7.app.AlertDialog
-import android.support.v7.widget.GridLayoutManager
-import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.commons.dialogs.FilePickerDialog
-import com.simplemobiletools.commons.extensions.beGoneIf
-import com.simplemobiletools.commons.extensions.beVisibleIf
-import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.commons.extensions.toast
-import com.simplemobiletools.commons.views.MyGridLayoutManager
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.DirectoryAdapter
-import com.simplemobiletools.gallery.extensions.addTempFolderIfNeeded
-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.*
-
-class PickDirectoryDialog(val activity: BaseSimpleActivity, val sourcePath: String, val callback: (path: String) -> Unit) {
- var dialog: AlertDialog
- var shownDirectories = ArrayList()
- var view = activity.layoutInflater.inflate(R.layout.dialog_directory_picker, null)
- var isGridViewType = activity.config.viewTypeFolders == VIEW_TYPE_GRID
-
- init {
- (view.directories_grid.layoutManager as MyGridLayoutManager).apply {
- orientation = if (activity.config.scrollHorizontally && isGridViewType) GridLayoutManager.HORIZONTAL else GridLayoutManager.VERTICAL
- spanCount = if (isGridViewType) activity.config.dirColumnCnt else 1
- }
-
- dialog = AlertDialog.Builder(activity)
- .setPositiveButton(R.string.ok, null)
- .setNegativeButton(R.string.cancel, null)
- .setNeutralButton(R.string.other_folder, { dialogInterface, i -> showOtherFolder() })
- .create().apply {
- activity.setupDialogStuff(view, this, R.string.select_destination)
- }
-
- activity.getCachedDirectories {
- if (it.isNotEmpty()) {
- activity.runOnUiThread {
- gotDirectories(activity.addTempFolderIfNeeded(it))
- }
- }
- }
- }
-
- private fun showOtherFolder() {
- val showHidden = activity.config.shouldShowHidden
- FilePickerDialog(activity, sourcePath, false, showHidden, true) {
- callback(it)
- }
- }
-
- private fun gotDirectories(newDirs: ArrayList) {
- val dirs = activity.getSortedDirectories(newDirs)
- if (dirs.hashCode() == shownDirectories.hashCode())
- return
-
- shownDirectories = dirs
- val adapter = DirectoryAdapter(activity, dirs.clone() as ArrayList, null, view.directories_grid, true) {
- if ((it as Directory).path.trimEnd('/') == sourcePath) {
- activity.toast(R.string.source_and_destination_same)
- return@DirectoryAdapter
- } else {
- callback(it.path)
- dialog.dismiss()
- }
- }
-
- val scrollHorizontally = activity.config.scrollHorizontally && isGridViewType
- val sorting = activity.config.directorySorting
- view.apply {
- directories_grid.adapter = adapter
-
- directories_vertical_fastscroller.isHorizontal = false
- directories_vertical_fastscroller.beGoneIf(scrollHorizontally)
-
- directories_horizontal_fastscroller.isHorizontal = true
- directories_horizontal_fastscroller.beVisibleIf(scrollHorizontally)
-
- if (scrollHorizontally) {
- directories_horizontal_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble
- directories_horizontal_fastscroller.setViews(directories_grid) {
- directories_horizontal_fastscroller.updateBubbleText(dirs[it].getBubbleText(sorting))
- }
- } else {
- directories_vertical_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble
- directories_vertical_fastscroller.setViews(directories_grid) {
- directories_vertical_fastscroller.updateBubbleText(dirs[it].getBubbleText(sorting))
- }
- }
- }
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ResizeDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ResizeDialog.kt
deleted file mode 100644
index 3489dd060..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ResizeDialog.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.simplemobiletools.gallery.dialogs
-
-import android.graphics.Point
-import android.support.v7.app.AlertDialog
-import android.text.Editable
-import android.text.TextWatcher
-import android.widget.EditText
-import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.commons.extensions.showKeyboard
-import com.simplemobiletools.commons.extensions.toast
-import com.simplemobiletools.commons.extensions.value
-import com.simplemobiletools.gallery.R
-import kotlinx.android.synthetic.main.resize_image.view.*
-
-class ResizeDialog(val activity: BaseSimpleActivity, val size: Point, val callback: (newSize: Point) -> Unit) {
- init {
- val view = activity.layoutInflater.inflate(R.layout.resize_image, null)
- val widthView = view.image_width
- val heightView = view.image_height
-
- widthView.setText(size.x.toString())
- heightView.setText(size.y.toString())
-
- val ratio = size.x / size.y.toFloat()
-
- widthView.addTextChangedListener(object : TextWatcher {
- override fun afterTextChanged(s: Editable?) {
- if (widthView.hasFocus()) {
- var width = getViewValue(widthView)
- if (width > size.x) {
- widthView.setText(size.x.toString())
- width = size.x
- }
-
- if (view.keep_aspect_ratio.isChecked) {
- heightView.setText((width / ratio).toInt().toString())
- }
- }
- }
-
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
- }
-
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- }
- })
-
- heightView.addTextChangedListener(object : TextWatcher {
- override fun afterTextChanged(s: Editable?) {
- if (heightView.hasFocus()) {
- var height = getViewValue(heightView)
- if (height > size.y) {
- heightView.setText(size.y.toString())
- height = size.y
- }
-
- if (view.keep_aspect_ratio.isChecked) {
- widthView.setText((height * ratio).toInt().toString())
- }
- }
- }
-
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
- }
-
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- }
- })
-
- AlertDialog.Builder(activity)
- .setPositiveButton(R.string.ok, null)
- .setNegativeButton(R.string.cancel, null)
- .create().apply {
- activity.setupDialogStuff(view, this, R.string.resize_and_save) {
- showKeyboard(view.image_width)
- getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
- val width = getViewValue(widthView)
- val height = getViewValue(heightView)
- if (width <= 0 || height <= 0) {
- activity.toast(R.string.invalid_values)
- return@setOnClickListener
- }
-
- val newSize = Point(getViewValue(widthView), getViewValue(heightView))
- callback(newSize)
- dismiss()
- }
- }
- }
- }
-
- fun getViewValue(view: EditText): Int {
- val textValue = view.value
- return if (textValue.isEmpty()) 0 else textValue.toInt()
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Activity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Activity.kt
deleted file mode 100644
index 0aee81bfe..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Activity.kt
+++ /dev/null
@@ -1,278 +0,0 @@
-package com.simplemobiletools.gallery.extensions
-
-import android.app.Activity
-import android.content.Intent
-import android.provider.MediaStore
-import android.support.v7.app.AppCompatActivity
-import android.view.View
-import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.commons.dialogs.ConfirmationDialog
-import com.simplemobiletools.commons.extensions.*
-import com.simplemobiletools.commons.helpers.*
-import com.simplemobiletools.commons.models.FAQItem
-import com.simplemobiletools.commons.models.FileDirItem
-import com.simplemobiletools.gallery.BuildConfig
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.activities.SimpleActivity
-import com.simplemobiletools.gallery.dialogs.PickDirectoryDialog
-import com.simplemobiletools.gallery.helpers.NOMEDIA
-import com.simplemobiletools.gallery.interfaces.MediumDao
-import java.io.File
-import java.io.InputStream
-import java.io.OutputStream
-import java.util.*
-
-fun Activity.sharePath(path: String) {
- sharePathIntent(path, BuildConfig.APPLICATION_ID)
-}
-
-fun Activity.sharePaths(paths: ArrayList) {
- sharePathsIntent(paths, BuildConfig.APPLICATION_ID)
-}
-
-fun Activity.shareMediumPath(path: String) {
- sharePath(path)
-}
-
-fun Activity.shareMediaPaths(paths: ArrayList) {
- sharePaths(paths)
-}
-
-fun Activity.setAs(path: String) {
- setAsIntent(path, BuildConfig.APPLICATION_ID)
-}
-
-fun Activity.openPath(path: String, forceChooser: Boolean) {
- openPathIntent(path, forceChooser, BuildConfig.APPLICATION_ID)
-}
-
-fun Activity.openEditor(path: String) {
- openEditorIntent(path, BuildConfig.APPLICATION_ID)
-}
-
-fun Activity.launchCamera() {
- val intent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
- if (intent.resolveActivity(packageManager) != null) {
- startActivity(intent)
- } else {
- toast(R.string.no_app_found)
- }
-}
-
-fun SimpleActivity.launchAbout() {
- val licenses = LICENSE_GLIDE or LICENSE_CROPPER or LICENSE_MULTISELECT or LICENSE_RTL or LICENSE_SUBSAMPLING or LICENSE_PATTERN or
- LICENSE_REPRINT or LICENSE_GIF_DRAWABLE or LICENSE_PHOTOVIEW or LICENSE_PICASSO or LICENSE_EXOPLAYER or LICENSE_PANORAMA_VIEW or LICENSE_SANSELAN or LICENSE_FILTERS
-
- val faqItems = arrayListOf(
- FAQItem(R.string.faq_5_title_commons, R.string.faq_5_text_commons),
- FAQItem(R.string.faq_1_title, R.string.faq_1_text),
- FAQItem(R.string.faq_2_title, R.string.faq_2_text),
- FAQItem(R.string.faq_3_title, R.string.faq_3_text),
- FAQItem(R.string.faq_4_title, R.string.faq_4_text),
- FAQItem(R.string.faq_5_title, R.string.faq_5_text),
- FAQItem(R.string.faq_6_title, R.string.faq_6_text),
- FAQItem(R.string.faq_7_title, R.string.faq_7_text),
- FAQItem(R.string.faq_8_title, R.string.faq_8_text),
- FAQItem(R.string.faq_10_title, R.string.faq_10_text),
- FAQItem(R.string.faq_11_title, R.string.faq_11_text),
- FAQItem(R.string.faq_12_title, R.string.faq_12_text),
- FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons))
-
- startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
-}
-
-fun AppCompatActivity.showSystemUI(toggleActionBarVisibility: Boolean) {
- if (toggleActionBarVisibility) {
- supportActionBar?.show()
- }
-
- window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-}
-
-fun AppCompatActivity.hideSystemUI(toggleActionBarVisibility: Boolean) {
- if (toggleActionBarVisibility) {
- supportActionBar?.hide()
- }
-
- window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
- View.SYSTEM_UI_FLAG_LOW_PROFILE or
- View.SYSTEM_UI_FLAG_FULLSCREEN or
- View.SYSTEM_UI_FLAG_IMMERSIVE
-}
-
-fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) {
- val file = File(path, NOMEDIA)
- if (file.exists()) {
- callback()
- return
- }
-
- if (needsStupidWritePermissions(path)) {
- handleSAFDialog(file.absolutePath) {
- val fileDocument = getDocumentFile(path)
- if (fileDocument?.exists() == true && fileDocument.isDirectory) {
- fileDocument.createFile("", NOMEDIA)
- applicationContext.scanFileRecursively(file) {
- callback()
- }
- } else {
- toast(R.string.unknown_error_occurred)
- callback()
- }
- }
- } else {
- try {
- file.createNewFile()
- applicationContext.scanFileRecursively(file) {
- callback()
- }
- } catch (e: Exception) {
- showErrorToast(e)
- callback()
- }
- }
-}
-
-fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = null) {
- val file = File(path, NOMEDIA)
- if (!file.exists()) {
- callback?.invoke()
- return
- }
-
- tryDeleteFileDirItem(file.toFileDirItem(applicationContext), false, false) {
- callback?.invoke()
- }
-}
-
-fun BaseSimpleActivity.toggleFileVisibility(oldPath: String, hide: Boolean, callback: ((newPath: String) -> Unit)? = null) {
- val path = oldPath.getParentPath()
- var filename = oldPath.getFilenameFromPath()
- if ((hide && filename.startsWith('.')) || (!hide && !filename.startsWith('.'))) {
- callback?.invoke(oldPath)
- return
- }
-
- filename = if (hide) {
- ".${filename.trimStart('.')}"
- } else {
- filename.substring(1, filename.length)
- }
-
- val newPath = "$path/$filename"
- renameFile(oldPath, newPath) {
- callback?.invoke(newPath)
- Thread {
- updateDBMediaPath(oldPath, newPath)
- }.start()
- }
-}
-
-fun BaseSimpleActivity.tryCopyMoveFilesTo(fileDirItems: ArrayList, isCopyOperation: Boolean, callback: (destinationPath: String) -> Unit) {
- if (fileDirItems.isEmpty()) {
- toast(R.string.unknown_error_occurred)
- return
- }
-
- val source = fileDirItems[0].getParentPath()
- PickDirectoryDialog(this, source) {
- copyMoveFilesTo(fileDirItems, source.trimEnd('/'), it, isCopyOperation, true, config.shouldShowHidden, callback)
- }
-}
-
-fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, deleteFromDatabase: Boolean,
- callback: ((wasSuccess: Boolean) -> Unit)? = null) {
- deleteFile(fileDirItem, allowDeleteFolder) {
- if (deleteFromDatabase) {
- Thread {
- galleryDB.MediumDao().deleteMediumPath(fileDirItem.path)
- runOnUiThread {
- callback?.invoke(it)
- }
- }.start()
- } else {
- callback?.invoke(it)
- }
- }
-}
-
-fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList, mediumDao: MediumDao = galleryDB.MediumDao(), callback: ((wasSuccess: Boolean) -> Unit)?) {
- Thread {
- var pathsCnt = paths.size
- paths.forEach {
- val file = File(it)
- val internalFile = File(filesDir.absolutePath, it)
- try {
- if (file.copyRecursively(internalFile, true)) {
- mediumDao.updateDeleted(it, System.currentTimeMillis())
- pathsCnt--
- }
- } catch (ignored: Exception) {
- }
- }
- callback?.invoke(pathsCnt == 0)
- }.start()
-}
-
-fun BaseSimpleActivity.restoreRecycleBinPath(path: String, callback: () -> Unit) {
- restoreRecycleBinPaths(arrayListOf(path), galleryDB.MediumDao(), callback)
-}
-
-fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList, mediumDao: MediumDao = galleryDB.MediumDao(), callback: () -> Unit) {
- Thread {
- paths.forEach {
- val source = it
- val destination = it.removePrefix(filesDir.absolutePath)
-
- var inputStream: InputStream? = null
- var out: OutputStream? = null
- try {
- out = getFileOutputStreamSync(destination, source.getMimeType())
- inputStream = getFileInputStreamSync(source)!!
- inputStream.copyTo(out!!)
- if (File(source).length() == File(destination).length()) {
- mediumDao.updateDeleted(destination, 0)
- }
- } catch (e: Exception) {
- showErrorToast(e)
- } finally {
- inputStream?.close()
- out?.close()
- }
- }
-
- runOnUiThread {
- callback()
- }
- }.start()
-}
-
-fun BaseSimpleActivity.emptyTheRecycleBin(callback: (() -> Unit)? = null) {
- Thread {
- filesDir.deleteRecursively()
- galleryDB.MediumDao().clearRecycleBin()
- galleryDB.DirectoryDao().deleteRecycleBin()
- toast(R.string.recycle_bin_emptied)
- callback?.invoke()
- }.start()
-}
-
-fun BaseSimpleActivity.emptyAndDisableTheRecycleBin(callback: () -> Unit) {
- Thread {
- emptyTheRecycleBin {
- config.useRecycleBin = false
- callback()
- }
- }.start()
-}
-
-fun BaseSimpleActivity.showRecycleBinEmptyingDialog(callback: () -> Unit) {
- ConfirmationDialog(this, "", R.string.empty_recycle_bin_confirmation, R.string.yes, R.string.no) {
- callback()
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/PhotoFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/PhotoFragment.kt
deleted file mode 100644
index ce012e47e..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/PhotoFragment.kt
+++ /dev/null
@@ -1,530 +0,0 @@
-package com.simplemobiletools.gallery.fragments
-
-import android.annotation.SuppressLint
-import android.content.Intent
-import android.content.res.Configuration
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
-import android.graphics.Color
-import android.graphics.Matrix
-import android.graphics.drawable.ColorDrawable
-import android.graphics.drawable.PictureDrawable
-import android.media.ExifInterface.*
-import android.net.Uri
-import android.os.Bundle
-import android.os.Handler
-import android.util.DisplayMetrics
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.DecodeFormat
-import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.RequestOptions
-import com.davemorrissey.labs.subscaleview.ImageSource
-import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
-import com.simplemobiletools.commons.extensions.*
-import com.simplemobiletools.commons.helpers.OTG_PATH
-import com.simplemobiletools.commons.helpers.isJellyBean1Plus
-import com.simplemobiletools.commons.helpers.isLollipopPlus
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.activities.PanoramaActivity
-import com.simplemobiletools.gallery.activities.PhotoActivity
-import com.simplemobiletools.gallery.activities.ViewPagerActivity
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.svg.SvgSoftwareLayerSetter
-import com.squareup.picasso.Callback
-import com.squareup.picasso.Picasso
-import it.sephiroth.android.library.exif2.ExifInterface
-import kotlinx.android.synthetic.main.pager_photo_item.view.*
-import org.apache.sanselan.common.byteSources.ByteSourceInputStream
-import org.apache.sanselan.formats.jpeg.JpegImageParser
-import pl.droidsonroids.gif.GifDrawable
-import java.io.File
-import java.io.FileOutputStream
-
-class PhotoFragment : ViewPagerFragment() {
- private val DEFAULT_DOUBLE_TAP_ZOOM = 2f
- private val ZOOMABLE_VIEW_LOAD_DELAY = 300L
-
- private var isFragmentVisible = false
- private var isFullscreen = false
- private var wasInit = false
- private var isPanorama = false
- private var imageOrientation = -1
- private var gifDrawable: GifDrawable? = null
- private var loadZoomableViewHandler = Handler()
-
- private var storedShowExtendedDetails = false
- private var storedHideExtendedDetails = false
- private var storedExtendedDetails = 0
-
- lateinit var view: ViewGroup
- lateinit var medium: Medium
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- view = (inflater.inflate(R.layout.pager_photo_item, container, false) as ViewGroup).apply {
- subsampling_view.setOnClickListener { photoClicked() }
- photo_view.setOnClickListener { photoClicked() }
- instant_prev_item.setOnClickListener { listener?.goToPrevItem() }
- instant_next_item.setOnClickListener { listener?.goToNextItem() }
- panorama_outline.setOnClickListener { openPanorama() }
-
- instant_prev_item.parentView = container
- instant_next_item.parentView = container
-
- photo_brightness_controller.initialize(activity!!, slide_info, true, container) { x, y ->
- view.apply {
- if (subsampling_view.isVisible()) {
- subsampling_view.sendFakeClick(x, y)
- } else {
- photo_view.sendFakeClick(x, y)
- }
- }
- }
- }
-
- if (ViewPagerActivity.screenWidth == 0 || ViewPagerActivity.screenHeight == 0) {
- measureScreen()
- }
-
- storeStateVariables()
- if (!isFragmentVisible && activity is PhotoActivity) {
- isFragmentVisible = true
- }
-
- medium = arguments!!.getSerializable(MEDIUM) as Medium
- if (medium.path.startsWith("content://") && !medium.path.startsWith("content://mms/")) {
- val originalPath = medium.path
- medium.path = context!!.getRealPathFromURI(Uri.parse(originalPath)) ?: medium.path
-
- if (medium.path.isEmpty()) {
- var out: FileOutputStream? = null
- try {
- var inputStream = context!!.contentResolver.openInputStream(Uri.parse(originalPath))
- val exif = ExifInterface()
- exif.readExif(inputStream, ExifInterface.Options.OPTION_ALL)
- val tag = exif.getTag(ExifInterface.TAG_ORIENTATION)
- val orientation = tag?.getValueAsInt(-1) ?: -1
- inputStream = context!!.contentResolver.openInputStream(Uri.parse(originalPath))
- val original = BitmapFactory.decodeStream(inputStream)
- val rotated = rotateViaMatrix(original, orientation)
- exif.setTagValue(ExifInterface.TAG_ORIENTATION, 1)
- exif.removeCompressedThumbnail()
-
- val file = File(context!!.externalCacheDir, Uri.parse(originalPath).lastPathSegment)
- out = FileOutputStream(file)
- rotated.compress(Bitmap.CompressFormat.JPEG, 100, out)
- medium.path = file.absolutePath
- } catch (e: Exception) {
- activity!!.toast(R.string.unknown_error_occurred)
- return view
- } finally {
- out?.close()
- }
- }
- }
-
- isFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN
- loadImage()
- initExtendedDetails()
- wasInit = true
- checkIfPanorama()
-
- return view
- }
-
- override fun onPause() {
- super.onPause()
- storeStateVariables()
- }
-
- override fun onResume() {
- super.onResume()
- if (wasInit && (context!!.config.showExtendedDetails != storedShowExtendedDetails || context!!.config.extendedDetails != storedExtendedDetails)) {
- initExtendedDetails()
- }
-
- val allowPhotoGestures = context!!.config.allowPhotoGestures
- val allowInstantChange = context!!.config.allowInstantChange
-
- view.apply {
- photo_brightness_controller.beVisibleIf(allowPhotoGestures)
- instant_prev_item.beVisibleIf(allowInstantChange)
- instant_next_item.beVisibleIf(allowInstantChange)
- photo_view.setAllowFingerDragZoom(activity!!.config.oneFingerZoom)
- }
-
- storeStateVariables()
- }
-
- override fun setMenuVisibility(menuVisible: Boolean) {
- super.setMenuVisibility(menuVisible)
- isFragmentVisible = menuVisible
- if (wasInit) {
- if (medium.isGIF()) {
- gifFragmentVisibilityChanged(menuVisible)
- } else {
- photoFragmentVisibilityChanged(menuVisible)
- }
- }
- }
-
- private fun storeStateVariables() {
- context!!.config.apply {
- storedShowExtendedDetails = showExtendedDetails
- storedHideExtendedDetails = hideExtendedDetails
- storedExtendedDetails = extendedDetails
- }
- }
-
- @SuppressLint("NewApi")
- private fun measureScreen() {
- val metrics = DisplayMetrics()
- if (isJellyBean1Plus()) {
- activity!!.windowManager.defaultDisplay.getRealMetrics(metrics)
- ViewPagerActivity.screenWidth = metrics.widthPixels
- ViewPagerActivity.screenHeight = metrics.heightPixels
- } else {
- activity!!.windowManager.defaultDisplay.getMetrics(metrics)
- ViewPagerActivity.screenWidth = metrics.widthPixels
- ViewPagerActivity.screenHeight = metrics.heightPixels
- }
- }
-
- private fun gifFragmentVisibilityChanged(isVisible: Boolean) {
- if (isVisible) {
- gifDrawable?.start()
- } else {
- gifDrawable?.stop()
- }
- }
-
- private fun photoFragmentVisibilityChanged(isVisible: Boolean) {
- if (isVisible) {
- scheduleZoomableView()
- } else {
- view.subsampling_view.recycle()
- view.subsampling_view.beGone()
- loadZoomableViewHandler.removeCallbacksAndMessages(null)
- }
- }
-
- private fun degreesForRotation(orientation: Int) = when (orientation) {
- ORIENTATION_ROTATE_270 -> 270
- ORIENTATION_ROTATE_180 -> 180
- ORIENTATION_ROTATE_90 -> 90
- else -> 0
- }
-
- private fun rotateViaMatrix(original: Bitmap, orientation: Int): Bitmap {
- val degrees = degreesForRotation(orientation).toFloat()
- return if (degrees == 0f) {
- original
- } else {
- val matrix = Matrix()
- matrix.setRotate(degrees)
- Bitmap.createBitmap(original, 0, 0, original.width, original.height, matrix, true)
- }
- }
-
- private fun loadImage() {
- imageOrientation = getImageOrientation()
- when {
- medium.isGIF() -> loadGif()
- medium.isSVG() -> loadSVG()
- else -> loadBitmap()
- }
- }
-
- private fun loadGif() {
- try {
- val pathToLoad = getPathToLoad(medium)
- gifDrawable = if (pathToLoad.startsWith("content://") || pathToLoad.startsWith("file://")) {
- GifDrawable(context!!.contentResolver, Uri.parse(pathToLoad))
- } else {
- GifDrawable(pathToLoad)
- }
-
- if (!isFragmentVisible) {
- gifDrawable!!.stop()
- }
-
- view.photo_view.setImageDrawable(gifDrawable)
- } catch (e: Exception) {
- gifDrawable = null
- loadBitmap()
- } catch (e: OutOfMemoryError) {
- gifDrawable = null
- loadBitmap()
- }
- }
-
- private fun loadSVG() {
- Glide.with(this)
- .`as`(PictureDrawable::class.java)
- .listener(SvgSoftwareLayerSetter())
- .load(medium.path)
- .into(view.photo_view)
- }
-
- private fun loadBitmap(degrees: Int = 0) {
- var pathToLoad = if (medium.path.startsWith("content://")) medium.path else "file://${medium.path}"
- pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
-
- try {
- val picasso = Picasso.get()
- .load(pathToLoad)
- .centerInside()
- .resize(ViewPagerActivity.screenWidth, ViewPagerActivity.screenHeight)
-
- if (degrees != 0) {
- picasso.rotate(degrees.toFloat())
- }
-
- picasso.into(view.photo_view, object : Callback {
- override fun onSuccess() {
- view.photo_view.isZoomable = degrees != 0 || context?.config?.allowZoomingImages == false
- if (isFragmentVisible && degrees == 0) {
- scheduleZoomableView()
- }
- }
-
- override fun onError(e: Exception) {
- if (context != null) {
- tryLoadingWithGlide()
- }
- }
- })
- } catch (ignored: Exception) {
- }
- }
-
- private fun tryLoadingWithGlide() {
- var targetWidth = if (ViewPagerActivity.screenWidth == 0) com.bumptech.glide.request.target.Target.SIZE_ORIGINAL else ViewPagerActivity.screenWidth
- var targetHeight = if (ViewPagerActivity.screenHeight == 0) com.bumptech.glide.request.target.Target.SIZE_ORIGINAL else ViewPagerActivity.screenHeight
-
- if (imageOrientation == ORIENTATION_ROTATE_90) {
- targetWidth = targetHeight
- targetHeight = com.bumptech.glide.request.target.Target.SIZE_ORIGINAL
- }
-
- val options = RequestOptions()
- .signature(medium.path.getFileSignature())
- .format(DecodeFormat.PREFER_ARGB_8888)
- .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
- .override(targetWidth, targetHeight)
-
- Glide.with(context!!)
- .asBitmap()
- .load(getPathToLoad(medium))
- .apply(options)
- .listener(object : RequestListener {
- override fun onLoadFailed(e: GlideException?, model: Any?, target: com.bumptech.glide.request.target.Target?, isFirstResource: Boolean) = false
-
- override fun onResourceReady(resource: Bitmap?, model: Any?, target: com.bumptech.glide.request.target.Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
- if (isFragmentVisible) {
- scheduleZoomableView()
- }
- return false
- }
- }).into(view.photo_view)
- }
-
- private fun openPanorama() {
- Intent(context, PanoramaActivity::class.java).apply {
- putExtra(PATH, medium.path)
- startActivity(this)
- }
- }
-
- private fun scheduleZoomableView() {
- loadZoomableViewHandler.removeCallbacksAndMessages(null)
- loadZoomableViewHandler.postDelayed({
- if (isFragmentVisible && context?.config?.allowZoomingImages == true && medium.isImage() && view.subsampling_view.isGone()) {
- addZoomableView()
- }
- }, ZOOMABLE_VIEW_LOAD_DELAY)
- }
-
- private fun addZoomableView() {
- val rotation = degreesForRotation(imageOrientation)
-
- view.subsampling_view.apply {
- setMinimumTileDpi(getMinTileDpi())
- background = ColorDrawable(Color.TRANSPARENT)
- setBitmapDecoderFactory { PicassoDecoder(medium.path, Picasso.get(), rotation) }
- setRegionDecoderFactory { PicassoRegionDecoder() }
- maxScale = 10f
- beVisible()
- isQuickScaleEnabled = context.config.oneFingerZoom
- setResetScaleOnSizeChange(context.config.screenRotation != ROTATE_BY_ASPECT_RATIO)
- setImage(ImageSource.uri(getPathToLoad(medium)))
- orientation = rotation
- setEagerLoadingEnabled(false)
- setOnImageEventListener(object : SubsamplingScaleImageView.OnImageEventListener {
- override fun onImageLoaded() {
- }
-
- override fun onReady() {
- background = ColorDrawable(if (context.config.blackBackground) Color.BLACK else context.config.backgroundColor)
- val useWidth = if (imageOrientation == ORIENTATION_ROTATE_90 || imageOrientation == ORIENTATION_ROTATE_270) sHeight else sWidth
- val useHeight = if (imageOrientation == ORIENTATION_ROTATE_90 || imageOrientation == ORIENTATION_ROTATE_270) sWidth else sHeight
- setDoubleTapZoomScale(getDoubleTapZoomScale(useWidth, useHeight))
- }
-
- override fun onTileLoadError(e: Exception?) {
- }
-
- override fun onPreviewReleased() {
- }
-
- override fun onImageLoadError(e: Exception) {
- view.photo_view.isZoomable = true
- background = ColorDrawable(Color.TRANSPARENT)
- beGone()
- }
-
- override fun onPreviewLoadError(e: Exception?) {
- background = ColorDrawable(Color.TRANSPARENT)
- beGone()
- }
- })
- }
- }
-
- private fun getMinTileDpi(): Int {
- val metrics = resources.displayMetrics
- val averageDpi = (metrics.xdpi + metrics.ydpi) / 2
- return when {
- averageDpi > 400 -> 320
- averageDpi > 300 -> 240
- else -> 160
- }
- }
-
- private fun checkIfPanorama() {
- isPanorama = try {
- val inputStream = if (medium.path.startsWith("content:/")) context!!.contentResolver.openInputStream(Uri.parse(medium.path)) else File(medium.path).inputStream()
- val imageParser = JpegImageParser().getXmpXml(ByteSourceInputStream(inputStream, medium.name), HashMap())
- imageParser.contains("GPano:UsePanoramaViewer=\"True\"", true) || imageParser.contains("True", true)
- } catch (e: Exception) {
- false
- } catch (e: OutOfMemoryError) {
- false
- }
-
- view.panorama_outline.beVisibleIf(isPanorama && isLollipopPlus())
- }
-
- private fun getImageOrientation(): Int {
- val defaultOrientation = -1
- var orient = defaultOrientation
-
- try {
- val pathToLoad = getPathToLoad(medium)
- val exif = android.media.ExifInterface(pathToLoad)
- orient = exif.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, defaultOrientation)
-
- if (orient == defaultOrientation || medium.path.startsWith(OTG_PATH)) {
- val uri = if (pathToLoad.startsWith("content:/")) Uri.parse(pathToLoad) else Uri.fromFile(File(pathToLoad))
- val inputStream = context!!.contentResolver.openInputStream(uri)
- val exif2 = ExifInterface()
- exif2.readExif(inputStream, ExifInterface.Options.OPTION_ALL)
- orient = exif2.getTag(ExifInterface.TAG_ORIENTATION)?.getValueAsInt(defaultOrientation) ?: defaultOrientation
- }
- } catch (ignored: Exception) {
- } catch (ignored: OutOfMemoryError) {
- }
- return orient
- }
-
- private fun getDoubleTapZoomScale(width: Int, height: Int): Float {
- val bitmapAspectRatio = height / width.toFloat()
- val screenAspectRatio = ViewPagerActivity.screenHeight / ViewPagerActivity.screenWidth.toFloat()
-
- return if (context == null || bitmapAspectRatio == screenAspectRatio) {
- DEFAULT_DOUBLE_TAP_ZOOM
- } else if (context!!.portrait && bitmapAspectRatio <= screenAspectRatio) {
- ViewPagerActivity.screenHeight / height.toFloat()
- } else if (context!!.portrait && bitmapAspectRatio > screenAspectRatio) {
- ViewPagerActivity.screenWidth / width.toFloat()
- } else if (!context!!.portrait && bitmapAspectRatio >= screenAspectRatio) {
- ViewPagerActivity.screenWidth / width.toFloat()
- } else if (!context!!.portrait && bitmapAspectRatio < screenAspectRatio) {
- ViewPagerActivity.screenHeight / height.toFloat()
- } else {
- DEFAULT_DOUBLE_TAP_ZOOM
- }
- }
-
- fun rotateImageViewBy(degrees: Int) {
- loadZoomableViewHandler.removeCallbacksAndMessages(null)
- view.subsampling_view.beGone()
- loadBitmap(degrees)
- }
-
- private fun initExtendedDetails() {
- if (context!!.config.showExtendedDetails) {
- view.photo_details.apply {
- beInvisible() // make it invisible so we can measure it, but not show yet
- text = getMediumExtendedDetails(medium)
- onGlobalLayout {
- if (isAdded) {
- val realY = getExtendedDetailsY(height)
- if (realY > 0) {
- y = realY
- beVisibleIf(text.isNotEmpty())
- alpha = if (!context!!.config.hideExtendedDetails || !isFullscreen) 1f else 0f
- }
- }
- }
- }
- } else {
- view.photo_details.beGone()
- }
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- if (activity?.isActivityDestroyed() == false) {
- view.subsampling_view.recycle()
- }
- loadZoomableViewHandler.removeCallbacksAndMessages(null)
- }
-
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
- loadImage()
- initExtendedDetails()
- }
-
- private fun photoClicked() {
- listener?.fragmentClicked()
- }
-
- override fun fullscreenToggled(isFullscreen: Boolean) {
- this.isFullscreen = isFullscreen
- view.photo_details.apply {
- if (storedShowExtendedDetails && isVisible()) {
- animate().y(getExtendedDetailsY(height))
-
- if (storedHideExtendedDetails) {
- animate().alpha(if (isFullscreen) 0f else 1f).start()
- }
- }
- }
- }
-
- private fun getExtendedDetailsY(height: Int): Float {
- val smallMargin = resources.getDimension(R.dimen.small_margin)
- val fullscreenOffset = context!!.navigationBarHeight.toFloat() - smallMargin
- val actionsHeight = if (context!!.config.bottomActions && !isFullscreen) resources.getDimension(R.dimen.bottom_actions_height) else 0f
- return context!!.usableScreenSize.y - height - actionsHeight + if (isFullscreen) fullscreenOffset else -smallMargin
- }
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/ViewPagerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/ViewPagerFragment.kt
deleted file mode 100644
index d1c9a4253..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/ViewPagerFragment.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.simplemobiletools.gallery.fragments
-
-import android.support.v4.app.Fragment
-import com.simplemobiletools.commons.extensions.*
-import com.simplemobiletools.commons.helpers.OTG_PATH
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.models.Medium
-import java.io.File
-
-abstract class ViewPagerFragment : Fragment() {
- var listener: FragmentListener? = null
-
- abstract fun fullscreenToggled(isFullscreen: Boolean)
-
- interface FragmentListener {
- fun fragmentClicked()
-
- fun videoEnded(): Boolean
-
- fun goToPrevItem()
-
- fun goToNextItem()
- }
-
- fun getMediumExtendedDetails(medium: Medium): String {
- val file = File(medium.path)
- if (!file.exists()) {
- return ""
- }
-
- val path = "${file.parent.trimEnd('/')}/"
- val exif = android.media.ExifInterface(medium.path)
- val details = StringBuilder()
- val detailsFlag = context!!.config.extendedDetails
- if (detailsFlag and EXT_NAME != 0) {
- medium.name.let { if (it.isNotEmpty()) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_PATH != 0) {
- path.let { if (it.isNotEmpty()) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_SIZE != 0) {
- file.length().formatSize().let { if (it.isNotEmpty()) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_RESOLUTION != 0) {
- file.absolutePath.getResolution()?.formatAsResolution().let { if (it?.isNotEmpty() == true) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_LAST_MODIFIED != 0) {
- file.lastModified().formatDate().let { if (it.isNotEmpty()) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_DATE_TAKEN != 0) {
- path.getExifDateTaken(exif).let { if (it.isNotEmpty()) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_CAMERA_MODEL != 0) {
- path.getExifCameraModel(exif).let { if (it.isNotEmpty()) details.appendln(it) }
- }
-
- if (detailsFlag and EXT_EXIF_PROPERTIES != 0) {
- path.getExifProperties(exif).let { if (it.isNotEmpty()) details.appendln(it) }
- }
- return details.toString().trim()
- }
-
- fun getPathToLoad(medium: Medium) = if (medium.path.startsWith(OTG_PATH)) medium.path.getOTGPublicPath(context!!) else medium.path
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/FilterAdapterListener.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/FilterAdapterListener.kt
deleted file mode 100644
index 379e9e964..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/FilterAdapterListener.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.simplemobiletools.gallery.interfaces
-
-import com.simplemobiletools.gallery.models.FilterItem
-
-interface FilterAdapterListener {
- fun getCurrentFilter(): FilterItem
-
- fun setCurrentFilter(position: Int)
-}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/models/ThumbnailItem.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/models/ThumbnailItem.kt
deleted file mode 100644
index f53c5df10..000000000
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/models/ThumbnailItem.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.simplemobiletools.gallery.models
-
-open class ThumbnailItem
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/App.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt
similarity index 74%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/App.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt
index e1562b1b4..670ba17a4 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/App.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt
@@ -1,6 +1,6 @@
-package com.simplemobiletools.gallery
+package com.simplemobiletools.gallery.pro
-import android.support.multidex.MultiDexApplication
+import androidx.multidex.MultiDexApplication
import com.github.ajalt.reprint.core.Reprint
import com.simplemobiletools.commons.extensions.checkUseEnglish
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/EditActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/EditActivity.kt
similarity index 64%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/EditActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/EditActivity.kt
index 129655165..2916e5ce7 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/EditActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/EditActivity.kt
@@ -1,38 +1,46 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
+import android.annotation.TargetApi
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.Color
import android.graphics.Point
+import android.media.ExifInterface
import android.net.Uri
+import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
-import android.support.v7.widget.LinearLayoutManager
import android.view.Menu
import android.view.MenuItem
import android.widget.RelativeLayout
+import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
+import com.simplemobiletools.commons.dialogs.ColorPickerDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.OTG_PATH
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.REAL_FILE_PATH
+import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.commons.models.FileDirItem
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.FiltersAdapter
-import com.simplemobiletools.gallery.dialogs.ResizeDialog
-import com.simplemobiletools.gallery.dialogs.SaveAsDialog
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.extensions.openEditor
-import com.simplemobiletools.gallery.helpers.FilterThumbnailsManager
-import com.simplemobiletools.gallery.models.FilterItem
+import com.simplemobiletools.gallery.pro.BuildConfig
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.FiltersAdapter
+import com.simplemobiletools.gallery.pro.dialogs.OtherAspectRatioDialog
+import com.simplemobiletools.gallery.pro.dialogs.ResizeDialog
+import com.simplemobiletools.gallery.pro.dialogs.SaveAsDialog
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.extensions.openEditor
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.FilterItem
import com.theartofdev.edmodo.cropper.CropImageView
import com.zomato.photofilters.FilterPack
import com.zomato.photofilters.imageprocessors.Filter
@@ -40,6 +48,7 @@ import kotlinx.android.synthetic.main.activity_edit.*
import kotlinx.android.synthetic.main.bottom_actions_aspect_ratio.*
import kotlinx.android.synthetic.main.bottom_editor_actions_filter.*
import kotlinx.android.synthetic.main.bottom_editor_crop_rotate_actions.*
+import kotlinx.android.synthetic.main.bottom_editor_draw_actions.*
import kotlinx.android.synthetic.main.bottom_editor_primary_actions.*
import java.io.*
@@ -50,19 +59,16 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
}
+ private val TEMP_FOLDER_NAME = "images"
private val ASPECT_X = "aspectX"
private val ASPECT_Y = "aspectY"
private val CROP = "crop"
- private val ASPECT_RATIO_FREE = 0
- private val ASPECT_RATIO_ONE_ONE = 1
- private val ASPECT_RATIO_FOUR_THREE = 2
- private val ASPECT_RATIO_SIXTEEN_NINE = 3
-
// constants for bottom primary action groups
private val PRIMARY_ACTION_NONE = 0
private val PRIMARY_ACTION_FILTER = 1
private val PRIMARY_ACTION_CROP_ROTATE = 2
+ private val PRIMARY_ACTION_DRAW = 3
private val CROP_ROTATE_NONE = 0
private val CROP_ROTATE_ASPECT_RATIO = 1
@@ -71,13 +77,17 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
private lateinit var saveUri: Uri
private var resizeWidth = 0
private var resizeHeight = 0
+ private var drawColor = 0
+ private var lastOtherAspectRatio: Pair? = null
private var currPrimaryAction = PRIMARY_ACTION_NONE
private var currCropRotateAction = CROP_ROTATE_NONE
private var currAspectRatio = ASPECT_RATIO_FREE
private var isCropIntent = false
private var isEditingWithThirdParty = false
-
- private var initialBitmap: Bitmap? = null
+ private var isSharingBitmap = false
+ private var wasDrawCanvasPositioned = false
+ private var oldExif: ExifInterface? = null
+ private var filterInitialBitmap: Bitmap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -93,6 +103,34 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
}
+ override fun onResume() {
+ super.onResume()
+ isEditingWithThirdParty = false
+ bottom_draw_width.setColors(config.textColor, getAdjustedPrimaryColor(), config.backgroundColor)
+ }
+
+ override fun onStop() {
+ super.onStop()
+ if (isEditingWithThirdParty) {
+ finish()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_editor, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.save_as -> saveImage()
+ R.id.edit -> editWith()
+ R.id.share -> shareImage()
+ else -> return super.onOptionsItemSelected(item)
+ }
+ return true
+ }
+
private fun initEditActivity() {
if (intent.data == null) {
toast(R.string.invalid_image_path)
@@ -133,37 +171,25 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
loadDefaultImageView()
setupBottomActions()
- }
- override fun onResume() {
- super.onResume()
- isEditingWithThirdParty = false
- }
+ if (config.lastEditorCropAspectRatio == ASPECT_RATIO_OTHER) {
+ if (config.lastEditorCropOtherAspectRatioX == 0) {
+ config.lastEditorCropOtherAspectRatioX = 1
+ }
- override fun onStop() {
- super.onStop()
- if (isEditingWithThirdParty) {
- finish()
+ if (config.lastEditorCropOtherAspectRatioY == 0) {
+ config.lastEditorCropOtherAspectRatioY = 1
+ }
+
+ lastOtherAspectRatio = Pair(config.lastEditorCropOtherAspectRatioX, config.lastEditorCropOtherAspectRatioY)
}
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu_editor, menu)
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- when (item.itemId) {
- R.id.save_as -> saveImage()
- R.id.edit -> editWith()
- else -> return super.onOptionsItemSelected(item)
- }
- return true
+ updateAspectRatio(config.lastEditorCropAspectRatio)
}
private fun loadDefaultImageView() {
default_image_view.beVisible()
crop_image_view.beGone()
+ editor_draw_canvas.beGone()
val options = RequestOptions()
.skipMemoryCache(true)
@@ -178,21 +204,22 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
override fun onResourceReady(bitmap: Bitmap?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
val currentFilter = getFiltersAdapter()?.getCurrentFilter()
- if (initialBitmap == null) {
+ if (filterInitialBitmap == null) {
loadCropImageView()
bottomCropRotateClicked()
}
- if (initialBitmap != null && currentFilter != null && currentFilter.filter.name != getString(R.string.none)) {
+ if (filterInitialBitmap != null && currentFilter != null && currentFilter.filter.name != getString(R.string.none)) {
default_image_view.onGlobalLayout {
applyFilter(currentFilter)
}
} else {
- initialBitmap = bitmap
+ filterInitialBitmap = bitmap
}
if (isCropIntent) {
bottom_primary_filter.beGone()
+ bottom_primary_draw.beGone()
}
return false
@@ -202,6 +229,7 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
private fun loadCropImageView() {
default_image_view.beGone()
+ editor_draw_canvas.beGone()
crop_image_view.apply {
beVisible()
setOnCropImageCompleteListener(this@EditActivity)
@@ -216,9 +244,79 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
}
+ private fun loadDrawCanvas() {
+ default_image_view.beGone()
+ crop_image_view.beGone()
+ editor_draw_canvas.beVisible()
+
+ if (!wasDrawCanvasPositioned) {
+ wasDrawCanvasPositioned = true
+ editor_draw_canvas.onGlobalLayout {
+ Thread {
+ fillCanvasBackground()
+ }.start()
+ }
+ }
+ }
+
+ private fun fillCanvasBackground() {
+ val size = Point()
+ windowManager.defaultDisplay.getSize(size)
+ val options = RequestOptions()
+ .format(DecodeFormat.PREFER_ARGB_8888)
+ .skipMemoryCache(true)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+
+ try {
+ val builder = Glide.with(applicationContext)
+ .asBitmap()
+ .load(uri)
+ .apply(options)
+ .into(editor_draw_canvas.width, editor_draw_canvas.height)
+
+ val bitmap = builder.get()
+ runOnUiThread {
+ editor_draw_canvas.apply {
+ updateBackgroundBitmap(bitmap)
+ layoutParams.width = bitmap.width
+ layoutParams.height = bitmap.height
+ y = (height - bitmap.height) / 2f
+ requestLayout()
+ }
+ }
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.N)
private fun saveImage() {
+ var inputStream: InputStream? = null
+ try {
+ if (isNougatPlus()) {
+ inputStream = contentResolver.openInputStream(uri)
+ oldExif = ExifInterface(inputStream)
+ }
+ } catch (e: Exception) {
+ } finally {
+ inputStream?.close()
+ }
+
if (crop_image_view.isVisible()) {
crop_image_view.getCroppedImageAsync()
+ } else if (editor_draw_canvas.isVisible()) {
+ val bitmap = editor_draw_canvas.getBitmap()
+ if (saveUri.scheme == "file") {
+ SaveAsDialog(this, saveUri.path, true) {
+ saveBitmapToFile(bitmap, it, true)
+ }
+ } else if (saveUri.scheme == "content") {
+ val filePathGetter = getNewFilePath()
+ SaveAsDialog(this, filePathGetter.first, filePathGetter.second) {
+ saveBitmapToFile(bitmap, it, true)
+ }
+ }
} else {
val currentFilter = getFiltersAdapter()?.getCurrentFilter() ?: return
val filePathGetter = getNewFilePath()
@@ -244,12 +342,78 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
}
+ private fun shareImage() {
+ Thread {
+ when {
+ default_image_view.isVisible() -> {
+ val currentFilter = getFiltersAdapter()?.getCurrentFilter()
+ if (currentFilter == null) {
+ toast(R.string.unknown_error_occurred)
+ return@Thread
+ }
+
+ val originalBitmap = Glide.with(applicationContext).asBitmap().load(uri).submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).get()
+ currentFilter!!.filter.processFilter(originalBitmap)
+ shareBitmap(originalBitmap)
+ }
+ crop_image_view.isVisible() -> {
+ isSharingBitmap = true
+ runOnUiThread {
+ crop_image_view.getCroppedImageAsync()
+ }
+ }
+ editor_draw_canvas.isVisible() -> shareBitmap(editor_draw_canvas.getBitmap())
+ }
+ }.start()
+ }
+
+ private fun getTempImagePath(bitmap: Bitmap, callback: (path: String?) -> Unit) {
+ val bytes = ByteArrayOutputStream()
+ bitmap.compress(Bitmap.CompressFormat.PNG, 0, bytes)
+
+ val folder = File(cacheDir, TEMP_FOLDER_NAME)
+ if (!folder.exists()) {
+ if (!folder.mkdir()) {
+ callback(null)
+ return
+ }
+ }
+
+ val filename = applicationContext.getFilenameFromContentUri(saveUri) ?: "tmp.jpg"
+ val newPath = "$folder/$filename"
+ val fileDirItem = FileDirItem(newPath, filename)
+ getFileOutputStream(fileDirItem, true) {
+ if (it != null) {
+ try {
+ it.write(bytes.toByteArray())
+ callback(newPath)
+ } catch (e: Exception) {
+ } finally {
+ it.close()
+ }
+ } else {
+ callback("")
+ }
+ }
+ }
+
+ private fun shareBitmap(bitmap: Bitmap) {
+ getTempImagePath(bitmap) {
+ if (it != null) {
+ sharePathIntent(it, BuildConfig.APPLICATION_ID)
+ } else {
+ toast(R.string.unknown_error_occurred)
+ }
+ }
+ }
+
private fun getFiltersAdapter() = bottom_actions_filter_list.adapter as? FiltersAdapter
private fun setupBottomActions() {
setupPrimaryActionButtons()
setupCropRotateActionButtons()
setupAspectRatioButtons()
+ setupDrawButtons()
}
private fun setupPrimaryActionButtons() {
@@ -260,6 +424,10 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
bottom_primary_crop_rotate.setOnClickListener {
bottomCropRotateClicked()
}
+
+ bottom_primary_draw.setOnClickListener {
+ bottomDrawClicked()
+ }
}
private fun bottomFilterClicked() {
@@ -280,6 +448,15 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
updatePrimaryActionButtons()
}
+ private fun bottomDrawClicked() {
+ currPrimaryAction = if (currPrimaryAction == PRIMARY_ACTION_DRAW) {
+ PRIMARY_ACTION_NONE
+ } else {
+ PRIMARY_ACTION_DRAW
+ }
+ updatePrimaryActionButtons()
+ }
+
private fun setupCropRotateActionButtons() {
bottom_rotate.setOnClickListener {
crop_image_view.rotateImage(90)
@@ -328,34 +505,83 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
bottom_aspect_ratio_sixteen_nine.setOnClickListener {
updateAspectRatio(ASPECT_RATIO_SIXTEEN_NINE)
}
+
+ bottom_aspect_ratio_other.setOnClickListener {
+ OtherAspectRatioDialog(this, lastOtherAspectRatio) {
+ lastOtherAspectRatio = it
+ config.lastEditorCropOtherAspectRatioX = it.first
+ config.lastEditorCropOtherAspectRatioY = it.second
+ updateAspectRatio(ASPECT_RATIO_OTHER)
+ }
+ }
+
updateAspectRatioButtons()
}
+ private fun setupDrawButtons() {
+ updateDrawColor(config.lastEditorDrawColor)
+ bottom_draw_width.progress = config.lastEditorBrushSize
+ updateBrushSize(config.lastEditorBrushSize)
+
+ bottom_draw_color_clickable.setOnClickListener {
+ ColorPickerDialog(this, drawColor) { wasPositivePressed, color ->
+ if (wasPositivePressed) {
+ updateDrawColor(color)
+ }
+ }
+ }
+
+ bottom_draw_width.onSeekBarChangeListener {
+ config.lastEditorBrushSize = it
+ updateBrushSize(it)
+ }
+
+ bottom_draw_undo.setOnClickListener {
+ editor_draw_canvas.undo()
+ }
+ }
+
+ private fun updateBrushSize(percent: Int) {
+ editor_draw_canvas.updateBrushSize(percent)
+ val scale = Math.max(0.03f, percent / 100f)
+ bottom_draw_color.scaleX = scale
+ bottom_draw_color.scaleY = scale
+ }
+
private fun updatePrimaryActionButtons() {
if (crop_image_view.isGone() && currPrimaryAction == PRIMARY_ACTION_CROP_ROTATE) {
loadCropImageView()
} else if (default_image_view.isGone() && currPrimaryAction == PRIMARY_ACTION_FILTER) {
loadDefaultImageView()
+ } else if (editor_draw_canvas.isGone() && currPrimaryAction == PRIMARY_ACTION_DRAW) {
+ loadDrawCanvas()
}
- arrayOf(bottom_primary_filter, bottom_primary_crop_rotate).forEach {
+ arrayOf(bottom_primary_filter, bottom_primary_crop_rotate, bottom_primary_draw).forEach {
it.applyColorFilter(Color.WHITE)
}
val currentPrimaryActionButton = when (currPrimaryAction) {
PRIMARY_ACTION_FILTER -> bottom_primary_filter
PRIMARY_ACTION_CROP_ROTATE -> bottom_primary_crop_rotate
+ PRIMARY_ACTION_DRAW -> bottom_primary_draw
else -> null
}
- currentPrimaryActionButton?.applyColorFilter(config.primaryColor)
+ currentPrimaryActionButton?.applyColorFilter(getAdjustedPrimaryColor())
bottom_editor_filter_actions.beVisibleIf(currPrimaryAction == PRIMARY_ACTION_FILTER)
bottom_editor_crop_rotate_actions.beVisibleIf(currPrimaryAction == PRIMARY_ACTION_CROP_ROTATE)
+ bottom_editor_draw_actions.beVisibleIf(currPrimaryAction == PRIMARY_ACTION_DRAW)
if (currPrimaryAction == PRIMARY_ACTION_FILTER && bottom_actions_filter_list.adapter == null) {
Thread {
val thumbnailSize = resources.getDimension(R.dimen.bottom_filters_thumbnail_size).toInt()
- val bitmap = Glide.with(this).asBitmap().load(uri).submit(thumbnailSize, thumbnailSize).get()
+ val bitmap = Glide.with(this)
+ .asBitmap()
+ .load(uri)
+ .submit(thumbnailSize, thumbnailSize)
+ .get()
+
runOnUiThread {
val filterThumbnailsManager = FilterThumbnailsManager()
filterThumbnailsManager.clearThumbs()
@@ -394,12 +620,13 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
private fun applyFilter(filterItem: FilterItem) {
- val newBitmap = Bitmap.createBitmap(initialBitmap)
+ val newBitmap = Bitmap.createBitmap(filterInitialBitmap)
default_image_view.setImageBitmap(filterItem.filter.processFilter(newBitmap))
}
private fun updateAspectRatio(aspectRatio: Int) {
currAspectRatio = aspectRatio
+ config.lastEditorCropAspectRatio = aspectRatio
updateAspectRatioButtons()
crop_image_view.apply {
@@ -409,7 +636,8 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
val newAspectRatio = when (aspectRatio) {
ASPECT_RATIO_ONE_ONE -> Pair(1, 1)
ASPECT_RATIO_FOUR_THREE -> Pair(4, 3)
- else -> Pair(16, 9)
+ ASPECT_RATIO_SIXTEEN_NINE -> Pair(16, 9)
+ else -> Pair(lastOtherAspectRatio!!.first, lastOtherAspectRatio!!.second)
}
setAspectRatio(newAspectRatio.first, newAspectRatio.second)
@@ -418,7 +646,7 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
private fun updateAspectRatioButtons() {
- arrayOf(bottom_aspect_ratio_free, bottom_aspect_ratio_one_one, bottom_aspect_ratio_four_three, bottom_aspect_ratio_sixteen_nine).forEach {
+ arrayOf(bottom_aspect_ratio_free, bottom_aspect_ratio_one_one, bottom_aspect_ratio_four_three, bottom_aspect_ratio_sixteen_nine, bottom_aspect_ratio_other).forEach {
it.setTextColor(Color.WHITE)
}
@@ -426,10 +654,11 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
ASPECT_RATIO_FREE -> bottom_aspect_ratio_free
ASPECT_RATIO_ONE_ONE -> bottom_aspect_ratio_one_one
ASPECT_RATIO_FOUR_THREE -> bottom_aspect_ratio_four_three
- else -> bottom_aspect_ratio_sixteen_nine
+ ASPECT_RATIO_SIXTEEN_NINE -> bottom_aspect_ratio_sixteen_nine
+ else -> bottom_aspect_ratio_other
}
- currentAspectRatioButton.setTextColor(config.primaryColor)
+ currentAspectRatioButton.setTextColor(getAdjustedPrimaryColor())
}
private fun updateCropRotateActionButtons() {
@@ -442,7 +671,14 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
else -> null
}
- primaryActionView?.applyColorFilter(config.primaryColor)
+ primaryActionView?.applyColorFilter(getAdjustedPrimaryColor())
+ }
+
+ private fun updateDrawColor(color: Int) {
+ drawColor = color
+ bottom_draw_color.applyColorFilter(color)
+ config.lastEditorDrawColor = color
+ editor_draw_canvas.updateColor(color)
}
private fun resizeImage() {
@@ -480,15 +716,22 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
override fun onCropImageComplete(view: CropImageView, result: CropImageView.CropResult) {
if (result.error == null) {
+ val bitmap = result.bitmap
+ if (isSharingBitmap) {
+ isSharingBitmap = false
+ shareBitmap(bitmap)
+ return
+ }
+
if (isCropIntent) {
if (saveUri.scheme == "file") {
- saveBitmapToFile(result.bitmap, saveUri.path, true)
+ saveBitmapToFile(bitmap, saveUri.path, true)
} else {
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val stream = ByteArrayOutputStream()
- result.bitmap.compress(CompressFormat.JPEG, 100, stream)
+ bitmap.compress(CompressFormat.JPEG, 100, stream)
inputStream = ByteArrayInputStream(stream.toByteArray())
outputStream = contentResolver.openOutputStream(saveUri)
inputStream.copyTo(outputStream)
@@ -506,12 +749,12 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
} else if (saveUri.scheme == "file") {
SaveAsDialog(this, saveUri.path, true) {
- saveBitmapToFile(result.bitmap, it, true)
+ saveBitmapToFile(bitmap, it, true)
}
} else if (saveUri.scheme == "content") {
val filePathGetter = getNewFilePath()
SaveAsDialog(this, filePathGetter.first, filePathGetter.second) {
- saveBitmapToFile(result.bitmap, it, true)
+ saveBitmapToFile(bitmap, it, true)
}
} else {
toast(R.string.unknown_file_location)
@@ -561,6 +804,7 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
}
}
+ @TargetApi(Build.VERSION_CODES.N)
private fun saveBitmap(file: File, bitmap: Bitmap, out: OutputStream, showSavingToast: Boolean) {
if (showSavingToast) {
toast(R.string.saving)
@@ -572,13 +816,22 @@ class EditActivity : SimpleActivity(), CropImageView.OnCropImageCompleteListener
} else {
bitmap.compress(file.absolutePath.getCompressionFormat(), 90, out)
}
+
+ try {
+ if (isNougatPlus()) {
+ val newExif = ExifInterface(file.absolutePath)
+ oldExif?.copyTo(newExif, false)
+ }
+ } catch (e: Exception) {
+ }
+
setResult(Activity.RESULT_OK, intent)
scanFinalPath(file.absolutePath)
out.close()
}
private fun editWith() {
- openEditor(uri.toString())
+ openEditor(uri.toString(), true)
isEditingWithThirdParty = true
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ExcludedFoldersActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/ExcludedFoldersActivity.kt
similarity index 84%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/ExcludedFoldersActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/ExcludedFoldersActivity.kt
index d59f0b7f4..0d3f96fdc 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ExcludedFoldersActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/ExcludedFoldersActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.os.Bundle
import android.view.Menu
@@ -6,9 +6,9 @@ import android.view.MenuItem
import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.ManageFoldersAdapter
-import com.simplemobiletools.gallery.extensions.config
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.ManageFoldersAdapter
+import com.simplemobiletools.gallery.pro.extensions.config
import kotlinx.android.synthetic.main.activity_manage_folders.*
class ExcludedFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
@@ -20,7 +20,7 @@ class ExcludedFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
private fun updateFolders() {
val folders = ArrayList()
- config.excludedFolders.mapTo(folders, { it })
+ config.excludedFolders.mapTo(folders) { it }
manage_folders_placeholder.apply {
text = getString(R.string.excluded_activity_placeholder)
beVisibleIf(folders.isEmpty())
@@ -49,7 +49,7 @@ class ExcludedFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
}
private fun addFolder() {
- FilePickerDialog(this, config.lastFilepickerPath, false, config.shouldShowHidden) {
+ FilePickerDialog(this, config.lastFilepickerPath, false, config.shouldShowHidden, false, true) {
config.lastFilepickerPath = it
config.addExcludedFolder(it)
updateFolders()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/HiddenFoldersActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/HiddenFoldersActivity.kt
similarity index 82%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/HiddenFoldersActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/HiddenFoldersActivity.kt
index 598e153be..98d5eb9d9 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/HiddenFoldersActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/HiddenFoldersActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.os.Bundle
import android.view.Menu
@@ -6,11 +6,11 @@ import android.view.MenuItem
import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.ManageHiddenFoldersAdapter
-import com.simplemobiletools.gallery.extensions.addNoMedia
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.extensions.getNoMediaFolders
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.ManageHiddenFoldersAdapter
+import com.simplemobiletools.gallery.pro.extensions.addNoMedia
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.extensions.getNoMediaFolders
import kotlinx.android.synthetic.main.activity_manage_folders.*
class HiddenFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
@@ -53,7 +53,7 @@ class HiddenFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
}
private fun addFolder() {
- FilePickerDialog(this, config.lastFilepickerPath, false, config.shouldShowHidden) {
+ FilePickerDialog(this, config.lastFilepickerPath, false, config.shouldShowHidden, false, true) {
config.lastFilepickerPath = it
Thread {
addNoMedia(it) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/IncludedFoldersActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/IncludedFoldersActivity.kt
similarity index 85%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/IncludedFoldersActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/IncludedFoldersActivity.kt
index ede3ab8d8..b6be4add2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/IncludedFoldersActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/IncludedFoldersActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.os.Bundle
import android.view.Menu
@@ -7,9 +7,9 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.scanPathRecursively
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.ManageFoldersAdapter
-import com.simplemobiletools.gallery.extensions.config
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.ManageFoldersAdapter
+import com.simplemobiletools.gallery.pro.extensions.config
import kotlinx.android.synthetic.main.activity_manage_folders.*
class IncludedFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
@@ -21,7 +21,7 @@ class IncludedFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
private fun updateFolders() {
val folders = ArrayList()
- config.includedFolders.mapTo(folders, { it })
+ config.includedFolders.mapTo(folders) { it }
manage_folders_placeholder.apply {
text = getString(R.string.included_activity_placeholder)
beVisibleIf(folders.isEmpty())
@@ -50,7 +50,7 @@ class IncludedFoldersActivity : SimpleActivity(), RefreshRecyclerViewListener {
}
private fun addFolder() {
- FilePickerDialog(this, config.lastFilepickerPath, false, config.shouldShowHidden) {
+ FilePickerDialog(this, config.lastFilepickerPath, false, config.shouldShowHidden, false, true) {
config.lastFilepickerPath = it
config.addIncludedFolder(it)
updateFolders()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/MainActivity.kt
similarity index 75%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/MainActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/MainActivity.kt
index 6db737334..38de24828 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/MainActivity.kt
@@ -1,42 +1,46 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.app.Activity
+import android.app.SearchManager
import android.content.ClipData
+import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.provider.MediaStore
-import android.support.v7.widget.GridLayoutManager
import android.view.Menu
import android.view.MenuItem
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
+import androidx.appcompat.widget.SearchView
+import androidx.core.view.MenuItemCompat
+import androidx.recyclerview.widget.RecyclerView
+import com.simplemobiletools.commons.dialogs.ConfirmationDialog
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.*
import com.simplemobiletools.commons.models.FileDirItem
-import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.models.Release
import com.simplemobiletools.commons.views.MyGridLayoutManager
import com.simplemobiletools.commons.views.MyRecyclerView
-import com.simplemobiletools.gallery.BuildConfig
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.DirectoryAdapter
-import com.simplemobiletools.gallery.databases.GalleryDatabase
-import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog
-import com.simplemobiletools.gallery.dialogs.FilterMediaDialog
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.interfaces.DirectoryDao
-import com.simplemobiletools.gallery.interfaces.DirectoryOperationsListener
-import com.simplemobiletools.gallery.interfaces.MediumDao
-import com.simplemobiletools.gallery.models.AlbumCover
-import com.simplemobiletools.gallery.models.Directory
-import com.simplemobiletools.gallery.models.Medium
+import com.simplemobiletools.gallery.pro.BuildConfig
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.DirectoryAdapter
+import com.simplemobiletools.gallery.pro.databases.GalleryDatabase
+import com.simplemobiletools.gallery.pro.dialogs.ChangeSortingDialog
+import com.simplemobiletools.gallery.pro.dialogs.ChangeViewTypeDialog
+import com.simplemobiletools.gallery.pro.dialogs.FilterMediaDialog
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
+import com.simplemobiletools.gallery.pro.interfaces.DirectoryOperationsListener
+import com.simplemobiletools.gallery.pro.interfaces.MediumDao
+import com.simplemobiletools.gallery.pro.models.AlbumCover
+import com.simplemobiletools.gallery.pro.models.Directory
+import com.simplemobiletools.gallery.pro.models.Medium
import kotlinx.android.synthetic.main.activity_main.*
import java.io.*
import java.util.*
@@ -60,11 +64,16 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private var mIsPasswordProtectionPending = false
private var mWasProtectionHandled = false
private var mShouldStopFetching = false
+ private var mIsSearchOpen = false
private var mLatestMediaId = 0L
private var mLatestMediaDateId = 0L
+ private var mCurrentPathPrefix = "" // used at "Group direct subfolders" for navigation
+ private var mOpenedSubfolders = arrayListOf("") // used at "Group direct subfolders" for navigating Up with the back button
private var mLastMediaHandler = Handler()
private var mTempShowHiddenHandler = Handler()
private var mZoomListener: MyRecyclerView.MyZoomListener? = null
+ private var mSearchMenuItem: MenuItem? = null
+ private var mDirs = ArrayList()
private var mStoredAnimateGifs = true
private var mStoredCropThumbnails = true
@@ -110,7 +119,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
showFilterMediaDialog()
}
- mIsPasswordProtectionPending = config.appPasswordProtectionOn
+ mIsPasswordProtectionPending = config.isAppPasswordProtectionOn
setupLatestMediaId()
// notify some users about the Clock app
@@ -119,10 +128,6 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
NewAppDialog(this, NEW_APP_PACKAGE, "Simple Clock")
}*/
- if (!config.wasOTGHandled && hasPermission(PERMISSION_WRITE_STORAGE)) {
- checkOTGInclusion()
- }
-
if (!config.wereFavoritesPinned) {
config.addPinnedFolders(hashSetOf(FAVORITES))
config.wereFavoritesPinned = true
@@ -140,6 +145,8 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
config.filterMedia += TYPE_SVGS
}
}
+
+ updateWidgets()
}
override fun onStart() {
@@ -213,6 +220,8 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
override fun onStop() {
super.onStop()
+ mSearchMenuItem?.collapseActionView()
+
if (config.temporarilyShowHidden || config.tempSkipDeleteConfirmation) {
mTempShowHiddenHandler.postDelayed({
config.temporarilyShowHidden = false
@@ -230,7 +239,24 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
config.tempSkipDeleteConfirmation = false
mTempShowHiddenHandler.removeCallbacksAndMessages(null)
removeTempFolder()
- GalleryDatabase.destroyInstance()
+
+ if (!config.showAll) {
+ GalleryDatabase.destroyInstance()
+ }
+ }
+ }
+
+ override fun onBackPressed() {
+ if (config.groupDirectSubfolders) {
+ if (mCurrentPathPrefix.isEmpty()) {
+ super.onBackPressed()
+ } else {
+ mOpenedSubfolders.removeAt(mOpenedSubfolders.size - 1)
+ mCurrentPathPrefix = mOpenedSubfolders.last()
+ setupAdapter(mDirs)
+ }
+ } else {
+ super.onBackPressed()
}
}
@@ -242,10 +268,13 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
menu.apply {
findItem(R.id.increase_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt < MAX_COLUMN_COUNT
findItem(R.id.reduce_column_count).isVisible = config.viewTypeFolders == VIEW_TYPE_GRID && config.dirColumnCnt > 1
+ setupSearch(this)
}
}
+
menu.findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
menu.findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
+
return true
}
@@ -292,6 +321,43 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
}
+ private fun setupSearch(menu: Menu) {
+ val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
+ mSearchMenuItem = menu.findItem(R.id.search)
+ (mSearchMenuItem?.actionView as? SearchView)?.apply {
+ setSearchableInfo(searchManager.getSearchableInfo(componentName))
+ isSubmitButtonEnabled = false
+ setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String) = false
+
+ override fun onQueryTextChange(newText: String): Boolean {
+ if (mIsSearchOpen) {
+ setupAdapter(mDirs, newText)
+ }
+ return true
+ }
+ })
+ }
+
+ MenuItemCompat.setOnActionExpandListener(mSearchMenuItem, object : MenuItemCompat.OnActionExpandListener {
+ override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
+ mIsSearchOpen = true
+ directories_refresh_layout.isEnabled = false
+ return true
+ }
+
+ // this triggers on device rotation too, avoid doing anything
+ override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
+ if (mIsSearchOpen) {
+ mIsSearchOpen = false
+ directories_refresh_layout.isEnabled = config.enablePullToRefresh
+ setupAdapter(mDirs, "")
+ }
+ return true
+ }
+ })
+ }
+
private fun removeTempFolder() {
if (config.tempFolderPath.isNotEmpty()) {
val newFolder = File(config.tempFolderPath)
@@ -309,8 +375,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
Thread {
if (hasOTGConnected()) {
runOnUiThread {
- handleOTGPermission {
- config.addIncludedFolder(OTG_PATH)
+ ConfirmationDialog(this, getString(R.string.usb_detected), positive = R.string.ok, negative = 0) {
+ handleOTGPermission {
+ config.addIncludedFolder(OTG_PATH)
+ }
}
}
config.wasOTGHandled = true
@@ -321,6 +389,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun tryLoadGallery() {
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
+ if (!config.wasOTGHandled && hasPermission(PERMISSION_WRITE_STORAGE)) {
+ checkOTGInclusion()
+ }
+
if (config.showAll) {
showAllMedia()
} else {
@@ -387,17 +459,11 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
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
+ ChangeViewTypeDialog(this, true) {
invalidateOptionsMenu()
setupLayoutManager()
- val dirs = getCurrentlyDisplayedDirs()
directories_grid.adapter = null
- setupAdapter(dirs)
+ setupAdapter(mDirs)
}
}
@@ -420,16 +486,31 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
override fun deleteFolders(folders: ArrayList) {
- val fileDirItems = folders.map { FileDirItem(it.absolutePath, it.name, true) } as ArrayList
- fileDirItems.forEach {
- toast(String.format(getString(R.string.deleting_folder), it.name), Toast.LENGTH_LONG)
+ val fileDirItems = folders.asSequence().filter { it.isDirectory }.map { FileDirItem(it.absolutePath, it.name, true) }.toMutableList() as ArrayList
+ when {
+ fileDirItems.isEmpty() -> return
+ fileDirItems.size == 1 -> toast(String.format(getString(R.string.deleting_folder), fileDirItems.first().name))
+ else -> {
+ val baseString = if (config.useRecycleBin) R.plurals.moving_items_into_bin else R.plurals.delete_items
+ val deletingItems = resources.getQuantityString(baseString, fileDirItems.size, fileDirItems.size)
+ toast(deletingItems)
+ }
}
if (config.useRecycleBin) {
val pathsToDelete = ArrayList()
+ val filter = config.filterMedia
+ val showHidden = config.shouldShowHidden
fileDirItems.filter { it.isDirectory }.forEach {
val files = File(it.path).listFiles()
- files?.filter { it.absolutePath.isMediaFile() }?.mapTo(pathsToDelete) { it.absolutePath }
+ files?.filter {
+ it.absolutePath.isMediaFile() && (showHidden || !it.name.startsWith('.')) &&
+ ((it.isImageFast() && filter and TYPE_IMAGES != 0) ||
+ (it.isVideoFast() && filter and TYPE_VIDEOS != 0) ||
+ (it.isGif() && filter and TYPE_GIFS != 0) ||
+ (it.isRawFast() && filter and TYPE_RAWS != 0) ||
+ (it.isSvg() && filter and TYPE_SVGS != 0))
+ }?.mapTo(pathsToDelete) { it.absolutePath }
}
movePathsInRecycleBin(pathsToDelete, mMediumDao) {
@@ -469,10 +550,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun setupGridLayoutManager() {
val layoutManager = directories_grid.layoutManager as MyGridLayoutManager
if (config.scrollHorizontally) {
- layoutManager.orientation = GridLayoutManager.HORIZONTAL
+ layoutManager.orientation = RecyclerView.HORIZONTAL
directories_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
} else {
- layoutManager.orientation = GridLayoutManager.VERTICAL
+ layoutManager.orientation = RecyclerView.VERTICAL
directories_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
@@ -531,13 +612,13 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun setupListLayoutManager() {
val layoutManager = directories_grid.layoutManager as MyGridLayoutManager
layoutManager.spanCount = 1
- layoutManager.orientation = GridLayoutManager.VERTICAL
+ layoutManager.orientation = RecyclerView.VERTICAL
directories_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
mZoomListener = null
}
private fun createNewFolder() {
- FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden) {
+ FilePickerDialog(this, internalStoragePath, false, config.shouldShowHidden, false, true) {
CreateNewFolderDialog(this, it) {
config.tempFolderPath = it
Thread {
@@ -597,14 +678,22 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_MEDIA && resultData != null) {
val resultIntent = Intent()
+ var resultUri: Uri? = null
if (mIsThirdPartyIntent) {
when {
- intent.extras?.containsKey(MediaStore.EXTRA_OUTPUT) == true -> fillExtraOutput(resultData)
+ intent.extras?.containsKey(MediaStore.EXTRA_OUTPUT) == true -> {
+ resultUri = fillExtraOutput(resultData)
+ }
resultData.extras?.containsKey(PICKED_PATHS) == true -> fillPickedPaths(resultData, resultIntent)
else -> fillIntentPath(resultData, resultIntent)
}
}
+ if (resultUri != null) {
+ resultIntent.data = resultUri
+ resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+
setResult(Activity.RESULT_OK, resultIntent)
finish()
} else if (requestCode == PICK_WALLPAPER) {
@@ -615,22 +704,25 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
super.onActivityResult(requestCode, resultCode, resultData)
}
- private fun fillExtraOutput(resultData: Intent) {
- val path = resultData.data.path
+ private fun fillExtraOutput(resultData: Intent): Uri? {
+ val file = File(resultData.data.path)
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val output = intent.extras.get(MediaStore.EXTRA_OUTPUT) as Uri
- inputStream = FileInputStream(File(path))
+ inputStream = FileInputStream(file)
outputStream = contentResolver.openOutputStream(output)
inputStream.copyTo(outputStream)
} catch (e: SecurityException) {
showErrorToast(e)
} catch (ignored: FileNotFoundException) {
+ return getFilePublicUri(file, BuildConfig.APPLICATION_ID)
} finally {
inputStream?.close()
outputStream?.close()
}
+
+ return null
}
private fun fillPickedPaths(resultData: Intent, resultIntent: Intent) {
@@ -678,9 +770,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
private fun gotDirectories(newDirs: ArrayList) {
- // if hidden item showing is disabled but all Favorite items are hidden, hide the Favorites folder
mIsGettingDirs = false
mShouldStopFetching = false
+
+ // if hidden item showing is disabled but all Favorite items are hidden, hide the Favorites folder
if (!config.shouldShowHidden) {
val favoritesFolder = newDirs.firstOrNull { it.areFavorites() }
if (favoritesFolder != null && favoritesFolder.tmb.getFilenameFromPath().startsWith('.')) {
@@ -694,10 +787,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
runOnUiThread {
checkPlaceholderVisibility(dirs)
- val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
+ val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID
directories_vertical_fastscroller.beVisibleIf(directories_grid.isVisible() && !allowHorizontalScroll)
directories_horizontal_fastscroller.beVisibleIf(directories_grid.isVisible() && allowHorizontalScroll)
- setupAdapter(dirs)
+ setupAdapter(dirs.clone() as ArrayList)
}
// cached folders have been loaded, recheck folders one by one starting with the first displayed
@@ -707,9 +800,11 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
val hiddenString = getString(R.string.hidden)
val albumCovers = config.parseAlbumCovers()
val includedFolders = config.includedFolders
+ val tempFolderPath = config.tempFolderPath
val isSortingAscending = config.directorySorting and SORT_DESCENDING == 0
val getProperDateTaken = config.directorySorting and SORT_BY_DATE_TAKEN != 0
val favoritePaths = getFavoritePaths()
+ val dirPathsToRemove = ArrayList()
try {
for (directory in dirs) {
@@ -717,15 +812,18 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
return
}
- val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths)
+ val curMedia = mediaFetcher.getFilesFrom(directory.path, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths, false)
val newDir = if (curMedia.isEmpty()) {
+ if (directory.path != tempFolderPath) {
+ dirPathsToRemove.add(directory.path)
+ }
directory
} else {
createDirectoryFromMedia(directory.path, curMedia, albumCovers, hiddenString, includedFolders, isSortingAscending)
}
// we are looping through the already displayed folders looking for changes, do not do anything if nothing changed
- if (directory == newDir) {
+ if (directory.copy(subfoldersCount = 0, subfoldersMediaCount = 0) == newDir) {
continue
}
@@ -739,7 +837,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
types = newDir.types
}
- showSortedDirs(dirs)
+ setupAdapter(dirs)
// update directories and media files in the local db, delete invalid items
updateDBDirectory(directory, mDirectoryDao)
@@ -751,7 +849,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
if (!curMedia.contains(it)) {
val path = (it as? Medium)?.path
if (path != null) {
- mMediumDao.deleteMediumPath(path)
+ deleteDBPath(mMediumDao, path)
}
}
}
@@ -760,9 +858,18 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
} catch (ignored: Exception) {
}
+ if (dirPathsToRemove.isNotEmpty()) {
+ val dirsToRemove = dirs.filter { dirPathsToRemove.contains(it.path) }
+ dirsToRemove.forEach {
+ mDirectoryDao.deleteDirPath(it.path)
+ }
+ dirs.removeAll(dirsToRemove)
+ setupAdapter(dirs)
+ }
+
val foldersToScan = mediaFetcher.getFoldersToScan()
foldersToScan.add(FAVORITES)
- if (config.showRecycleBinAtFolders) {
+ if (config.useRecycleBin && config.showRecycleBinAtFolders) {
foldersToScan.add(RECYCLE_BIN)
} else {
foldersToScan.remove(RECYCLE_BIN)
@@ -778,7 +885,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
return
}
- val newMedia = mediaFetcher.getFilesFrom(folder, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths)
+ val newMedia = mediaFetcher.getFilesFrom(folder, getImagesOnly, getVideosOnly, getProperDateTaken, favoritePaths, false)
if (newMedia.isEmpty()) {
continue
}
@@ -794,7 +901,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
val newDir = createDirectoryFromMedia(folder, newMedia, albumCovers, hiddenString, includedFolders, isSortingAscending)
dirs.add(newDir)
- showSortedDirs(dirs)
+ setupAdapter(dirs)
mDirectoryDao.insert(newDir)
if (folder != RECYCLE_BIN) {
mMediumDao.insertAll(newMedia)
@@ -818,6 +925,11 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
} catch (e: Exception) {
config.everShownFolders = HashSet()
}
+ mDirs = dirs.clone() as ArrayList
+
+ if (mDirs.size > 55) {
+ excludeSpamFolders()
+ }
}
private fun checkPlaceholderVisibility(dirs: ArrayList) {
@@ -826,15 +938,6 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
directories_grid.beVisibleIf(directories_empty_text_label.isGone())
}
- private fun showSortedDirs(dirs: ArrayList) {
- var sortedDirs = getSortedDirectories(dirs)
- sortedDirs = sortedDirs.distinctBy { it.path.getDistinctPath() } as ArrayList
-
- runOnUiThread {
- (directories_grid.adapter as? DirectoryAdapter)?.updateDirs(sortedDirs)
- }
- }
-
private fun createDirectoryFromMedia(path: String, curMedia: ArrayList, albumCovers: ArrayList, hiddenString: String,
includedFolders: MutableSet, isSortingAscending: Boolean): Directory {
var thumbnail = curMedia.firstOrNull { getDoesFilePathExist(it.path) }?.path ?: ""
@@ -848,43 +951,59 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
}
- val mediaTypes = curMedia.getDirMediaTypes()
- val dirName = when (path) {
- FAVORITES -> getString(R.string.favorites)
- RECYCLE_BIN -> getString(R.string.recycle_bin)
- else -> checkAppendingHidden(path, hiddenString, includedFolders)
- }
-
val firstItem = curMedia.first()
val lastItem = curMedia.last()
+ val dirName = checkAppendingHidden(path, hiddenString, includedFolders)
val lastModified = if (isSortingAscending) Math.min(firstItem.modified, lastItem.modified) else Math.max(firstItem.modified, lastItem.modified)
val dateTaken = if (isSortingAscending) Math.min(firstItem.taken, lastItem.taken) else Math.max(firstItem.taken, lastItem.taken)
val size = curMedia.sumByLong { it.size }
+ val mediaTypes = curMedia.getDirMediaTypes()
return Directory(null, path, thumbnail, dirName, curMedia.size, lastModified, dateTaken, size, getPathLocation(path), mediaTypes)
}
- private fun setupAdapter(dirs: ArrayList) {
+ private fun setupAdapter(dirs: ArrayList, textToSearch: String = "") {
val currAdapter = directories_grid.adapter
+ val distinctDirs = dirs.distinctBy { it.path.getDistinctPath() }.toMutableList() as ArrayList
+ val sortedDirs = getSortedDirectories(distinctDirs)
+ var dirsToShow = getDirsToShow(sortedDirs, mDirs, mCurrentPathPrefix).clone() as ArrayList
+
if (currAdapter == null) {
initZoomListener()
val fastscroller = if (config.scrollHorizontally) directories_horizontal_fastscroller else directories_vertical_fastscroller
- DirectoryAdapter(this, dirs.clone() as ArrayList, this, directories_grid, isPickIntent(intent) || isGetAnyContentIntent(intent), fastscroller) {
- val path = (it as Directory).path
- if (path != config.tempFolderPath) {
- itemClicked(path)
+ DirectoryAdapter(this, dirsToShow, this, directories_grid, isPickIntent(intent) || isGetAnyContentIntent(intent), fastscroller) {
+ val clickedDir = it as Directory
+ val path = clickedDir.path
+ if (clickedDir.subfoldersCount == 1 || !config.groupDirectSubfolders) {
+ if (path != config.tempFolderPath) {
+ itemClicked(path)
+ }
+ } else {
+ mCurrentPathPrefix = path
+ mOpenedSubfolders.add(path)
+ setupAdapter(mDirs, "")
}
}.apply {
setupZoomListener(mZoomListener)
- directories_grid.adapter = this
+ runOnUiThread {
+ directories_grid.adapter = this
+ setupScrollDirection()
+ }
}
+ measureRecyclerViewContent(dirsToShow)
} else {
- (currAdapter as DirectoryAdapter).updateDirs(dirs)
+ if (textToSearch.isNotEmpty()) {
+ dirsToShow = dirsToShow.filter { it.name.contains(textToSearch, true) }.sortedBy { !it.name.startsWith(textToSearch, true) }.toMutableList() as ArrayList
+ }
+ runOnUiThread {
+ (directories_grid.adapter as? DirectoryAdapter)?.updateDirs(dirsToShow)
+ measureRecyclerViewContent(dirsToShow)
+ }
}
- getRecyclerAdapter()?.dirs?.apply {
- measureRecyclerViewContent(this)
- }
- setupScrollDirection()
+ // recyclerview sometimes becomes empty at init/update, triggering an invisible refresh like this seems to work fine
+ directories_grid.postDelayed({
+ directories_grid.scrollBy(0, 0)
+ }, 500)
}
private fun setupScrollDirection() {
@@ -915,7 +1034,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
invalidDirs.add(it)
} else if (it.path != config.tempFolderPath) {
val children = if (it.path.startsWith(OTG_PATH)) getOTGFolderChildrenNames(it.path) else File(it.path).list()?.asList()
- val hasMediaFile = children?.any { it.isMediaFile() } ?: false
+ val hasMediaFile = children?.any { it?.isMediaFile() == true } ?: false
if (!hasMediaFile) {
invalidDirs.add(it)
}
@@ -938,7 +1057,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
if (invalidDirs.isNotEmpty()) {
dirs.removeAll(invalidDirs)
- showSortedDirs(dirs)
+ setupAdapter(dirs)
invalidDirs.forEach {
mDirectoryDao.deleteDirPath(it.path)
}
@@ -959,7 +1078,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
private fun checkLastMediaChanged() {
- if (isActivityDestroyed()) {
+ if (isDestroyed) {
return
}
@@ -981,18 +1100,64 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}, LAST_MEDIA_CHECK_PERIOD)
}
-
private fun checkRecycleBinItems() {
if (config.useRecycleBin && config.lastBinCheck < System.currentTimeMillis() - DAY_SECONDS * 1000) {
config.lastBinCheck = System.currentTimeMillis()
Handler().postDelayed({
Thread {
- mMediumDao.deleteOldRecycleBinItems(System.currentTimeMillis() - MONTH_MILLISECONDS)
+ try {
+ mMediumDao.deleteOldRecycleBinItems(System.currentTimeMillis() - MONTH_MILLISECONDS)
+ } catch (e: Exception) {
+ }
}.start()
}, 3000L)
}
}
+ // exclude probably unwanted folders, for example facebook stickers are split between hundreds of separate folders like
+ // /storage/emulated/0/Android/data/com.facebook.orca/files/stickers/175139712676531/209575122566323
+ // /storage/emulated/0/Android/data/com.facebook.orca/files/stickers/497837993632037/499671223448714
+ private fun excludeSpamFolders() {
+ Thread {
+ try {
+ val internalPath = internalStoragePath
+ val checkedPaths = ArrayList()
+ val oftenRepeatedPaths = ArrayList()
+ val paths = mDirs.map { it.path.removePrefix(internalPath) }.toMutableList() as ArrayList
+ paths.forEach {
+ val parts = it.split("/")
+ var currentString = ""
+ for (i in 0 until parts.size) {
+ currentString += "${parts[i]}/"
+
+ if (!checkedPaths.contains(currentString)) {
+ val cnt = paths.count { it.startsWith(currentString) }
+ if (cnt > 50 && currentString.startsWith("/Android/data", true)) {
+ oftenRepeatedPaths.add(currentString)
+ }
+ }
+
+ checkedPaths.add(currentString)
+ }
+ }
+
+ val substringToRemove = oftenRepeatedPaths.filter {
+ val path = it
+ it == "/" || oftenRepeatedPaths.any { it != path && it.startsWith(path) }
+ }
+
+ oftenRepeatedPaths.removeAll(substringToRemove)
+ oftenRepeatedPaths.forEach {
+ val file = File("$internalPath/$it")
+ if (file.exists()) {
+ config.addExcludedFolder(file.absolutePath)
+ }
+ }
+ } catch (e: Exception) {
+ }
+ }.start()
+ }
+
override fun refreshItems() {
getDirectories()
}
@@ -1012,55 +1177,11 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
private fun checkWhatsNewDialog() {
arrayListOf().apply {
- add(Release(46, R.string.release_46))
- add(Release(47, R.string.release_47))
- add(Release(49, R.string.release_49))
- add(Release(50, R.string.release_50))
- add(Release(51, R.string.release_51))
- add(Release(52, R.string.release_52))
- add(Release(54, R.string.release_54))
- add(Release(58, R.string.release_58))
- add(Release(62, R.string.release_62))
- add(Release(65, R.string.release_65))
- add(Release(66, R.string.release_66))
- add(Release(69, R.string.release_69))
- add(Release(70, R.string.release_70))
- add(Release(72, R.string.release_72))
- add(Release(74, R.string.release_74))
- add(Release(76, R.string.release_76))
- add(Release(77, R.string.release_77))
- add(Release(83, R.string.release_83))
- add(Release(84, R.string.release_84))
- add(Release(88, R.string.release_88))
- add(Release(89, R.string.release_89))
- add(Release(93, R.string.release_93))
- add(Release(94, R.string.release_94))
- add(Release(97, R.string.release_97))
- add(Release(98, R.string.release_98))
- add(Release(108, R.string.release_108))
- add(Release(112, R.string.release_112))
- add(Release(114, R.string.release_114))
- add(Release(115, R.string.release_115))
- add(Release(118, R.string.release_118))
- add(Release(119, R.string.release_119))
- add(Release(122, R.string.release_122))
- 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))
- add(Release(136, R.string.release_136))
- add(Release(137, R.string.release_137))
- add(Release(138, R.string.release_138))
- add(Release(143, R.string.release_143))
- add(Release(158, R.string.release_158))
- add(Release(159, R.string.release_159))
- add(Release(163, R.string.release_163))
- add(Release(177, R.string.release_177))
- add(Release(178, R.string.release_178))
- add(Release(180, R.string.release_180))
- add(Release(181, R.string.release_181))
- add(Release(182, R.string.release_182))
- add(Release(184, R.string.release_184))
+ add(Release(213, R.string.release_213))
+ add(Release(217, R.string.release_217))
+ add(Release(220, R.string.release_220))
+ add(Release(221, R.string.release_221))
+ add(Release(225, R.string.release_225))
checkWhatsNew(this, BuildConfig.VERSION_CODE)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/MediaActivity.kt
similarity index 86%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/MediaActivity.kt
index 5107c3d21..5a474751e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/MediaActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/MediaActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.app.Activity
import android.app.SearchManager
@@ -9,42 +9,39 @@ import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.os.Handler
-import android.support.v4.view.MenuItemCompat
-import android.support.v7.widget.GridLayoutManager
-import android.support.v7.widget.SearchView
import android.view.Menu
import android.view.MenuItem
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.appcompat.widget.SearchView
+import androidx.core.view.MenuItemCompat
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
-import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.OTG_PATH
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.REQUEST_EDIT_IMAGE
import com.simplemobiletools.commons.models.FileDirItem
-import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.MyGridLayoutManager
import com.simplemobiletools.commons.views.MyRecyclerView
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.MediaAdapter
-import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
-import com.simplemobiletools.gallery.dialogs.ChangeGroupingDialog
-import com.simplemobiletools.gallery.dialogs.ChangeSortingDialog
-import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
-import com.simplemobiletools.gallery.dialogs.FilterMediaDialog
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.interfaces.DirectoryDao
-import com.simplemobiletools.gallery.interfaces.MediaOperationsListener
-import com.simplemobiletools.gallery.interfaces.MediumDao
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
-import com.simplemobiletools.gallery.models.ThumbnailSection
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.MediaAdapter
+import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
+import com.simplemobiletools.gallery.pro.databases.GalleryDatabase
+import com.simplemobiletools.gallery.pro.dialogs.*
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
+import com.simplemobiletools.gallery.pro.interfaces.MediaOperationsListener
+import com.simplemobiletools.gallery.pro.interfaces.MediumDao
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.models.ThumbnailSection
import kotlinx.android.synthetic.main.activity_media.*
import java.io.File
import java.io.IOException
@@ -116,6 +113,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
media_empty_text.setOnClickListener {
showFilterMediaDialog()
}
+
+ updateWidgets()
}
override fun onStart() {
@@ -154,7 +153,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
media_horizontal_fastscroller.allowBubbleDisplay = config.showInfoBubble
media_vertical_fastscroller.allowBubbleDisplay = config.showInfoBubble
media_refresh_layout.isEnabled = config.enablePullToRefresh
- tryloadGallery()
+ tryLoadGallery()
invalidateOptionsMenu()
media_empty_text_label.setTextColor(config.textColor)
media_empty_text.setTextColor(getAdjustedPrimaryColor())
@@ -191,6 +190,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
if (config.showAll && !isChangingConfigurations) {
config.temporarilyShowHidden = false
config.tempSkipDeleteConfirmation = false
+ GalleryDatabase.destroyInstance()
}
mTempShowHiddenHandler.removeCallbacksAndMessages(null)
@@ -219,10 +219,10 @@ class MediaActivity : SimpleActivity(), 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.viewTypeFiles == VIEW_TYPE_GRID && config.mediaColumnCnt < MAX_COLUMN_COUNT
- 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
+ val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
+ findItem(R.id.increase_column_count).isVisible = viewType == VIEW_TYPE_GRID && config.mediaColumnCnt < MAX_COLUMN_COUNT
+ findItem(R.id.reduce_column_count).isVisible = viewType == VIEW_TYPE_GRID && config.mediaColumnCnt > 1
+ findItem(R.id.toggle_filename).isVisible = viewType == VIEW_TYPE_GRID
}
setupSearch(menu)
@@ -248,6 +248,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
R.id.stop_showing_hidden -> tryToggleTemporarilyShowHidden()
R.id.increase_column_count -> increaseColumnCount()
R.id.reduce_column_count -> reduceColumnCount()
+ R.id.slideshow -> startSlideshow()
R.id.settings -> launchSettings()
R.id.about -> launchAbout()
else -> return super.onOptionsItemSelected(item)
@@ -255,6 +256,19 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
return true
}
+ private fun startSlideshow() {
+ if (mMedia.isNotEmpty()) {
+ Intent(this, ViewPagerActivity::class.java).apply {
+ val item = mMedia.firstOrNull { it is Medium } as? Medium
+ ?: return
+ putExtra(PATH, item.path)
+ putExtra(SHOW_ALL, mShowAll)
+ putExtra(SLIDESHOW_START_ON_ENTER, true)
+ startActivity(this)
+ }
+ }
+ }
+
private fun storeStateVariables() {
config.apply {
mStoredAnimateGifs = animateGifs
@@ -306,23 +320,26 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun searchQueryChanged(text: String) {
Thread {
- val filtered = mMedia.filter { it is Medium && it.name.contains(text, true) } as ArrayList
- filtered.sortBy { it is Medium && !it.name.startsWith(text, true) }
- val grouped = MediaFetcher(applicationContext).groupMedia(filtered as ArrayList, mPath)
- runOnUiThread {
- getMediaAdapter()?.updateMedia(grouped)
- measureRecyclerViewContent(grouped)
+ try {
+ val filtered = mMedia.filter { it is Medium && it.name.contains(text, true) } as ArrayList
+ filtered.sortBy { it is Medium && !it.name.startsWith(text, true) }
+ val grouped = MediaFetcher(applicationContext).groupMedia(filtered as ArrayList, mPath)
+ runOnUiThread {
+ getMediaAdapter()?.updateMedia(grouped)
+ measureRecyclerViewContent(grouped)
+ }
+ } catch (ignored: Exception) {
}
}.start()
}
- private fun tryloadGallery() {
+ private fun tryLoadGallery() {
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
val dirName = when {
mPath == FAVORITES -> getString(R.string.favorites)
mPath == RECYCLE_BIN -> getString(R.string.recycle_bin)
- mPath == OTG_PATH -> getString(R.string.otg)
+ mPath == OTG_PATH -> getString(R.string.usb)
mPath.startsWith(OTG_PATH) -> mPath.trimEnd('/').substringAfterLast('/')
else -> getHumanizedFilename(mPath)
}
@@ -348,7 +365,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
initZoomListener()
val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_fastscroller
MediaAdapter(this, mMedia.clone() as ArrayList, this, mIsGetImageIntent || mIsGetVideoIntent || mIsGetAnyIntent,
- mAllowPickingMultiple, media_grid, fastscroller) {
+ mAllowPickingMultiple, mPath, media_grid, fastscroller) {
if (it is Medium) {
itemClicked(it.path)
}
@@ -366,7 +383,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun setupScrollDirection() {
- val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
+ val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
+ val allowHorizontalScroll = config.scrollHorizontally && viewType == VIEW_TYPE_GRID
media_vertical_fastscroller.isHorizontal = false
media_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
@@ -397,7 +415,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun checkLastMediaChanged() {
- if (isActivityDestroyed()) {
+ if (isDestroyed) {
return
}
@@ -474,12 +492,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
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
+ ChangeViewTypeDialog(this, false, mPath) {
invalidateOptionsMenu()
setupLayoutManager()
media_grid.adapter = null
@@ -545,7 +558,9 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
mIsGettingMedia = true
- if (!mLoadedInitialPhotos) {
+ if (mLoadedInitialPhotos) {
+ startAsyncTask()
+ } else {
getCachedMedia(mPath, mIsGetVideoIntent, mIsGetImageIntent, mMediumDao) {
if (it.isEmpty()) {
runOnUiThread {
@@ -556,9 +571,6 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
startAsyncTask()
}
- } else {
- media_refresh_layout.isRefreshing = true
- startAsyncTask()
}
mLoadedInitialPhotos = true
@@ -568,7 +580,15 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
mCurrAsyncTask?.stopFetching()
mCurrAsyncTask = GetMediaAsynctask(applicationContext, mPath, mIsGetImageIntent, mIsGetVideoIntent, mShowAll) {
Thread {
- gotMedia(it)
+ val oldMedia = mMedia.clone() as ArrayList
+ val newMedia = it
+ gotMedia(newMedia, false)
+ try {
+ oldMedia.filter { !newMedia.contains(it) }.mapNotNull { it as? Medium }.filter { !File(it.path).exists() }.forEach {
+ mMediumDao.deleteMediumPath(it.path)
+ }
+ } catch (e: Exception) {
+ }
}.start()
}
@@ -619,7 +639,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun setupLayoutManager() {
- if (config.viewTypeFiles == VIEW_TYPE_GRID) {
+ val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
+ if (viewType == VIEW_TYPE_GRID) {
setupGridLayoutManager()
} else {
setupListLayoutManager()
@@ -629,10 +650,10 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun setupGridLayoutManager() {
val layoutManager = media_grid.layoutManager as MyGridLayoutManager
if (config.scrollHorizontally) {
- layoutManager.orientation = GridLayoutManager.HORIZONTAL
+ layoutManager.orientation = RecyclerView.HORIZONTAL
media_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
} else {
- layoutManager.orientation = GridLayoutManager.VERTICAL
+ layoutManager.orientation = RecyclerView.VERTICAL
media_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
@@ -672,8 +693,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
val pathToCheck = if (mPath.isEmpty()) SHOW_ALL else mPath
val hasSections = config.getFolderGrouping(pathToCheck) and GROUP_BY_NONE == 0 && !config.scrollHorizontally
val sectionTitleHeight = if (hasSections) layoutManager.getChildAt(0)?.height ?: 0 else 0
- val thumbnailHeight = if (hasSections) layoutManager.getChildAt(1)?.height ?: 0 else layoutManager.getChildAt(0)?.height
- ?: 0
+ val thumbnailHeight = if (hasSections) layoutManager.getChildAt(1)?.height ?: 0 else layoutManager.getChildAt(0)?.height ?: 0
var fullHeight = 0
var curSectionItems = 0
@@ -696,7 +716,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun initZoomListener() {
- if (config.viewTypeFiles == VIEW_TYPE_GRID) {
+ val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
+ if (viewType == VIEW_TYPE_GRID) {
val layoutManager = media_grid.layoutManager as MyGridLayoutManager
mZoomListener = object : MyRecyclerView.MyZoomListener {
override fun zoomIn() {
@@ -721,7 +742,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
private fun setupListLayoutManager() {
val layoutManager = media_grid.layoutManager as MyGridLayoutManager
layoutManager.spanCount = 1
- layoutManager.orientation = GridLayoutManager.VERTICAL
+ layoutManager.orientation = RecyclerView.VERTICAL
media_refresh_layout.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
mZoomListener = null
}
@@ -803,7 +824,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
}
- private fun gotMedia(media: ArrayList, isFromCache: Boolean = false) {
+ private fun gotMedia(media: ArrayList, isFromCache: Boolean) {
mIsGettingMedia = false
checkLastMediaChanged()
mMedia = media
@@ -814,7 +835,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
media_empty_text.beVisibleIf(media.isEmpty() && !isFromCache)
media_grid.beVisibleIf(media_empty_text_label.isGone())
- val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
+ val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
+ val allowHorizontalScroll = config.scrollHorizontally && viewType == VIEW_TYPE_GRID
media_vertical_fastscroller.beVisibleIf(media_grid.isVisible() && !allowHorizontalScroll)
media_horizontal_fastscroller.beVisibleIf(media_grid.isVisible() && allowHorizontalScroll)
setupAdapter()
@@ -832,11 +854,15 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
override fun tryDeleteFiles(fileDirItems: ArrayList) {
- val filtered = fileDirItems.filter { it.path.isMediaFile() } as ArrayList
- val deletingItems = resources.getQuantityString(R.plurals.deleting_items, filtered.size, filtered.size)
- toast(deletingItems)
+ val filtered = fileDirItems.filter { File(it.path).isFile && it.path.isMediaFile() } as ArrayList
+ if (filtered.isEmpty()) {
+ return
+ }
+
+ if (config.useRecycleBin && !filtered.first().path.startsWith(recycleBinPath)) {
+ val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size)
+ toast(movingItems)
- if (config.useRecycleBin && !filtered.first().path.startsWith(filesDir.absolutePath)) {
movePathsInRecycleBin(filtered.map { it.path } as ArrayList, mMediumDao) {
if (it) {
deleteFilteredFiles(filtered)
@@ -845,6 +871,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
}
} else {
+ val deletingItems = resources.getQuantityString(R.plurals.deleting_items, filtered.size, filtered.size)
+ toast(deletingItems)
deleteFilteredFiles(filtered)
}
}
@@ -861,8 +889,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
Thread {
val useRecycleBin = config.useRecycleBin
filtered.forEach {
- if (!useRecycleBin) {
- mMediumDao.deleteMediumPath(it.path)
+ if (it.path.startsWith(recycleBinPath) || !useRecycleBin) {
+ deleteDBPath(mMediumDao, it.path)
}
}
}.start()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PanoramaActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PanoramaPhotoActivity.kt
similarity index 81%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/PanoramaActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PanoramaPhotoActivity.kt
index 576a5c792..fa8dcfb54 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PanoramaActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PanoramaPhotoActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.content.res.Configuration
import android.graphics.Bitmap
@@ -11,18 +11,19 @@ import android.widget.RelativeLayout
import com.google.vr.sdk.widgets.pano.VrPanoramaEventListener
import com.google.vr.sdk.widgets.pano.VrPanoramaView
import com.simplemobiletools.commons.extensions.beVisible
+import com.simplemobiletools.commons.extensions.onGlobalLayout
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.PATH
-import kotlinx.android.synthetic.main.activity_panorama.*
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.PATH
+import kotlinx.android.synthetic.main.activity_panorama_photo.*
-open class PanoramaActivity : SimpleActivity() {
+open class PanoramaPhotoActivity : SimpleActivity() {
private val CARDBOARD_DISPLAY_MODE = 3
- private var isFullScreen = false
+ private var isFullscreen = false
private var isExploreEnabled = true
private var isRendering = false
@@ -30,8 +31,10 @@ open class PanoramaActivity : SimpleActivity() {
useDynamicTheme = false
requestWindowFeature(Window.FEATURE_NO_TITLE)
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_panorama)
+ setContentView(R.layout.activity_panorama_photo)
supportActionBar?.hide()
+
+ checkNotchSupport()
setupButtonMargins()
cardboard.setOnClickListener {
@@ -61,6 +64,8 @@ open class PanoramaActivity : SimpleActivity() {
if (config.blackBackground) {
updateStatusbarColor(Color.BLACK)
}
+
+ window.statusBarColor = resources.getColor(R.color.circle_black_background)
}
override fun onPause() {
@@ -97,11 +102,6 @@ open class PanoramaActivity : SimpleActivity() {
loadImageFromBitmap(bitmap, options)
setFlingingEnabled(true)
setPureTouchTracking(true)
- setEventListener(object : VrPanoramaEventListener() {
- override fun onClick() {
- handleClick()
- }
- })
// add custom buttons so we can position them and toggle visibility as desired
setFullscreenButtonEnabled(false)
@@ -112,6 +112,12 @@ open class PanoramaActivity : SimpleActivity() {
setOnClickListener {
handleClick()
}
+
+ setEventListener(object : VrPanoramaEventListener() {
+ override fun onClick() {
+ handleClick()
+ }
+ })
}
}
}.start()
@@ -120,7 +126,7 @@ open class PanoramaActivity : SimpleActivity() {
}
window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
- isFullScreen = visibility and View.SYSTEM_UI_FLAG_FULLSCREEN != 0
+ isFullscreen = visibility and View.SYSTEM_UI_FLAG_FULLSCREEN != 0
toggleButtonVisibility()
}
}
@@ -148,26 +154,30 @@ open class PanoramaActivity : SimpleActivity() {
}
private fun setupButtonMargins() {
+ val navBarHeight = navigationBarHeight
(cardboard.layoutParams as RelativeLayout.LayoutParams).apply {
- bottomMargin = navigationBarHeight
+ bottomMargin = navBarHeight
rightMargin = navigationBarWidth
}
(explore.layoutParams as RelativeLayout.LayoutParams).bottomMargin = navigationBarHeight
+
+ cardboard.onGlobalLayout {
+ panorama_gradient_background.layoutParams.height = navBarHeight + cardboard.height
+ }
}
private fun toggleButtonVisibility() {
- cardboard.animate().alpha(if (isFullScreen) 0f else 1f)
- cardboard.isClickable = !isFullScreen
-
- explore.animate().alpha(if (isFullScreen) 0f else 1f)
- explore.isClickable = !isFullScreen
+ arrayOf(cardboard, explore, panorama_gradient_background).forEach {
+ it.animate().alpha(if (isFullscreen) 0f else 1f)
+ it.isClickable = !isFullscreen
+ }
}
private fun handleClick() {
- isFullScreen = !isFullScreen
+ isFullscreen = !isFullscreen
toggleButtonVisibility()
- if (isFullScreen) {
+ if (isFullscreen) {
hideSystemUI(false)
} else {
showSystemUI(false)
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PanoramaVideoActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PanoramaVideoActivity.kt
new file mode 100644
index 000000000..e394858d2
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PanoramaVideoActivity.kt
@@ -0,0 +1,322 @@
+package com.simplemobiletools.gallery.pro.activities
+
+import android.content.res.Configuration
+import android.graphics.Color
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.view.View
+import android.view.Window
+import android.view.WindowManager
+import android.widget.RelativeLayout
+import android.widget.SeekBar
+import com.google.vr.sdk.widgets.video.VrVideoEventListener
+import com.google.vr.sdk.widgets.video.VrVideoView
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.MIN_SKIP_LENGTH
+import com.simplemobiletools.gallery.pro.helpers.PATH
+import kotlinx.android.synthetic.main.activity_panorama_video.*
+import kotlinx.android.synthetic.main.bottom_video_time_holder.*
+import java.io.File
+
+open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener {
+ private val CARDBOARD_DISPLAY_MODE = 3
+
+ private var mIsFullscreen = false
+ private var mIsExploreEnabled = true
+ private var mIsRendering = false
+ private var mIsPlaying = false
+ private var mIsDragged = false
+ private var mPlayOnReady = false
+ private var mDuration = 0
+ private var mCurrTime = 0
+
+ private var mTimerHandler = Handler()
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ useDynamicTheme = false
+ requestWindowFeature(Window.FEATURE_NO_TITLE)
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_panorama_video)
+ supportActionBar?.hide()
+
+ checkNotchSupport()
+ handlePermission(PERMISSION_WRITE_STORAGE) {
+ if (it) {
+ checkIntent()
+ } else {
+ toast(R.string.no_storage_permissions)
+ finish()
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ vr_video_view.resumeRendering()
+ mIsRendering = true
+ if (config.blackBackground) {
+ updateStatusbarColor(Color.BLACK)
+ }
+
+ window.statusBarColor = resources.getColor(R.color.circle_black_background)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ vr_video_view.pauseRendering()
+ mIsRendering = false
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ if (mIsRendering) {
+ vr_video_view.shutdown()
+ }
+
+ if (!isChangingConfigurations) {
+ mTimerHandler.removeCallbacksAndMessages(null)
+ }
+ }
+
+ private fun checkIntent() {
+ val path = intent.getStringExtra(PATH)
+ if (path == null) {
+ toast(R.string.invalid_image_path)
+ finish()
+ return
+ }
+
+ setupButtons()
+ intent.removeExtra(PATH)
+
+ video_curr_time.setOnClickListener { skip(false) }
+ video_duration.setOnClickListener { skip(true) }
+
+ try {
+ val options = VrVideoView.Options()
+ options.inputType = VrVideoView.Options.TYPE_MONO
+
+ vr_video_view.apply {
+ loadVideo(Uri.fromFile(File(path)), options)
+ pauseVideo()
+
+ setFlingingEnabled(true)
+ setPureTouchTracking(true)
+
+ // add custom buttons so we can position them and toggle visibility as desired
+ setFullscreenButtonEnabled(false)
+ setInfoButtonEnabled(false)
+ setTransitionViewEnabled(false)
+ setStereoModeButtonEnabled(false)
+
+ setOnClickListener {
+ handleClick()
+ }
+
+ setEventListener(object : VrVideoEventListener() {
+ override fun onClick() {
+ handleClick()
+ }
+
+ override fun onLoadSuccess() {
+ if (mDuration == 0) {
+ setupDuration(duration)
+ setupTimer()
+ }
+
+ if (mPlayOnReady || config.autoplayVideos) {
+ mPlayOnReady = false
+ mIsPlaying = true
+ resumeVideo()
+ } else {
+ video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
+ }
+ video_toggle_play_pause.beVisible()
+ }
+
+ override fun onCompletion() {
+ videoCompleted()
+ }
+ })
+ }
+
+ video_toggle_play_pause.setOnClickListener {
+ togglePlayPause()
+ }
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+
+ window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
+ mIsFullscreen = visibility and View.SYSTEM_UI_FLAG_FULLSCREEN != 0
+ toggleButtonVisibility()
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ setupButtons()
+ }
+
+ private fun setupDuration(duration: Long) {
+ mDuration = (duration / 1000).toInt()
+ video_seekbar.max = mDuration
+ video_duration.text = mDuration.getFormattedDuration()
+ setVideoProgress(0)
+ }
+
+ private fun setupTimer() {
+ runOnUiThread(object : Runnable {
+ override fun run() {
+ if (mIsPlaying && !mIsDragged) {
+ mCurrTime = (vr_video_view!!.currentPosition / 1000).toInt()
+ video_seekbar.progress = mCurrTime
+ video_curr_time.text = mCurrTime.getFormattedDuration()
+ }
+
+ mTimerHandler.postDelayed(this, 1000)
+ }
+ })
+ }
+
+ private fun togglePlayPause() {
+ mIsPlaying = !mIsPlaying
+ if (mIsPlaying) {
+ resumeVideo()
+ } else {
+ pauseVideo()
+ }
+ }
+
+ private fun resumeVideo() {
+ video_toggle_play_pause.setImageResource(R.drawable.ic_pause_outline)
+ if (mCurrTime == mDuration) {
+ setVideoProgress(0)
+ mPlayOnReady = true
+ return
+ }
+
+ vr_video_view.playVideo()
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ }
+
+ private fun pauseVideo() {
+ vr_video_view.pauseVideo()
+ video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
+ window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ }
+
+ private fun setVideoProgress(seconds: Int) {
+ vr_video_view.seekTo(seconds * 1000L)
+ video_seekbar.progress = seconds
+ mCurrTime = seconds
+ video_curr_time.text = seconds.getFormattedDuration()
+ }
+
+ private fun videoCompleted() {
+ mIsPlaying = false
+ mCurrTime = (vr_video_view.duration / 1000).toInt()
+ video_seekbar.progress = video_seekbar.max
+ video_curr_time.text = mDuration.getFormattedDuration()
+ pauseVideo()
+ }
+
+ private fun setupButtons() {
+ var right = 0
+ var bottom = 0
+
+ if (hasNavBar()) {
+ if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ bottom += navigationBarHeight
+ } else {
+ right += navigationBarWidth
+ bottom += navigationBarHeight
+ }
+ }
+
+ video_time_holder.setPadding(0, 0, right, bottom)
+ video_time_holder.background = resources.getDrawable(R.drawable.gradient_background)
+ video_time_holder.onGlobalLayout {
+ val newBottomMargin = video_time_holder.height - resources.getDimension(R.dimen.video_player_play_pause_size).toInt() - resources.getDimension(R.dimen.activity_margin).toInt()
+ (explore.layoutParams as RelativeLayout.LayoutParams).bottomMargin = newBottomMargin
+
+ (cardboard.layoutParams as RelativeLayout.LayoutParams).apply {
+ bottomMargin = newBottomMargin
+ rightMargin = navigationBarWidth
+ }
+ explore.requestLayout()
+ }
+ video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
+
+ cardboard.setOnClickListener {
+ vr_video_view.displayMode = CARDBOARD_DISPLAY_MODE
+ }
+
+ explore.setOnClickListener {
+ mIsExploreEnabled = !mIsExploreEnabled
+ vr_video_view.setPureTouchTracking(mIsExploreEnabled)
+ explore.setImageResource(if (mIsExploreEnabled) R.drawable.ic_explore else R.drawable.ic_explore_off)
+ }
+ }
+
+ private fun toggleButtonVisibility() {
+ val newAlpha = if (mIsFullscreen) 0f else 1f
+ arrayOf(cardboard, explore).forEach {
+ it.animate().alpha(newAlpha)
+ }
+
+ arrayOf(cardboard, explore, video_toggle_play_pause, video_curr_time, video_duration).forEach {
+ it.isClickable = !mIsFullscreen
+ }
+
+ video_seekbar.setOnSeekBarChangeListener(if (mIsFullscreen) null else this)
+ video_time_holder.animate().alpha(newAlpha).start()
+ }
+
+ private fun handleClick() {
+ mIsFullscreen = !mIsFullscreen
+ toggleButtonVisibility()
+ if (mIsFullscreen) {
+ hideSystemUI(false)
+ } else {
+ showSystemUI(false)
+ }
+ }
+
+ private fun skip(forward: Boolean) {
+ if (forward && mCurrTime == mDuration) {
+ return
+ }
+
+ val curr = vr_video_view.currentPosition
+ val twoPercents = Math.max((vr_video_view.duration / 50).toInt(), MIN_SKIP_LENGTH)
+ val newProgress = if (forward) curr + twoPercents else curr - twoPercents
+ val roundProgress = Math.round(newProgress / 1000f)
+ val limitedProgress = Math.max(Math.min(vr_video_view.duration.toInt(), roundProgress), 0)
+ setVideoProgress(limitedProgress)
+ if (!mIsPlaying) {
+ togglePlayPause()
+ }
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ if (fromUser) {
+ setVideoProgress(progress)
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {
+ vr_video_view.pauseVideo()
+ mIsDragged = true
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {
+ mIsPlaying = true
+ resumeVideo()
+ mIsDragged = false
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoActivity.kt
similarity index 79%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoActivity.kt
index 44e83dbfd..95c439d3a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.os.Bundle
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt
similarity index 67%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt
index 9ae24e5d8..c9c413caa 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/PhotoVideoActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.content.Intent
import android.content.res.Configuration
@@ -14,19 +14,20 @@ import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.IS_FROM_GALLERY
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.REAL_FILE_PATH
-import com.simplemobiletools.gallery.R
-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.*
-import com.simplemobiletools.gallery.models.Medium
+import com.simplemobiletools.gallery.pro.BuildConfig
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.fragments.PhotoFragment
+import com.simplemobiletools.gallery.pro.fragments.VideoFragment
+import com.simplemobiletools.gallery.pro.fragments.ViewPagerFragment
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.Medium
import kotlinx.android.synthetic.main.bottom_actions.*
import kotlinx.android.synthetic.main.fragment_holder.*
import java.io.File
+import java.io.FileInputStream
open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentListener {
-
private var mMedium: Medium? = null
private var mIsFullScreen = false
private var mIsFromGallery = false
@@ -38,7 +39,6 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_holder)
- setTranslucentNavigation()
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
@@ -52,7 +52,15 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
override fun onResume() {
super.onResume()
- supportActionBar?.setBackgroundDrawable(resources.getDrawable(R.drawable.actionbar_gradient_background))
+ supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.statusBarColor = Color.TRANSPARENT
+
+ if (config.bottomActions) {
+ window.navigationBarColor = Color.TRANSPARENT
+ } else {
+ setTranslucentNavigation()
+ }
+
if (config.blackBackground) {
updateStatusbarColor(Color.BLACK)
}
@@ -60,22 +68,36 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private fun checkIntent(savedInstanceState: Bundle? = null) {
mUri = intent.data ?: return
- if (intent.extras?.containsKey(REAL_FILE_PATH) == true) {
- val realPath = intent.extras.getString(REAL_FILE_PATH)
- sendViewPagerIntent(realPath)
- finish()
+ var filename = getFilenameFromUri(mUri!!)
+ mIsFromGallery = intent.getBooleanExtra(IS_FROM_GALLERY, false)
+ if (mIsFromGallery && filename.isVideoFast() && config.openVideosOnSeparateScreen) {
+ launchVideoPlayer()
return
}
- mIsFromGallery = intent.getBooleanExtra(IS_FROM_GALLERY, false)
+ if (intent.extras?.containsKey(REAL_FILE_PATH) == true) {
+ val realPath = intent.extras.getString(REAL_FILE_PATH)
+ if (realPath != null) {
+ if (realPath.getFilenameFromPath().contains('.') || filename.contains('.')) {
+ sendViewPagerIntent(realPath)
+ finish()
+ return
+ } else {
+ filename = realPath.getFilenameFromPath()
+ }
+ }
+ }
+
if (mUri!!.scheme == "file") {
- scanPathRecursively(mUri!!.path)
- sendViewPagerIntent(mUri!!.path)
- finish()
- return
+ if (filename.contains('.')) {
+ scanPathRecursively(mUri!!.path)
+ sendViewPagerIntent(mUri!!.path)
+ finish()
+ return
+ }
} else {
val path = applicationContext.getRealPathFromURI(mUri!!) ?: ""
- if (path != mUri.toString() && path.isNotEmpty() && mUri!!.authority != "mms") {
+ if (path != mUri.toString() && path.isNotEmpty() && mUri!!.authority != "mms" && filename.contains('.')) {
scanPathRecursively(mUri!!.path)
sendViewPagerIntent(path)
finish()
@@ -83,10 +105,10 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
}
}
+ checkNotchSupport()
showSystemUI(true)
val bundle = Bundle()
val file = File(mUri.toString())
- val filename = getFilenameFromUri(mUri!!)
val type = when {
filename.isVideoFast() -> TYPE_VIDEOS
filename.isGif() -> TYPE_GIFS
@@ -95,7 +117,8 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
else -> TYPE_IMAGES
}
- mMedium = Medium(null, filename, mUri.toString(), mUri!!.path.getParentPath(), 0, 0, file.length(), type, false, 0L)
+ mIsVideo = type == TYPE_VIDEOS
+ mMedium = Medium(null, filename, mUri.toString(), mUri!!.path.getParentPath(), 0, 0, file.length(), type, 0, false, 0L)
supportActionBar?.title = mMedium!!.name
bundle.putSerializable(MEDIUM, mMedium)
@@ -103,7 +126,7 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
mFragment = if (mIsVideo) VideoFragment() else PhotoFragment()
mFragment!!.listener = this
mFragment!!.arguments = bundle
- supportFragmentManager.beginTransaction().replace(R.id.fragment_placeholder, mFragment).commit()
+ supportFragmentManager.beginTransaction().replace(R.id.fragment_placeholder, mFragment!!).commit()
}
if (config.blackBackground) {
@@ -118,6 +141,46 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
initBottomActions()
}
+ private fun launchVideoPlayer() {
+ val newUri = getFinalUriFromPath(mUri.toString(), BuildConfig.APPLICATION_ID)
+ if (newUri == null) {
+ toast(R.string.unknown_error_occurred)
+ return
+ }
+
+ var isPanorama = false
+ val realPath = intent?.extras?.getString(REAL_FILE_PATH) ?: ""
+ try {
+ if (realPath.isNotEmpty()) {
+ val fis = FileInputStream(File(realPath))
+ parseFileChannel(realPath, fis.channel, 0, 0, 0) {
+ isPanorama = true
+ }
+ }
+ } catch (ignored: Exception) {
+ } catch (ignored: OutOfMemoryError) {
+ }
+
+ if (isPanorama) {
+ Intent(applicationContext, PanoramaVideoActivity::class.java).apply {
+ putExtra(PATH, realPath)
+ startActivity(this)
+ }
+ } else {
+ val mimeType = getUriMimeType(mUri.toString(), newUri)
+ Intent(applicationContext, VideoPlayerActivity::class.java).apply {
+ setDataAndType(newUri, mimeType)
+ addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+ if (intent.extras != null) {
+ putExtras(intent.extras!!)
+ }
+
+ startActivity(this)
+ }
+ }
+ finish()
+ }
+
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
initBottomActionsLayout()
@@ -181,7 +244,8 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
}
private fun initBottomActionButtons() {
- arrayListOf(bottom_favorite, bottom_delete, bottom_rotate, bottom_properties, bottom_change_orientation, bottom_slideshow, bottom_show_on_map, bottom_toggle_file_visibility).forEach {
+ arrayListOf(bottom_favorite, bottom_delete, bottom_rotate, bottom_properties, bottom_change_orientation, bottom_slideshow, bottom_show_on_map,
+ bottom_toggle_file_visibility, bottom_rename, bottom_copy).forEach {
it.beGone()
}
@@ -214,8 +278,10 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
showSystemUI(true)
}
+ val newAlpha = if (mIsFullScreen) 0f else 1f
+ top_shadow.animate().alpha(newAlpha).start()
if (!bottom_actions.isGone()) {
- bottom_actions.animate().alpha(if (mIsFullScreen) 0f else 1f).start()
+ bottom_actions.animate().alpha(newAlpha).start()
}
}
@@ -224,4 +290,6 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
override fun goToPrevItem() {}
override fun goToNextItem() {}
+
+ override fun launchViewVideoIntent(path: String) {}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SetWallpaperActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SetWallpaperActivity.kt
similarity index 96%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/SetWallpaperActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SetWallpaperActivity.kt
index 5f02df853..946e0176f 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SetWallpaperActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SetWallpaperActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.annotation.SuppressLint
import android.app.Activity
@@ -10,11 +10,10 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
-import com.simplemobiletools.commons.extensions.isActivityDestroyed
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.commons.models.RadioItem
-import com.simplemobiletools.gallery.R
+import com.simplemobiletools.gallery.pro.R
import com.theartofdev.edmodo.cropper.CropImageView
import kotlinx.android.synthetic.main.activity_set_wallpaper.*
import kotlinx.android.synthetic.main.bottom_set_wallpaper_actions.*
@@ -113,7 +112,7 @@ class SetWallpaperActivity : SimpleActivity(), CropImageView.OnCropImageComplete
@SuppressLint("NewApi")
override fun onCropImageComplete(view: CropImageView?, result: CropImageView.CropResult) {
- if (isActivityDestroyed())
+ if (isDestroyed)
return
if (result.error == null) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SettingsActivity.kt
new file mode 100644
index 000000000..fd581e8ef
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SettingsActivity.kt
@@ -0,0 +1,773 @@
+package com.simplemobiletools.gallery.pro.activities
+
+import android.content.Intent
+import android.os.Bundle
+import android.text.TextUtils
+import com.simplemobiletools.commons.dialogs.ConfirmationDialog
+import com.simplemobiletools.commons.dialogs.FilePickerDialog
+import com.simplemobiletools.commons.dialogs.RadioGroupDialog
+import com.simplemobiletools.commons.dialogs.SecurityDialog
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.*
+import com.simplemobiletools.commons.models.RadioItem
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.dialogs.ManageBottomActionsDialog
+import com.simplemobiletools.gallery.pro.dialogs.ManageExtendedDetailsDialog
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.extensions.emptyTheRecycleBin
+import com.simplemobiletools.gallery.pro.extensions.galleryDB
+import com.simplemobiletools.gallery.pro.extensions.showRecycleBinEmptyingDialog
+import com.simplemobiletools.gallery.pro.helpers.*
+import kotlinx.android.synthetic.main.activity_settings.*
+import java.io.File
+import java.util.*
+
+class SettingsActivity : SimpleActivity() {
+ private var mRecycleBinContentSize = 0L
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_settings)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ setupSettingItems()
+ }
+
+ private fun setupSettingItems() {
+ setupCustomizeColors()
+ setupUseEnglish()
+ setupManageIncludedFolders()
+ setupManageExcludedFolders()
+ setupManageHiddenFolders()
+ setupShowHiddenItems()
+ setupDoExtraCheck()
+ setupAutoplayVideos()
+ setupRememberLastVideo()
+ setupLoopVideos()
+ setupOpenVideosOnSeparateScreen()
+ setupAnimateGifs()
+ setupMaxBrightness()
+ setupCropThumbnails()
+ setupDarkBackground()
+ setupScrollHorizontally()
+ setupScreenRotation()
+ setupHideSystemUI()
+ setupHiddenItemPasswordProtection()
+ setupAppPasswordProtection()
+ setupFileDeletionPasswordProtection()
+ setupDeleteEmptyFolders()
+ setupAllowPhotoGestures()
+ setupAllowVideoGestures()
+ setupAllowDownGesture()
+ setupShowNotch()
+ setupBottomActions()
+ setupThumbnailVideoDuration()
+ setupShowMediaCount()
+ setupKeepLastModified()
+ setupShowInfoBubble()
+ setupEnablePullToRefresh()
+ setupAllowZoomingImages()
+ setupShowHighestQuality()
+ setupAllowOneToOneZoom()
+ setupAllowInstantChange()
+ setupShowExtendedDetails()
+ setupHideExtendedDetails()
+ setupManageExtendedDetails()
+ setupSkipDeleteConfirmation()
+ setupManageBottomActions()
+ setupUseRecycleBin()
+ setupShowRecycleBin()
+ setupShowRecycleBinLast()
+ setupEmptyRecycleBin()
+ updateTextColors(settings_holder)
+ setupSectionColors()
+ setupExportSettings()
+ setupImportSettings()
+ }
+
+ private fun setupSectionColors() {
+ val adjustedPrimaryColor = getAdjustedPrimaryColor()
+ arrayListOf(visibility_label, videos_label, thumbnails_label, scrolling_label, fullscreen_media_label, security_label,
+ file_operations_label, deep_zoomable_images_label, extended_details_label, bottom_actions_label, recycle_bin_label,
+ migrating_label).forEach {
+ it.setTextColor(adjustedPrimaryColor)
+ }
+ }
+
+ private fun setupCustomizeColors() {
+ settings_customize_colors_holder.setOnClickListener {
+ startCustomizationActivity()
+ }
+ }
+
+ private fun setupUseEnglish() {
+ settings_use_english_holder.beVisibleIf(config.wasUseEnglishToggled || Locale.getDefault().language != "en")
+ settings_use_english.isChecked = config.useEnglish
+ settings_use_english_holder.setOnClickListener {
+ settings_use_english.toggle()
+ config.useEnglish = settings_use_english.isChecked
+ System.exit(0)
+ }
+ }
+
+ private fun setupManageIncludedFolders() {
+ settings_manage_included_folders_holder.setOnClickListener {
+ startActivity(Intent(this, IncludedFoldersActivity::class.java))
+ }
+ }
+
+ private fun setupManageExcludedFolders() {
+ settings_manage_excluded_folders_holder.setOnClickListener {
+ startActivity(Intent(this, ExcludedFoldersActivity::class.java))
+ }
+ }
+
+ private fun setupManageHiddenFolders() {
+ settings_manage_hidden_folders_holder.setOnClickListener {
+ handleHiddenFolderPasswordProtection {
+ startActivity(Intent(this, HiddenFoldersActivity::class.java))
+ }
+ }
+ }
+
+ private fun setupShowHiddenItems() {
+ settings_show_hidden_items.isChecked = config.showHiddenMedia
+ settings_show_hidden_items_holder.setOnClickListener {
+ if (config.showHiddenMedia) {
+ toggleHiddenItems()
+ } else {
+ handleHiddenFolderPasswordProtection {
+ toggleHiddenItems()
+ }
+ }
+ }
+ }
+
+ private fun toggleHiddenItems() {
+ settings_show_hidden_items.toggle()
+ config.showHiddenMedia = settings_show_hidden_items.isChecked
+ }
+
+ private fun setupDoExtraCheck() {
+ settings_do_extra_check.isChecked = config.doExtraCheck
+ settings_do_extra_check_holder.setOnClickListener {
+ settings_do_extra_check.toggle()
+ config.doExtraCheck = settings_do_extra_check.isChecked
+ }
+ }
+
+ private fun setupAutoplayVideos() {
+ settings_autoplay_videos.isChecked = config.autoplayVideos
+ settings_autoplay_videos_holder.setOnClickListener {
+ settings_autoplay_videos.toggle()
+ config.autoplayVideos = settings_autoplay_videos.isChecked
+ }
+ }
+
+ private fun setupRememberLastVideo() {
+ settings_remember_last_video_position.isChecked = config.rememberLastVideoPosition
+ settings_remember_last_video_position_holder.setOnClickListener {
+ settings_remember_last_video_position.toggle()
+ config.rememberLastVideoPosition = settings_remember_last_video_position.isChecked
+ }
+ }
+
+ private fun setupLoopVideos() {
+ settings_loop_videos.isChecked = config.loopVideos
+ settings_loop_videos_holder.setOnClickListener {
+ settings_loop_videos.toggle()
+ config.loopVideos = settings_loop_videos.isChecked
+ }
+ }
+
+ private fun setupOpenVideosOnSeparateScreen() {
+ settings_open_videos_on_separate_screen.isChecked = config.openVideosOnSeparateScreen
+ settings_open_videos_on_separate_screen_holder.setOnClickListener {
+ settings_open_videos_on_separate_screen.toggle()
+ config.openVideosOnSeparateScreen = settings_open_videos_on_separate_screen.isChecked
+ }
+ }
+
+ private fun setupAnimateGifs() {
+ settings_animate_gifs.isChecked = config.animateGifs
+ settings_animate_gifs_holder.setOnClickListener {
+ settings_animate_gifs.toggle()
+ config.animateGifs = settings_animate_gifs.isChecked
+ }
+ }
+
+ private fun setupMaxBrightness() {
+ settings_max_brightness.isChecked = config.maxBrightness
+ settings_max_brightness_holder.setOnClickListener {
+ settings_max_brightness.toggle()
+ config.maxBrightness = settings_max_brightness.isChecked
+ }
+ }
+
+ private fun setupCropThumbnails() {
+ settings_crop_thumbnails.isChecked = config.cropThumbnails
+ settings_crop_thumbnails_holder.setOnClickListener {
+ settings_crop_thumbnails.toggle()
+ config.cropThumbnails = settings_crop_thumbnails.isChecked
+ }
+ }
+
+ private fun setupThumbnailVideoDuration() {
+ settings_show_thumbnail_video_duration.isChecked = config.showThumbnailVideoDuration
+ settings_show_thumbnail_video_duration_holder.setOnClickListener {
+ settings_show_thumbnail_video_duration.toggle()
+ config.showThumbnailVideoDuration = settings_show_thumbnail_video_duration.isChecked
+ }
+ }
+
+ private fun setupDarkBackground() {
+ settings_black_background.isChecked = config.blackBackground
+ settings_black_background_holder.setOnClickListener {
+ settings_black_background.toggle()
+ config.blackBackground = settings_black_background.isChecked
+ }
+ }
+
+ private fun setupScrollHorizontally() {
+ settings_scroll_horizontally.isChecked = config.scrollHorizontally
+ settings_scroll_horizontally_holder.setOnClickListener {
+ settings_scroll_horizontally.toggle()
+ config.scrollHorizontally = settings_scroll_horizontally.isChecked
+
+ if (config.scrollHorizontally) {
+ config.enablePullToRefresh = false
+ settings_enable_pull_to_refresh.isChecked = false
+ }
+ }
+ }
+
+ private fun setupHideSystemUI() {
+ settings_hide_system_ui.isChecked = config.hideSystemUI
+ settings_hide_system_ui_holder.setOnClickListener {
+ settings_hide_system_ui.toggle()
+ config.hideSystemUI = settings_hide_system_ui.isChecked
+ }
+ }
+
+ private fun setupHiddenItemPasswordProtection() {
+ settings_hidden_item_password_protection.isChecked = config.isHiddenPasswordProtectionOn
+ settings_hidden_item_password_protection_holder.setOnClickListener {
+ val tabToShow = if (config.isHiddenPasswordProtectionOn) config.hiddenProtectionType else SHOW_ALL_TABS
+ SecurityDialog(this, config.hiddenPasswordHash, tabToShow) { hash, type, success ->
+ if (success) {
+ val hasPasswordProtection = config.isHiddenPasswordProtectionOn
+ settings_hidden_item_password_protection.isChecked = !hasPasswordProtection
+ config.isHiddenPasswordProtectionOn = !hasPasswordProtection
+ config.hiddenPasswordHash = if (hasPasswordProtection) "" else hash
+ config.hiddenProtectionType = type
+
+ if (config.isHiddenPasswordProtectionOn) {
+ val confirmationTextId = if (config.hiddenProtectionType == PROTECTION_FINGERPRINT)
+ R.string.fingerprint_setup_successfully else R.string.protection_setup_successfully
+ ConfirmationDialog(this, "", confirmationTextId, R.string.ok, 0) { }
+ }
+ }
+ }
+ }
+ }
+
+ private fun setupAppPasswordProtection() {
+ settings_app_password_protection.isChecked = config.isAppPasswordProtectionOn
+ settings_app_password_protection_holder.setOnClickListener {
+ val tabToShow = if (config.isAppPasswordProtectionOn) config.appProtectionType else SHOW_ALL_TABS
+ SecurityDialog(this, config.appPasswordHash, tabToShow) { hash, type, success ->
+ if (success) {
+ val hasPasswordProtection = config.isAppPasswordProtectionOn
+ settings_app_password_protection.isChecked = !hasPasswordProtection
+ config.isAppPasswordProtectionOn = !hasPasswordProtection
+ config.appPasswordHash = if (hasPasswordProtection) "" else hash
+ config.appProtectionType = type
+
+ if (config.isAppPasswordProtectionOn) {
+ val confirmationTextId = if (config.appProtectionType == PROTECTION_FINGERPRINT)
+ R.string.fingerprint_setup_successfully else R.string.protection_setup_successfully
+ ConfirmationDialog(this, "", confirmationTextId, R.string.ok, 0) { }
+ }
+ }
+ }
+ }
+ }
+
+ private fun setupFileDeletionPasswordProtection() {
+ settings_file_deletion_password_protection.isChecked = config.isDeletePasswordProtectionOn
+ settings_file_deletion_password_protection_holder.setOnClickListener {
+ val tabToShow = if (config.isDeletePasswordProtectionOn) config.deleteProtectionType else SHOW_ALL_TABS
+ SecurityDialog(this, config.deletePasswordHash, tabToShow) { hash, type, success ->
+ if (success) {
+ val hasPasswordProtection = config.isDeletePasswordProtectionOn
+ settings_file_deletion_password_protection.isChecked = !hasPasswordProtection
+ config.isDeletePasswordProtectionOn = !hasPasswordProtection
+ config.deletePasswordHash = if (hasPasswordProtection) "" else hash
+ config.deleteProtectionType = type
+
+ if (config.isDeletePasswordProtectionOn) {
+ val confirmationTextId = if (config.deleteProtectionType == PROTECTION_FINGERPRINT)
+ R.string.fingerprint_setup_successfully else R.string.protection_setup_successfully
+ ConfirmationDialog(this, "", confirmationTextId, R.string.ok, 0) { }
+ }
+ }
+ }
+ }
+ }
+
+ private fun setupDeleteEmptyFolders() {
+ settings_delete_empty_folders.isChecked = config.deleteEmptyFolders
+ settings_delete_empty_folders_holder.setOnClickListener {
+ settings_delete_empty_folders.toggle()
+ config.deleteEmptyFolders = settings_delete_empty_folders.isChecked
+ }
+ }
+
+ private fun setupAllowPhotoGestures() {
+ settings_allow_photo_gestures.isChecked = config.allowPhotoGestures
+ settings_allow_photo_gestures_holder.setOnClickListener {
+ settings_allow_photo_gestures.toggle()
+ config.allowPhotoGestures = settings_allow_photo_gestures.isChecked
+ }
+ }
+
+ private fun setupAllowVideoGestures() {
+ settings_allow_video_gestures.isChecked = config.allowVideoGestures
+ settings_allow_video_gestures_holder.setOnClickListener {
+ settings_allow_video_gestures.toggle()
+ config.allowVideoGestures = settings_allow_video_gestures.isChecked
+ }
+ }
+
+ private fun setupAllowDownGesture() {
+ settings_allow_down_gesture.isChecked = config.allowDownGesture
+ settings_allow_down_gesture_holder.setOnClickListener {
+ settings_allow_down_gesture.toggle()
+ config.allowDownGesture = settings_allow_down_gesture.isChecked
+ }
+ }
+
+ private fun setupShowNotch() {
+ settings_show_notch_holder.beVisibleIf(isPiePlus())
+ settings_show_notch.isChecked = config.showNotch
+ settings_show_notch_holder.setOnClickListener {
+ settings_show_notch.toggle()
+ config.showNotch = settings_show_notch.isChecked
+ }
+ }
+
+ private fun setupShowMediaCount() {
+ settings_show_media_count.isChecked = config.showMediaCount
+ settings_show_media_count_holder.setOnClickListener {
+ settings_show_media_count.toggle()
+ config.showMediaCount = settings_show_media_count.isChecked
+ }
+ }
+
+ private fun setupKeepLastModified() {
+ settings_keep_last_modified.isChecked = config.keepLastModified
+ settings_keep_last_modified_holder.setOnClickListener {
+ settings_keep_last_modified.toggle()
+ config.keepLastModified = settings_keep_last_modified.isChecked
+ }
+ }
+
+ private fun setupShowInfoBubble() {
+ settings_show_info_bubble.isChecked = config.showInfoBubble
+ settings_show_info_bubble_holder.setOnClickListener {
+ settings_show_info_bubble.toggle()
+ config.showInfoBubble = settings_show_info_bubble.isChecked
+ }
+ }
+
+ private fun setupEnablePullToRefresh() {
+ settings_enable_pull_to_refresh.isChecked = config.enablePullToRefresh
+ settings_enable_pull_to_refresh_holder.setOnClickListener {
+ settings_enable_pull_to_refresh.toggle()
+ config.enablePullToRefresh = settings_enable_pull_to_refresh.isChecked
+ }
+ }
+
+ private fun setupAllowZoomingImages() {
+ settings_allow_zooming_images.isChecked = config.allowZoomingImages
+ updateDeepZoomToggleButtons()
+ settings_allow_zooming_images_holder.setOnClickListener {
+ settings_allow_zooming_images.toggle()
+ config.allowZoomingImages = settings_allow_zooming_images.isChecked
+ updateDeepZoomToggleButtons()
+ }
+ }
+
+ private fun updateDeepZoomToggleButtons() {
+ settings_show_highest_quality_holder.beVisibleIf(config.allowZoomingImages)
+ settings_allow_one_to_one_zoom_holder.beVisibleIf(config.allowZoomingImages)
+ }
+
+ private fun setupShowHighestQuality() {
+ settings_show_highest_quality.isChecked = config.showHighestQuality
+ settings_show_highest_quality_holder.setOnClickListener {
+ settings_show_highest_quality.toggle()
+ config.showHighestQuality = settings_show_highest_quality.isChecked
+ }
+ }
+
+ private fun setupAllowOneToOneZoom() {
+ settings_allow_one_to_one_zoom.isChecked = config.allowOneToOneZoom
+ settings_allow_one_to_one_zoom_holder.setOnClickListener {
+ settings_allow_one_to_one_zoom.toggle()
+ config.allowOneToOneZoom = settings_allow_one_to_one_zoom.isChecked
+ }
+ }
+
+ private fun setupAllowInstantChange() {
+ settings_allow_instant_change.isChecked = config.allowInstantChange
+ settings_allow_instant_change_holder.setOnClickListener {
+ settings_allow_instant_change.toggle()
+ config.allowInstantChange = settings_allow_instant_change.isChecked
+ }
+ }
+
+ private fun setupShowExtendedDetails() {
+ settings_show_extended_details.isChecked = config.showExtendedDetails
+ settings_show_extended_details_holder.setOnClickListener {
+ settings_show_extended_details.toggle()
+ config.showExtendedDetails = settings_show_extended_details.isChecked
+ settings_manage_extended_details_holder.beVisibleIf(config.showExtendedDetails)
+ settings_hide_extended_details_holder.beVisibleIf(config.showExtendedDetails)
+ }
+ }
+
+ private fun setupHideExtendedDetails() {
+ settings_hide_extended_details_holder.beVisibleIf(config.showExtendedDetails)
+ settings_hide_extended_details.isChecked = config.hideExtendedDetails
+ settings_hide_extended_details_holder.setOnClickListener {
+ settings_hide_extended_details.toggle()
+ config.hideExtendedDetails = settings_hide_extended_details.isChecked
+ }
+ }
+
+ private fun setupManageExtendedDetails() {
+ settings_manage_extended_details_holder.beVisibleIf(config.showExtendedDetails)
+ settings_manage_extended_details_holder.setOnClickListener {
+ ManageExtendedDetailsDialog(this) {
+ if (config.extendedDetails == 0) {
+ settings_show_extended_details_holder.callOnClick()
+ }
+ }
+ }
+ }
+
+ private fun setupSkipDeleteConfirmation() {
+ settings_skip_delete_confirmation.isChecked = config.skipDeleteConfirmation
+ settings_skip_delete_confirmation_holder.setOnClickListener {
+ settings_skip_delete_confirmation.toggle()
+ config.skipDeleteConfirmation = settings_skip_delete_confirmation.isChecked
+ }
+ }
+
+ private fun setupScreenRotation() {
+ settings_screen_rotation.text = getScreenRotationText()
+ settings_screen_rotation_holder.setOnClickListener {
+ val items = arrayListOf(
+ RadioItem(ROTATE_BY_SYSTEM_SETTING, getString(R.string.screen_rotation_system_setting)),
+ RadioItem(ROTATE_BY_DEVICE_ROTATION, getString(R.string.screen_rotation_device_rotation)),
+ RadioItem(ROTATE_BY_ASPECT_RATIO, getString(R.string.screen_rotation_aspect_ratio)))
+
+ RadioGroupDialog(this@SettingsActivity, items, config.screenRotation) {
+ config.screenRotation = it as Int
+ settings_screen_rotation.text = getScreenRotationText()
+ }
+ }
+ }
+
+ private fun getScreenRotationText() = getString(when (config.screenRotation) {
+ ROTATE_BY_SYSTEM_SETTING -> R.string.screen_rotation_system_setting
+ ROTATE_BY_DEVICE_ROTATION -> R.string.screen_rotation_device_rotation
+ else -> R.string.screen_rotation_aspect_ratio
+ })
+
+ private fun setupBottomActions() {
+ settings_bottom_actions.isChecked = config.bottomActions
+ settings_bottom_actions_holder.setOnClickListener {
+ settings_bottom_actions.toggle()
+ config.bottomActions = settings_bottom_actions.isChecked
+ settings_manage_bottom_actions_holder.beVisibleIf(config.bottomActions)
+ }
+ }
+
+ private fun setupManageBottomActions() {
+ settings_manage_bottom_actions_holder.beVisibleIf(config.bottomActions)
+ settings_manage_bottom_actions_holder.setOnClickListener {
+ ManageBottomActionsDialog(this) {
+ if (config.visibleBottomActions == 0) {
+ settings_bottom_actions_holder.callOnClick()
+ config.bottomActions = false
+ config.visibleBottomActions = DEFAULT_BOTTOM_ACTIONS
+ }
+ }
+ }
+ }
+
+ private fun setupUseRecycleBin() {
+ settings_empty_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
+ settings_show_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
+ settings_show_recycle_bin_last_holder.beVisibleIf(config.useRecycleBin && config.showRecycleBinAtFolders)
+ settings_use_recycle_bin.isChecked = config.useRecycleBin
+ settings_use_recycle_bin_holder.setOnClickListener {
+ settings_use_recycle_bin.toggle()
+ config.useRecycleBin = settings_use_recycle_bin.isChecked
+ settings_empty_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
+ settings_show_recycle_bin_holder.beVisibleIf(config.useRecycleBin)
+ settings_show_recycle_bin_last_holder.beVisibleIf(config.useRecycleBin && config.showRecycleBinAtFolders)
+ }
+ }
+
+ private fun setupShowRecycleBin() {
+ settings_show_recycle_bin.isChecked = config.showRecycleBinAtFolders
+ settings_show_recycle_bin_holder.setOnClickListener {
+ settings_show_recycle_bin.toggle()
+ config.showRecycleBinAtFolders = settings_show_recycle_bin.isChecked
+ settings_show_recycle_bin_last_holder.beVisibleIf(config.useRecycleBin && config.showRecycleBinAtFolders)
+ }
+ }
+
+ private fun setupShowRecycleBinLast() {
+ settings_show_recycle_bin_last.isChecked = config.showRecycleBinLast
+ settings_show_recycle_bin_last_holder.setOnClickListener {
+ settings_show_recycle_bin_last.toggle()
+ config.showRecycleBinLast = settings_show_recycle_bin_last.isChecked
+ if (config.showRecycleBinLast) {
+ config.removePinnedFolders(setOf(RECYCLE_BIN))
+ }
+ }
+ }
+
+ private fun setupEmptyRecycleBin() {
+ Thread {
+ mRecycleBinContentSize = galleryDB.MediumDao().getDeletedMedia().sumByLong { it.size }
+ runOnUiThread {
+ settings_empty_recycle_bin_size.text = mRecycleBinContentSize.formatSize()
+ }
+ }.start()
+
+ settings_empty_recycle_bin_holder.setOnClickListener {
+ if (mRecycleBinContentSize == 0L) {
+ toast(R.string.recycle_bin_empty)
+ } else {
+ showRecycleBinEmptyingDialog {
+ emptyTheRecycleBin()
+ mRecycleBinContentSize = 0L
+ settings_empty_recycle_bin_size.text = 0L.formatSize()
+ }
+ }
+ }
+ }
+
+ private fun setupExportSettings() {
+ settings_export_holder.setOnClickListener {
+ val configItems = LinkedHashMap().apply {
+ put(IS_USING_SHARED_THEME, config.isUsingSharedTheme)
+ put(TEXT_COLOR, config.textColor)
+ put(BACKGROUND_COLOR, config.backgroundColor)
+ put(PRIMARY_COLOR, config.primaryColor)
+ put(APP_ICON_COLOR, config.appIconColor)
+ put(USE_ENGLISH, config.useEnglish)
+ put(WAS_USE_ENGLISH_TOGGLED, config.wasUseEnglishToggled)
+ put(INCLUDED_FOLDERS, TextUtils.join(",", config.includedFolders))
+ put(EXCLUDED_FOLDERS, TextUtils.join(",", config.excludedFolders))
+ put(SHOW_HIDDEN_MEDIA, config.showHiddenMedia)
+ put(DO_EXTRA_CHECK, config.doExtraCheck)
+ put(AUTOPLAY_VIDEOS, config.autoplayVideos)
+ put(REMEMBER_LAST_VIDEO_POSITION, config.rememberLastVideoPosition)
+ put(LAST_VIDEO_PATH, config.lastVideoPath)
+ put(LOOP_VIDEOS, config.loopVideos)
+ put(OPEN_VIDEOS_ON_SEPARATE_SCREEN, config.openVideosOnSeparateScreen)
+ put(ALLOW_VIDEO_GESTURES, config.allowVideoGestures)
+ put(ANIMATE_GIFS, config.animateGifs)
+ put(CROP_THUMBNAILS, config.cropThumbnails)
+ put(SHOW_THUMBNAIL_VIDEO_DURATION, config.showThumbnailVideoDuration)
+ put(SHOW_MEDIA_COUNT, config.showMediaCount)
+ put(SHOW_INFO_BUBBLE, config.showInfoBubble)
+ put(SCROLL_HORIZONTALLY, config.scrollHorizontally)
+ put(ENABLE_PULL_TO_REFRESH, config.enablePullToRefresh)
+ put(MAX_BRIGHTNESS, config.maxBrightness)
+ put(BLACK_BACKGROUND, config.blackBackground)
+ put(HIDE_SYSTEM_UI, config.hideSystemUI)
+ put(ALLOW_INSTANT_CHANGE, config.allowInstantChange)
+ put(ALLOW_PHOTO_GESTURES, config.allowPhotoGestures)
+ put(ALLOW_DOWN_GESTURE, config.allowDownGesture)
+ put(SHOW_NOTCH, config.showNotch)
+ put(SCREEN_ROTATION, config.screenRotation)
+ put(ALLOW_ZOOMING_IMAGES, config.allowZoomingImages)
+ put(SHOW_HIGHEST_QUALITY, config.showHighestQuality)
+ put(ALLOW_ONE_TO_ONE_ZOOM, config.allowOneToOneZoom)
+ put(SHOW_EXTENDED_DETAILS, config.showExtendedDetails)
+ put(HIDE_EXTENDED_DETAILS, config.hideExtendedDetails)
+ put(EXTENDED_DETAILS, config.extendedDetails)
+ put(DELETE_EMPTY_FOLDERS, config.deleteEmptyFolders)
+ put(KEEP_LAST_MODIFIED, config.keepLastModified)
+ put(SKIP_DELETE_CONFIRMATION, config.skipDeleteConfirmation)
+ put(BOTTOM_ACTIONS, config.bottomActions)
+ put(VISIBLE_BOTTOM_ACTIONS, config.visibleBottomActions)
+ put(USE_RECYCLE_BIN, config.useRecycleBin)
+ put(SHOW_RECYCLE_BIN_AT_FOLDERS, config.showRecycleBinAtFolders)
+ put(SHOW_RECYCLE_BIN_LAST, config.showRecycleBinLast)
+ put(SORT_ORDER, config.sorting)
+ put(DIRECTORY_SORT_ORDER, config.directorySorting)
+ put(GROUP_BY, config.groupBy)
+ put(GROUP_DIRECT_SUBFOLDERS, config.groupDirectSubfolders)
+ put(PINNED_FOLDERS, TextUtils.join(",", config.pinnedFolders))
+ put(DISPLAY_FILE_NAMES, config.displayFileNames)
+ put(FILTER_MEDIA, config.filterMedia)
+ put(DIR_COLUMN_CNT, config.dirColumnCnt)
+ put(MEDIA_COLUMN_CNT, config.mediaColumnCnt)
+ put(SHOW_ALL, config.showAll)
+ put(SHOW_WIDGET_FOLDER_NAME, config.showWidgetFolderName)
+ put(WIDGET_BG_COLOR, config.widgetBgColor)
+ put(WIDGET_TEXT_COLOR, config.widgetTextColor)
+ put(VIEW_TYPE_FILES, config.viewTypeFiles)
+ put(VIEW_TYPE_FOLDERS, config.viewTypeFolders)
+ put(SLIDESHOW_INTERVAL, config.slideshowInterval)
+ put(SLIDESHOW_INCLUDE_VIDEOS, config.slideshowIncludeVideos)
+ put(SLIDESHOW_INCLUDE_GIFS, config.slideshowIncludeGIFs)
+ put(SLIDESHOW_RANDOM_ORDER, config.slideshowRandomOrder)
+ put(SLIDESHOW_MOVE_BACKWARDS, config.slideshowMoveBackwards)
+ put(SLIDESHOW_LOOP, config.loopSlideshow)
+ put(LAST_EDITOR_CROP_ASPECT_RATIO, config.lastEditorCropAspectRatio)
+ put(LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_X, config.lastEditorCropOtherAspectRatioX)
+ put(LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_Y, config.lastEditorCropOtherAspectRatioY)
+ put(LAST_EDITOR_DRAW_COLOR, config.lastEditorDrawColor)
+ put(LAST_EDITOR_BRUSH_SIZE, config.lastEditorBrushSize)
+ put(LAST_CONFLICT_RESOLUTION, config.lastConflictResolution)
+ put(LAST_CONFLICT_APPLY_TO_ALL, config.lastConflictApplyToAll)
+ }
+
+ exportSettings(configItems, "gallery-settings.txt")
+ }
+ }
+
+ private fun setupImportSettings() {
+ settings_import_holder.setOnClickListener {
+ FilePickerDialog(this) {
+ Thread {
+ try {
+ parseFile(it)
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+ }.start()
+ }
+ }
+ }
+
+ private fun parseFile(path: String) {
+ val inputStream = File(path).inputStream()
+ var importedItems = 0
+ val configValues = LinkedHashMap()
+ inputStream.bufferedReader().use {
+ while (true) {
+ try {
+ val line = it.readLine() ?: break
+ val split = line.split("=".toRegex(), 2)
+ if (split.size == 2) {
+ configValues[split[0]] = split[1]
+ }
+ importedItems++
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+ }
+ }
+
+ for ((key, value) in configValues) {
+ when (key) {
+ IS_USING_SHARED_THEME -> config.isUsingSharedTheme = value.toBoolean()
+ TEXT_COLOR -> config.textColor = value.toInt()
+ BACKGROUND_COLOR -> config.backgroundColor = value.toInt()
+ PRIMARY_COLOR -> config.primaryColor = value.toInt()
+ APP_ICON_COLOR -> {
+ if (getAppIconColors().contains(value.toInt())) {
+ config.appIconColor = value.toInt()
+ checkAppIconColor()
+ }
+ }
+ USE_ENGLISH -> config.useEnglish = value.toBoolean()
+ WAS_USE_ENGLISH_TOGGLED -> config.wasUseEnglishToggled = value.toBoolean()
+ INCLUDED_FOLDERS -> config.addIncludedFolders(value.toStringSet())
+ EXCLUDED_FOLDERS -> config.addExcludedFolders(value.toStringSet())
+ SHOW_HIDDEN_MEDIA -> config.showHiddenMedia = value.toBoolean()
+ DO_EXTRA_CHECK -> config.doExtraCheck = value.toBoolean()
+ AUTOPLAY_VIDEOS -> config.autoplayVideos = value.toBoolean()
+ REMEMBER_LAST_VIDEO_POSITION -> config.rememberLastVideoPosition = value.toBoolean()
+ LAST_VIDEO_PATH -> config.lastVideoPath = value.toString()
+ LOOP_VIDEOS -> config.loopVideos = value.toBoolean()
+ OPEN_VIDEOS_ON_SEPARATE_SCREEN -> config.openVideosOnSeparateScreen = value.toBoolean()
+ ALLOW_VIDEO_GESTURES -> config.allowVideoGestures = value.toBoolean()
+ ANIMATE_GIFS -> config.animateGifs = value.toBoolean()
+ CROP_THUMBNAILS -> config.cropThumbnails = value.toBoolean()
+ SHOW_THUMBNAIL_VIDEO_DURATION -> config.showThumbnailVideoDuration = value.toBoolean()
+ SHOW_MEDIA_COUNT -> config.showMediaCount = value.toBoolean()
+ SHOW_INFO_BUBBLE -> config.showInfoBubble = value.toBoolean()
+ SCROLL_HORIZONTALLY -> config.scrollHorizontally = value.toBoolean()
+ ENABLE_PULL_TO_REFRESH -> config.enablePullToRefresh = value.toBoolean()
+ MAX_BRIGHTNESS -> config.maxBrightness = value.toBoolean()
+ BLACK_BACKGROUND -> config.blackBackground = value.toBoolean()
+ HIDE_SYSTEM_UI -> config.hideSystemUI = value.toBoolean()
+ ALLOW_INSTANT_CHANGE -> config.allowInstantChange = value.toBoolean()
+ ALLOW_PHOTO_GESTURES -> config.allowPhotoGestures = value.toBoolean()
+ ALLOW_DOWN_GESTURE -> config.allowDownGesture = value.toBoolean()
+ SHOW_NOTCH -> config.showNotch = value.toBoolean()
+ SCREEN_ROTATION -> config.screenRotation = value.toInt()
+ ALLOW_ZOOMING_IMAGES -> config.allowZoomingImages = value.toBoolean()
+ SHOW_HIGHEST_QUALITY -> config.showHighestQuality = value.toBoolean()
+ ALLOW_ONE_TO_ONE_ZOOM -> config.allowOneToOneZoom = value.toBoolean()
+ SHOW_EXTENDED_DETAILS -> config.showExtendedDetails = value.toBoolean()
+ HIDE_EXTENDED_DETAILS -> config.hideExtendedDetails = value.toBoolean()
+ EXTENDED_DETAILS -> config.extendedDetails = value.toInt()
+ DELETE_EMPTY_FOLDERS -> config.deleteEmptyFolders = value.toBoolean()
+ KEEP_LAST_MODIFIED -> config.keepLastModified = value.toBoolean()
+ SKIP_DELETE_CONFIRMATION -> config.skipDeleteConfirmation = value.toBoolean()
+ BOTTOM_ACTIONS -> config.bottomActions = value.toBoolean()
+ VISIBLE_BOTTOM_ACTIONS -> config.visibleBottomActions = value.toInt()
+ USE_RECYCLE_BIN -> config.useRecycleBin = value.toBoolean()
+ SHOW_RECYCLE_BIN_AT_FOLDERS -> config.showRecycleBinAtFolders = value.toBoolean()
+ SHOW_RECYCLE_BIN_LAST -> config.showRecycleBinLast = value.toBoolean()
+ SORT_ORDER -> config.sorting = value.toInt()
+ DIRECTORY_SORT_ORDER -> config.directorySorting = value.toInt()
+ GROUP_BY -> config.groupBy = value.toInt()
+ GROUP_DIRECT_SUBFOLDERS -> config.groupDirectSubfolders = value.toBoolean()
+ PINNED_FOLDERS -> config.addPinnedFolders(value.toStringSet())
+ DISPLAY_FILE_NAMES -> config.displayFileNames = value.toBoolean()
+ FILTER_MEDIA -> config.filterMedia = value.toInt()
+ DIR_COLUMN_CNT -> config.dirColumnCnt = value.toInt()
+ MEDIA_COLUMN_CNT -> config.mediaColumnCnt = value.toInt()
+ SHOW_ALL -> config.showAll = value.toBoolean()
+ SHOW_WIDGET_FOLDER_NAME -> config.showWidgetFolderName = value.toBoolean()
+ WIDGET_BG_COLOR -> config.widgetBgColor = value.toInt()
+ WIDGET_TEXT_COLOR -> config.widgetTextColor = value.toInt()
+ VIEW_TYPE_FILES -> config.viewTypeFiles = value.toInt()
+ VIEW_TYPE_FOLDERS -> config.viewTypeFolders = value.toInt()
+ SLIDESHOW_INTERVAL -> config.slideshowInterval = value.toInt()
+ SLIDESHOW_INCLUDE_VIDEOS -> config.slideshowIncludeVideos = value.toBoolean()
+ SLIDESHOW_INCLUDE_GIFS -> config.slideshowIncludeGIFs = value.toBoolean()
+ SLIDESHOW_RANDOM_ORDER -> config.slideshowRandomOrder = value.toBoolean()
+ SLIDESHOW_MOVE_BACKWARDS -> config.slideshowMoveBackwards = value.toBoolean()
+ SLIDESHOW_LOOP -> config.loopSlideshow = value.toBoolean()
+ LAST_EDITOR_CROP_ASPECT_RATIO -> config.lastEditorCropAspectRatio = value.toInt()
+ LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_X -> config.lastEditorCropOtherAspectRatioX = value.toInt()
+ LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_Y -> config.lastEditorCropOtherAspectRatioY = value.toInt()
+ LAST_EDITOR_DRAW_COLOR -> config.lastEditorDrawColor = value.toInt()
+ LAST_EDITOR_BRUSH_SIZE -> config.lastEditorBrushSize = value.toInt()
+ LAST_CONFLICT_RESOLUTION -> config.lastConflictResolution = value.toInt()
+ LAST_CONFLICT_APPLY_TO_ALL -> config.lastConflictApplyToAll = value.toBoolean()
+ }
+ }
+
+ toast(if (configValues.size > 0) R.string.settings_imported_successfully else R.string.no_entries_for_importing)
+ runOnUiThread {
+ setupSettingItems()
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SimpleActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SimpleActivity.kt
similarity index 54%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/SimpleActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SimpleActivity.kt
index 627ff7da9..8d0017ff8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SimpleActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SimpleActivity.kt
@@ -1,7 +1,11 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
+import android.annotation.SuppressLint
+import android.view.WindowManager
import com.simplemobiletools.commons.activities.BaseSimpleActivity
-import com.simplemobiletools.gallery.R
+import com.simplemobiletools.commons.helpers.isPiePlus
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
open class SimpleActivity : BaseSimpleActivity() {
override fun getAppIconIDs() = arrayListOf(
@@ -27,4 +31,19 @@ open class SimpleActivity : BaseSimpleActivity() {
)
override fun getAppLauncherName() = getString(R.string.app_launcher_name)
+
+ @SuppressLint("InlinedApi")
+ protected fun checkNotchSupport() {
+ if (isPiePlus()) {
+ val cutoutMode = when {
+ config.showNotch -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+ else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+ }
+
+ window.attributes.layoutInDisplayCutoutMode = cutoutMode
+ if (config.showNotch) {
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+ }
+ }
+ }
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SplashActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SplashActivity.kt
similarity index 73%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/SplashActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SplashActivity.kt
index a0d0c7acd..611cfe083 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/SplashActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/SplashActivity.kt
@@ -1,9 +1,11 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.content.Intent
import com.simplemobiletools.commons.activities.BaseSplashActivity
class SplashActivity : BaseSplashActivity() {
+ override fun getAppPackageName() = "-1"
+
override fun initActivity() {
startActivity(Intent(this, MainActivity::class.java))
finish()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/VideoActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/VideoActivity.kt
similarity index 79%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/VideoActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/VideoActivity.kt
index 7ca92e902..78fa61955 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/VideoActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/VideoActivity.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.os.Bundle
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/VideoPlayerActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/VideoPlayerActivity.kt
new file mode 100644
index 000000000..4578e906e
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/VideoPlayerActivity.kt
@@ -0,0 +1,616 @@
+package com.simplemobiletools.gallery.pro.activities
+
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.SurfaceTexture
+import android.graphics.drawable.ColorDrawable
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.util.DisplayMetrics
+import android.view.*
+import android.widget.SeekBar
+import com.google.android.exoplayer2.*
+import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
+import com.google.android.exoplayer2.source.ExtractorMediaSource
+import com.google.android.exoplayer2.source.TrackGroupArray
+import com.google.android.exoplayer2.trackselection.TrackSelectionArray
+import com.google.android.exoplayer2.upstream.ContentDataSource
+import com.google.android.exoplayer2.upstream.DataSource
+import com.google.android.exoplayer2.upstream.DataSpec
+import com.google.android.exoplayer2.video.VideoListener
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.*
+import kotlinx.android.synthetic.main.activity_video_player.*
+import kotlinx.android.synthetic.main.bottom_video_time_holder.*
+
+open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener {
+ private val PLAY_WHEN_READY_DRAG_DELAY = 100L
+
+ private var mIsFullscreen = false
+ private var mIsPlaying = false
+ private var mWasVideoStarted = false
+ private var mIsDragged = false
+ private var mIsOrientationLocked = false
+ private var mScreenWidth = 0
+ private var mCurrTime = 0
+ private var mDuration = 0
+ private var mDragThreshold = 0f
+ private var mTouchDownX = 0f
+ private var mTouchDownY = 0f
+ private var mTouchDownTime = 0L
+ private var mProgressAtDown = 0L
+ private var mCloseDownThreshold = 100f
+
+ private var mUri: Uri? = null
+ private var mExoPlayer: SimpleExoPlayer? = null
+ private var mVideoSize = Point(0, 0)
+ private var mTimerHandler = Handler()
+ private var mPlayWhenReadyHandler = Handler()
+
+ private var mIgnoreCloseDown = false
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_video_player)
+ setupOrientation()
+ checkNotchSupport()
+
+ handlePermission(PERMISSION_WRITE_STORAGE) {
+ if (it) {
+ initPlayer()
+ } else {
+ toast(R.string.no_storage_permissions)
+ finish()
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ top_shadow.layoutParams.height = statusBarHeight + actionBarHeight
+ supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.statusBarColor = Color.TRANSPARENT
+ window.navigationBarColor = Color.TRANSPARENT
+ if (config.blackBackground) {
+ video_player_holder.background = ColorDrawable(Color.BLACK)
+ }
+
+ if (config.maxBrightness) {
+ val attributes = window.attributes
+ attributes.screenBrightness = 1f
+ window.attributes = attributes
+ }
+
+ updateTextColors(video_player_holder)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ pauseVideo()
+
+ if (config.rememberLastVideoPosition && mWasVideoStarted) {
+ saveVideoProgress()
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ if (!isChangingConfigurations) {
+ pauseVideo()
+ video_curr_time.text = 0.getFormattedDuration()
+ releaseExoPlayer()
+ video_seekbar.progress = 0
+ mTimerHandler.removeCallbacksAndMessages(null)
+ mPlayWhenReadyHandler.removeCallbacksAndMessages(null)
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_video_player, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.menu_change_orientation -> changeOrientation()
+ R.id.menu_open_with -> openPath(mUri!!.toString(), true)
+ R.id.menu_share -> shareMediumPath(mUri!!.toString())
+ else -> return super.onOptionsItemSelected(item)
+ }
+ return true
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ setVideoSize()
+ initTimeHolder()
+ video_surface_frame.onGlobalLayout {
+ video_surface_frame.controller.resetState()
+ }
+ }
+
+ private fun setupOrientation() {
+ if (!mIsOrientationLocked) {
+ if (config.screenRotation == ROTATE_BY_DEVICE_ROTATION) {
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
+ } else if (config.screenRotation == ROTATE_BY_SYSTEM_SETTING) {
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ }
+ }
+ }
+
+ private fun initPlayer() {
+ mUri = intent.data ?: return
+ supportActionBar?.title = getFilenameFromUri(mUri!!)
+ initTimeHolder()
+
+ showSystemUI(true)
+ window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
+ val isFullscreen = visibility and View.SYSTEM_UI_FLAG_FULLSCREEN != 0
+ fullscreenToggled(isFullscreen)
+ }
+
+ video_curr_time.setOnClickListener { skip(false) }
+ video_duration.setOnClickListener { skip(true) }
+ video_toggle_play_pause.setOnClickListener { togglePlayPause() }
+ video_surface_frame.setOnClickListener { toggleFullscreen() }
+
+ video_next_file.beVisibleIf(intent.getBooleanExtra(SHOW_NEXT_ITEM, false))
+ video_next_file.setOnClickListener { handleNextFile() }
+
+ video_prev_file.beVisibleIf(intent.getBooleanExtra(SHOW_PREV_ITEM, false))
+ video_prev_file.setOnClickListener { handlePrevFile() }
+
+ video_surface_frame.setOnTouchListener { view, event ->
+ handleEvent(event)
+ false
+ }
+
+ initExoPlayer()
+ video_surface.surfaceTextureListener = this
+
+ if (config.allowVideoGestures) {
+ video_brightness_controller.initialize(this, slide_info, true, video_player_holder) { x, y ->
+ toggleFullscreen()
+ }
+
+ video_volume_controller.initialize(this, slide_info, false, video_player_holder) { x, y ->
+ toggleFullscreen()
+ }
+ } else {
+ video_brightness_controller.beGone()
+ video_volume_controller.beGone()
+ }
+
+ if (config.hideSystemUI) {
+ Handler().postDelayed({
+ fullscreenToggled(true)
+ }, HIDE_SYSTEM_UI_DELAY)
+ }
+
+ mDragThreshold = DRAG_THRESHOLD * resources.displayMetrics.density
+ }
+
+ private fun initExoPlayer() {
+ val dataSpec = DataSpec(mUri)
+ val fileDataSource = ContentDataSource(applicationContext)
+ try {
+ fileDataSource.open(dataSpec)
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+
+ val factory = DataSource.Factory { fileDataSource }
+ val audioSource = ExtractorMediaSource(fileDataSource.uri, factory, DefaultExtractorsFactory(), null, null)
+ mExoPlayer = ExoPlayerFactory.newSimpleInstance(applicationContext).apply {
+ seekParameters = SeekParameters.CLOSEST_SYNC
+ audioStreamType = C.STREAM_TYPE_MUSIC
+ prepare(audioSource)
+ }
+ initExoPlayerListeners()
+ }
+
+ private fun initExoPlayerListeners() {
+ mExoPlayer!!.addListener(object : Player.EventListener {
+ override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {}
+
+ override fun onSeekProcessed() {}
+
+ override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
+
+ override fun onPlayerError(error: ExoPlaybackException?) {}
+
+ override fun onLoadingChanged(isLoading: Boolean) {}
+
+ override fun onPositionDiscontinuity(reason: Int) {}
+
+ override fun onRepeatModeChanged(repeatMode: Int) {}
+
+ override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
+
+ override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
+
+ override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
+ when (playbackState) {
+ Player.STATE_READY -> videoPrepared()
+ Player.STATE_ENDED -> videoCompleted()
+ }
+ }
+ })
+
+ mExoPlayer!!.addVideoListener(object : VideoListener {
+ override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
+ mVideoSize.x = width
+ mVideoSize.y = height
+ setVideoSize()
+ }
+
+ override fun onRenderedFirstFrame() {}
+ })
+ }
+
+ private fun videoPrepared() {
+ if (!mWasVideoStarted) {
+ video_toggle_play_pause.beVisible()
+ mDuration = (mExoPlayer!!.duration / 1000).toInt()
+ video_seekbar.max = mDuration
+ video_duration.text = mDuration.getFormattedDuration()
+ setPosition(mCurrTime)
+
+ if (config.rememberLastVideoPosition) {
+ setLastVideoSavedPosition()
+ }
+
+ if (config.autoplayVideos) {
+ resumeVideo()
+ } else {
+ video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
+ }
+ }
+ }
+
+ private fun resumeVideo() {
+ video_toggle_play_pause.setImageResource(R.drawable.ic_pause_outline)
+ if (mExoPlayer == null) {
+ return
+ }
+
+ val wasEnded = didVideoEnd()
+ if (wasEnded) {
+ setPosition(0)
+ }
+
+ mWasVideoStarted = true
+ mIsPlaying = true
+ mExoPlayer?.playWhenReady = true
+ window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ }
+
+ private fun pauseVideo() {
+ video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
+ if (mExoPlayer == null) {
+ return
+ }
+
+ mIsPlaying = false
+ if (!didVideoEnd()) {
+ mExoPlayer?.playWhenReady = false
+ }
+
+ window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ }
+
+ private fun togglePlayPause() {
+ mIsPlaying = !mIsPlaying
+ if (mIsPlaying) {
+ resumeVideo()
+ } else {
+ pauseVideo()
+ }
+ }
+
+ private fun setPosition(seconds: Int) {
+ mExoPlayer?.seekTo(seconds * 1000L)
+ video_seekbar.progress = seconds
+ video_curr_time.text = seconds.getFormattedDuration()
+ }
+
+ private fun setLastVideoSavedPosition() {
+ if (config.lastVideoPath == mUri.toString() && config.lastVideoPosition > 0) {
+ setPosition(config.lastVideoPosition)
+ }
+ }
+
+ private fun videoCompleted() {
+ if (mExoPlayer == null) {
+ return
+ }
+
+ clearLastVideoSavedProgress()
+ mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
+ if (config.loopVideos) {
+ resumeVideo()
+ } else {
+ video_seekbar.progress = video_seekbar.max
+ video_curr_time.text = mDuration.getFormattedDuration()
+ pauseVideo()
+ }
+ }
+
+ private fun didVideoEnd(): Boolean {
+ val currentPos = mExoPlayer?.currentPosition ?: 0
+ val duration = mExoPlayer?.duration ?: 0
+ return currentPos != 0L && currentPos >= duration
+ }
+
+ private fun saveVideoProgress() {
+ if (!didVideoEnd()) {
+ config.apply {
+ lastVideoPosition = mExoPlayer!!.currentPosition.toInt() / 1000
+ lastVideoPath = mUri.toString()
+ }
+ }
+ }
+
+ private fun clearLastVideoSavedProgress() {
+ config.apply {
+ lastVideoPosition = 0
+ lastVideoPath = ""
+ }
+ }
+
+ private fun setVideoSize() {
+ val videoProportion = mVideoSize.x.toFloat() / mVideoSize.y.toFloat()
+ val display = windowManager.defaultDisplay
+ val screenWidth: Int
+ val screenHeight: Int
+
+ val realMetrics = DisplayMetrics()
+ display.getRealMetrics(realMetrics)
+ screenWidth = realMetrics.widthPixels
+ screenHeight = realMetrics.heightPixels
+
+ val screenProportion = screenWidth.toFloat() / screenHeight.toFloat()
+
+ video_surface.layoutParams.apply {
+ if (videoProportion > screenProportion) {
+ width = screenWidth
+ height = (screenWidth.toFloat() / videoProportion).toInt()
+ } else {
+ width = (videoProportion * screenHeight.toFloat()).toInt()
+ height = screenHeight
+ }
+ video_surface.layoutParams = this
+ }
+
+ val multiplier = if (screenWidth > screenHeight) 0.5 else 0.8
+ mScreenWidth = (screenWidth * multiplier).toInt()
+
+ if (config.screenRotation == ROTATE_BY_ASPECT_RATIO) {
+ if (mVideoSize.x > mVideoSize.y) {
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ } else if (mVideoSize.x < mVideoSize.y) {
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ }
+ }
+ }
+
+ private fun changeOrientation() {
+ mIsOrientationLocked = true
+ requestedOrientation = if (resources.configuration.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
+ ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ } else {
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ }
+ }
+
+ private fun toggleFullscreen() {
+ fullscreenToggled(!mIsFullscreen)
+ }
+
+ private fun fullscreenToggled(isFullScreen: Boolean) {
+ mIsFullscreen = isFullScreen
+ if (isFullScreen) {
+ hideSystemUI(true)
+ } else {
+ showSystemUI(true)
+ }
+
+ val newAlpha = if (isFullScreen) 0f else 1f
+ arrayOf(video_prev_file, video_toggle_play_pause, video_next_file, video_curr_time, video_seekbar, video_duration, top_shadow, video_bottom_gradient).forEach {
+ it.animate().alpha(newAlpha).start()
+ }
+ video_seekbar.setOnSeekBarChangeListener(if (mIsFullscreen) null else this)
+ arrayOf(video_prev_file, video_next_file, video_curr_time, video_duration).forEach {
+ it.isClickable = !mIsFullscreen
+ }
+ }
+
+ private fun initTimeHolder() {
+ var right = 0
+ var bottom = 0
+
+ if (hasNavBar()) {
+ if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ bottom += navigationBarHeight
+ } else {
+ right += navigationBarWidth
+ bottom += navigationBarHeight
+ }
+ }
+
+ video_time_holder.setPadding(0, 0, right, bottom)
+ video_seekbar.setOnSeekBarChangeListener(this)
+ video_seekbar.max = mDuration
+ video_duration.text = mDuration.getFormattedDuration()
+ video_curr_time.text = mCurrTime.getFormattedDuration()
+ setupTimer()
+ }
+
+ private fun setupTimer() {
+ runOnUiThread(object : Runnable {
+ override fun run() {
+ if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
+ mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
+ video_seekbar.progress = mCurrTime
+ video_curr_time.text = mCurrTime.getFormattedDuration()
+ }
+
+ mTimerHandler.postDelayed(this, 1000)
+ }
+ })
+ }
+
+ private fun skip(forward: Boolean) {
+ if (mExoPlayer == null) {
+ return
+ }
+
+ val curr = mExoPlayer!!.currentPosition
+ val twoPercents = Math.max((mExoPlayer!!.duration / 50).toInt(), MIN_SKIP_LENGTH)
+ val newProgress = if (forward) curr + twoPercents else curr - twoPercents
+ val roundProgress = Math.round(newProgress / 1000f)
+ val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt(), roundProgress), 0)
+ setPosition(limitedProgress)
+ if (!mIsPlaying) {
+ togglePlayPause()
+ }
+ }
+
+ private fun handleEvent(event: MotionEvent) {
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ mTouchDownX = event.x
+ mTouchDownY = event.y
+ mTouchDownTime = System.currentTimeMillis()
+ mProgressAtDown = mExoPlayer!!.currentPosition
+ }
+ MotionEvent.ACTION_POINTER_DOWN -> mIgnoreCloseDown = true
+ MotionEvent.ACTION_MOVE -> {
+ val diffX = event.x - mTouchDownX
+ val diffY = event.y - mTouchDownY
+
+ if (mIsDragged || (Math.abs(diffX) > mDragThreshold && Math.abs(diffX) > Math.abs(diffY)) && video_surface_frame.controller.state.zoom == 1f) {
+ if (!mIsDragged) {
+ arrayOf(video_curr_time, video_seekbar, video_duration).forEach {
+ it.animate().alpha(1f).start()
+ }
+ }
+ mIgnoreCloseDown = true
+ mIsDragged = true
+ var percent = ((diffX / mScreenWidth) * 100).toInt()
+ percent = Math.min(100, Math.max(-100, percent))
+
+ val skipLength = (mDuration * 1000f) * (percent / 100f)
+ var newProgress = mProgressAtDown + skipLength
+ newProgress = Math.max(Math.min(mExoPlayer!!.duration.toFloat(), newProgress), 0f)
+ val newSeconds = (newProgress / 1000).toInt()
+ setPosition(newSeconds)
+ resetPlayWhenReady()
+ }
+ }
+ MotionEvent.ACTION_UP -> {
+ val diffX = mTouchDownX - event.x
+ val diffY = mTouchDownY - event.y
+
+ val downGestureDuration = System.currentTimeMillis() - mTouchDownTime
+ if (config.allowDownGesture && !mIgnoreCloseDown && Math.abs(diffY) > Math.abs(diffX) && diffY < -mCloseDownThreshold &&
+ downGestureDuration < MAX_CLOSE_DOWN_GESTURE_DURATION &&
+ video_surface_frame.controller.state.zoom == 1f) {
+ supportFinishAfterTransition()
+ }
+
+ mIgnoreCloseDown = false
+ if (mIsDragged) {
+ if (mIsFullscreen) {
+ arrayOf(video_curr_time, video_seekbar, video_duration).forEach {
+ it.animate().alpha(0f).start()
+ }
+ }
+
+ if (!mIsPlaying) {
+ togglePlayPause()
+ }
+ }
+ mIsDragged = false
+ }
+ }
+ }
+
+ private fun handleNextFile() {
+ Intent().apply {
+ putExtra(GO_TO_NEXT_ITEM, true)
+ setResult(Activity.RESULT_OK, this)
+ }
+ finish()
+ }
+
+ private fun handlePrevFile() {
+ Intent().apply {
+ putExtra(GO_TO_PREV_ITEM, true)
+ setResult(Activity.RESULT_OK, this)
+ }
+ finish()
+ }
+
+ private fun resetPlayWhenReady() {
+ mExoPlayer?.playWhenReady = false
+ mPlayWhenReadyHandler.removeCallbacksAndMessages(null)
+ mPlayWhenReadyHandler.postDelayed({
+ mExoPlayer?.playWhenReady = true
+ }, PLAY_WHEN_READY_DRAG_DELAY)
+ }
+
+ private fun releaseExoPlayer() {
+ mExoPlayer?.stop()
+ Thread {
+ mExoPlayer?.release()
+ mExoPlayer = null
+ }.start()
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ if (mExoPlayer != null && fromUser) {
+ setPosition(progress)
+ resetPlayWhenReady()
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {
+ mIsDragged = true
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {
+ if (mExoPlayer == null)
+ return
+
+ if (mIsPlaying) {
+ mExoPlayer!!.playWhenReady = true
+ } else {
+ togglePlayPause()
+ }
+
+ mIsDragged = false
+ }
+
+ override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
+ }
+
+ override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?) = false
+
+ override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
+ Thread {
+ mExoPlayer?.setVideoSurface(Surface(video_surface!!.surfaceTexture))
+ }.start()
+ }
+
+ override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {}
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/ViewPagerActivity.kt
similarity index 79%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/ViewPagerActivity.kt
index 644db9dde..0ea59f08f 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/activities/ViewPagerActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/ViewPagerActivity.kt
@@ -1,67 +1,59 @@
-package com.simplemobiletools.gallery.activities
+package com.simplemobiletools.gallery.pro.activities
import android.animation.Animator
import android.animation.ValueAnimator
-import android.annotation.SuppressLint
-import android.annotation.TargetApi
import android.app.Activity
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.database.Cursor
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
import android.graphics.Color
-import android.graphics.Matrix
import android.graphics.drawable.ColorDrawable
import android.media.ExifInterface
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.provider.MediaStore
-import android.support.v4.view.ViewPager
-import android.util.DisplayMetrics
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.WindowManager
import android.view.animation.DecelerateInterpolator
-import com.bumptech.glide.Glide
+import android.widget.Toast
+import androidx.viewpager.widget.ViewPager
import com.simplemobiletools.commons.dialogs.PropertiesDialog
import com.simplemobiletools.commons.dialogs.RenameItemDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FileDirItem
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.MyPagerAdapter
-import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
-import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog
-import com.simplemobiletools.gallery.dialogs.SaveAsDialog
-import com.simplemobiletools.gallery.dialogs.SlideshowDialog
-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.*
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.BuildConfig
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.MyPagerAdapter
+import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
+import com.simplemobiletools.gallery.pro.dialogs.DeleteWithRememberDialog
+import com.simplemobiletools.gallery.pro.dialogs.SaveAsDialog
+import com.simplemobiletools.gallery.pro.dialogs.SlideshowDialog
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.fragments.PhotoFragment
+import com.simplemobiletools.gallery.pro.fragments.VideoFragment
+import com.simplemobiletools.gallery.pro.fragments.ViewPagerFragment
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
import kotlinx.android.synthetic.main.activity_medium.*
import kotlinx.android.synthetic.main.bottom_actions.*
import java.io.File
-import java.io.FileOutputStream
-import java.io.InputStream
-import java.io.OutputStream
import java.util.*
class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, ViewPagerFragment.FragmentListener {
+ private val REQUEST_VIEW_VIDEO = 1
+
private var mPath = ""
private var mDirectory = ""
private var mIsFullScreen = false
private var mPos = -1
private var mShowAll = false
private var mIsSlideshowActive = false
- private var mRotationDegrees = 0
private var mPrevHashcode = 0
private var mSlideshowHandler = Handler()
@@ -69,19 +61,18 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private var mSlideshowMoveBackwards = false
private var mSlideshowMedia = mutableListOf()
private var mAreSlideShowMediaVisible = false
+
private var mIsOrientationLocked = false
private var mMediaFiles = ArrayList()
private var mFavoritePaths = ArrayList()
- companion object {
- var screenWidth = 0
- var screenHeight = 0
- }
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_medium)
+
+ top_shadow.layoutParams.height = statusBarHeight + actionBarHeight
+ checkNotchSupport()
(MediaActivity.mMedia.clone() as ArrayList).filter { it is Medium }.mapTo(mMediaFiles) { it as Medium }
handlePermission(PERMISSION_WRITE_STORAGE) {
@@ -96,7 +87,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
initFavorites()
}
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun onResume() {
super.onResume()
if (!hasPermission(PERMISSION_WRITE_STORAGE)) {
@@ -105,15 +95,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
if (config.bottomActions) {
- if (isLollipopPlus()) {
- window.navigationBarColor = Color.TRANSPARENT
- }
+ window.navigationBarColor = Color.TRANSPARENT
} else {
setTranslucentNavigation()
}
initBottomActions()
- supportActionBar?.setBackgroundDrawable(resources.getDrawable(R.drawable.actionbar_gradient_background))
if (config.maxBrightness) {
val attributes = window.attributes
@@ -121,12 +108,11 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
window.attributes = attributes
}
- setupRotation()
+ setupOrientation()
invalidateOptionsMenu()
- if (config.blackBackground) {
- updateStatusbarColor(Color.BLACK)
- }
+ supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.statusBarColor = Color.TRANSPARENT
}
override fun onPause() {
@@ -149,8 +135,81 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
}
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_viewpager, menu)
+ val currentMedium = getCurrentMedium() ?: return true
+ currentMedium.isFavorite = mFavoritePaths.contains(currentMedium.path)
+ val visibleBottomActions = if (config.bottomActions) config.visibleBottomActions else 0
+
+ val rotationDegrees = getCurrentPhotoFragment()?.mCurrentRotationDegrees ?: 0
+ menu.apply {
+ findItem(R.id.menu_show_on_map).isVisible = visibleBottomActions and BOTTOM_ACTION_SHOW_ON_MAP == 0
+ findItem(R.id.menu_slideshow).isVisible = visibleBottomActions and BOTTOM_ACTION_SLIDESHOW == 0
+ findItem(R.id.menu_properties).isVisible = visibleBottomActions and BOTTOM_ACTION_PROPERTIES == 0
+ findItem(R.id.menu_delete).isVisible = visibleBottomActions and BOTTOM_ACTION_DELETE == 0
+ findItem(R.id.menu_share).isVisible = visibleBottomActions and BOTTOM_ACTION_SHARE == 0
+ findItem(R.id.menu_edit).isVisible = visibleBottomActions and BOTTOM_ACTION_EDIT == 0 && !currentMedium.isSVG()
+ findItem(R.id.menu_rename).isVisible = visibleBottomActions and BOTTOM_ACTION_RENAME == 0 && !currentMedium.getIsInRecycleBin()
+ findItem(R.id.menu_rotate).isVisible = currentMedium.isImage() && visibleBottomActions and BOTTOM_ACTION_ROTATE == 0
+ findItem(R.id.menu_set_as).isVisible = visibleBottomActions and BOTTOM_ACTION_SET_AS == 0
+ findItem(R.id.menu_copy_to).isVisible = visibleBottomActions and BOTTOM_ACTION_COPY == 0
+ findItem(R.id.menu_save_as).isVisible = rotationDegrees != 0
+ findItem(R.id.menu_hide).isVisible = !currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0 && !currentMedium.getIsInRecycleBin()
+ findItem(R.id.menu_unhide).isVisible = currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0 && !currentMedium.getIsInRecycleBin()
+ findItem(R.id.menu_add_to_favorites).isVisible = !currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0
+ findItem(R.id.menu_remove_from_favorites).isVisible = currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0
+ findItem(R.id.menu_restore_file).isVisible = currentMedium.path.startsWith(recycleBinPath)
+ findItem(R.id.menu_change_orientation).isVisible = rotationDegrees == 0 && visibleBottomActions and BOTTOM_ACTION_CHANGE_ORIENTATION == 0
+ findItem(R.id.menu_change_orientation).icon = resources.getDrawable(getChangeOrientationIcon())
+ findItem(R.id.menu_rotate).setShowAsAction(
+ if (rotationDegrees != 0) {
+ MenuItem.SHOW_AS_ACTION_ALWAYS
+ } else {
+ MenuItem.SHOW_AS_ACTION_IF_ROOM
+ })
+ }
+
+ if (visibleBottomActions != 0) {
+ updateBottomActionIcons(currentMedium)
+ }
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (getCurrentMedium() == null)
+ return true
+
+ when (item.itemId) {
+ R.id.menu_set_as -> setAs(getCurrentPath())
+ R.id.menu_slideshow -> initSlideshow()
+ R.id.menu_copy_to -> copyMoveTo(true)
+ R.id.menu_move_to -> moveFileTo()
+ R.id.menu_open_with -> openPath(getCurrentPath(), true)
+ R.id.menu_hide -> toggleFileVisibility(true)
+ R.id.menu_unhide -> toggleFileVisibility(false)
+ R.id.menu_share -> shareMediumPath(getCurrentPath())
+ R.id.menu_delete -> checkDeleteConfirmation()
+ R.id.menu_rename -> renameFile()
+ R.id.menu_edit -> openEditor(getCurrentPath())
+ R.id.menu_properties -> showProperties()
+ R.id.menu_show_on_map -> showOnMap()
+ R.id.menu_rotate_right -> rotateImage(90)
+ R.id.menu_rotate_left -> rotateImage(-90)
+ R.id.menu_rotate_one_eighty -> rotateImage(180)
+ R.id.menu_add_to_favorites -> toggleFavorite()
+ R.id.menu_remove_from_favorites -> toggleFavorite()
+ R.id.menu_restore_file -> restoreFile()
+ R.id.menu_force_portrait -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ R.id.menu_force_landscape -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+ R.id.menu_default_orientation -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
+ R.id.menu_save_as -> saveImageAs()
+ R.id.menu_settings -> launchSettings()
+ else -> return super.onOptionsItemSelected(item)
+ }
+ return true
+ }
+
private fun initViewPager() {
- measureScreen()
val uri = intent.data
if (uri != null) {
var cursor: Cursor? = null
@@ -191,7 +250,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
if (intent.extras?.containsKey(IS_VIEW_INTENT) == true) {
if (isShowHiddenFlagNeeded()) {
- if (!config.isPasswordProtectionOn) {
+ if (!config.isHiddenPasswordProtectionOn) {
config.temporarilyShowHidden = true
}
}
@@ -214,9 +273,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
supportActionBar?.title = mPath.getFilenameFromPath()
view_pager.onGlobalLayout {
- if (!isActivityDestroyed()) {
+ if (!isDestroyed) {
if (mMediaFiles.isNotEmpty()) {
gotMedia(mMediaFiles as ArrayList)
+ checkSlideshowOnEnter()
}
}
}
@@ -232,7 +292,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
view_pager.onGlobalLayout {
Handler().postDelayed({
fragmentClicked()
- }, 500)
+ }, HIDE_SYSTEM_UI_DELAY)
}
}
@@ -246,8 +306,10 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
view_pager.adapter?.let {
(it as MyPagerAdapter).toggleFullscreen(mIsFullScreen)
checkSystemUI()
- if (!bottom_actions.isGone()) {
- bottom_actions.animate().alpha(if (mIsFullScreen) 0f else 1f).start()
+ val newAlpha = if (mIsFullScreen) 0f else 1f
+ top_shadow.animate().alpha(newAlpha).start()
+ if (bottom_actions.isVisible()) {
+ bottom_actions.animate().alpha(newAlpha).start()
arrayOf(bottom_favorite, bottom_edit, bottom_share, bottom_delete, bottom_rotate, bottom_properties, bottom_change_orientation,
bottom_slideshow, bottom_show_on_map, bottom_toggle_file_visibility, bottom_rename).forEach {
it.isClickable = !mIsFullScreen
@@ -255,6 +317,24 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
}
}
+
+ if (intent.action == "com.android.camera.action.REVIEW") {
+ Thread {
+ if (galleryDB.MediumDao().getMediaFromPath(mPath).isEmpty()) {
+ val type = when {
+ mPath.isVideoFast() -> TYPE_VIDEOS
+ mPath.isGif() -> TYPE_GIFS
+ mPath.isSvg() -> TYPE_SVGS
+ mPath.isRawFast() -> TYPE_RAWS
+ else -> TYPE_IMAGES
+ }
+
+ val duration = if (type == TYPE_VIDEOS) mPath.getVideoDuration() else 0
+ val medium = Medium(null, mPath.getFilenameFromPath(), mPath, mPath.getParentPath(), System.currentTimeMillis(), System.currentTimeMillis(), File(mPath).length(), type, duration, false, 0)
+ galleryDB.MediumDao().insert(medium)
+ }
+ }.start()
+ }
}
private fun initBottomActions() {
@@ -268,7 +348,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}.start()
}
- private fun setupRotation() {
+ private fun setupOrientation() {
if (!mIsOrientationLocked) {
if (config.screenRotation == ROTATE_BY_DEVICE_ROTATION) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
@@ -278,81 +358,9 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
}
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu_viewpager, menu)
- val currentMedium = getCurrentMedium() ?: return true
- currentMedium.isFavorite = mFavoritePaths.contains(currentMedium.path)
- val visibleBottomActions = if (config.bottomActions) config.visibleBottomActions else 0
-
- menu.apply {
- findItem(R.id.menu_show_on_map).isVisible = visibleBottomActions and BOTTOM_ACTION_SHOW_ON_MAP == 0
- findItem(R.id.menu_slideshow).isVisible = visibleBottomActions and BOTTOM_ACTION_SLIDESHOW == 0
- findItem(R.id.menu_properties).isVisible = visibleBottomActions and BOTTOM_ACTION_PROPERTIES == 0
- findItem(R.id.menu_delete).isVisible = visibleBottomActions and BOTTOM_ACTION_DELETE == 0
- findItem(R.id.menu_share).isVisible = visibleBottomActions and BOTTOM_ACTION_SHARE == 0
- findItem(R.id.menu_edit).isVisible = visibleBottomActions and BOTTOM_ACTION_EDIT == 0 && !currentMedium.isSVG()
- findItem(R.id.menu_rename).isVisible = visibleBottomActions and BOTTOM_ACTION_RENAME == 0 && !currentMedium.getIsInRecycleBin()
- findItem(R.id.menu_rotate).isVisible = currentMedium.isImage() && visibleBottomActions and BOTTOM_ACTION_ROTATE == 0
- findItem(R.id.menu_set_as).isVisible = visibleBottomActions and BOTTOM_ACTION_SET_AS == 0
- findItem(R.id.menu_save_as).isVisible = mRotationDegrees != 0
- findItem(R.id.menu_hide).isVisible = !currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0 && !currentMedium.getIsInRecycleBin()
- findItem(R.id.menu_unhide).isVisible = currentMedium.isHidden() && visibleBottomActions and BOTTOM_ACTION_TOGGLE_VISIBILITY == 0 && !currentMedium.getIsInRecycleBin()
- findItem(R.id.menu_add_to_favorites).isVisible = !currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0
- findItem(R.id.menu_remove_from_favorites).isVisible = currentMedium.isFavorite && visibleBottomActions and BOTTOM_ACTION_TOGGLE_FAVORITE == 0
- findItem(R.id.menu_restore_file).isVisible = currentMedium.path.startsWith(filesDir.absolutePath)
- findItem(R.id.menu_change_orientation).isVisible = mRotationDegrees == 0 && visibleBottomActions and BOTTOM_ACTION_CHANGE_ORIENTATION == 0
- findItem(R.id.menu_change_orientation).icon = resources.getDrawable(getChangeOrientationIcon())
- findItem(R.id.menu_rotate).setShowAsAction(
- if (mRotationDegrees != 0) {
- MenuItem.SHOW_AS_ACTION_ALWAYS
- } else {
- MenuItem.SHOW_AS_ACTION_IF_ROOM
- })
- }
-
- if (visibleBottomActions != 0) {
- updateBottomActionIcons(currentMedium)
- }
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (getCurrentMedium() == null)
- return true
-
- when (item.itemId) {
- R.id.menu_set_as -> setAs(getCurrentPath())
- R.id.menu_slideshow -> initSlideshow()
- R.id.menu_copy_to -> copyMoveTo(true)
- R.id.menu_move_to -> copyMoveTo(false)
- R.id.menu_open_with -> openPath(getCurrentPath(), true)
- R.id.menu_hide -> toggleFileVisibility(true)
- R.id.menu_unhide -> toggleFileVisibility(false)
- R.id.menu_share -> shareMediumPath(getCurrentPath())
- R.id.menu_delete -> checkDeleteConfirmation()
- R.id.menu_rename -> renameFile()
- R.id.menu_edit -> openEditor(getCurrentPath())
- R.id.menu_properties -> showProperties()
- R.id.menu_show_on_map -> showOnMap()
- R.id.menu_rotate_right -> rotateImage(90)
- R.id.menu_rotate_left -> rotateImage(270)
- R.id.menu_rotate_one_eighty -> rotateImage(180)
- R.id.menu_add_to_favorites -> toggleFavorite()
- R.id.menu_remove_from_favorites -> toggleFavorite()
- R.id.menu_restore_file -> restoreFile()
- R.id.menu_force_portrait -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- R.id.menu_force_landscape -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
- R.id.menu_default_orientation -> toggleOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
- R.id.menu_save_as -> saveImageAs()
- R.id.menu_settings -> launchSettings()
- else -> return super.onOptionsItemSelected(item)
- }
- return true
- }
-
private fun updatePagerItems(media: MutableList) {
val pagerAdapter = MyPagerAdapter(this, supportFragmentManager, media)
- if (!isActivityDestroyed()) {
+ if (!isDestroyed) {
view_pager.apply {
adapter = pagerAdapter
currentItem = mPos
@@ -362,6 +370,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
}
+ private fun checkSlideshowOnEnter() {
+ if (intent.getBooleanExtra(SLIDESHOW_START_ON_ENTER, false)) {
+ initSlideshow()
+ }
+ }
+
private fun initSlideshow() {
SlideshowDialog(this) {
startSlideshow()
@@ -371,7 +385,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun startSlideshow() {
if (getMediaForSlideshow()) {
view_pager.onGlobalLayout {
- if (!isActivityDestroyed()) {
+ if (!isDestroyed) {
hideSystemUI(true)
mSlideshowInterval = config.slideshowInterval
mSlideshowMoveBackwards = config.slideshowMoveBackwards
@@ -421,7 +435,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
val dragOffset = dragPosition - oldDragPosition
oldDragPosition = dragPosition
try {
- view_pager.fakeDragBy(dragOffset * (if (forward) 1f else -1f))
+ view_pager.fakeDragBy(dragOffset * (if (forward) -1f else 1f))
} catch (e: Exception) {
stopSlideshow()
}
@@ -461,7 +475,7 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
if (mIsSlideshowActive) {
if (getCurrentMedium()!!.isImage() || getCurrentMedium()!!.isGIF()) {
mSlideshowHandler.postDelayed({
- if (mIsSlideshowActive && !isActivityDestroyed()) {
+ if (mIsSlideshowActive && !isDestroyed) {
swipeToNextMedium()
}
}, mSlideshowInterval * 1000L)
@@ -476,18 +490,9 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
private fun getMediaForSlideshow(): Boolean {
- mSlideshowMedia = mMediaFiles.toMutableList()
- if (!config.slideshowIncludePhotos) {
- mSlideshowMedia = mSlideshowMedia.filter { !it.isImage() } as MutableList
- }
-
- if (!config.slideshowIncludeVideos) {
- mSlideshowMedia = mSlideshowMedia.filter { it.isImage() || it.isGIF() } as MutableList
- }
-
- if (!config.slideshowIncludeGIFs) {
- mSlideshowMedia = mSlideshowMedia.filter { !it.isGIF() } as MutableList
- }
+ mSlideshowMedia = mMediaFiles.filter {
+ it.isImage() || (config.slideshowIncludeVideos && it.isVideo() || (config.slideshowIncludeGIFs && it.isGIF()))
+ }.toMutableList()
if (config.slideshowRandomOrder) {
mSlideshowMedia.shuffle()
@@ -507,13 +512,25 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
}
+ private fun moveFileTo() {
+ handleDeletePasswordProtection {
+ copyMoveTo(false)
+ }
+ }
+
private fun copyMoveTo(isCopyOperation: Boolean) {
val currPath = getCurrentPath()
+ if (!isCopyOperation && currPath.startsWith(recycleBinPath)) {
+ toast(R.string.moving_recycle_bin_items_disabled, Toast.LENGTH_LONG)
+ return
+ }
+
val fileDirItems = arrayListOf(FileDirItem(currPath, currPath.getFilenameFromPath()))
tryCopyMoveFilesTo(fileDirItems, isCopyOperation) {
config.tempFolderPath = ""
if (!isCopyOperation) {
refreshViewPager()
+ updateFavoritePaths(fileDirItems, it)
}
}
}
@@ -534,10 +551,18 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
private fun rotateImage(degrees: Int) {
- mRotationDegrees = (mRotationDegrees + degrees) % 360
- getCurrentFragment()?.let {
- (it as? PhotoFragment)?.rotateImageViewBy(mRotationDegrees)
+ val currentPath = getCurrentPath()
+ if (needsStupidWritePermissions(currentPath)) {
+ handleSAFDialog(currentPath) {
+ rotateBy(degrees)
+ }
+ } else {
+ rotateBy(degrees)
}
+ }
+
+ private fun rotateBy(degrees: Int) {
+ getCurrentPhotoFragment()?.rotateImageViewBy(degrees)
supportInvalidateOptionsMenu()
}
@@ -563,110 +588,20 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
val currPath = getCurrentPath()
SaveAsDialog(this, currPath, false) {
handleSAFDialog(it) {
+ toast(R.string.saving)
Thread {
- saveImageToFile(currPath, it)
+ val photoFragment = getCurrentPhotoFragment() ?: return@Thread
+ saveRotatedImageToFile(currPath, it, photoFragment.mCurrentRotationDegrees, true) {
+ toast(R.string.file_saved)
+ getCurrentPhotoFragment()?.mCurrentRotationDegrees = 0
+ invalidateOptionsMenu()
+ }
}.start()
}
}
}
- private fun saveImageToFile(oldPath: String, newPath: String) {
- toast(R.string.saving)
- if (oldPath == newPath && oldPath.isJpg()) {
- if (tryRotateByExif(oldPath)) {
- return
- }
- }
-
- val tmpPath = "${filesDir.absolutePath}/.tmp_${newPath.getFilenameFromPath()}"
- val tmpFileDirItem = FileDirItem(tmpPath, tmpPath.getFilenameFromPath())
- try {
- getFileOutputStream(tmpFileDirItem) {
- if (it == null) {
- toast(R.string.unknown_error_occurred)
- return@getFileOutputStream
- }
-
- val oldLastModified = getCurrentFile().lastModified()
- if (oldPath.isJpg()) {
- copyFile(getCurrentPath(), tmpPath)
- saveExifRotation(ExifInterface(tmpPath), mRotationDegrees)
- } else {
- val inputstream = getFileInputStreamSync(oldPath)
- val bitmap = BitmapFactory.decodeStream(inputstream)
- saveFile(tmpPath, bitmap, it as FileOutputStream)
- }
-
- if (getDoesFilePathExist(newPath)) {
- tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath()), false, true)
- }
-
- copyFile(tmpPath, newPath)
- scanPathRecursively(newPath)
- toast(R.string.file_saved)
-
- if (config.keepLastModified) {
- File(newPath).setLastModified(oldLastModified)
- updateLastModified(newPath, oldLastModified)
- }
-
- it.flush()
- it.close()
- mRotationDegrees = 0
- invalidateOptionsMenu()
-
- // we cannot refresh a specific image in Glide Cache, so just clear it all
- val glide = Glide.get(applicationContext)
- glide.clearDiskCache()
- runOnUiThread {
- glide.clearMemory()
- }
- }
- } catch (e: OutOfMemoryError) {
- toast(R.string.out_of_memory_error)
- } catch (e: Exception) {
- showErrorToast(e)
- } finally {
- tryDeleteFileDirItem(tmpFileDirItem, false, true)
- }
- }
-
- @TargetApi(Build.VERSION_CODES.N)
- private fun tryRotateByExif(path: String): Boolean {
- return try {
- if (saveImageRotation(path, mRotationDegrees)) {
- mRotationDegrees = 0
- invalidateOptionsMenu()
- toast(R.string.file_saved)
- true
- } else {
- false
- }
- } catch (e: Exception) {
- showErrorToast(e)
- false
- }
- }
-
- private fun copyFile(source: String, destination: String) {
- var inputStream: InputStream? = null
- var out: OutputStream? = null
- try {
- out = getFileOutputStreamSync(destination, source.getMimeType())
- inputStream = getFileInputStreamSync(source)
- inputStream?.copyTo(out!!)
- } finally {
- inputStream?.close()
- out?.close()
- }
- }
-
- private fun saveFile(path: String, bitmap: Bitmap, out: FileOutputStream) {
- val matrix = Matrix()
- matrix.postRotate(mRotationDegrees.toFloat())
- val bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
- bmp.compress(path.getCompressionFormat(), 90, out)
- }
+ private fun getCurrentPhotoFragment() = getCurrentFragment() as? PhotoFragment
private fun isShowHiddenFlagNeeded(): Boolean {
val file = File(mPath)
@@ -839,6 +774,11 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
bottom_set_as.setOnClickListener {
setAs(getCurrentPath())
}
+
+ bottom_copy.beVisibleIf(visibleBottomActions and BOTTOM_ACTION_COPY != 0)
+ bottom_copy.setOnClickListener {
+ copyMoveTo(true)
+ }
}
private fun updateBottomActionIcons(medium: Medium?) {
@@ -877,15 +817,17 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
- if (requestCode == REQUEST_EDIT_IMAGE) {
- if (resultCode == Activity.RESULT_OK && resultData != null) {
- mPos = -1
- mPrevHashcode = 0
- refreshViewPager()
- }
- } else if (requestCode == REQUEST_SET_AS) {
- if (resultCode == Activity.RESULT_OK) {
- toast(R.string.wallpaper_set_successfully)
+ if (requestCode == REQUEST_EDIT_IMAGE && resultCode == Activity.RESULT_OK && resultData != null) {
+ mPos = -1
+ mPrevHashcode = 0
+ refreshViewPager()
+ } else if (requestCode == REQUEST_SET_AS && resultCode == Activity.RESULT_OK) {
+ toast(R.string.wallpaper_set_successfully)
+ } else if (requestCode == REQUEST_VIEW_VIDEO && resultCode == Activity.RESULT_OK && resultData != null) {
+ if (resultData.getBooleanExtra(GO_TO_NEXT_ITEM, false)) {
+ goToNextItem()
+ } else if (resultData.getBooleanExtra(GO_TO_PREV_ITEM, false)) {
+ goToPrevItem()
}
}
super.onActivityResult(requestCode, resultCode, resultData)
@@ -896,7 +838,11 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
return
}
- if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) {
+ if (config.isDeletePasswordProtectionOn) {
+ handleDeletePasswordProtection {
+ deleteConfirmed()
+ }
+ } else if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) {
deleteConfirmed()
} else {
askConfirmDelete()
@@ -904,8 +850,16 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
private fun askConfirmDelete() {
- val message = if (config.useRecycleBin && !getCurrentMedium()!!.getIsInRecycleBin()) R.string.are_you_sure_recycle_bin else R.string.are_you_sure_delete
- DeleteWithRememberDialog(this, getString(message)) {
+ val filename = "\"${getCurrentPath().getFilenameFromPath()}\""
+
+ val baseString = if (config.useRecycleBin && !getCurrentMedium()!!.getIsInRecycleBin()) {
+ R.string.move_to_recycle_bin_confirmation
+ } else {
+ R.string.deletion_confirmation
+ }
+
+ val message = String.format(resources.getString(baseString), filename)
+ DeleteWithRememberDialog(this, message) {
config.tempSkipDeleteConfirmation = it
deleteConfirmed()
}
@@ -962,32 +916,19 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
- measureScreen()
initBottomActionsLayout()
}
- @SuppressLint("NewApi")
- private fun measureScreen() {
- val metrics = DisplayMetrics()
- if (isJellyBean1Plus()) {
- windowManager.defaultDisplay.getRealMetrics(metrics)
- screenWidth = metrics.widthPixels
- screenHeight = metrics.heightPixels
- } else {
- windowManager.defaultDisplay.getMetrics(metrics)
- screenWidth = metrics.widthPixels
- screenHeight = metrics.heightPixels
+ private fun refreshViewPager() {
+ if (config.getFileSorting(mDirectory) and SORT_BY_RANDOM == 0) {
+ GetMediaAsynctask(applicationContext, mDirectory, false, false, mShowAll) {
+ gotMedia(it)
+ }.execute()
}
}
- private fun refreshViewPager() {
- GetMediaAsynctask(applicationContext, mDirectory, false, false, mShowAll) {
- gotMedia(it)
- }.execute()
- }
-
private fun gotMedia(thumbnailItems: ArrayList) {
- val media = thumbnailItems.filter { it is Medium }.map { it as Medium } as ArrayList
+ val media = thumbnailItems.asSequence().filter { it is Medium }.map { it as Medium }.toMutableList() as ArrayList
if (isDirEmpty(media) || media.hashCode() == mPrevHashcode) {
return
}
@@ -1069,6 +1010,34 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
checkOrientation()
}
+ override fun launchViewVideoIntent(path: String) {
+ Thread {
+ val newUri = getFinalUriFromPath(path, BuildConfig.APPLICATION_ID) ?: return@Thread
+ val mimeType = getUriMimeType(path, newUri)
+ Intent().apply {
+ action = Intent.ACTION_VIEW
+ setDataAndType(newUri, mimeType)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ putExtra(IS_FROM_GALLERY, true)
+ putExtra(REAL_FILE_PATH, path)
+ putExtra(SHOW_PREV_ITEM, view_pager.currentItem != 0)
+ putExtra(SHOW_NEXT_ITEM, view_pager.currentItem != mMediaFiles.size - 1)
+
+ if (resolveActivity(packageManager) != null) {
+ try {
+ startActivityForResult(this, REQUEST_VIEW_VIDEO)
+ } catch (e: NullPointerException) {
+ showErrorToast(e)
+ }
+ } else {
+ if (!tryGenericMimeType(this, mimeType, newUri)) {
+ toast(R.string.no_app_found)
+ }
+ }
+ }
+ }.start()
+ }
+
private fun checkSystemUI() {
if (mIsFullScreen) {
hideSystemUI(true)
@@ -1098,15 +1067,12 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
private fun getCurrentPath() = getCurrentMedium()?.path ?: ""
- private fun getCurrentFile() = File(getCurrentPath())
-
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
if (mPos != position) {
mPos = position
updateActionbarTitle()
- mRotationDegrees = 0
invalidateOptionsMenu()
scheduleSwipe()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/WidgetConfigureActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/WidgetConfigureActivity.kt
new file mode 100644
index 000000000..3a9050af2
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/WidgetConfigureActivity.kt
@@ -0,0 +1,184 @@
+package com.simplemobiletools.gallery.pro.activities
+
+import android.app.Activity
+import android.appwidget.AppWidgetManager
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.widget.RelativeLayout
+import android.widget.RemoteViews
+import com.simplemobiletools.commons.dialogs.ColorPickerDialog
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.dialogs.PickDirectoryDialog
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.MyWidgetProvider
+import com.simplemobiletools.gallery.pro.models.Directory
+import com.simplemobiletools.gallery.pro.models.Widget
+import kotlinx.android.synthetic.main.activity_widget_config.*
+
+class WidgetConfigureActivity : SimpleActivity() {
+ private var mBgAlpha = 0f
+ private var mWidgetId = 0
+ private var mBgColor = 0
+ private var mBgColorWithoutTransparency = 0
+ private var mTextColor = 0
+ private var mFolderPath = ""
+ private var mDirectories = ArrayList()
+
+ public override fun onCreate(savedInstanceState: Bundle?) {
+ useDynamicTheme = false
+ super.onCreate(savedInstanceState)
+ setResult(RESULT_CANCELED)
+ setContentView(R.layout.activity_widget_config)
+ initVariables()
+
+ mWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
+
+ if (mWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish()
+ }
+
+ config_save.setOnClickListener { saveConfig() }
+ config_bg_color.setOnClickListener { pickBackgroundColor() }
+ config_text_color.setOnClickListener { pickTextColor() }
+ folder_picker_value.setOnClickListener { changeSelectedFolder() }
+ config_image_holder.setOnClickListener { changeSelectedFolder() }
+ folder_picker_show_folder_name.isChecked = config.showWidgetFolderName
+ handleFolderNameDisplay()
+ folder_picker_show_folder_name_holder.setOnClickListener {
+ folder_picker_show_folder_name.toggle()
+ handleFolderNameDisplay()
+ }
+
+ updateTextColors(folder_picker_holder)
+ folder_picker_holder.background = ColorDrawable(config.backgroundColor)
+
+ getCachedDirectories(false, false) {
+ mDirectories = it
+ val path = it.firstOrNull()?.path
+ if (path != null) {
+ updateFolderImage(path)
+ }
+ }
+ }
+
+ private fun initVariables() {
+ mBgColor = config.widgetBgColor
+ if (mBgColor == 1) {
+ mBgColor = Color.BLACK
+ mBgAlpha = .2f
+ } else {
+ mBgAlpha = Color.alpha(mBgColor) / 255f
+ }
+
+ mBgColorWithoutTransparency = Color.rgb(Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor))
+ config_bg_seekbar.apply {
+ progress = (mBgAlpha * 100).toInt()
+
+ onSeekBarChangeListener {
+ mBgAlpha = it / 100f
+ updateBackgroundColor()
+ }
+ }
+ updateBackgroundColor()
+
+ mTextColor = config.widgetTextColor
+ updateTextColor()
+ }
+
+ private fun saveConfig() {
+ val views = RemoteViews(packageName, R.layout.widget)
+ views.setBackgroundColor(R.id.widget_holder, mBgColor)
+ AppWidgetManager.getInstance(this).updateAppWidget(mWidgetId, views)
+ config.showWidgetFolderName = folder_picker_show_folder_name.isChecked
+ val widget = Widget(null, mWidgetId, mFolderPath)
+ Thread {
+ widgetsDB.insertOrUpdate(widget)
+ }.start()
+
+ storeWidgetColors()
+ requestWidgetUpdate()
+
+ Intent().apply {
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId)
+ setResult(Activity.RESULT_OK, this)
+ }
+ finish()
+ }
+
+ private fun storeWidgetColors() {
+ config.apply {
+ widgetBgColor = mBgColor
+ widgetTextColor = mTextColor
+ }
+ }
+
+ private fun requestWidgetUpdate() {
+ Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidgetProvider::class.java).apply {
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(mWidgetId))
+ sendBroadcast(this)
+ }
+ }
+
+ private fun updateBackgroundColor() {
+ mBgColor = mBgColorWithoutTransparency.adjustAlpha(mBgAlpha)
+ config_save.setBackgroundColor(mBgColor)
+ config_image_holder.setBackgroundColor(mBgColor)
+ config_bg_color.setFillWithStroke(mBgColor, Color.BLACK)
+ }
+
+ private fun updateTextColor() {
+ config_save.setTextColor(mTextColor)
+ config_folder_name.setTextColor(mTextColor)
+ config_text_color.setFillWithStroke(mTextColor, Color.BLACK)
+ }
+
+ private fun pickBackgroundColor() {
+ ColorPickerDialog(this, mBgColorWithoutTransparency) { wasPositivePressed, color ->
+ if (wasPositivePressed) {
+ mBgColorWithoutTransparency = color
+ updateBackgroundColor()
+ }
+ }
+ }
+
+ private fun pickTextColor() {
+ ColorPickerDialog(this, mTextColor) { wasPositivePressed, color ->
+ if (wasPositivePressed) {
+ mTextColor = color
+ updateTextColor()
+ }
+ }
+ }
+
+ private fun changeSelectedFolder() {
+ PickDirectoryDialog(this, "", false) {
+ updateFolderImage(it)
+ }
+ }
+
+ private fun updateFolderImage(folderPath: String) {
+ mFolderPath = folderPath
+ runOnUiThread {
+ folder_picker_value.text = getFolderNameFromPath(folderPath)
+ config_folder_name.text = getFolderNameFromPath(folderPath)
+ }
+
+ Thread {
+ val path = directoryDB.getDirectoryThumbnail(folderPath)
+ if (path != null) {
+ runOnUiThread {
+ loadJpg(path, config_image, config.cropThumbnails)
+ }
+ }
+ }.start()
+ }
+
+ private fun handleFolderNameDisplay() {
+ val showFolderName = folder_picker_show_folder_name.isChecked
+ config_folder_name.beVisibleIf(showFolderName)
+ (config_image.layoutParams as RelativeLayout.LayoutParams).bottomMargin = if (showFolderName) 0 else resources.getDimension(R.dimen.normal_margin).toInt()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/DirectoryAdapter.kt
similarity index 60%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/DirectoryAdapter.kt
index 1c6f525f0..80ec519f3 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/DirectoryAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/DirectoryAdapter.kt
@@ -1,6 +1,5 @@
-package com.simplemobiletools.gallery.adapters
+package com.simplemobiletools.gallery.pro.adapters
-import android.util.SparseArray
import android.view.Menu
import android.view.View
import android.view.ViewGroup
@@ -11,23 +10,22 @@ import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.PropertiesDialog
import com.simplemobiletools.commons.dialogs.RenameItemDialog
+import com.simplemobiletools.commons.dialogs.RenameItemsDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.OTG_PATH
import com.simplemobiletools.commons.models.FileDirItem
import com.simplemobiletools.commons.views.FastScroller
import com.simplemobiletools.commons.views.MyRecyclerView
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.dialogs.ExcludeFolderDialog
-import com.simplemobiletools.gallery.dialogs.PickMediumDialog
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.interfaces.DirectoryOperationsListener
-import com.simplemobiletools.gallery.models.AlbumCover
-import com.simplemobiletools.gallery.models.Directory
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.dialogs.ExcludeFolderDialog
+import com.simplemobiletools.gallery.pro.dialogs.PickMediumDialog
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.interfaces.DirectoryOperationsListener
+import com.simplemobiletools.gallery.pro.models.AlbumCover
+import com.simplemobiletools.gallery.pro.models.Directory
import kotlinx.android.synthetic.main.directory_item_list.view.*
import java.io.File
-import java.util.*
-import kotlin.collections.ArrayList
class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList, val listener: DirectoryOperationsListener?, recyclerView: MyRecyclerView,
val isPickIntent: Boolean, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
@@ -40,6 +38,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList
+ holder.bindView(dir, true, !isPickIntent) { itemView, adapterPosition ->
setupView(itemView, dir)
}
- bindViewHolder(holder, position, view)
+ bindViewHolder(holder)
}
override fun getItemCount() = dirs.size
override fun prepareActionMode(menu: Menu) {
- if (getSelectedPaths().isEmpty()) {
+ val selectedPaths = getSelectedPaths()
+ if (selectedPaths.isEmpty()) {
return
}
- val selectedPaths = getSelectedPaths()
+ val isOneItemSelected = isOneItemSelected()
menu.apply {
- findItem(R.id.cab_rename).isVisible = isOneItemSelected() && !selectedPaths.contains(FAVORITES) && !selectedPaths.contains(RECYCLE_BIN)
- findItem(R.id.cab_change_cover_image).isVisible = isOneItemSelected()
+ findItem(R.id.cab_rename).isVisible = !selectedPaths.contains(FAVORITES) && !selectedPaths.contains(RECYCLE_BIN)
+ findItem(R.id.cab_change_cover_image).isVisible = isOneItemSelected
- findItem(R.id.cab_empty_recycle_bin).isVisible = isOneItemSelected() && selectedPaths.first() == RECYCLE_BIN
- findItem(R.id.cab_empty_disable_recycle_bin).isVisible = isOneItemSelected() && selectedPaths.first() == RECYCLE_BIN
+ findItem(R.id.cab_empty_recycle_bin).isVisible = isOneItemSelected && selectedPaths.first() == RECYCLE_BIN
+ findItem(R.id.cab_empty_disable_recycle_bin).isVisible = isOneItemSelected && selectedPaths.first() == RECYCLE_BIN
- checkHideBtnVisibility(this)
- checkPinBtnVisibility(this)
+ checkHideBtnVisibility(this, selectedPaths)
+ checkPinBtnVisibility(this, selectedPaths)
}
}
override fun actionItemPressed(id: Int) {
- if (selectedPositions.isEmpty()) {
+ if (selectedKeys.isEmpty()) {
return
}
@@ -105,7 +97,7 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList toggleFoldersVisibility(false)
R.id.cab_exclude -> tryExcludeFolder()
R.id.cab_copy_to -> copyMoveTo(true)
- R.id.cab_move_to -> copyMoveTo(false)
+ R.id.cab_move_to -> moveFilesTo()
R.id.cab_select_all -> selectAll()
R.id.cab_delete -> askConfirmDelete()
R.id.cab_select_photo -> changeAlbumCover(false)
@@ -117,49 +109,33 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList 0
- menu.findItem(R.id.cab_unhide).isVisible = hiddenCnt > 0
+ private fun checkHideBtnVisibility(menu: Menu, selectedPaths: ArrayList) {
+ menu.findItem(R.id.cab_hide).isVisible = selectedPaths.any { !File(it).doesThisOrParentHaveNoMedia() }
+ menu.findItem(R.id.cab_unhide).isVisible = selectedPaths.any { File(it).doesThisOrParentHaveNoMedia() }
}
- private fun checkPinBtnVisibility(menu: Menu) {
+ private fun checkPinBtnVisibility(menu: Menu, selectedPaths: ArrayList) {
val pinnedFolders = config.pinnedFolders
- var pinnedCnt = 0
- var unpinnedCnt = 0
- selectedPositions.mapNotNull { dirs.getOrNull(it)?.path }.forEach {
- if (pinnedFolders.contains(it)) {
- pinnedCnt++
- } else {
- unpinnedCnt++
- }
- }
-
- menu.findItem(R.id.cab_pin).isVisible = unpinnedCnt > 0
- menu.findItem(R.id.cab_unpin).isVisible = pinnedCnt > 0
+ menu.findItem(R.id.cab_pin).isVisible = selectedPaths.any { !pinnedFolders.contains(it) }
+ menu.findItem(R.id.cab_unpin).isVisible = selectedPaths.any { pinnedFolders.contains(it) }
}
private fun showProperties() {
- if (selectedPositions.size <= 1) {
- val path = dirs[selectedPositions.first()].path
+ if (selectedKeys.size <= 1) {
+ val path = getFirstSelectedItemPath() ?: return
if (path != FAVORITES && path != RECYCLE_BIN) {
- PropertiesDialog(activity, dirs[selectedPositions.first()].path, config.shouldShowHidden)
+ PropertiesDialog(activity, path, config.shouldShowHidden)
}
} else {
PropertiesDialog(activity, getSelectedPaths().filter { it != FAVORITES && it != RECYCLE_BIN }.toMutableList(), config.shouldShowHidden)
@@ -167,26 +143,33 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList
+ RenameItemsDialog(activity, paths) {
+ listener?.refreshItems()
}
}
}
@@ -283,18 +266,6 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList()
- val cnt = viewHolders.size()
- for (i in 0..cnt) {
- if (affectedPositions.contains(i)) {
- continue
- }
-
- val view = viewHolders.get(i, null)
- val newIndex = i - selectedPositions.count { it <= i }
- newViewHolders.put(newIndex, view)
- }
- viewHolders = newViewHolders
currentDirectoriesHash = newDirs.hashCode()
dirs = newDirs
@@ -330,27 +301,38 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList()
val showHidden = activity.config.shouldShowHidden
- selectedPositions.forEach {
- val path = dirs[it].path
- if (path.startsWith(OTG_PATH)) {
- paths.addAll(getOTGFilePaths(path, showHidden))
- } else if (path != FAVORITES) {
- File(path).listFiles()?.filter {
- !activity.getIsPathDirectory(it.absolutePath) && it.isMediaFile() && (showHidden || !it.name.startsWith('.'))
+ getSelectedPaths().forEach {
+ if (it.startsWith(OTG_PATH)) {
+ paths.addAll(getOTGFilePaths(it, showHidden))
+ } else if (it != FAVORITES) {
+ val filter = config.filterMedia
+ File(it).listFiles()?.filter {
+ !activity.getIsPathDirectory(it.absolutePath) &&
+ it.absolutePath.isMediaFile() && (showHidden || !it.name.startsWith('.')) &&
+ ((it.isImageFast() && filter and TYPE_IMAGES != 0) ||
+ (it.isVideoFast() && filter and TYPE_VIDEOS != 0) ||
+ (it.isGif() && filter and TYPE_GIFS != 0) ||
+ (it.isRawFast() && filter and TYPE_RAWS != 0) ||
+ (it.isSvg() && filter and TYPE_SVGS != 0))
}?.mapTo(paths) { it.absolutePath }
}
}
@@ -365,8 +347,16 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList {
val paths = ArrayList()
- activity.getOTGFolderChildren(path)?.forEach {
- if (!it.isDirectory && it.name.isMediaFile() && (showHidden || !it.name.startsWith('.'))) {
+ val filter = config.filterMedia
+ activity.getOTGFolderChildren(path)?.filter { it.name != null }?.forEach {
+ if (!it.isDirectory &&
+ it.name!!.isMediaFile() && (showHidden || !it.name!!.startsWith('.')) &&
+ ((it.name!!.isImageFast() && filter and TYPE_IMAGES != 0) ||
+ (it.name!!.isVideoFast() && filter and TYPE_VIDEOS != 0) ||
+ (it.name!!.isGif() && filter and TYPE_GIFS != 0) ||
+ (it.name!!.isRawFast() && filter and TYPE_RAWS != 0) ||
+ (it.name!!.isSvg() && filter and TYPE_SVGS != 0))
+ ) {
val relativePath = it.uri.path.substringAfterLast("${activity.config.OTGPartition}:")
paths.add("$OTG_PATH$relativePath")
}
@@ -375,81 +365,80 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList activity.handleDeletePasswordProtection {
deleteFolders()
}
+ config.skipDeleteConfirmation -> deleteFolders()
+ else -> {
+ val itemsCnt = selectedKeys.size
+ val items = if (itemsCnt == 1) {
+ "\"${getSelectedPaths().first().getFilenameFromPath()}\""
+ } else {
+ resources.getQuantityString(R.plurals.delete_items, itemsCnt, itemsCnt)
+ }
+
+ val fileDirItem = getFirstSelectedItem() ?: return
+ val baseString = if (!config.useRecycleBin || (isOneItemSelected() && fileDirItem.isRecycleBin()) || (isOneItemSelected() && fileDirItem.areFavorites())) {
+ R.string.deletion_confirmation
+ } else {
+ R.string.move_to_recycle_bin_confirmation
+ }
+
+ var question = String.format(resources.getString(baseString), items)
+ val warning = resources.getQuantityString(R.plurals.delete_warning, itemsCnt, itemsCnt)
+ question += "\n\n$warning"
+ ConfirmationDialog(activity, question) {
+ deleteFolders()
+ }
+ }
}
}
private fun deleteFolders() {
- if (selectedPositions.isEmpty()) {
+ if (selectedKeys.isEmpty()) {
return
}
- val folders = ArrayList(selectedPositions.size)
- val removeFolders = ArrayList(selectedPositions.size)
-
var SAFPath = ""
- selectedPositions.forEach {
- if (dirs.size > it) {
- val path = dirs[it].path
- if (activity.needsStupidWritePermissions(path) && config.treeUri.isEmpty()) {
- SAFPath = path
- }
+ val selectedDirs = getSelectedItems()
+ selectedDirs.forEach {
+ val path = it.path
+ if (activity.needsStupidWritePermissions(path) && config.treeUri.isEmpty()) {
+ SAFPath = path
}
}
activity.handleSAFDialog(SAFPath) {
- selectedPositions.sortedDescending().forEach {
- val directory = dirs.getOrNull(it)
- if (directory != null) {
- if (directory.areFavorites() || directory.isRecycleBin()) {
- if (directory.isRecycleBin()) {
- tryEmptyRecycleBin(false)
- } else {
- Thread {
- activity.galleryDB.MediumDao().clearFavorites()
- listener?.refreshItems()
- }.start()
- }
-
- if (selectedPositions.size == 1) {
- finishActMode()
- } else {
- selectedPositions.remove(it)
- toggleItemSelection(false, it)
- }
+ val foldersToDelete = ArrayList(selectedKeys.size)
+ selectedDirs.forEach {
+ if (it.areFavorites() || it.isRecycleBin()) {
+ if (it.isRecycleBin()) {
+ tryEmptyRecycleBin(false)
} else {
- folders.add(File(directory.path))
- removeFolders.add(directory)
+ Thread {
+ activity.galleryDB.MediumDao().clearFavorites()
+ listener?.refreshItems()
+ }.start()
}
+
+ if (selectedKeys.size == 1) {
+ finishActMode()
+ }
+ } else {
+ foldersToDelete.add(File(it.path))
}
}
- listener?.deleteFolders(folders)
+ listener?.deleteFolders(foldersToDelete)
}
}
private fun changeAlbumCover(useDefault: Boolean) {
- if (selectedPositions.size != 1)
+ if (selectedKeys.size != 1)
return
- val path = dirs[selectedPositions.first()].path
+ val path = getFirstSelectedItemPath() ?: return
if (useDefault) {
val albumCovers = getAlbumCoversWithout(path)
@@ -480,15 +469,15 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList {
- val paths = HashSet(selectedPositions.size)
- selectedPositions.forEach {
- (dirs.getOrNull(it))?.apply {
- paths.add(path)
- }
- }
- return paths
- }
+ private fun getSelectedItems() = dirs.filter { selectedKeys.contains(it.path.hashCode()) } as ArrayList
+
+ private fun getSelectedPaths() = getSelectedItems().map { it.path } as ArrayList
+
+ private fun getFirstSelectedItem() = getItemWithKey(selectedKeys.first())
+
+ private fun getFirstSelectedItemPath() = getFirstSelectedItem()?.path
+
+ private fun getItemWithKey(key: Int): Directory? = dirs.firstOrNull { it.path.hashCode() == key }
fun updateDirs(newDirs: ArrayList) {
val directories = newDirs.clone() as ArrayList
@@ -516,10 +505,11 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList TYPE_VIDEOS
directory.tmb.isGif() -> TYPE_GIFS
@@ -528,6 +518,11 @@ class DirectoryAdapter(activity: BaseSimpleActivity, var dirs: ArrayList TYPE_IMAGES
}
+ dir_check?.beVisibleIf(isSelected)
+ if (isSelected) {
+ dir_check.background?.applyColorFilter(primaryColor)
+ }
+
activity.loadImage(thumbnailType, directory.tmb, dir_thumbnail, scrollHorizontally, animateGifs, cropThumbnails)
dir_pin.beVisibleIf(pinnedFolders.contains(directory.path))
dir_location.beVisibleIf(directory.location != LOCAITON_INTERNAL)
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/FiltersAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/FiltersAdapter.kt
similarity index 60%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/adapters/FiltersAdapter.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/FiltersAdapter.kt
index 782412a94..b614787b8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/FiltersAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/FiltersAdapter.kt
@@ -1,37 +1,34 @@
-package com.simplemobiletools.gallery.adapters
+package com.simplemobiletools.gallery.pro.adapters
import android.content.Context
-import android.graphics.drawable.Drawable
-import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.interfaces.FilterAdapterListener
-import com.simplemobiletools.gallery.models.FilterItem
+import androidx.recyclerview.widget.RecyclerView
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.models.FilterItem
import kotlinx.android.synthetic.main.editor_filter_item.view.*
import java.util.*
-class FiltersAdapter(val context: Context, val filterItems: ArrayList, val itemClick: (Int) -> Unit) : RecyclerView.Adapter(),
- FilterAdapterListener {
+class FiltersAdapter(val context: Context, val filterItems: ArrayList, val itemClick: (Int) -> Unit) : RecyclerView.Adapter() {
private var currentSelection = filterItems.first()
private var strokeBackground = context.resources.getDrawable(R.drawable.stroke_background)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- holder.bindView(filterItems[position], strokeBackground)
+ holder.bindView(filterItems[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.editor_filter_item, parent, false)
- return ViewHolder(view, this)
+ return ViewHolder(view)
}
override fun getItemCount() = filterItems.size
- override fun getCurrentFilter() = currentSelection
+ fun getCurrentFilter() = currentSelection
- override fun setCurrentFilter(position: Int) {
+ private fun setCurrentFilter(position: Int) {
val filterItem = filterItems.getOrNull(position) ?: return
if (currentSelection != filterItem) {
currentSelection = filterItem
@@ -40,19 +37,19 @@ class FiltersAdapter(val context: Context, val filterItems: ArrayList removeSelection()
@@ -41,20 +35,27 @@ class ManageFoldersAdapter(activity: BaseSimpleActivity, var folders: ArrayList<
override fun getIsItemSelectable(position: Int) = true
+ override fun getItemSelectionKey(position: Int) = folders.getOrNull(position)?.hashCode()
+
+ override fun getItemKeyPosition(key: Int) = folders.indexOfFirst { it.hashCode() == key }
+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_folder, parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val folder = folders[position]
- val view = holder.bindView(folder, true, true) { itemView, adapterPosition ->
+ holder.bindView(folder, true, true) { itemView, adapterPosition ->
setupView(itemView, folder)
}
- bindViewHolder(holder, position, view)
+ bindViewHolder(holder)
}
override fun getItemCount() = folders.size
+ private fun getSelectedItems() = folders.filter { selectedKeys.contains(it.hashCode()) } as ArrayList
+
private fun setupView(view: View, folder: String) {
view.apply {
+ manage_folder_holder?.isSelected = selectedKeys.contains(folder.hashCode())
manage_folder_title.apply {
text = folder
setTextColor(config.textColor)
@@ -63,20 +64,20 @@ class ManageFoldersAdapter(activity: BaseSimpleActivity, var folders: ArrayList<
}
private fun removeSelection() {
- val removeFolders = ArrayList(selectedPositions.size)
+ val removeFolders = ArrayList(selectedKeys.size)
+ val positions = getSelectedItemPositions()
- selectedPositions.sortedDescending().forEach {
- val folder = folders[it]
- removeFolders.add(folder)
+ getSelectedItems().forEach {
+ removeFolders.add(it)
if (isShowingExcludedFolders) {
- config.removeExcludedFolder(folder)
+ config.removeExcludedFolder(it)
} else {
- config.removeIncludedFolder(folder)
+ config.removeIncludedFolder(it)
}
}
folders.removeAll(removeFolders)
- removeSelectedItems()
+ removeSelectedItems(positions)
if (folders.isEmpty()) {
listener?.refreshItems()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/ManageHiddenFoldersAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/ManageHiddenFoldersAdapter.kt
similarity index 68%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/adapters/ManageHiddenFoldersAdapter.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/ManageHiddenFoldersAdapter.kt
index 101b12764..209ce1e40 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/ManageHiddenFoldersAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/ManageHiddenFoldersAdapter.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.adapters
+package com.simplemobiletools.gallery.pro.adapters
import android.view.Menu
import android.view.View
@@ -8,9 +8,9 @@ import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.extensions.isPathOnSD
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
import com.simplemobiletools.commons.views.MyRecyclerView
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.extensions.removeNoMedia
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.extensions.removeNoMedia
import kotlinx.android.synthetic.main.item_manage_folder.view.*
import java.util.*
@@ -27,12 +27,6 @@ class ManageHiddenFoldersAdapter(activity: BaseSimpleActivity, var folders: Arra
override fun prepareActionMode(menu: Menu) {}
- override fun prepareItemSelection(viewHolder: ViewHolder) {}
-
- override fun markViewHolderSelection(select: Boolean, viewHolder: ViewHolder?) {
- viewHolder?.itemView?.manage_folder_holder?.isSelected = select
- }
-
override fun actionItemPressed(id: Int) {
when (id) {
R.id.cab_unhide -> tryUnhideFolders()
@@ -43,20 +37,27 @@ class ManageHiddenFoldersAdapter(activity: BaseSimpleActivity, var folders: Arra
override fun getIsItemSelectable(position: Int) = true
+ override fun getItemSelectionKey(position: Int) = folders.getOrNull(position)?.hashCode()
+
+ override fun getItemKeyPosition(key: Int) = folders.indexOfFirst { it.hashCode() == key }
+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_manage_folder, parent)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val folder = folders[position]
- val view = holder.bindView(folder, true, true) { itemView, adapterPosition ->
+ holder.bindView(folder, true, true) { itemView, adapterPosition ->
setupView(itemView, folder)
}
- bindViewHolder(holder, position, view)
+ bindViewHolder(holder)
}
override fun getItemCount() = folders.size
+ private fun getSelectedItems() = folders.filter { selectedKeys.contains(it.hashCode()) } as ArrayList
+
private fun setupView(view: View, folder: String) {
view.apply {
+ manage_folder_holder?.isSelected = selectedKeys.contains(folder.hashCode())
manage_folder_title.apply {
text = folder
setTextColor(config.textColor)
@@ -65,12 +66,12 @@ class ManageHiddenFoldersAdapter(activity: BaseSimpleActivity, var folders: Arra
}
private fun tryUnhideFolders() {
- val removeFolders = ArrayList(selectedPositions.size)
+ val removeFolders = ArrayList(selectedKeys.size)
val sdCardPaths = ArrayList()
- selectedPositions.forEach {
- if (activity.isPathOnSD(folders[it])) {
- sdCardPaths.add(folders[it])
+ getSelectedItems().forEach {
+ if (activity.isPathOnSD(it)) {
+ sdCardPaths.add(it)
}
}
@@ -84,14 +85,14 @@ class ManageHiddenFoldersAdapter(activity: BaseSimpleActivity, var folders: Arra
}
private fun unhideFolders(removeFolders: ArrayList) {
- selectedPositions.sortedDescending().forEach {
- val folder = folders[it]
- removeFolders.add(folder)
- activity.removeNoMedia(folder)
+ val position = getSelectedItemPositions()
+ getSelectedItems().forEach {
+ removeFolders.add(it)
+ activity.removeNoMedia(it)
}
folders.removeAll(removeFolders)
- removeSelectedItems()
+ removeSelectedItems(position)
if (folders.isEmpty()) {
listener?.refreshItems()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/MediaAdapter.kt
similarity index 54%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/MediaAdapter.kt
index 22ecca74d..f92a5d01b 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/adapters/MediaAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/adapters/MediaAdapter.kt
@@ -1,50 +1,49 @@
-package com.simplemobiletools.gallery.adapters
+package com.simplemobiletools.gallery.pro.adapters
-import android.content.ContentProviderOperation
-import android.media.ExifInterface
import android.os.Handler
import android.os.Looper
-import android.provider.MediaStore
import android.view.Menu
import android.view.View
import android.view.ViewGroup
+import android.widget.Toast
import com.bumptech.glide.Glide
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.PropertiesDialog
import com.simplemobiletools.commons.dialogs.RenameItemDialog
+import com.simplemobiletools.commons.dialogs.RenameItemsDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.OTG_PATH
import com.simplemobiletools.commons.models.FileDirItem
import com.simplemobiletools.commons.views.FastScroller
import com.simplemobiletools.commons.views.MyRecyclerView
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.dialogs.DeleteWithRememberDialog
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.VIEW_TYPE_LIST
-import com.simplemobiletools.gallery.interfaces.MediaOperationsListener
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
-import com.simplemobiletools.gallery.models.ThumbnailSection
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.dialogs.DeleteWithRememberDialog
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
+import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_LIST
+import com.simplemobiletools.gallery.pro.interfaces.MediaOperationsListener
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.models.ThumbnailSection
import kotlinx.android.synthetic.main.photo_video_item_grid.view.*
import kotlinx.android.synthetic.main.thumbnail_section.view.*
-import java.lang.Exception
-import java.text.SimpleDateFormat
import java.util.*
class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
- val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
+ val allowMultiplePicks: Boolean, val path: String, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private val INSTANT_LOAD_DURATION = 2000L
private val IMAGE_LOAD_DELAY = 100L
- private val BATCH_SIZE = 100
private val ITEM_SECTION = 0
private val ITEM_MEDIUM = 1
private val config = activity.config
- private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST
+ private val viewType = config.getFolderViewType(if (config.showAll) SHOW_ALL else path)
+ private val isListViewType = viewType == VIEW_TYPE_LIST
private var visibleItemPaths = ArrayList()
+ private var rotatedImagePaths = ArrayList()
private var loadImageInstantly = false
private var delayHandler = Handler(Looper.getMainLooper())
private var currentMediaHash = media.hashCode()
@@ -62,14 +61,6 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList
+ val allowLongPress = (!isAGetIntent || allowMultiplePicks) && tmbItem is Medium
+ holder.bindView(tmbItem, tmbItem is Medium, allowLongPress) { itemView, adapterPosition ->
if (tmbItem is Medium) {
setupThumbnail(itemView, tmbItem)
} else {
setupSection(itemView, tmbItem as ThumbnailSection)
}
}
- bindViewHolder(holder, position, view)
+ bindViewHolder(holder)
}
override fun getItemCount() = media.size
@@ -112,19 +103,26 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList 0
- findItem(R.id.cab_restore_recycle_bin_files).isVisible = getSelectedPaths().all { it.startsWith(activity.filesDir.absolutePath) }
+ val selectedItems = getSelectedItems()
+ if (selectedItems.isEmpty()) {
+ return
+ }
- checkHideBtnVisibility(this)
- checkFavoriteBtnVisibility(this)
+ val isOneItemSelected = isOneItemSelected()
+ val selectedPaths = selectedItems.map { it.path } as ArrayList
+ menu.apply {
+ findItem(R.id.cab_rename).isVisible = selectedItems.firstOrNull()?.getIsInRecycleBin() == false
+ findItem(R.id.cab_open_with).isVisible = isOneItemSelected
+ findItem(R.id.cab_confirm_selection).isVisible = isAGetIntent && allowMultiplePicks && selectedKeys.isNotEmpty()
+ findItem(R.id.cab_restore_recycle_bin_files).isVisible = selectedPaths.all { it.startsWith(activity.recycleBinPath) }
+
+ checkHideBtnVisibility(this, selectedItems)
+ checkFavoriteBtnVisibility(this, selectedItems)
}
}
override fun actionItemPressed(id: Int) {
- if (selectedPositions.isEmpty()) {
+ if (selectedKeys.isEmpty()) {
return
}
@@ -139,12 +137,15 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList toggleFavorites(false)
R.id.cab_restore_recycle_bin_files -> restoreFiles()
R.id.cab_share -> shareMedia()
+ R.id.cab_rotate_right -> rotateSelection(90)
+ R.id.cab_rotate_left -> rotateSelection(270)
+ R.id.cab_rotate_one_eighty -> rotateSelection(180)
R.id.cab_copy_to -> copyMoveTo(true)
- R.id.cab_move_to -> copyMoveTo(false)
+ R.id.cab_move_to -> moveFilesTo()
R.id.cab_select_all -> selectAll()
- R.id.cab_open_with -> activity.openPath(getCurrentPath(), true)
+ R.id.cab_open_with -> openPath()
R.id.cab_fix_date_taken -> fixDateTaken()
- R.id.cab_set_as -> activity.setAs(getCurrentPath())
+ R.id.cab_set_as -> setAs()
R.id.cab_delete -> checkDeleteConfirmation()
}
}
@@ -153,12 +154,16 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList 0
- menu.findItem(R.id.cab_unhide).isVisible = hiddenCnt > 0
+ private fun checkHideBtnVisibility(menu: Menu, selectedItems: ArrayList) {
+ val isInRecycleBin = selectedItems.firstOrNull()?.getIsInRecycleBin() == true
+ menu.findItem(R.id.cab_hide).isVisible = !isInRecycleBin && selectedItems.any { !it.isHidden() }
+ menu.findItem(R.id.cab_unhide).isVisible = !isInRecycleBin && selectedItems.any { it.isHidden() }
}
- private fun checkFavoriteBtnVisibility(menu: Menu) {
- var favoriteCnt = 0
- var nonFavoriteCnt = 0
- getSelectedMedia().forEach {
- if (it.isFavorite) {
- favoriteCnt++
- } else {
- nonFavoriteCnt++
- }
- }
-
- menu.findItem(R.id.cab_add_to_favorites).isVisible = nonFavoriteCnt > 0
- menu.findItem(R.id.cab_remove_from_favorites).isVisible = favoriteCnt > 0
+ private fun checkFavoriteBtnVisibility(menu: Menu, selectedItems: ArrayList) {
+ menu.findItem(R.id.cab_add_to_favorites).isVisible = selectedItems.any { !it.isFavorite }
+ menu.findItem(R.id.cab_remove_from_favorites).isVisible = selectedItems.any { it.isFavorite }
}
private fun confirmSelection() {
@@ -202,8 +188,9 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList 1) {
+ if (selectedKeys.size == 1 && selectedKeys.first() != -1) {
+ activity.shareMediumPath(getSelectedItems().first().path)
+ } else if (selectedKeys.size > 1) {
activity.shareMediaPaths(getSelectedPaths())
}
}
+ private fun rotateSelection(degrees: Int) {
+ activity.toast(R.string.saving)
+ Thread {
+ val paths = getSelectedPaths().filter { it.isImageFast() }
+ var fileCnt = paths.size
+ rotatedImagePaths.clear()
+ paths.forEach {
+ rotatedImagePaths.add(it)
+ activity.saveRotatedImageToFile(it, it, degrees, true) {
+ fileCnt--
+ if (fileCnt == 0) {
+ activity.runOnUiThread {
+ listener?.refreshItems()
+ finishActMode()
+ }
+ }
+ }
+ }
+ }.start()
+ }
+
+ private fun moveFilesTo() {
+ activity.handleDeletePasswordProtection {
+ copyMoveTo(false)
+ }
+ }
+
private fun copyMoveTo(isCopyOperation: Boolean) {
val paths = getSelectedPaths()
- val fileDirItems = paths.map {
+ val recycleBinPath = activity.recycleBinPath
+ val fileDirItems = paths.asSequence().filter { isCopyOperation || !it.startsWith(recycleBinPath) }.map {
FileDirItem(it, it.getFilenameFromPath())
- } as ArrayList
+ }.toMutableList() as ArrayList
+
+ if (!isCopyOperation && paths.any { it.startsWith(recycleBinPath) }) {
+ activity.toast(R.string.moving_recycle_bin_items_disabled, Toast.LENGTH_LONG)
+ }
+
+ if (fileDirItems.isEmpty()) {
+ return
+ }
activity.tryCopyMoveFilesTo(fileDirItems, isCopyOperation) {
config.tempFolderPath = ""
@@ -283,60 +325,26 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList()
- val mediumDao = activity.galleryDB.MediumDao()
- val paths = getSelectedPaths()
- for (path in paths) {
- val dateTime = ExifInterface(path).getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)
- ?: ExifInterface(path).getAttribute(ExifInterface.TAG_DATETIME) ?: continue
-
- // some formats contain a "T" in the middle, some don't
- // sample dates: 2015-07-26T14:55:23, 2018:09:05 15:09:05
- val t = if (dateTime.substring(10, 11) == "T") "\'T\'" else " "
- val separator = dateTime.substring(4, 5)
- val format = "yyyy${separator}MM${separator}dd${t}kk:mm:ss"
- val formatter = SimpleDateFormat(format, Locale.getDefault())
- val timestamp = formatter.parse(dateTime).time
-
- val uri = activity.getFileUri(path)
- ContentProviderOperation.newUpdate(uri).apply {
- val selection = "${MediaStore.Images.Media.DATA} = ?"
- val selectionArgs = arrayOf(path)
- withSelection(selection, selectionArgs)
- withValue(MediaStore.Images.Media.DATE_TAKEN, timestamp)
- operations.add(build())
- }
-
- if (operations.size % BATCH_SIZE == 0) {
- activity.contentResolver.applyBatch(MediaStore.AUTHORITY, operations)
- operations.clear()
- }
-
- mediumDao.updateFavoriteDateTaken(path, timestamp)
- }
-
- activity.contentResolver.applyBatch(MediaStore.AUTHORITY, operations)
- activity.toast(R.string.dates_fixed_successfully)
- activity.runOnUiThread {
- listener?.refreshItems()
- finishActMode()
- }
- } catch (e: Exception) {
- activity.showErrorToast(e)
+ activity.fixDateTaken(getSelectedPaths()) {
+ listener?.refreshItems()
+ finishActMode()
}
}.start()
}
private fun checkDeleteConfirmation() {
- if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) {
+ if (config.isDeletePasswordProtectionOn) {
+ activity.handleDeletePasswordProtection {
+ deleteFiles()
+ }
+ } else if (config.tempSkipDeleteConfirmation || config.skipDeleteConfirmation) {
deleteFiles()
} else {
askConfirmDelete()
@@ -344,8 +352,15 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList(selectedPositions.size)
- val removeMedia = ArrayList(selectedPositions.size)
-
- if (media.size <= selectedPositions.first()) {
- finishActMode()
- return
- }
-
- val SAFPath = (media[selectedPositions.first()] as Medium).path
+ val SAFPath = getSelectedPaths().firstOrNull { activity.isPathOnSD(it) } ?: getFirstSelectedItemPath() ?: return
activity.handleSAFDialog(SAFPath) {
- selectedPositions.sortedDescending().forEach {
- val thumbnailItem = media.getOrNull(it)
- if (thumbnailItem is Medium) {
- fileDirItems.add(FileDirItem(thumbnailItem.path, thumbnailItem.name))
- removeMedia.add(thumbnailItem)
- }
+ val fileDirItems = ArrayList(selectedKeys.size)
+ val removeMedia = ArrayList(selectedKeys.size)
+ val positions = getSelectedItemPositions()
+
+ getSelectedItems().forEach {
+ fileDirItems.add(FileDirItem(it.path, it.name))
+ removeMedia.add(it)
}
media.removeAll(removeMedia)
listener?.tryDeleteFiles(fileDirItems)
- removeSelectedItems()
+ removeSelectedItems(positions)
}
}
- private fun getSelectedMedia(): List {
- val selectedMedia = ArrayList(selectedPositions.size)
- selectedPositions.forEach {
- (media.getOrNull(it) as? Medium)?.apply {
- selectedMedia.add(this)
- }
- }
- return selectedMedia
- }
+ private fun getSelectedItems() = media.filter { selectedKeys.contains((it as? Medium)?.path?.hashCode()) } as ArrayList
- private fun getSelectedPaths() = getSelectedMedia().map { it.path } as ArrayList
+ private fun getSelectedPaths() = getSelectedItems().map { it.path } as ArrayList
+
+ private fun getFirstSelectedItemPath() = getItemWithKey(selectedKeys.first())?.path
+
+ private fun getItemWithKey(key: Int): Medium? = media.firstOrNull { (it as? Medium)?.path?.hashCode() == key } as? Medium
fun updateMedia(newMedia: ArrayList) {
val thumbnailItems = newMedia.clone() as ArrayList
@@ -436,11 +438,24 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList) : FragmentStatePagerAdapter(fm) {
private val fragments = HashMap()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/asynctasks/GetMediaAsynctask.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/asynctasks/GetMediaAsynctask.kt
similarity index 68%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/asynctasks/GetMediaAsynctask.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/asynctasks/GetMediaAsynctask.kt
index 26004f645..7801c40e5 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/asynctasks/GetMediaAsynctask.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/asynctasks/GetMediaAsynctask.kt
@@ -1,16 +1,16 @@
-package com.simplemobiletools.gallery.asynctasks
+package com.simplemobiletools.gallery.pro.asynctasks
import android.content.Context
import android.os.AsyncTask
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_TAKEN
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.extensions.getFavoritePaths
-import com.simplemobiletools.gallery.helpers.FAVORITES
-import com.simplemobiletools.gallery.helpers.MediaFetcher
-import com.simplemobiletools.gallery.helpers.RECYCLE_BIN
-import com.simplemobiletools.gallery.helpers.SHOW_ALL
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.extensions.getFavoritePaths
+import com.simplemobiletools.gallery.pro.helpers.FAVORITES
+import com.simplemobiletools.gallery.pro.helpers.MediaFetcher
+import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
+import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
import java.util.*
class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage: Boolean = false, val isPickVideo: Boolean = false,
@@ -22,18 +22,19 @@ class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage
val pathToUse = if (showAll) SHOW_ALL else mPath
val getProperDateTaken = context.config.getFileSorting(pathToUse) and SORT_BY_DATE_TAKEN != 0
val favoritePaths = context.getFavoritePaths()
+ val getVideoDurations = context.config.showThumbnailVideoDuration
val media = if (showAll) {
val foldersToScan = mediaFetcher.getFoldersToScan().filter { it != RECYCLE_BIN && it != FAVORITES }
val media = ArrayList()
foldersToScan.forEach {
- val newMedia = mediaFetcher.getFilesFrom(it, isPickImage, isPickVideo, getProperDateTaken, favoritePaths)
+ val newMedia = mediaFetcher.getFilesFrom(it, isPickImage, isPickVideo, getProperDateTaken, favoritePaths, getVideoDurations, false)
media.addAll(newMedia)
}
mediaFetcher.sortMedia(media, context.config.getFileSorting(SHOW_ALL))
media
} else {
- mediaFetcher.getFilesFrom(mPath, isPickImage, isPickVideo, getProperDateTaken, favoritePaths)
+ mediaFetcher.getFilesFrom(mPath, isPickImage, isPickVideo, getProperDateTaken, favoritePaths, getVideoDurations)
}
return mediaFetcher.groupMedia(media, pathToUse)
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/databases/GalleryDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/databases/GalleryDatabase.kt
new file mode 100644
index 000000000..b5ac89748
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/databases/GalleryDatabase.kt
@@ -0,0 +1,60 @@
+package com.simplemobiletools.gallery.pro.databases
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
+import com.simplemobiletools.gallery.pro.interfaces.MediumDao
+import com.simplemobiletools.gallery.pro.interfaces.WidgetsDao
+import com.simplemobiletools.gallery.pro.models.Directory
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.Widget
+
+@Database(entities = [Directory::class, Medium::class, Widget::class], version = 6)
+abstract class GalleryDatabase : RoomDatabase() {
+
+ abstract fun DirectoryDao(): DirectoryDao
+
+ abstract fun MediumDao(): MediumDao
+
+ abstract fun WidgetsDao(): WidgetsDao
+
+ companion object {
+ private var db: GalleryDatabase? = null
+
+ fun getInstance(context: Context): GalleryDatabase {
+ if (db == null) {
+ synchronized(GalleryDatabase::class) {
+ if (db == null) {
+ db = Room.databaseBuilder(context.applicationContext, GalleryDatabase::class.java, "gallery.db")
+ .fallbackToDestructiveMigration()
+ .addMigrations(MIGRATION_4_5)
+ .addMigrations(MIGRATION_5_6)
+ .build()
+ }
+ }
+ }
+ return db!!
+ }
+
+ fun destroyInstance() {
+ db = null
+ }
+
+ private val MIGRATION_4_5 = object : Migration(4, 5) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE media ADD COLUMN video_duration INTEGER default 0 NOT NULL")
+ }
+ }
+
+ private val MIGRATION_5_6 = object : Migration(5, 6) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("CREATE TABLE IF NOT EXISTS `widgets` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `widget_id` INTEGER NOT NULL, `folder_path` TEXT NOT NULL)")
+ database.execSQL("CREATE UNIQUE INDEX `index_widgets_widget_id` ON `widgets` (`widget_id`)")
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ChangeGroupingDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeGroupingDialog.kt
similarity index 93%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ChangeGroupingDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeGroupingDialog.kt
index 9c32dcf55..90d1bdf6c 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ChangeGroupingDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeGroupingDialog.kt
@@ -1,14 +1,14 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
import android.content.DialogInterface
-import android.support.v7.app.AlertDialog
import android.view.View
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.*
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.*
import kotlinx.android.synthetic.main.dialog_change_grouping.view.*
class ChangeGroupingDialog(val activity: BaseSimpleActivity, val path: String = "", val callback: () -> Unit) :
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ChangeSortingDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeSortingDialog.kt
similarity index 89%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ChangeSortingDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeSortingDialog.kt
index dc26bb6bf..5dfbe80cb 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ChangeSortingDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeSortingDialog.kt
@@ -1,15 +1,15 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
import android.content.DialogInterface
-import android.support.v7.app.AlertDialog
import android.view.View
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.helpers.*
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.SHOW_ALL
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
import kotlinx.android.synthetic.main.dialog_change_sorting.view.*
class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorting: Boolean, showFolderCheckbox: Boolean,
@@ -47,6 +47,7 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
currSorting and SORT_BY_SIZE != 0 -> sortingRadio.sorting_dialog_radio_size
currSorting and SORT_BY_DATE_MODIFIED != 0 -> sortingRadio.sorting_dialog_radio_last_modified
currSorting and SORT_BY_DATE_TAKEN != 0 -> sortingRadio.sorting_dialog_radio_date_taken
+ currSorting and SORT_BY_RANDOM != 0 -> sortingRadio.sorting_dialog_radio_random
else -> sortingRadio.sorting_dialog_radio_name
}
sortBtn.isChecked = true
@@ -69,6 +70,7 @@ class ChangeSortingDialog(val activity: BaseSimpleActivity, val isDirectorySorti
R.id.sorting_dialog_radio_path -> SORT_BY_PATH
R.id.sorting_dialog_radio_size -> SORT_BY_SIZE
R.id.sorting_dialog_radio_last_modified -> SORT_BY_DATE_MODIFIED
+ R.id.sorting_dialog_radio_random -> SORT_BY_RANDOM
else -> SORT_BY_DATE_TAKEN
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeViewTypeDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeViewTypeDialog.kt
new file mode 100644
index 000000000..78537069a
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ChangeViewTypeDialog.kt
@@ -0,0 +1,73 @@
+package com.simplemobiletools.gallery.pro.dialogs
+
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.extensions.beVisibleIf
+import com.simplemobiletools.commons.extensions.setupDialogStuff
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
+import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_GRID
+import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_LIST
+import kotlinx.android.synthetic.main.dialog_change_view_type.view.*
+
+class ChangeViewTypeDialog(val activity: BaseSimpleActivity, val fromFoldersView: Boolean, val path: String = "", val callback: () -> Unit) {
+ private var view: View
+ private var config = activity.config
+ private var pathToUse = if (path.isEmpty()) SHOW_ALL else path
+
+ init {
+ view = activity.layoutInflater.inflate(R.layout.dialog_change_view_type, null).apply {
+ val viewToCheck = if (fromFoldersView) {
+ if (config.viewTypeFolders == VIEW_TYPE_GRID) {
+ change_view_type_dialog_radio_grid.id
+ } else {
+ change_view_type_dialog_radio_list.id
+ }
+ } else {
+ val currViewType = config.getFolderViewType(pathToUse)
+ if (currViewType == VIEW_TYPE_GRID) {
+ change_view_type_dialog_radio_grid.id
+ } else {
+ change_view_type_dialog_radio_list.id
+ }
+ }
+
+ change_view_type_dialog_radio.check(viewToCheck)
+ change_view_type_dialog_group_direct_subfolders.apply {
+ beVisibleIf(fromFoldersView)
+ isChecked = config.groupDirectSubfolders
+ }
+
+ change_view_type_dialog_use_for_this_folder.apply {
+ beVisibleIf(!fromFoldersView)
+ isChecked = config.hasCustomViewType(pathToUse)
+ }
+ }
+
+ AlertDialog.Builder(activity)
+ .setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
+ .setNegativeButton(R.string.cancel, null)
+ .create().apply {
+ activity.setupDialogStuff(view, this)
+ }
+ }
+
+ private fun dialogConfirmed() {
+ val viewType = if (view.change_view_type_dialog_radio.checkedRadioButtonId == view.change_view_type_dialog_radio_grid.id) VIEW_TYPE_GRID else VIEW_TYPE_LIST
+ if (fromFoldersView) {
+ config.viewTypeFolders = viewType
+ config.groupDirectSubfolders = view.change_view_type_dialog_group_direct_subfolders.isChecked
+ } else {
+ if (view.change_view_type_dialog_use_for_this_folder.isChecked) {
+ config.saveFolderViewType(pathToUse, viewType)
+ } else {
+ config.removeFolderViewType(pathToUse)
+ config.viewTypeFiles = viewType
+ }
+ }
+
+ callback()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/CustomAspectRatioDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/CustomAspectRatioDialog.kt
new file mode 100644
index 000000000..830c839de
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/CustomAspectRatioDialog.kt
@@ -0,0 +1,39 @@
+package com.simplemobiletools.gallery.pro.dialogs
+
+import android.widget.EditText
+import androidx.appcompat.app.AlertDialog
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.extensions.setupDialogStuff
+import com.simplemobiletools.commons.extensions.showKeyboard
+import com.simplemobiletools.commons.extensions.value
+import com.simplemobiletools.gallery.pro.R
+import kotlinx.android.synthetic.main.dialog_custom_aspect_ratio.view.*
+
+class CustomAspectRatioDialog(val activity: BaseSimpleActivity, val defaultCustomAspectRatio: Pair?, val callback: (aspectRatio: Pair) -> Unit) {
+ init {
+ val view = activity.layoutInflater.inflate(R.layout.dialog_custom_aspect_ratio, null).apply {
+ aspect_ratio_width.setText(defaultCustomAspectRatio?.first?.toString() ?: "")
+ aspect_ratio_height.setText(defaultCustomAspectRatio?.second?.toString() ?: "")
+ }
+
+ AlertDialog.Builder(activity)
+ .setPositiveButton(R.string.ok, null)
+ .setNegativeButton(R.string.cancel, null)
+ .create().apply {
+ activity.setupDialogStuff(view, this) {
+ showKeyboard(view.aspect_ratio_width)
+ getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+ val width = getViewValue(view.aspect_ratio_width)
+ val height = getViewValue(view.aspect_ratio_height)
+ callback(Pair(width, height))
+ dismiss()
+ }
+ }
+ }
+ }
+
+ private fun getViewValue(view: EditText): Int {
+ val textValue = view.value
+ return if (textValue.isEmpty()) 0 else textValue.toInt()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/DeleteWithRememberDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/DeleteWithRememberDialog.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/DeleteWithRememberDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/DeleteWithRememberDialog.kt
index f07705e35..c570820b8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/DeleteWithRememberDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/DeleteWithRememberDialog.kt
@@ -1,9 +1,9 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
import android.app.Activity
-import android.support.v7.app.AlertDialog
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.gallery.R
+import com.simplemobiletools.gallery.pro.R
import kotlinx.android.synthetic.main.dialog_delete_with_remember.view.*
class DeleteWithRememberDialog(val activity: Activity, val message: String, val callback: (remember: Boolean) -> Unit) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ExcludeFolderDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ExcludeFolderDialog.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ExcludeFolderDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ExcludeFolderDialog.kt
index 0721683e0..8575f75a4 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ExcludeFolderDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ExcludeFolderDialog.kt
@@ -1,15 +1,15 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
import android.view.ViewGroup
import android.widget.RadioButton
import android.widget.RadioGroup
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.getBasePath
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
import kotlinx.android.synthetic.main.dialog_exclude_folder.view.*
class ExcludeFolderDialog(val activity: BaseSimpleActivity, val selectedPaths: List, val callback: () -> Unit) {
@@ -34,11 +34,11 @@ class ExcludeFolderDialog(val activity: BaseSimpleActivity, val selectedPaths: L
}
AlertDialog.Builder(activity)
- .setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() })
+ .setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.create().apply {
- activity.setupDialogStuff(view, this)
- }
+ activity.setupDialogStuff(view, this)
+ }
}
private fun dialogConfirmed() {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/FilterMediaDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/FilterMediaDialog.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/FilterMediaDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/FilterMediaDialog.kt
index 18b404505..0802d5c67 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/FilterMediaDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/FilterMediaDialog.kt
@@ -1,11 +1,11 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.*
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.*
import kotlinx.android.synthetic.main.dialog_filter_media.view.*
class FilterMediaDialog(val activity: BaseSimpleActivity, val callback: (result: Int) -> Unit) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ManageBottomActionsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ManageBottomActionsDialog.kt
similarity index 88%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ManageBottomActionsDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ManageBottomActionsDialog.kt
index 0bd797c54..760f51be2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ManageBottomActionsDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ManageBottomActionsDialog.kt
@@ -1,11 +1,11 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.*
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.*
import kotlinx.android.synthetic.main.dialog_manage_bottom_actions.view.*
class ManageBottomActionsDialog(val activity: BaseSimpleActivity, val callback: (result: Int) -> Unit) {
@@ -26,6 +26,7 @@ class ManageBottomActionsDialog(val activity: BaseSimpleActivity, val callback:
manage_bottom_actions_toggle_visibility.isChecked = actions and BOTTOM_ACTION_TOGGLE_VISIBILITY != 0
manage_bottom_actions_rename.isChecked = actions and BOTTOM_ACTION_RENAME != 0
manage_bottom_actions_set_as.isChecked = actions and BOTTOM_ACTION_SET_AS != 0
+ manage_bottom_actions_copy.isChecked = actions and BOTTOM_ACTION_COPY != 0
}
AlertDialog.Builder(activity)
@@ -63,6 +64,8 @@ class ManageBottomActionsDialog(val activity: BaseSimpleActivity, val callback:
result += BOTTOM_ACTION_RENAME
if (manage_bottom_actions_set_as.isChecked)
result += BOTTOM_ACTION_SET_AS
+ if (manage_bottom_actions_copy.isChecked)
+ result += BOTTOM_ACTION_COPY
}
activity.config.visibleBottomActions = result
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ManageExtendedDetailsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ManageExtendedDetailsDialog.kt
similarity index 90%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ManageExtendedDetailsDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ManageExtendedDetailsDialog.kt
index 62e50b612..e1d6dbf18 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/ManageExtendedDetailsDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ManageExtendedDetailsDialog.kt
@@ -1,11 +1,11 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.*
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.*
import kotlinx.android.synthetic.main.dialog_manage_extended_details.view.*
class ManageExtendedDetailsDialog(val activity: BaseSimpleActivity, val callback: (result: Int) -> Unit) {
@@ -31,8 +31,8 @@ class ManageExtendedDetailsDialog(val activity: BaseSimpleActivity, val callback
.setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.create().apply {
- activity.setupDialogStuff(view, this)
- }
+ activity.setupDialogStuff(view, this)
+ }
}
private fun dialogConfirmed() {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/OtherAspectRatioDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/OtherAspectRatioDialog.kt
new file mode 100644
index 000000000..4fe770c8c
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/OtherAspectRatioDialog.kt
@@ -0,0 +1,74 @@
+package com.simplemobiletools.gallery.pro.dialogs
+
+import androidx.appcompat.app.AlertDialog
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.extensions.setupDialogStuff
+import com.simplemobiletools.gallery.pro.R
+import kotlinx.android.synthetic.main.dialog_other_aspect_ratio.view.*
+
+class OtherAspectRatioDialog(val activity: BaseSimpleActivity, val lastOtherAspectRatio: Pair?, val callback: (aspectRatio: Pair) -> Unit) {
+ private val dialog: AlertDialog
+
+ init {
+ val view = activity.layoutInflater.inflate(R.layout.dialog_other_aspect_ratio, null).apply {
+ other_aspect_ratio_2_1.setOnClickListener { ratioPicked(Pair(2, 1)) }
+ other_aspect_ratio_3_2.setOnClickListener { ratioPicked(Pair(3, 2)) }
+ other_aspect_ratio_4_3.setOnClickListener { ratioPicked(Pair(4, 3)) }
+ other_aspect_ratio_5_3.setOnClickListener { ratioPicked(Pair(5, 3)) }
+ other_aspect_ratio_16_9.setOnClickListener { ratioPicked(Pair(16, 9)) }
+ other_aspect_ratio_19_9.setOnClickListener { ratioPicked(Pair(19, 9)) }
+ other_aspect_ratio_custom.setOnClickListener { customRatioPicked() }
+
+ other_aspect_ratio_1_2.setOnClickListener { ratioPicked(Pair(1, 2)) }
+ other_aspect_ratio_2_3.setOnClickListener { ratioPicked(Pair(2, 3)) }
+ other_aspect_ratio_3_4.setOnClickListener { ratioPicked(Pair(3, 4)) }
+ other_aspect_ratio_3_5.setOnClickListener { ratioPicked(Pair(3, 5)) }
+ other_aspect_ratio_9_16.setOnClickListener { ratioPicked(Pair(9, 16)) }
+ other_aspect_ratio_9_19.setOnClickListener { ratioPicked(Pair(9, 19)) }
+
+ val radio1SelectedItemId = when (lastOtherAspectRatio) {
+ Pair(2, 1) -> other_aspect_ratio_2_1.id
+ Pair(3, 2) -> other_aspect_ratio_3_2.id
+ Pair(4, 3) -> other_aspect_ratio_4_3.id
+ Pair(5, 3) -> other_aspect_ratio_5_3.id
+ Pair(16, 9) -> other_aspect_ratio_16_9.id
+ Pair(19, 9) -> other_aspect_ratio_19_9.id
+ else -> 0
+ }
+ other_aspect_ratio_dialog_radio_1.check(radio1SelectedItemId)
+
+ val radio2SelectedItemId = when (lastOtherAspectRatio) {
+ Pair(1, 2) -> other_aspect_ratio_1_2.id
+ Pair(2, 3) -> other_aspect_ratio_2_3.id
+ Pair(3, 4) -> other_aspect_ratio_3_4.id
+ Pair(3, 5) -> other_aspect_ratio_3_5.id
+ Pair(9, 16) -> other_aspect_ratio_9_16.id
+ Pair(9, 19) -> other_aspect_ratio_9_19.id
+ else -> 0
+ }
+ other_aspect_ratio_dialog_radio_2.check(radio2SelectedItemId)
+
+ if (radio1SelectedItemId == 0 && radio2SelectedItemId == 0) {
+ other_aspect_ratio_dialog_radio_1.check(other_aspect_ratio_custom.id)
+ }
+ }
+
+ dialog = AlertDialog.Builder(activity)
+ .setNegativeButton(R.string.cancel, null)
+ .create().apply {
+ activity.setupDialogStuff(view, this)
+ }
+ }
+
+ private fun customRatioPicked() {
+ CustomAspectRatioDialog(activity, lastOtherAspectRatio) {
+ callback(it)
+ dialog.dismiss()
+ }
+ }
+
+ private fun ratioPicked(pair: Pair) {
+ callback(pair)
+ dialog.dismiss()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/PickDirectoryDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/PickDirectoryDialog.kt
new file mode 100644
index 000000000..2adcb6835
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/PickDirectoryDialog.kt
@@ -0,0 +1,151 @@
+package com.simplemobiletools.gallery.pro.dialogs
+
+import android.view.KeyEvent
+import androidx.appcompat.app.AlertDialog
+import androidx.recyclerview.widget.RecyclerView
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.dialogs.FilePickerDialog
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.views.MyGridLayoutManager
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.DirectoryAdapter
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_GRID
+import com.simplemobiletools.gallery.pro.models.Directory
+import kotlinx.android.synthetic.main.dialog_directory_picker.view.*
+
+class PickDirectoryDialog(val activity: BaseSimpleActivity, val sourcePath: String, showOtherFolderButton: Boolean, val callback: (path: String) -> Unit) {
+ private var dialog: AlertDialog
+ private var shownDirectories = ArrayList()
+ private var allDirectories = ArrayList()
+ private var openedSubfolders = arrayListOf("")
+ private var view = activity.layoutInflater.inflate(R.layout.dialog_directory_picker, null)
+ private var isGridViewType = activity.config.viewTypeFolders == VIEW_TYPE_GRID
+ private var showHidden = activity.config.shouldShowHidden
+ private var currentPathPrefix = ""
+
+ init {
+ (view.directories_grid.layoutManager as MyGridLayoutManager).apply {
+ orientation = if (activity.config.scrollHorizontally && isGridViewType) RecyclerView.HORIZONTAL else RecyclerView.VERTICAL
+ spanCount = if (isGridViewType) activity.config.dirColumnCnt else 1
+ }
+
+ val builder = AlertDialog.Builder(activity)
+ .setPositiveButton(R.string.ok, null)
+ .setNegativeButton(R.string.cancel, null)
+ .setOnKeyListener { dialogInterface, i, keyEvent ->
+ if (keyEvent.action == KeyEvent.ACTION_UP && i == KeyEvent.KEYCODE_BACK) {
+ backPressed()
+ }
+ true
+ }
+
+ if (showOtherFolderButton) {
+ builder.setNeutralButton(R.string.other_folder) { dialogInterface, i -> showOtherFolder() }
+ }
+
+ dialog = builder.create().apply {
+ activity.setupDialogStuff(view, this, R.string.select_destination) {
+ view.directories_show_hidden.beVisibleIf(!context.config.shouldShowHidden)
+ view.directories_show_hidden.setOnClickListener {
+ activity.handleHiddenFolderPasswordProtection {
+ view.directories_show_hidden.beGone()
+ showHidden = true
+ fetchDirectories(true)
+ }
+ }
+ }
+ }
+
+ fetchDirectories(false)
+ }
+
+ private fun fetchDirectories(forceShowHidden: Boolean) {
+ activity.getCachedDirectories(forceShowHidden = forceShowHidden) {
+ if (it.isNotEmpty()) {
+ it.forEach {
+ it.subfoldersMediaCount = it.mediaCnt
+ }
+
+ activity.runOnUiThread {
+ gotDirectories(activity.addTempFolderIfNeeded(it))
+ }
+ }
+ }
+ }
+
+ private fun showOtherFolder() {
+ FilePickerDialog(activity, sourcePath, false, showHidden, true, true) {
+ callback(it)
+ }
+ }
+
+ private fun gotDirectories(newDirs: ArrayList) {
+ if (allDirectories.isEmpty()) {
+ allDirectories = newDirs.clone() as ArrayList
+ }
+ val distinctDirs = newDirs.distinctBy { it.path.getDistinctPath() }.toMutableList() as ArrayList
+ val sortedDirs = activity.getSortedDirectories(distinctDirs)
+ val dirs = activity.getDirsToShow(sortedDirs, allDirectories, currentPathPrefix).clone() as ArrayList
+ if (dirs.hashCode() == shownDirectories.hashCode()) {
+ return
+ }
+
+ shownDirectories = dirs
+ val adapter = DirectoryAdapter(activity, dirs.clone() as ArrayList, null, view.directories_grid, true) {
+ val clickedDir = it as Directory
+ val path = clickedDir.path
+ if (clickedDir.subfoldersCount == 1 || !activity.config.groupDirectSubfolders) {
+ if (path.trimEnd('/') == sourcePath) {
+ activity.toast(R.string.source_and_destination_same)
+ return@DirectoryAdapter
+ } else {
+ callback(path)
+ dialog.dismiss()
+ }
+ } else {
+ currentPathPrefix = path
+ openedSubfolders.add(path)
+ gotDirectories(allDirectories)
+ }
+ }
+
+ val scrollHorizontally = activity.config.scrollHorizontally && isGridViewType
+ val sorting = activity.config.directorySorting
+ view.apply {
+ directories_grid.adapter = adapter
+
+ directories_vertical_fastscroller.isHorizontal = false
+ directories_vertical_fastscroller.beGoneIf(scrollHorizontally)
+
+ directories_horizontal_fastscroller.isHorizontal = true
+ directories_horizontal_fastscroller.beVisibleIf(scrollHorizontally)
+
+ if (scrollHorizontally) {
+ directories_horizontal_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble
+ directories_horizontal_fastscroller.setViews(directories_grid) {
+ directories_horizontal_fastscroller.updateBubbleText(dirs[it].getBubbleText(sorting))
+ }
+ } else {
+ directories_vertical_fastscroller.allowBubbleDisplay = activity.config.showInfoBubble
+ directories_vertical_fastscroller.setViews(directories_grid) {
+ directories_vertical_fastscroller.updateBubbleText(dirs[it].getBubbleText(sorting))
+ }
+ }
+ }
+ }
+
+ private fun backPressed() {
+ if (activity.config.groupDirectSubfolders) {
+ if (currentPathPrefix.isEmpty()) {
+ dialog.dismiss()
+ } else {
+ openedSubfolders.removeAt(openedSubfolders.size - 1)
+ currentPathPrefix = openedSubfolders.last()
+ gotDirectories(allDirectories)
+ }
+ } else {
+ dialog.dismiss()
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/PickMediumDialog.kt
similarity index 76%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/PickMediumDialog.kt
index 1ccef0923..f75a8f301 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/PickMediumDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/PickMediumDialog.kt
@@ -1,32 +1,33 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
-import android.support.v7.widget.GridLayoutManager
+import androidx.appcompat.app.AlertDialog
+import androidx.recyclerview.widget.RecyclerView
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beGoneIf
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.views.MyGridLayoutManager
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.adapters.MediaAdapter
-import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.extensions.getCachedMedia
-import com.simplemobiletools.gallery.helpers.SHOW_ALL
-import com.simplemobiletools.gallery.helpers.VIEW_TYPE_GRID
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.adapters.MediaAdapter
+import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.extensions.getCachedMedia
+import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
+import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_GRID
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
import kotlinx.android.synthetic.main.dialog_medium_picker.view.*
class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val callback: (path: String) -> Unit) {
var dialog: AlertDialog
var shownMedia = ArrayList()
val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null)
- var isGridViewType = activity.config.viewTypeFiles == VIEW_TYPE_GRID
+ val viewType = activity.config.getFolderViewType(if (activity.config.showAll) SHOW_ALL else path)
+ var isGridViewType = viewType == VIEW_TYPE_GRID
init {
(view.media_grid.layoutManager as MyGridLayoutManager).apply {
- orientation = if (activity.config.scrollHorizontally && isGridViewType) GridLayoutManager.HORIZONTAL else GridLayoutManager.VERTICAL
+ orientation = if (activity.config.scrollHorizontally && isGridViewType) RecyclerView.HORIZONTAL else RecyclerView.VERTICAL
spanCount = if (isGridViewType) activity.config.mediaColumnCnt else 1
}
@@ -53,7 +54,7 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
}
private fun showOtherFolder() {
- PickDirectoryDialog(activity, path) {
+ PickDirectoryDialog(activity, path, true) {
callback(it)
dialog.dismiss()
}
@@ -64,7 +65,7 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
return
shownMedia = media
- val adapter = MediaAdapter(activity, shownMedia.clone() as ArrayList, null, true, false, view.media_grid, null) {
+ val adapter = MediaAdapter(activity, shownMedia.clone() as ArrayList, null, true, false, path, view.media_grid, null) {
if (it is Medium) {
callback(it.path)
dialog.dismiss()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ResizeDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ResizeDialog.kt
new file mode 100644
index 000000000..96ef3b50b
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/ResizeDialog.kt
@@ -0,0 +1,76 @@
+package com.simplemobiletools.gallery.pro.dialogs
+
+import android.graphics.Point
+import android.widget.EditText
+import androidx.appcompat.app.AlertDialog
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.gallery.pro.R
+import kotlinx.android.synthetic.main.dialog_resize_image.view.*
+
+class ResizeDialog(val activity: BaseSimpleActivity, val size: Point, val callback: (newSize: Point) -> Unit) {
+ init {
+ val view = activity.layoutInflater.inflate(R.layout.dialog_resize_image, null)
+ val widthView = view.image_width
+ val heightView = view.image_height
+
+ widthView.setText(size.x.toString())
+ heightView.setText(size.y.toString())
+
+ val ratio = size.x / size.y.toFloat()
+
+ widthView.onTextChangeListener {
+ if (widthView.hasFocus()) {
+ var width = getViewValue(widthView)
+ if (width > size.x) {
+ widthView.setText(size.x.toString())
+ width = size.x
+ }
+
+ if (view.keep_aspect_ratio.isChecked) {
+ heightView.setText((width / ratio).toInt().toString())
+ }
+ }
+ }
+
+ heightView.onTextChangeListener {
+ if (heightView.hasFocus()) {
+ var height = getViewValue(heightView)
+ if (height > size.y) {
+ heightView.setText(size.y.toString())
+ height = size.y
+ }
+
+ if (view.keep_aspect_ratio.isChecked) {
+ widthView.setText((height * ratio).toInt().toString())
+ }
+ }
+ }
+
+ AlertDialog.Builder(activity)
+ .setPositiveButton(R.string.ok, null)
+ .setNegativeButton(R.string.cancel, null)
+ .create().apply {
+ activity.setupDialogStuff(view, this, R.string.resize_and_save) {
+ showKeyboard(view.image_width)
+ getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+ val width = getViewValue(widthView)
+ val height = getViewValue(heightView)
+ if (width <= 0 || height <= 0) {
+ activity.toast(R.string.invalid_values)
+ return@setOnClickListener
+ }
+
+ val newSize = Point(getViewValue(widthView), getViewValue(heightView))
+ callback(newSize)
+ dismiss()
+ }
+ }
+ }
+ }
+
+ private fun getViewValue(view: EditText): Int {
+ val textValue = view.value
+ return if (textValue.isEmpty()) 0 else textValue.toInt()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/SaveAsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/SaveAsDialog.kt
similarity index 95%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/SaveAsDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/SaveAsDialog.kt
index 3199ff9d7..614fb7bfc 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/SaveAsDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/SaveAsDialog.kt
@@ -1,11 +1,11 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.*
-import com.simplemobiletools.gallery.R
+import com.simplemobiletools.gallery.pro.R
import kotlinx.android.synthetic.main.dialog_save_as.view.*
class SaveAsDialog(val activity: BaseSimpleActivity, val path: String, val appendFilename: Boolean, val callback: (savePath: String) -> Unit) {
@@ -32,7 +32,7 @@ class SaveAsDialog(val activity: BaseSimpleActivity, val path: String, val appen
save_as_name.setText(name)
save_as_path.setOnClickListener {
- FilePickerDialog(activity, realPath, false, false, true) {
+ FilePickerDialog(activity, realPath, false, false, true, true) {
save_as_path.text = activity.humanizePath(it)
realPath = it
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/SlideshowDialog.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/SlideshowDialog.kt
similarity index 80%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/SlideshowDialog.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/SlideshowDialog.kt
index 58a4935dd..7ab0371a8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/dialogs/SlideshowDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/dialogs/SlideshowDialog.kt
@@ -1,14 +1,13 @@
-package com.simplemobiletools.gallery.dialogs
+package com.simplemobiletools.gallery.pro.dialogs
-import android.support.v7.app.AlertDialog
import android.view.View
+import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.hideKeyboard
import com.simplemobiletools.commons.extensions.setupDialogStuff
-import com.simplemobiletools.commons.extensions.toast
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.config
-import com.simplemobiletools.gallery.helpers.SLIDESHOW_DEFAULT_INTERVAL
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.SLIDESHOW_DEFAULT_INTERVAL
import kotlinx.android.synthetic.main.dialog_slideshow.view.*
class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit) {
@@ -29,11 +28,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
activity.hideKeyboard(v)
}
- include_photos_holder.setOnClickListener {
- interval_value.clearFocus()
- include_photos.toggle()
- }
-
include_videos_holder.setOnClickListener {
interval_value.clearFocus()
include_videos.toggle()
@@ -73,11 +67,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
activity.setupDialogStuff(view, this) {
hideKeyboard()
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
- if (!view.include_photos.isChecked && !view.include_videos.isChecked && !view.include_gifs.isChecked) {
- activity.toast(R.string.no_media_for_slideshow)
- return@setOnClickListener
- }
-
storeValues()
callback()
dismiss()
@@ -90,7 +79,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
val config = activity.config
view.apply {
interval_value.setText(config.slideshowInterval.toString())
- include_photos.isChecked = config.slideshowIncludePhotos
include_videos.isChecked = config.slideshowIncludeVideos
include_gifs.isChecked = config.slideshowIncludeGIFs
random_order.isChecked = config.slideshowRandomOrder
@@ -107,7 +95,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
activity.config.apply {
slideshowInterval = interval.toInt()
- slideshowIncludePhotos = view.include_photos.isChecked
slideshowIncludeVideos = view.include_videos.isChecked
slideshowIncludeGIFs = view.include_gifs.isChecked
slideshowRandomOrder = view.random_order.isChecked
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Activity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Activity.kt
new file mode 100644
index 000000000..487f45272
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Activity.kt
@@ -0,0 +1,490 @@
+package com.simplemobiletools.gallery.pro.extensions
+
+import android.annotation.TargetApi
+import android.app.Activity
+import android.content.ContentProviderOperation
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Matrix
+import android.media.ExifInterface
+import android.os.Build
+import android.provider.MediaStore
+import android.util.DisplayMetrics
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import com.bumptech.glide.Glide
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.dialogs.ConfirmationDialog
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.*
+import com.simplemobiletools.commons.models.FAQItem
+import com.simplemobiletools.commons.models.FileDirItem
+import com.simplemobiletools.gallery.pro.BuildConfig
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.activities.SimpleActivity
+import com.simplemobiletools.gallery.pro.dialogs.PickDirectoryDialog
+import com.simplemobiletools.gallery.pro.helpers.NOMEDIA
+import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
+import com.simplemobiletools.gallery.pro.interfaces.MediumDao
+import com.squareup.picasso.Picasso
+import java.io.File
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Activity.sharePath(path: String) {
+ sharePathIntent(path, BuildConfig.APPLICATION_ID)
+}
+
+fun Activity.sharePaths(paths: ArrayList) {
+ sharePathsIntent(paths, BuildConfig.APPLICATION_ID)
+}
+
+fun Activity.shareMediumPath(path: String) {
+ sharePath(path)
+}
+
+fun Activity.shareMediaPaths(paths: ArrayList) {
+ sharePaths(paths)
+}
+
+fun Activity.setAs(path: String) {
+ setAsIntent(path, BuildConfig.APPLICATION_ID)
+}
+
+fun Activity.openPath(path: String, forceChooser: Boolean) {
+ openPathIntent(path, forceChooser, BuildConfig.APPLICATION_ID)
+}
+
+fun Activity.openEditor(path: String, forceChooser: Boolean = false) {
+ openEditorIntent(path, forceChooser, BuildConfig.APPLICATION_ID)
+}
+
+fun Activity.launchCamera() {
+ val intent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+ if (intent.resolveActivity(packageManager) != null) {
+ startActivity(intent)
+ } else {
+ toast(R.string.no_app_found)
+ }
+}
+
+fun SimpleActivity.launchAbout() {
+ val licenses = LICENSE_GLIDE or LICENSE_CROPPER or LICENSE_RTL or LICENSE_SUBSAMPLING or LICENSE_PATTERN or LICENSE_REPRINT or LICENSE_GIF_DRAWABLE or
+ LICENSE_PICASSO or LICENSE_EXOPLAYER or LICENSE_PANORAMA_VIEW or LICENSE_SANSELAN or LICENSE_FILTERS or LICENSE_GESTURE_VIEWS
+
+ val faqItems = arrayListOf(
+ FAQItem(R.string.faq_5_title_commons, R.string.faq_5_text_commons),
+ FAQItem(R.string.faq_1_title, R.string.faq_1_text),
+ FAQItem(R.string.faq_2_title, R.string.faq_2_text),
+ FAQItem(R.string.faq_3_title, R.string.faq_3_text),
+ FAQItem(R.string.faq_4_title, R.string.faq_4_text),
+ FAQItem(R.string.faq_5_title, R.string.faq_5_text),
+ FAQItem(R.string.faq_6_title, R.string.faq_6_text),
+ FAQItem(R.string.faq_7_title, R.string.faq_7_text),
+ FAQItem(R.string.faq_8_title, R.string.faq_8_text),
+ FAQItem(R.string.faq_10_title, R.string.faq_10_text),
+ FAQItem(R.string.faq_11_title, R.string.faq_11_text),
+ FAQItem(R.string.faq_12_title, R.string.faq_12_text),
+ FAQItem(R.string.faq_13_title, R.string.faq_13_text),
+ FAQItem(R.string.faq_14_title, R.string.faq_14_text),
+ FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
+ FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons))
+
+ startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
+}
+
+fun AppCompatActivity.showSystemUI(toggleActionBarVisibility: Boolean) {
+ if (toggleActionBarVisibility) {
+ supportActionBar?.show()
+ }
+
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+}
+
+fun AppCompatActivity.hideSystemUI(toggleActionBarVisibility: Boolean) {
+ if (toggleActionBarVisibility) {
+ supportActionBar?.hide()
+ }
+
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LOW_PROFILE or
+ View.SYSTEM_UI_FLAG_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_IMMERSIVE
+}
+
+fun BaseSimpleActivity.addNoMedia(path: String, callback: () -> Unit) {
+ val file = File(path, NOMEDIA)
+ if (file.exists()) {
+ callback()
+ return
+ }
+
+ if (needsStupidWritePermissions(path)) {
+ handleSAFDialog(file.absolutePath) {
+ val fileDocument = getDocumentFile(path)
+ if (fileDocument?.exists() == true && fileDocument.isDirectory) {
+ fileDocument.createFile("", NOMEDIA)
+ applicationContext.scanFileRecursively(file) {
+ callback()
+ }
+ } else {
+ toast(R.string.unknown_error_occurred)
+ callback()
+ }
+ }
+ } else {
+ try {
+ file.createNewFile()
+ applicationContext.scanFileRecursively(file) {
+ callback()
+ }
+ } catch (e: Exception) {
+ showErrorToast(e)
+ callback()
+ }
+ }
+}
+
+fun BaseSimpleActivity.removeNoMedia(path: String, callback: (() -> Unit)? = null) {
+ val file = File(path, NOMEDIA)
+ if (!file.exists()) {
+ callback?.invoke()
+ return
+ }
+
+ tryDeleteFileDirItem(file.toFileDirItem(applicationContext), false, false) {
+ scanPathRecursively(file.parent)
+ callback?.invoke()
+ }
+}
+
+fun BaseSimpleActivity.toggleFileVisibility(oldPath: String, hide: Boolean, callback: ((newPath: String) -> Unit)? = null) {
+ val path = oldPath.getParentPath()
+ var filename = oldPath.getFilenameFromPath()
+ if ((hide && filename.startsWith('.')) || (!hide && !filename.startsWith('.'))) {
+ callback?.invoke(oldPath)
+ return
+ }
+
+ filename = if (hide) {
+ ".${filename.trimStart('.')}"
+ } else {
+ filename.substring(1, filename.length)
+ }
+
+ val newPath = "$path/$filename"
+ renameFile(oldPath, newPath) {
+ callback?.invoke(newPath)
+ Thread {
+ updateDBMediaPath(oldPath, newPath)
+ }.start()
+ }
+}
+
+fun BaseSimpleActivity.tryCopyMoveFilesTo(fileDirItems: ArrayList, isCopyOperation: Boolean, callback: (destinationPath: String) -> Unit) {
+ if (fileDirItems.isEmpty()) {
+ toast(R.string.unknown_error_occurred)
+ return
+ }
+
+ val source = fileDirItems[0].getParentPath()
+ PickDirectoryDialog(this, source, true) {
+ copyMoveFilesTo(fileDirItems, source.trimEnd('/'), it, isCopyOperation, true, config.shouldShowHidden, callback)
+ }
+}
+
+fun BaseSimpleActivity.tryDeleteFileDirItem(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, deleteFromDatabase: Boolean,
+ callback: ((wasSuccess: Boolean) -> Unit)? = null) {
+ deleteFile(fileDirItem, allowDeleteFolder) {
+ if (deleteFromDatabase) {
+ Thread {
+ deleteDBPath(galleryDB.MediumDao(), fileDirItem.path)
+ runOnUiThread {
+ callback?.invoke(it)
+ }
+ }.start()
+ } else {
+ callback?.invoke(it)
+ }
+ }
+}
+
+fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList, mediumDao: MediumDao = galleryDB.MediumDao(), callback: ((wasSuccess: Boolean) -> Unit)?) {
+ Thread {
+ var pathsCnt = paths.size
+ paths.forEach {
+ val file = File(it)
+ val internalFile = File(recycleBinPath, it)
+ try {
+ if (file.copyRecursively(internalFile, true)) {
+ mediumDao.updateDeleted("$RECYCLE_BIN$it", System.currentTimeMillis(), it)
+ pathsCnt--
+ }
+ } catch (e: Exception) {
+ showErrorToast(e)
+ return@forEach
+ }
+ }
+ callback?.invoke(pathsCnt == 0)
+ }.start()
+}
+
+fun BaseSimpleActivity.restoreRecycleBinPath(path: String, callback: () -> Unit) {
+ restoreRecycleBinPaths(arrayListOf(path), galleryDB.MediumDao(), callback)
+}
+
+fun BaseSimpleActivity.restoreRecycleBinPaths(paths: ArrayList, mediumDao: MediumDao = galleryDB.MediumDao(), callback: () -> Unit) {
+ Thread {
+ val newPaths = ArrayList()
+ paths.forEach {
+ val source = it
+ val destination = it.removePrefix(recycleBinPath)
+
+ var inputStream: InputStream? = null
+ var out: OutputStream? = null
+ try {
+ out = getFileOutputStreamSync(destination, source.getMimeType())
+ inputStream = getFileInputStreamSync(source)!!
+ inputStream.copyTo(out!!)
+ if (File(source).length() == File(destination).length()) {
+ mediumDao.updateDeleted(destination.removePrefix(recycleBinPath), 0, "$RECYCLE_BIN$destination")
+ }
+ newPaths.add(destination)
+ } catch (e: Exception) {
+ showErrorToast(e)
+ } finally {
+ inputStream?.close()
+ out?.close()
+ }
+ }
+
+ runOnUiThread {
+ callback()
+ }
+
+ fixDateTaken(newPaths)
+ }.start()
+}
+
+fun BaseSimpleActivity.emptyTheRecycleBin(callback: (() -> Unit)? = null) {
+ Thread {
+ recycleBin.deleteRecursively()
+ galleryDB.MediumDao().clearRecycleBin()
+ galleryDB.DirectoryDao().deleteRecycleBin()
+ toast(R.string.recycle_bin_emptied)
+ callback?.invoke()
+ }.start()
+}
+
+fun BaseSimpleActivity.emptyAndDisableTheRecycleBin(callback: () -> Unit) {
+ Thread {
+ emptyTheRecycleBin {
+ config.useRecycleBin = false
+ callback()
+ }
+ }.start()
+}
+
+fun BaseSimpleActivity.showRecycleBinEmptyingDialog(callback: () -> Unit) {
+ ConfirmationDialog(this, "", R.string.empty_recycle_bin_confirmation, R.string.yes, R.string.no) {
+ callback()
+ }
+}
+
+fun BaseSimpleActivity.updateFavoritePaths(fileDirItems: ArrayList, destination: String) {
+ Thread {
+ fileDirItems.forEach {
+ val newPath = "$destination/${it.name}"
+ updateDBMediaPath(it.path, newPath)
+ }
+ }.start()
+}
+
+fun Activity.hasNavBar(): Boolean {
+ val display = windowManager.defaultDisplay
+
+ val realDisplayMetrics = DisplayMetrics()
+ display.getRealMetrics(realDisplayMetrics)
+
+ val displayMetrics = DisplayMetrics()
+ display.getMetrics(displayMetrics)
+
+ return (realDisplayMetrics.widthPixels - displayMetrics.widthPixels > 0) || (realDisplayMetrics.heightPixels - displayMetrics.heightPixels > 0)
+}
+
+fun Activity.fixDateTaken(paths: ArrayList, callback: (() -> Unit)? = null) {
+ val BATCH_SIZE = 50
+ toast(R.string.fixing)
+ try {
+ var didUpdateFile = false
+ val operations = ArrayList()
+ val mediumDao = galleryDB.MediumDao()
+ for (path in paths) {
+ val dateTime = ExifInterface(path).getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)
+ ?: ExifInterface(path).getAttribute(ExifInterface.TAG_DATETIME) ?: continue
+
+ // some formats contain a "T" in the middle, some don't
+ // sample dates: 2015-07-26T14:55:23, 2018:09:05 15:09:05
+ val t = if (dateTime.substring(10, 11) == "T") "\'T\'" else " "
+ val separator = dateTime.substring(4, 5)
+ val format = "yyyy${separator}MM${separator}dd${t}kk:mm:ss"
+ val formatter = SimpleDateFormat(format, Locale.getDefault())
+ val timestamp = formatter.parse(dateTime).time
+
+ val uri = getFileUri(path)
+ ContentProviderOperation.newUpdate(uri).apply {
+ val selection = "${MediaStore.Images.Media.DATA} = ?"
+ val selectionArgs = arrayOf(path)
+ withSelection(selection, selectionArgs)
+ withValue(MediaStore.Images.Media.DATE_TAKEN, timestamp)
+ operations.add(build())
+ }
+
+ if (operations.size % BATCH_SIZE == 0) {
+ contentResolver.applyBatch(MediaStore.AUTHORITY, operations)
+ operations.clear()
+ }
+
+ mediumDao.updateFavoriteDateTaken(path, timestamp)
+ didUpdateFile = true
+ }
+
+ val resultSize = contentResolver.applyBatch(MediaStore.AUTHORITY, operations).size
+ if (resultSize == 0) {
+ didUpdateFile = false
+ rescanPaths(paths)
+ }
+
+ toast(if (didUpdateFile) R.string.dates_fixed_successfully else R.string.unknown_error_occurred)
+ runOnUiThread {
+ callback?.invoke()
+ }
+ } catch (e: Exception) {
+ showErrorToast(e)
+ }
+}
+
+fun BaseSimpleActivity.saveRotatedImageToFile(oldPath: String, newPath: String, degrees: Int, showToasts: Boolean, callback: () -> Unit) {
+ var newDegrees = degrees
+ if (newDegrees < 0) {
+ newDegrees += 360
+ }
+
+ if (oldPath == newPath && oldPath.isJpg()) {
+ if (tryRotateByExif(oldPath, newDegrees, showToasts, callback)) {
+ return
+ }
+ }
+
+ val tmpPath = "$recycleBinPath/.tmp_${newPath.getFilenameFromPath()}"
+ val tmpFileDirItem = FileDirItem(tmpPath, tmpPath.getFilenameFromPath())
+ try {
+ getFileOutputStream(tmpFileDirItem) {
+ if (it == null) {
+ if (showToasts) {
+ toast(R.string.unknown_error_occurred)
+ }
+ return@getFileOutputStream
+ }
+
+ val oldLastModified = File(oldPath).lastModified()
+ if (oldPath.isJpg()) {
+ copyFile(oldPath, tmpPath)
+ saveExifRotation(ExifInterface(tmpPath), newDegrees)
+ } else {
+ val inputstream = getFileInputStreamSync(oldPath)
+ val bitmap = BitmapFactory.decodeStream(inputstream)
+ saveFile(tmpPath, bitmap, it as FileOutputStream, newDegrees)
+ }
+
+ if (getDoesFilePathExist(newPath)) {
+ tryDeleteFileDirItem(FileDirItem(newPath, newPath.getFilenameFromPath()), false, true)
+ }
+
+ copyFile(tmpPath, newPath)
+ scanPathRecursively(newPath)
+ fileRotatedSuccessfully(newPath, oldLastModified)
+
+ it.flush()
+ it.close()
+ callback.invoke()
+ }
+ } catch (e: OutOfMemoryError) {
+ if (showToasts) {
+ toast(R.string.out_of_memory_error)
+ }
+ } catch (e: Exception) {
+ if (showToasts) {
+ showErrorToast(e)
+ }
+ } finally {
+ tryDeleteFileDirItem(tmpFileDirItem, false, true)
+ }
+}
+
+@TargetApi(Build.VERSION_CODES.N)
+fun Activity.tryRotateByExif(path: String, degrees: Int, showToasts: Boolean, callback: () -> Unit): Boolean {
+ return try {
+ val file = File(path)
+ val oldLastModified = file.lastModified()
+ if (saveImageRotation(path, degrees)) {
+ fileRotatedSuccessfully(path, oldLastModified)
+ callback.invoke()
+ if (showToasts) {
+ toast(R.string.file_saved)
+ }
+ true
+ } else {
+ false
+ }
+ } catch (e: Exception) {
+ if (showToasts) {
+ showErrorToast(e)
+ }
+ false
+ }
+}
+
+fun Activity.fileRotatedSuccessfully(path: String, lastModified: Long) {
+ if (config.keepLastModified) {
+ File(path).setLastModified(lastModified)
+ updateLastModified(path, lastModified)
+ }
+
+ Picasso.get().invalidate(path.getFileKey())
+ // we cannot refresh a specific image in Glide Cache, so just clear it all
+ val glide = Glide.get(applicationContext)
+ glide.clearDiskCache()
+ runOnUiThread {
+ glide.clearMemory()
+ }
+}
+
+fun BaseSimpleActivity.copyFile(source: String, destination: String) {
+ var inputStream: InputStream? = null
+ var out: OutputStream? = null
+ try {
+ out = getFileOutputStreamSync(destination, source.getMimeType())
+ inputStream = getFileInputStreamSync(source)
+ inputStream?.copyTo(out!!)
+ } finally {
+ inputStream?.close()
+ out?.close()
+ }
+}
+
+fun saveFile(path: String, bitmap: Bitmap, out: FileOutputStream, degrees: Int) {
+ val matrix = Matrix()
+ matrix.postRotate(degrees.toFloat())
+ val bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
+ bmp.compress(path.getCompressionFormat(), 90, out)
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/ArrayList.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/ArrayList.kt
similarity index 72%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/ArrayList.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/ArrayList.kt
index b72d4cf52..27289c37f 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/ArrayList.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/ArrayList.kt
@@ -1,7 +1,7 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.models.Medium
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.Medium
fun ArrayList.getDirMediaTypes(): Int {
var types = 0
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt
similarity index 57%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Context.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt
index eb41408b1..9e2ce8af0 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Context.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt
@@ -1,5 +1,7 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
@@ -8,7 +10,6 @@ import android.database.sqlite.SQLiteException
import android.graphics.Point
import android.graphics.drawable.PictureDrawable
import android.media.AudioManager
-import android.os.Build
import android.provider.MediaStore
import android.view.WindowManager
import android.widget.ImageView
@@ -19,20 +20,28 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.activities.SettingsActivity
-import com.simplemobiletools.gallery.asynctasks.GetMediaAsynctask
-import com.simplemobiletools.gallery.databases.GalleryDatabase
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.interfaces.DirectoryDao
-import com.simplemobiletools.gallery.interfaces.MediumDao
-import com.simplemobiletools.gallery.models.Directory
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
-import com.simplemobiletools.gallery.svg.SvgSoftwareLayerSetter
-import com.simplemobiletools.gallery.views.MySquareImageView
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.activities.SettingsActivity
+import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
+import com.simplemobiletools.gallery.pro.databases.GalleryDatabase
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
+import com.simplemobiletools.gallery.pro.interfaces.MediumDao
+import com.simplemobiletools.gallery.pro.interfaces.WidgetsDao
+import com.simplemobiletools.gallery.pro.models.Directory
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.svg.SvgSoftwareLayerSetter
+import com.simplemobiletools.gallery.pro.views.MySquareImageView
import pl.droidsonroids.gif.GifDrawable
import java.io.File
+import java.io.FileInputStream
+import java.nio.ByteBuffer
+import java.nio.channels.FileChannel
+import java.util.HashSet
+import java.util.LinkedHashSet
+import kotlin.Comparator
+import kotlin.collections.ArrayList
val Context.portrait get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
val Context.audioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager
@@ -44,11 +53,40 @@ val Context.navigationBarWidth: Int get() = if (navigationBarRight) navigationBa
internal val Context.navigationBarSize: Point
get() = when {
- navigationBarRight -> Point(realScreenSize.x - usableScreenSize.x, usableScreenSize.y)
- navigationBarBottom -> Point(usableScreenSize.x, realScreenSize.y - usableScreenSize.y)
+ navigationBarRight -> Point(newNavigationBarHeight, usableScreenSize.y)
+ navigationBarBottom -> Point(usableScreenSize.x, newNavigationBarHeight)
else -> Point()
}
+internal val Context.newNavigationBarHeight: Int
+ get() {
+ var navigationBarHeight = 0
+ val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
+ if (resourceId > 0) {
+ navigationBarHeight = resources.getDimensionPixelSize(resourceId)
+ }
+ return navigationBarHeight
+ }
+
+internal val Context.statusBarHeight: Int
+ get() {
+ var statusBarHeight = 0
+ val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
+ if (resourceId > 0) {
+ statusBarHeight = resources.getDimensionPixelSize(resourceId)
+ }
+ return statusBarHeight
+ }
+
+internal val Context.actionBarHeight: Int
+ get() {
+ val styledAttributes = theme.obtainStyledAttributes(intArrayOf(android.R.attr.actionBarSize))
+ val actionBarHeight = styledAttributes.getDimension(0, 0f)
+ styledAttributes.recycle()
+ return actionBarHeight.toInt()
+ }
+
+
val Context.usableScreenSize: Point
get() {
val size = Point()
@@ -59,8 +97,7 @@ val Context.usableScreenSize: Point
val Context.realScreenSize: Point
get() {
val size = Point()
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
- windowManager.defaultDisplay.getRealSize(size)
+ windowManager.defaultDisplay.getRealSize(size)
return size
}
@@ -77,6 +114,14 @@ val Context.config: Config get() = Config.newInstance(applicationContext)
val Context.galleryDB: GalleryDatabase get() = GalleryDatabase.getInstance(applicationContext)
+val Context.widgetsDB: WidgetsDao get() = GalleryDatabase.getInstance(applicationContext).WidgetsDao()
+
+val Context.directoryDB: DirectoryDao get() = GalleryDatabase.getInstance(applicationContext).DirectoryDao()
+
+val Context.recycleBin: File get() = filesDir
+
+val Context.recycleBinPath: String get() = filesDir.absolutePath
+
fun Context.movePinnedDirectoriesToFront(dirs: ArrayList): ArrayList {
val foundFolders = ArrayList()
val pinnedFolders = config.pinnedFolders
@@ -96,6 +141,14 @@ fun Context.movePinnedDirectoriesToFront(dirs: ArrayList): ArrayList<
dirs.add(0, newFolder)
}
}
+
+ if (config.useRecycleBin && config.showRecycleBinAtFolders && config.showRecycleBinLast) {
+ val binIndex = dirs.indexOfFirst { it.isRecycleBin() }
+ if (binIndex != -1) {
+ val bin = dirs.removeAt(binIndex)
+ dirs.add(bin)
+ }
+ }
return dirs
}
@@ -104,6 +157,11 @@ fun Context.getSortedDirectories(source: ArrayList): ArrayList
+ if (sorting and SORT_BY_RANDOM != 0) {
+ dirs.shuffle()
+ return movePinnedDirectoriesToFront(dirs)
+ }
+
dirs.sortWith(Comparator { o1, o2 ->
o1 as Directory
o2 as Directory
@@ -124,6 +182,120 @@ fun Context.getSortedDirectories(source: ArrayList): ArrayList, allDirs: ArrayList, currentPathPrefix: String): ArrayList {
+ return if (config.groupDirectSubfolders) {
+ dirs.forEach {
+ it.subfoldersCount = 0
+ it.subfoldersMediaCount = it.mediaCnt
+ }
+
+ val dirFolders = dirs.map { it.path }.sorted().toMutableSet() as HashSet
+ val foldersToShow = getDirectParentSubfolders(dirFolders, currentPathPrefix)
+ val parentDirs = dirs.filter { foldersToShow.contains(it.path) } as ArrayList
+ updateSubfolderCounts(dirs, parentDirs)
+
+ // show the current folder as an available option too, not just subfolders
+ if (currentPathPrefix.isNotEmpty()) {
+ val currentFolder = allDirs.firstOrNull { parentDirs.firstOrNull { it.path == currentPathPrefix } == null && it.path == currentPathPrefix }
+ currentFolder?.apply {
+ subfoldersCount = 1
+ parentDirs.add(this)
+ }
+ }
+
+ parentDirs
+ } else {
+ dirs.forEach { it.subfoldersMediaCount = it.mediaCnt }
+ dirs
+ }
+}
+
+fun Context.getDirectParentSubfolders(folders: HashSet, currentPathPrefix: String): HashSet {
+ val internalPath = internalStoragePath
+ val sdPath = sdCardPath
+ val currentPaths = LinkedHashSet()
+
+ folders.forEach {
+ val path = it
+ if (path != RECYCLE_BIN && path != FAVORITES && !path.equals(internalPath, true) && !path.equals(sdPath, true)) {
+ if (currentPathPrefix.isNotEmpty()) {
+ if (path == currentPathPrefix || File(path).parent.equals(currentPathPrefix, true)) {
+ currentPaths.add(path)
+ }
+ } else if (folders.any { !it.equals(path, true) && (File(path).parent.equals(it, true) || File(it).parent.equals(File(path).parent, true)) }) {
+ // if we have folders like
+ // /storage/emulated/0/Pictures/Images and
+ // /storage/emulated/0/Pictures/Screenshots,
+ // but /storage/emulated/0/Pictures is empty, show Images and Screenshots as separate folders, do not group them at /Pictures
+ val parent = File(path).parent
+ if (folders.contains(parent)) {
+ currentPaths.add(parent)
+ } else {
+ currentPaths.add(path)
+ }
+ } else {
+ currentPaths.add(path)
+ }
+ }
+ }
+
+ var areDirectSubfoldersAvailable = false
+ currentPaths.forEach {
+ val path = it
+ currentPaths.forEach {
+ if (!it.equals(path) && File(it).parent?.equals(path) == true) {
+ areDirectSubfoldersAvailable = true
+ }
+ }
+ }
+
+ if (currentPathPrefix.isEmpty() && folders.contains(RECYCLE_BIN)) {
+ currentPaths.add(RECYCLE_BIN)
+ }
+
+ if (currentPathPrefix.isEmpty() && folders.contains(FAVORITES)) {
+ currentPaths.add(FAVORITES)
+ }
+
+ if (folders.size == currentPaths.size) {
+ return currentPaths
+ }
+
+ folders.clear()
+ folders.addAll(currentPaths)
+ return if (areDirectSubfoldersAvailable) {
+ getDirectParentSubfolders(folders, currentPathPrefix)
+ } else {
+ folders
+ }
+}
+
+fun Context.updateSubfolderCounts(children: ArrayList, parentDirs: ArrayList) {
+ for (child in children) {
+ var longestSharedPath = ""
+ for (parentDir in parentDirs) {
+ if (parentDir.path == child.path) {
+ longestSharedPath = child.path
+ continue
+ }
+
+ if (child.path.startsWith(parentDir.path, true) && parentDir.path.length > longestSharedPath.length) {
+ longestSharedPath = parentDir.path
+ }
+ }
+
+ // make sure we count only the proper direct subfolders, grouped the same way as on the main screen
+ parentDirs.firstOrNull { it.path == longestSharedPath }?.apply {
+ if (path.equals(child.path, true) || path.equals(File(child.path).parent, true) || children.any { it.path.equals(File(child.path).parent, true) }) {
+ subfoldersCount++
+ if (path != child.path) {
+ subfoldersMediaCount += child.mediaCnt
+ }
+ }
+ }
+ }
+}
+
fun Context.getNoMediaFolders(callback: (folders: ArrayList) -> Unit) {
Thread {
val folders = ArrayList()
@@ -174,7 +346,7 @@ fun Context.rescanFolderMediaSync(path: String) {
if (!newMedia.contains(it)) {
val mediumPath = (it as? Medium)?.path
if (mediumPath != null) {
- mediumDao.deleteMediumPath(mediumPath)
+ deleteDBPath(mediumDao, mediumPath)
}
}
}
@@ -190,10 +362,21 @@ fun Context.storeDirectoryItems(items: ArrayList, directoryDao: Direc
}
fun Context.checkAppendingHidden(path: String, hidden: String, includedFolders: MutableSet): String {
- val dirName = when (path) {
+ val dirName = getFolderNameFromPath(path)
+ return if (File(path).doesThisOrParentHaveNoMedia() && !path.isThisOrParentIncluded(includedFolders)) {
+ "$dirName $hidden"
+ } else {
+ dirName
+ }
+}
+
+fun Context.getFolderNameFromPath(path: String): String {
+ return when (path) {
internalStoragePath -> getString(R.string.internal)
sdCardPath -> getString(R.string.sd_card)
- OTG_PATH -> getString(R.string.otg)
+ OTG_PATH -> getString(R.string.usb)
+ FAVORITES -> getString(R.string.favorites)
+ RECYCLE_BIN -> getString(R.string.recycle_bin)
else -> {
if (path.startsWith(OTG_PATH)) {
path.trimEnd('/').substringAfterLast('/')
@@ -202,21 +385,16 @@ fun Context.checkAppendingHidden(path: String, hidden: String, includedFolders:
}
}
}
-
- return if (File(path).doesThisOrParentHaveNoMedia() && !path.isThisOrParentIncluded(includedFolders)) {
- "$dirName $hidden"
- } else {
- dirName
- }
}
-fun Context.loadImage(type: Int, path: String, target: MySquareImageView, horizontalScroll: Boolean, animateGifs: Boolean, cropThumbnails: Boolean) {
+fun Context.loadImage(type: Int, path: String, target: MySquareImageView, horizontalScroll: Boolean, animateGifs: Boolean, cropThumbnails: Boolean,
+ skipMemoryCacheAtPaths: ArrayList? = null) {
target.isHorizontalScrolling = horizontalScroll
if (type == TYPE_IMAGES || type == TYPE_VIDEOS || type == TYPE_RAWS) {
if (type == TYPE_IMAGES && path.isPng()) {
- loadPng(path, target, cropThumbnails)
+ loadPng(path, target, cropThumbnails, skipMemoryCacheAtPaths)
} else {
- loadJpg(path, target, cropThumbnails)
+ loadJpg(path, target, cropThumbnails, skipMemoryCacheAtPaths)
}
} else if (type == TYPE_GIFS) {
try {
@@ -230,9 +408,9 @@ fun Context.loadImage(type: Int, path: String, target: MySquareImageView, horizo
target.scaleType = if (cropThumbnails) ImageView.ScaleType.CENTER_CROP else ImageView.ScaleType.FIT_CENTER
} catch (e: Exception) {
- loadJpg(path, target, cropThumbnails)
+ loadJpg(path, target, cropThumbnails, skipMemoryCacheAtPaths)
} catch (e: OutOfMemoryError) {
- loadJpg(path, target, cropThumbnails)
+ loadJpg(path, target, cropThumbnails, skipMemoryCacheAtPaths)
}
} else if (type == TYPE_SVGS) {
loadSVG(path, target, cropThumbnails)
@@ -258,9 +436,10 @@ fun Context.getPathLocation(path: String): Int {
}
}
-fun Context.loadPng(path: String, target: MySquareImageView, cropThumbnails: Boolean) {
+fun Context.loadPng(path: String, target: MySquareImageView, cropThumbnails: Boolean, skipMemoryCacheAtPaths: ArrayList? = null) {
val options = RequestOptions()
.signature(path.getFileSignature())
+ .skipMemoryCache(skipMemoryCacheAtPaths?.contains(path) == true)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.format(DecodeFormat.PREFER_ARGB_8888)
@@ -272,9 +451,10 @@ fun Context.loadPng(path: String, target: MySquareImageView, cropThumbnails: Boo
builder.apply(options).into(target)
}
-fun Context.loadJpg(path: String, target: MySquareImageView, cropThumbnails: Boolean) {
+fun Context.loadJpg(path: String, target: MySquareImageView, cropThumbnails: Boolean, skipMemoryCacheAtPaths: ArrayList? = null) {
val options = RequestOptions()
.signature(path.getFileSignature())
+ .skipMemoryCache(skipMemoryCacheAtPaths?.contains(path) == true)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
val builder = Glide.with(applicationContext)
@@ -299,7 +479,7 @@ fun Context.loadSVG(path: String, target: MySquareImageView, cropThumbnails: Boo
.into(target)
}
-fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, directoryDao: DirectoryDao = galleryDB.DirectoryDao(), callback: (ArrayList) -> Unit) {
+fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly: Boolean = false, directoryDao: DirectoryDao = galleryDB.DirectoryDao(), forceShowHidden: Boolean = false, callback: (ArrayList) -> Unit) {
Thread {
val directories = try {
directoryDao.getAll() as ArrayList
@@ -307,11 +487,11 @@ fun Context.getCachedDirectories(getVideosOnly: Boolean = false, getImagesOnly:
ArrayList()
}
- if (!config.showRecycleBinAtFolders) {
+ if (!config.showRecycleBinAtFolders || !config.useRecycleBin) {
directories.removeAll { it.isRecycleBin() }
}
- val shouldShowHidden = config.shouldShowHidden
+ val shouldShowHidden = config.shouldShowHidden || forceShowHidden
val excludedPaths = config.excludedFolders
val includedPaths = config.includedFolders
var filteredDirectories = directories.filter { it.path.shouldFolderBeVisible(excludedPaths, includedPaths, shouldShowHidden) } as ArrayList
@@ -390,11 +570,10 @@ fun Context.getCachedMedia(path: String, getVideosOnly: Boolean = false, getImag
val grouped = mediaFetcher.groupMedia(media, pathToUse)
callback(grouped.clone() as ArrayList)
- val recycleBinPath = filesDir.absolutePath
val mediaToDelete = ArrayList()
media.filter { !getDoesFilePathExist(it.path) }.forEach {
if (it.path.startsWith(recycleBinPath)) {
- mediumDao.deleteMediumPath(it.path.removePrefix(recycleBinPath))
+ deleteDBPath(mediumDao, it.path)
} else {
mediaToDelete.add(it)
}
@@ -427,10 +606,83 @@ fun Context.getOTGFolderChildrenNames(path: String) = getOTGFolderChildren(path)
fun Context.getFavoritePaths() = galleryDB.MediumDao().getFavoritePaths() as ArrayList
+// remove the "recycle_bin" from the file path prefix, replace it with real bin path /data/user...
fun Context.getUpdatedDeletedMedia(mediumDao: MediumDao): ArrayList {
val media = mediumDao.getDeletedMedia() as ArrayList
media.forEach {
- it.path = File(filesDir.absolutePath, it.path).toString()
+ it.path = File(recycleBinPath, it.path.removePrefix(RECYCLE_BIN)).toString()
}
return media
}
+
+fun Context.deleteDBPath(mediumDao: MediumDao, path: String) {
+ mediumDao.deleteMediumPath(path.replaceFirst(recycleBinPath, RECYCLE_BIN))
+}
+
+fun Context.updateWidgets() {
+ val widgetIDs = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetProvider::class.java))
+ if (widgetIDs.isNotEmpty()) {
+ Intent(applicationContext, MyWidgetProvider::class.java).apply {
+ action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIDs)
+ sendBroadcast(this)
+ }
+ }
+}
+
+// based on https://github.com/sannies/mp4parser/blob/master/examples/src/main/java/com/google/code/mp4parser/example/PrintStructure.java
+fun Context.parseFileChannel(path: String, fc: FileChannel, level: Int, start: Long, end: Long, callback: () -> Unit) {
+ val FILE_CHANNEL_CONTAINERS = arrayListOf("moov", "trak", "mdia", "minf", "udta", "stbl")
+ try {
+ var iteration = 0
+ var currEnd = end
+ fc.position(start)
+ if (currEnd <= 0) {
+ currEnd = start + fc.size()
+ }
+
+ while (currEnd - fc.position() > 8) {
+ // just a check to avoid deadloop at some videos
+ if (iteration++ > 50) {
+ return
+ }
+
+ val begin = fc.position()
+ val byteBuffer = ByteBuffer.allocate(8)
+ fc.read(byteBuffer)
+ byteBuffer.rewind()
+ val size = IsoTypeReader.readUInt32(byteBuffer)
+ val type = IsoTypeReader.read4cc(byteBuffer)
+ val newEnd = begin + size
+
+ if (type == "uuid") {
+ val fis = FileInputStream(File(path))
+ fis.skip(begin)
+
+ val sb = StringBuilder()
+ val buffer = ByteArray(1024)
+ while (true) {
+ val n = fis.read(buffer)
+ if (n != -1) {
+ sb.append(String(buffer, 0, n))
+ } else {
+ break
+ }
+ }
+
+ val xmlString = sb.toString().toLowerCase()
+ if (xmlString.contains("gspherical:projectiontype>equirectangular") || xmlString.contains("gspherical:projectiontype=\"equirectangular\"")) {
+ callback.invoke()
+ }
+ return
+ }
+
+ if (FILE_CHANNEL_CONTAINERS.contains(type)) {
+ parseFileChannel(path, fc, level + 1, begin + 8, newEnd, callback)
+ }
+
+ fc.position(newEnd)
+ }
+ } catch (ignored: Exception) {
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/File.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/File.kt
similarity index 78%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/File.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/File.kt
index 024a3ff63..90b02aac1 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/File.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/File.kt
@@ -1,6 +1,6 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
-import com.simplemobiletools.gallery.helpers.NOMEDIA
+import com.simplemobiletools.gallery.pro.helpers.NOMEDIA
import java.io.File
fun File.containsNoMedia() = isDirectory && File(this, NOMEDIA).exists()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/FileDirItem.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/FileDirItem.kt
similarity index 81%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/FileDirItem.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/FileDirItem.kt
index b9f637574..726fcc8a9 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/FileDirItem.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/FileDirItem.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
import android.os.Environment
import com.simplemobiletools.commons.models.FileDirItem
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Resources.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Resources.kt
similarity index 93%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Resources.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Resources.kt
index 73a56c5ba..d086fb7a5 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/Resources.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Resources.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
import android.content.Context
import android.content.res.Resources
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/String.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/String.kt
similarity index 65%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/String.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/String.kt
index ad9a4ec58..b426c5a61 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/String.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/String.kt
@@ -1,13 +1,16 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
+import android.media.MediaMetadataRetriever
import com.bumptech.glide.signature.ObjectKey
import com.simplemobiletools.commons.helpers.OTG_PATH
import java.io.File
import java.io.IOException
-fun String.getFileSignature(): ObjectKey {
+fun String.getFileSignature() = ObjectKey(getFileKey())
+
+fun String.getFileKey(): String {
val file = File(this)
- return ObjectKey("${file.absolutePath}${file.lastModified()}")
+ return "${file.absolutePath}${file.lastModified()}"
}
fun String.isThisOrParentIncluded(includedPaths: MutableSet) = includedPaths.any { startsWith(it, true) }
@@ -18,8 +21,14 @@ fun String.shouldFolderBeVisible(excludedPaths: MutableSet, includedPath
val file = File(this)
return if (isEmpty()) {
false
+ } else if (!showHidden && file.isHidden) {
+ false
+ } else if (includedPaths.contains(this)) {
+ true
} else if (!showHidden && file.containsNoMedia()) {
false
+ } else if (excludedPaths.contains(this)) {
+ false
} else if (isThisOrParentIncluded(includedPaths)) {
true
} else if (isThisOrParentExcluded(excludedPaths)) {
@@ -43,3 +52,14 @@ fun String.getDistinctPath(): String {
toLowerCase()
}
}
+
+fun String.getVideoDuration(): Int {
+ var seconds = 0
+ try {
+ val retriever = MediaMetadataRetriever()
+ retriever.setDataSource(this)
+ seconds = Math.round(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toInt() / 1000f)
+ } catch (e: Exception) {
+ }
+ return seconds
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/View.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/View.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/extensions/View.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/View.kt
index cf1459c54..24107d2a2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/extensions/View.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/View.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.extensions
+package com.simplemobiletools.gallery.pro.extensions
import android.os.SystemClock
import android.view.MotionEvent
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/PhotoFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/PhotoFragment.kt
new file mode 100644
index 000000000..bb3c38d68
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/PhotoFragment.kt
@@ -0,0 +1,630 @@
+package com.simplemobiletools.gallery.pro.fragments
+
+import android.content.Intent
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.Matrix
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.PictureDrawable
+import android.media.ExifInterface.*
+import android.net.Uri
+import android.os.Build
+import android.os.Bundle
+import android.os.Handler
+import android.util.DisplayMetrics
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.alexvasilkov.gestures.GestureController
+import com.alexvasilkov.gestures.State
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.DecodeFormat
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.Target
+import com.davemorrissey.labs.subscaleview.DecoderFactory
+import com.davemorrissey.labs.subscaleview.ImageDecoder
+import com.davemorrissey.labs.subscaleview.ImageRegionDecoder
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.OTG_PATH
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.activities.PanoramaPhotoActivity
+import com.simplemobiletools.gallery.pro.activities.PhotoActivity
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.svg.SvgSoftwareLayerSetter
+import com.squareup.picasso.Callback
+import com.squareup.picasso.Picasso
+import it.sephiroth.android.library.exif2.ExifInterface
+import kotlinx.android.synthetic.main.pager_photo_item.view.*
+import org.apache.sanselan.common.byteSources.ByteSourceInputStream
+import org.apache.sanselan.formats.jpeg.JpegImageParser
+import pl.droidsonroids.gif.InputSource
+import java.io.File
+import java.io.FileOutputStream
+import java.util.*
+
+class PhotoFragment : ViewPagerFragment() {
+ private val DEFAULT_DOUBLE_TAP_ZOOM = 2f
+ private val ZOOMABLE_VIEW_LOAD_DELAY = 150L
+ private val SAME_ASPECT_RATIO_THRESHOLD = 0.01
+
+ // devices with good displays, but the rest of the hardware not good enough for them
+ private val WEIRD_DEVICES = arrayListOf(
+ "motorola xt1685",
+ "google nexus 5x"
+ )
+
+ var mCurrentRotationDegrees = 0
+ private var mIsFragmentVisible = false
+ private var mIsFullscreen = false
+ private var mWasInit = false
+ private var mIsPanorama = false
+ private var mIsSubsamplingVisible = false // checking view.visibility is unreliable, use an extra variable for it
+ private var mImageOrientation = -1
+ private var mLoadZoomableViewHandler = Handler()
+ private var mScreenWidth = 0
+ private var mScreenHeight = 0
+ private var mCurrentGestureViewZoom = 1f
+
+ private var mStoredShowExtendedDetails = false
+ private var mStoredHideExtendedDetails = false
+ private var mStoredAllowDeepZoomableImages = false
+ private var mStoredShowHighestQuality = false
+ private var mStoredExtendedDetails = 0
+
+ private lateinit var mView: ViewGroup
+ private lateinit var mMedium: Medium
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ mView = (inflater.inflate(R.layout.pager_photo_item, container, false) as ViewGroup).apply {
+ subsampling_view.setOnClickListener { photoClicked() }
+ gestures_view.setOnClickListener { photoClicked() }
+ gif_view.setOnClickListener { photoClicked() }
+ instant_prev_item.setOnClickListener { listener?.goToPrevItem() }
+ instant_next_item.setOnClickListener { listener?.goToNextItem() }
+ panorama_outline.setOnClickListener { openPanorama() }
+
+ instant_prev_item.parentView = container
+ instant_next_item.parentView = container
+
+ photo_brightness_controller.initialize(activity!!, slide_info, true, container) { x, y ->
+ mView.apply {
+ if (subsampling_view.isVisible()) {
+ subsampling_view.sendFakeClick(x, y)
+ } else {
+ gestures_view.sendFakeClick(x, y)
+ }
+ }
+ }
+
+ if (context.config.allowDownGesture) {
+ gif_view.setOnTouchListener { v, event ->
+ if (gif_view_frame.controller.state.zoom == 1f) {
+ handleEvent(event)
+ }
+ false
+ }
+
+ gestures_view.controller.addOnStateChangeListener(object : GestureController.OnStateChangeListener {
+ override fun onStateChanged(state: State) {
+ mCurrentGestureViewZoom = state.zoom
+ }
+ })
+
+ gestures_view.setOnTouchListener { v, event ->
+ if (mCurrentGestureViewZoom == 1f) {
+ handleEvent(event)
+ }
+ false
+ }
+
+ subsampling_view.setOnTouchListener { v, event ->
+ if (subsampling_view.isZoomedOut()) {
+ handleEvent(event)
+ }
+ false
+ }
+ }
+ }
+
+ checkScreenDimensions()
+ storeStateVariables()
+ if (!mIsFragmentVisible && activity is PhotoActivity) {
+ mIsFragmentVisible = true
+ }
+
+ mMedium = arguments!!.getSerializable(MEDIUM) as Medium
+ if (mMedium.path.startsWith("content://") && !mMedium.path.startsWith("content://mms/")) {
+ val originalPath = mMedium.path
+ mMedium.path = context!!.getRealPathFromURI(Uri.parse(originalPath)) ?: mMedium.path
+
+ if (mMedium.path.isEmpty()) {
+ var out: FileOutputStream? = null
+ try {
+ var inputStream = context!!.contentResolver.openInputStream(Uri.parse(originalPath))
+ val exif = ExifInterface()
+ exif.readExif(inputStream, ExifInterface.Options.OPTION_ALL)
+ val tag = exif.getTag(ExifInterface.TAG_ORIENTATION)
+ val orientation = tag?.getValueAsInt(-1) ?: -1
+ inputStream = context!!.contentResolver.openInputStream(Uri.parse(originalPath))
+ val original = BitmapFactory.decodeStream(inputStream)
+ val rotated = rotateViaMatrix(original, orientation)
+ exif.setTagValue(ExifInterface.TAG_ORIENTATION, 1)
+ exif.removeCompressedThumbnail()
+
+ val file = File(context!!.externalCacheDir, Uri.parse(originalPath).lastPathSegment)
+ out = FileOutputStream(file)
+ rotated.compress(Bitmap.CompressFormat.JPEG, 100, out)
+ mMedium.path = file.absolutePath
+ } catch (e: Exception) {
+ activity!!.toast(R.string.unknown_error_occurred)
+ return mView
+ } finally {
+ out?.close()
+ }
+ }
+ }
+
+ mIsFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN
+ loadImage()
+ initExtendedDetails()
+ mWasInit = true
+ checkIfPanorama()
+ updateInstantSwitchWidths()
+
+ return mView
+ }
+
+ override fun onPause() {
+ super.onPause()
+ storeStateVariables()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ val config = context!!.config
+ if (mWasInit && (config.showExtendedDetails != mStoredShowExtendedDetails || config.extendedDetails != mStoredExtendedDetails)) {
+ initExtendedDetails()
+ }
+
+ if (mWasInit) {
+ if (config.allowZoomingImages != mStoredAllowDeepZoomableImages || config.showHighestQuality != mStoredShowHighestQuality) {
+ mIsSubsamplingVisible = false
+ mView.subsampling_view.beGone()
+ loadImage()
+ } else if (mMedium.isGIF()) {
+ loadGif()
+ }
+ }
+
+ val allowPhotoGestures = config.allowPhotoGestures
+ val allowInstantChange = config.allowInstantChange
+
+ mView.apply {
+ photo_brightness_controller.beVisibleIf(allowPhotoGestures)
+ instant_prev_item.beVisibleIf(allowInstantChange)
+ instant_next_item.beVisibleIf(allowInstantChange)
+ }
+
+ storeStateVariables()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ if (activity?.isDestroyed == false) {
+ mView.subsampling_view.recycle()
+ }
+
+ mLoadZoomableViewHandler.removeCallbacksAndMessages(null)
+ if (mCurrentRotationDegrees != 0) {
+ Thread {
+ val path = mMedium.path
+ (activity as? BaseSimpleActivity)?.saveRotatedImageToFile(path, path, mCurrentRotationDegrees, false) {}
+ }.start()
+ }
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+
+ // avoid GIFs being skewed, played in wrong aspect ratio
+ if (mMedium.isGIF()) {
+ mView.onGlobalLayout {
+ measureScreen()
+ Handler().postDelayed({
+ mView.gif_view_frame.controller.resetState()
+ loadGif()
+ }, 50)
+ }
+ } else {
+ hideZoomableView()
+ loadImage()
+ }
+
+ initExtendedDetails()
+ updateInstantSwitchWidths()
+ }
+
+ override fun setMenuVisibility(menuVisible: Boolean) {
+ super.setMenuVisibility(menuVisible)
+ mIsFragmentVisible = menuVisible
+ if (mWasInit) {
+ if (!mMedium.isGIF()) {
+ photoFragmentVisibilityChanged(menuVisible)
+ }
+ }
+ }
+
+ private fun storeStateVariables() {
+ context!!.config.apply {
+ mStoredShowExtendedDetails = showExtendedDetails
+ mStoredHideExtendedDetails = hideExtendedDetails
+ mStoredAllowDeepZoomableImages = allowZoomingImages
+ mStoredShowHighestQuality = showHighestQuality
+ mStoredExtendedDetails = extendedDetails
+ }
+ }
+
+ private fun checkScreenDimensions() {
+ if (mScreenWidth == 0 || mScreenHeight == 0) {
+ measureScreen()
+ }
+ }
+
+ private fun measureScreen() {
+ val metrics = DisplayMetrics()
+ activity!!.windowManager.defaultDisplay.getRealMetrics(metrics)
+ mScreenWidth = metrics.widthPixels
+ mScreenHeight = metrics.heightPixels
+ }
+
+ private fun photoFragmentVisibilityChanged(isVisible: Boolean) {
+ if (isVisible) {
+ scheduleZoomableView()
+ } else {
+ hideZoomableView()
+ }
+ }
+
+ private fun degreesForRotation(orientation: Int) = when (orientation) {
+ ORIENTATION_ROTATE_270 -> 270
+ ORIENTATION_ROTATE_180 -> 180
+ ORIENTATION_ROTATE_90 -> 90
+ else -> 0
+ }
+
+ private fun rotateViaMatrix(original: Bitmap, orientation: Int): Bitmap {
+ val degrees = degreesForRotation(orientation).toFloat()
+ return if (degrees == 0f) {
+ original
+ } else {
+ val matrix = Matrix()
+ matrix.setRotate(degrees)
+ Bitmap.createBitmap(original, 0, 0, original.width, original.height, matrix, true)
+ }
+ }
+
+ private fun loadImage() {
+ checkScreenDimensions()
+ mImageOrientation = getImageOrientation()
+ when {
+ mMedium.isGIF() -> loadGif()
+ mMedium.isSVG() -> loadSVG()
+ else -> loadBitmap()
+ }
+ }
+
+ private fun loadGif() {
+ try {
+ val pathToLoad = getPathToLoad(mMedium)
+ val source = if (pathToLoad.startsWith("content://") || pathToLoad.startsWith("file://")) {
+ InputSource.UriSource(context!!.contentResolver, Uri.parse(pathToLoad))
+ } else {
+ InputSource.FileSource(pathToLoad)
+ }
+
+ mView.apply {
+ gestures_view.beGone()
+ gif_view.setInputSource(source)
+ gif_view_frame.beVisible()
+ }
+ } catch (e: Exception) {
+ loadBitmap()
+ } catch (e: OutOfMemoryError) {
+ loadBitmap()
+ }
+ }
+
+ private fun loadSVG() {
+ Glide.with(context!!)
+ .`as`(PictureDrawable::class.java)
+ .listener(SvgSoftwareLayerSetter())
+ .load(mMedium.path)
+ .into(mView.gestures_view)
+ }
+
+ private fun loadBitmap(addZoomableView: Boolean = true) {
+ val options = RequestOptions()
+ .signature(mMedium.path.getFileSignature())
+ .format(DecodeFormat.PREFER_ARGB_8888)
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ .fitCenter()
+
+ if (mCurrentRotationDegrees != 0) {
+ options.transform(GlideRotateTransformation(mCurrentRotationDegrees))
+ options.diskCacheStrategy(DiskCacheStrategy.NONE)
+ }
+
+ Glide.with(context!!)
+ .load(getPathToLoad(mMedium))
+ .apply(options)
+ .listener(object : RequestListener {
+ override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean {
+ if (activity != null) {
+ tryLoadingWithPicasso(addZoomableView)
+ }
+ return false
+ }
+
+ override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
+ if (mIsFragmentVisible && addZoomableView) {
+ scheduleZoomableView()
+ }
+ return false
+ }
+ }).into(mView.gestures_view)
+ }
+
+ private fun tryLoadingWithPicasso(addZoomableView: Boolean) {
+ var pathToLoad = if (mMedium.path.startsWith("content://")) mMedium.path else "file://${mMedium.path}"
+ pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
+
+ try {
+ val picasso = Picasso.get()
+ .load(pathToLoad)
+ .centerInside()
+ .stableKey(mMedium.path.getFileKey())
+ .resize(mScreenWidth, mScreenHeight)
+
+ if (mCurrentRotationDegrees != 0) {
+ picasso.rotate(mCurrentRotationDegrees.toFloat())
+ } else {
+ degreesForRotation(mImageOrientation).toFloat()
+ }
+
+ picasso.into(mView.gestures_view, object : Callback {
+ override fun onSuccess() {
+ mView.gestures_view.controller.settings.isZoomEnabled = mCurrentRotationDegrees != 0 || context?.config?.allowZoomingImages == false
+ if (mIsFragmentVisible && addZoomableView) {
+ scheduleZoomableView()
+ }
+ }
+
+ override fun onError(e: Exception) {}
+ })
+ } catch (ignored: Exception) {
+ }
+ }
+
+ private fun openPanorama() {
+ Intent(context, PanoramaPhotoActivity::class.java).apply {
+ putExtra(PATH, mMedium.path)
+ startActivity(this)
+ }
+ }
+
+ private fun scheduleZoomableView() {
+ mLoadZoomableViewHandler.removeCallbacksAndMessages(null)
+ mLoadZoomableViewHandler.postDelayed({
+ if (mIsFragmentVisible && context?.config?.allowZoomingImages == true && mMedium.isImage() && !mIsSubsamplingVisible) {
+ addZoomableView()
+ }
+ }, ZOOMABLE_VIEW_LOAD_DELAY)
+ }
+
+ private fun addZoomableView() {
+ val rotation = degreesForRotation(mImageOrientation)
+ val path = getPathToLoad(mMedium)
+ mIsSubsamplingVisible = true
+
+ val bitmapDecoder = object : DecoderFactory {
+ override fun make() = PicassoDecoder(path, Picasso.get(), rotation)
+ }
+
+ val regionDecoder = object : DecoderFactory {
+ override fun make() = PicassoRegionDecoder()
+ }
+
+ var newOrientation = (rotation + mCurrentRotationDegrees) % 360
+ if (newOrientation < 0) {
+ newOrientation += 360
+ }
+
+ val config = context!!.config
+ mView.subsampling_view.apply {
+ setMaxTileSize(if (config.showHighestQuality) Integer.MAX_VALUE else 4096)
+ setMinimumTileDpi(if (config.showHighestQuality) -1 else getMinTileDpi())
+ background = ColorDrawable(Color.TRANSPARENT)
+ bitmapDecoderFactory = bitmapDecoder
+ regionDecoderFactory = regionDecoder
+ maxScale = 10f
+ beVisible()
+ isOneToOneZoomEnabled = config.allowOneToOneZoom
+ orientation = newOrientation
+ setImage(path)
+ onImageEventListener = object : SubsamplingScaleImageView.OnImageEventListener {
+ override fun onReady() {
+ background = ColorDrawable(if (config.blackBackground) Color.BLACK else config.backgroundColor)
+ val useWidth = if (mImageOrientation == ORIENTATION_ROTATE_90 || mImageOrientation == ORIENTATION_ROTATE_270) sHeight else sWidth
+ val useHeight = if (mImageOrientation == ORIENTATION_ROTATE_90 || mImageOrientation == ORIENTATION_ROTATE_270) sWidth else sHeight
+ doubleTapZoomScale = getDoubleTapZoomScale(useWidth, useHeight)
+ }
+
+ override fun onImageLoadError(e: Exception) {
+ mView.gestures_view.controller.settings.isZoomEnabled = true
+ background = ColorDrawable(Color.TRANSPARENT)
+ mIsSubsamplingVisible = false
+ beGone()
+ }
+
+ override fun onImageRotation(degrees: Int) {
+ val fullRotation = (rotation + degrees) % 360
+ val useWidth = if (fullRotation == 90 || fullRotation == 270) sHeight else sWidth
+ val useHeight = if (fullRotation == 90 || fullRotation == 270) sWidth else sHeight
+ doubleTapZoomScale = getDoubleTapZoomScale(useWidth, useHeight)
+ mCurrentRotationDegrees = (mCurrentRotationDegrees + degrees) % 360
+ loadBitmap(false)
+ activity?.invalidateOptionsMenu()
+ }
+ }
+ }
+ }
+
+ private fun getMinTileDpi(): Int {
+ val metrics = resources.displayMetrics
+ val averageDpi = (metrics.xdpi + metrics.ydpi) / 2
+ val device = "${Build.BRAND} ${Build.MODEL}".toLowerCase()
+ return when {
+ WEIRD_DEVICES.contains(device) -> 240
+ averageDpi > 400 -> 280
+ averageDpi > 300 -> 220
+ else -> 160
+ }
+ }
+
+ private fun checkIfPanorama() {
+ mIsPanorama = try {
+ val inputStream = if (mMedium.path.startsWith("content:/")) context!!.contentResolver.openInputStream(Uri.parse(mMedium.path)) else File(mMedium.path).inputStream()
+ val imageParser = JpegImageParser().getXmpXml(ByteSourceInputStream(inputStream, mMedium.name), HashMap())
+ imageParser.contains("GPano:UsePanoramaViewer=\"True\"", true) || imageParser.contains("True", true)
+ } catch (e: Exception) {
+ false
+ } catch (e: OutOfMemoryError) {
+ false
+ }
+
+ mView.panorama_outline.beVisibleIf(mIsPanorama)
+ }
+
+ private fun getImageOrientation(): Int {
+ val defaultOrientation = -1
+ var orient = defaultOrientation
+
+ try {
+ val pathToLoad = getPathToLoad(mMedium)
+ val exif = android.media.ExifInterface(pathToLoad)
+ orient = exif.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, defaultOrientation)
+
+ if (orient == defaultOrientation || mMedium.path.startsWith(OTG_PATH)) {
+ val uri = if (pathToLoad.startsWith("content:/")) Uri.parse(pathToLoad) else Uri.fromFile(File(pathToLoad))
+ val inputStream = context!!.contentResolver.openInputStream(uri)
+ val exif2 = ExifInterface()
+ exif2.readExif(inputStream, ExifInterface.Options.OPTION_ALL)
+ orient = exif2.getTag(ExifInterface.TAG_ORIENTATION)?.getValueAsInt(defaultOrientation) ?: defaultOrientation
+ }
+ } catch (ignored: Exception) {
+ } catch (ignored: OutOfMemoryError) {
+ }
+ return orient
+ }
+
+ private fun getDoubleTapZoomScale(width: Int, height: Int): Float {
+ val bitmapAspectRatio = height / width.toFloat()
+ val screenAspectRatio = mScreenHeight / mScreenWidth.toFloat()
+
+ return if (context == null || Math.abs(bitmapAspectRatio - screenAspectRatio) < SAME_ASPECT_RATIO_THRESHOLD) {
+ DEFAULT_DOUBLE_TAP_ZOOM
+ } else if (context!!.portrait && bitmapAspectRatio <= screenAspectRatio) {
+ mScreenHeight / height.toFloat()
+ } else if (context!!.portrait && bitmapAspectRatio > screenAspectRatio) {
+ mScreenWidth / width.toFloat()
+ } else if (!context!!.portrait && bitmapAspectRatio >= screenAspectRatio) {
+ mScreenWidth / width.toFloat()
+ } else if (!context!!.portrait && bitmapAspectRatio < screenAspectRatio) {
+ mScreenHeight / height.toFloat()
+ } else {
+ DEFAULT_DOUBLE_TAP_ZOOM
+ }
+ }
+
+ fun rotateImageViewBy(degrees: Int) {
+ if (mIsSubsamplingVisible) {
+ mView.subsampling_view.rotateBy(degrees)
+ } else {
+ mCurrentRotationDegrees = (mCurrentRotationDegrees + degrees) % 360
+ mLoadZoomableViewHandler.removeCallbacksAndMessages(null)
+ mIsSubsamplingVisible = false
+ loadBitmap()
+ }
+ }
+
+ private fun initExtendedDetails() {
+ if (context!!.config.showExtendedDetails) {
+ mView.photo_details.apply {
+ beInvisible() // make it invisible so we can measure it, but not show yet
+ text = getMediumExtendedDetails(mMedium)
+ onGlobalLayout {
+ if (isAdded) {
+ val realY = getExtendedDetailsY(height)
+ if (realY > 0) {
+ y = realY
+ beVisibleIf(text.isNotEmpty())
+ alpha = if (!context!!.config.hideExtendedDetails || !mIsFullscreen) 1f else 0f
+ }
+ }
+ }
+ }
+ } else {
+ mView.photo_details.beGone()
+ }
+ }
+
+ private fun hideZoomableView() {
+ if (context?.config?.allowZoomingImages == true) {
+ mIsSubsamplingVisible = false
+ mView.subsampling_view.recycle()
+ mView.subsampling_view.beGone()
+ mLoadZoomableViewHandler.removeCallbacksAndMessages(null)
+ }
+ }
+
+ private fun photoClicked() {
+ listener?.fragmentClicked()
+ }
+
+ private fun updateInstantSwitchWidths() {
+ val newWidth = resources.getDimension(R.dimen.instant_change_bar_width) + if (activity?.portrait == false) activity!!.navigationBarWidth else 0
+ mView.instant_prev_item.layoutParams.width = newWidth.toInt()
+ mView.instant_next_item.layoutParams.width = newWidth.toInt()
+ }
+
+ override fun fullscreenToggled(isFullscreen: Boolean) {
+ this.mIsFullscreen = isFullscreen
+ mView.photo_details.apply {
+ if (mStoredShowExtendedDetails && isVisible()) {
+ animate().y(getExtendedDetailsY(height))
+
+ if (mStoredHideExtendedDetails) {
+ animate().alpha(if (isFullscreen) 0f else 1f).start()
+ }
+ }
+ }
+ }
+
+ private fun getExtendedDetailsY(height: Int): Float {
+ val smallMargin = resources.getDimension(R.dimen.small_margin)
+ val fullscreenOffset = smallMargin + if (mIsFullscreen) 0 else context!!.navigationBarHeight
+ val actionsHeight = if (context!!.config.bottomActions && !mIsFullscreen) resources.getDimension(R.dimen.bottom_actions_height) else 0f
+ return context!!.realScreenSize.y - height - actionsHeight - fullscreenOffset
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt
similarity index 50%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt
index 2c6927ea9..7cd46cb6e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt
@@ -1,18 +1,16 @@
-package com.simplemobiletools.gallery.fragments
+package com.simplemobiletools.gallery.pro.fragments
-import android.annotation.TargetApi
+import android.content.Intent
import android.content.res.Configuration
import android.graphics.Point
import android.graphics.SurfaceTexture
-import android.media.AudioManager
-import android.media.MediaMetadataRetriever
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.util.DisplayMetrics
import android.view.*
-import android.view.animation.AnimationUtils
+import android.widget.ImageView
+import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.TextView
import com.bumptech.glide.Glide
@@ -20,68 +18,115 @@ import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.TrackGroupArray
-import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
import com.google.android.exoplayer2.upstream.ContentDataSource
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DataSpec
import com.google.android.exoplayer2.upstream.FileDataSource
-import com.google.android.exoplayer2.video.VideoListener
import com.simplemobiletools.commons.extensions.*
-import com.simplemobiletools.commons.helpers.isJellyBean1Plus
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.activities.VideoActivity
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.helpers.MEDIUM
-import com.simplemobiletools.gallery.helpers.MediaSideScroll
-import com.simplemobiletools.gallery.models.Medium
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.activities.PanoramaVideoActivity
+import com.simplemobiletools.gallery.pro.activities.VideoActivity
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.helpers.Config
+import com.simplemobiletools.gallery.pro.helpers.MEDIUM
+import com.simplemobiletools.gallery.pro.helpers.MIN_SKIP_LENGTH
+import com.simplemobiletools.gallery.pro.helpers.PATH
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.views.MediaSideScroll
+import kotlinx.android.synthetic.main.bottom_video_time_holder.view.*
import kotlinx.android.synthetic.main.pager_video_item.view.*
import java.io.File
+import java.io.FileInputStream
class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener {
private val PROGRESS = "progress"
- private val MIN_SKIP_LENGTH = 2000
- private val HIDE_PAUSE_DELAY = 2000L
- private val PLAY_PAUSE_VISIBLE_ALPHA = 0.8f
- private var mTextureView: TextureView? = null
- private var mCurrTimeView: TextView? = null
- private var mSeekBar: SeekBar? = null
- private var mTimeHolder: View? = null
- private var mView: View? = null
+ private var mIsFullscreen = false
+ private var mWasFragmentInit = false
+ private var mIsPanorama = false
+ private var mIsFragmentVisible = false
+ private var mIsPlaying = false
+ private var mIsDragged = false
+ private var mWasVideoStarted = false
+ private var mCurrTime = 0
+ private var mDuration = 0
+
private var mExoPlayer: SimpleExoPlayer? = null
private var mVideoSize = Point(0, 0)
private var mTimerHandler = Handler()
- private var mHidePauseHandler = Handler()
-
- private var mIsPlaying = false
- private var mIsDragged = false
- private var mIsFullscreen = false
- private var mIsFragmentVisible = false
- private var mWasFragmentInit = false
- private var mIsExoPlayerInitialized = false
- private var mCurrTime = 0
- private var mDuration = 0
private var mStoredShowExtendedDetails = false
private var mStoredHideExtendedDetails = false
private var mStoredBottomActions = true
private var mStoredExtendedDetails = 0
+ private var mStoredRememberLastVideoPosition = false
+ private var mStoredLastVideoPath = ""
+ private var mStoredLastVideoPosition = 0
- private lateinit var brightnessSideScroll: MediaSideScroll
- private lateinit var volumeSideScroll: MediaSideScroll
-
- lateinit var medium: Medium
+ private lateinit var mTimeHolder: View
+ private lateinit var mBrightnessSideScroll: MediaSideScroll
+ private lateinit var mVolumeSideScroll: MediaSideScroll
+ private lateinit var mView: View
+ private lateinit var mMedium: Medium
+ private lateinit var mConfig: Config
+ private lateinit var mTextureView: TextureView
+ private lateinit var mCurrTimeView: TextView
+ private lateinit var mPlayPauseButton: ImageView
+ private lateinit var mSeekBar: SeekBar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ mConfig = context!!.config
mView = inflater.inflate(R.layout.pager_video_item, container, false).apply {
instant_prev_item.setOnClickListener { listener?.goToPrevItem() }
instant_next_item.setOnClickListener { listener?.goToNextItem() }
+ video_curr_time.setOnClickListener { skip(false) }
+ video_duration.setOnClickListener { skip(true) }
+ video_holder.setOnClickListener { toggleFullscreen() }
+ video_preview.setOnClickListener { toggleFullscreen() }
+ panorama_outline.setOnClickListener { openPanorama() }
+ video_play_outline.setOnClickListener {
+ if (mConfig.openVideosOnSeparateScreen) {
+ launchVideoPlayer()
+ } else {
+ togglePlayPause()
+ }
+ }
+
+ mPlayPauseButton = video_toggle_play_pause
+ mPlayPauseButton.setOnClickListener {
+ togglePlayPause()
+ }
+
+ mSeekBar = video_seekbar
+ mSeekBar.setOnSeekBarChangeListener(this@VideoFragment)
+ // adding an empty click listener just to avoid ripple animation at toggling fullscreen
+ mSeekBar.setOnClickListener { }
+
mTimeHolder = video_time_holder
+ mCurrTimeView = video_curr_time
+ mBrightnessSideScroll = video_brightness_controller
+ mVolumeSideScroll = video_volume_controller
+ mTextureView = video_surface
+
+ if (mConfig.allowDownGesture) {
+ video_preview.setOnTouchListener { view, event ->
+ handleEvent(event)
+ false
+ }
+
+ video_surface_frame.setOnTouchListener { view, event ->
+ if (video_surface_frame.controller.state.zoom == 1f) {
+ handleEvent(event)
+ }
+ false
+ }
+ }
}
storeStateVariables()
- medium = arguments!!.getSerializable(MEDIUM) as Medium
+ mMedium = arguments!!.getSerializable(MEDIUM) as Medium
+ Glide.with(context!!).load(mMedium.path).into(mView.video_preview)
// setMenuVisibility is not called at VideoActivity (third party intent)
if (!mIsFragmentVisible && activity is VideoActivity) {
@@ -89,81 +134,91 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
mIsFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN
+ initTimeHolder()
+ checkIfPanorama()
- setupPlayer()
- if (savedInstanceState != null) {
- mCurrTime = savedInstanceState.getInt(PROGRESS)
- }
-
- checkFullscreen()
- mWasFragmentInit = true
-
- mView!!.apply {
- brightnessSideScroll = video_brightness_controller
- brightnessSideScroll.initialize(activity!!, slide_info, true, container) { x, y ->
- video_holder.performClick()
- }
-
- volumeSideScroll = video_volume_controller
- volumeSideScroll.initialize(activity!!, slide_info, false, container) { x, y ->
- video_holder.performClick()
- }
-
- video_curr_time.setOnClickListener { skip(false) }
- video_duration.setOnClickListener { skip(true) }
- Glide.with(context!!).load(medium.path).into(video_preview)
- }
-
- mExoPlayer = ExoPlayerFactory.newSimpleInstance(context, DefaultTrackSelector())
- initExoPlayerListeners()
-
- medium.path.getVideoResolution()?.apply {
+ mMedium.path.getVideoResolution()?.apply {
mVideoSize.x = x
mVideoSize.y = y
- setVideoSize()
+ }
+
+ if (mIsPanorama) {
+ mView.apply {
+ panorama_outline.beVisible()
+ video_play_outline.beGone()
+ mVolumeSideScroll.beGone()
+ mBrightnessSideScroll.beGone()
+ Glide.with(context!!).load(mMedium.path).into(video_preview)
+ }
+ }
+
+ if (!mIsPanorama) {
+ setupPlayer()
+ if (savedInstanceState != null) {
+ mCurrTime = savedInstanceState.getInt(PROGRESS)
+ }
+
+ mWasFragmentInit = true
+
+ if (mVideoSize.x != 0 && mVideoSize.y != 0) {
+ setVideoSize()
+ }
+
+ mView.apply {
+ mBrightnessSideScroll.initialize(activity!!, slide_info, true, container) { x, y ->
+ video_holder.performClick()
+ }
+
+ mVolumeSideScroll.initialize(activity!!, slide_info, false, container) { x, y ->
+ video_holder.performClick()
+ }
+
+ video_surface.onGlobalLayout {
+ if (mIsFragmentVisible && mConfig.autoplayVideos && !mConfig.openVideosOnSeparateScreen) {
+ playVideo()
+ }
+ }
+ }
}
setupVideoDuration()
-
- mView!!.video_surface.onGlobalLayout {
- if (mIsFragmentVisible && context?.config?.autoplayVideos == true) {
- playVideo()
- }
+ if (mStoredRememberLastVideoPosition) {
+ setLastVideoSavedPosition()
}
+ updateInstantSwitchWidths()
return mView
}
override fun onResume() {
super.onResume()
- activity!!.updateTextColors(mView!!.video_holder)
- val config = context!!.config
- val allowVideoGestures = config.allowVideoGestures
- val allowInstantChange = config.allowInstantChange
- mView!!.apply {
- video_volume_controller.beVisibleIf(allowVideoGestures)
- video_brightness_controller.beVisibleIf(allowVideoGestures)
+ mConfig = context!!.config // make sure we get a new config, in case the user changed something in the app settings
+ activity!!.updateTextColors(mView.video_holder)
+ val allowVideoGestures = mConfig.allowVideoGestures
+ val allowInstantChange = mConfig.allowInstantChange
+ mTextureView.beGoneIf(mConfig.openVideosOnSeparateScreen || mIsPanorama)
+ mView.apply {
+ video_surface_frame.beGoneIf(mTextureView.isGone())
+
+ video_volume_controller.beVisibleIf(allowVideoGestures && !mIsPanorama)
+ video_brightness_controller.beVisibleIf(allowVideoGestures && !mIsPanorama)
instant_prev_item.beVisibleIf(allowInstantChange)
instant_next_item.beVisibleIf(allowInstantChange)
}
- if (config.showExtendedDetails != mStoredShowExtendedDetails || config.extendedDetails != mStoredExtendedDetails) {
- checkExtendedDetails()
- }
-
- if (config.bottomActions != mStoredBottomActions) {
- initTimeHolder()
- }
-
- mView!!.video_time_holder.setBackgroundResource(if (config.bottomActions) 0 else R.drawable.gradient_background)
+ checkExtendedDetails()
+ initTimeHolder()
storeStateVariables()
}
override fun onPause() {
super.onPause()
- pauseVideo()
storeStateVariables()
+ pauseVideo()
+ if (mStoredRememberLastVideoPosition && mIsFragmentVisible && mWasVideoStarted) {
+ saveVideoProgress()
+ }
}
override fun onDestroy() {
@@ -180,7 +235,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
mIsFragmentVisible = menuVisible
- if (mWasFragmentInit && menuVisible && context?.config?.autoplayVideos == true) {
+ if (mWasFragmentInit && menuVisible && mConfig.autoplayVideos && !mConfig.openVideosOnSeparateScreen) {
playVideo()
}
}
@@ -190,35 +245,85 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
setVideoSize()
initTimeHolder()
checkExtendedDetails()
+ updateInstantSwitchWidths()
+ mView.video_surface_frame.onGlobalLayout {
+ mView.video_surface_frame.controller.resetState()
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putInt(PROGRESS, mCurrTime)
}
private fun storeStateVariables() {
- context!!.config.apply {
+ mConfig.apply {
mStoredShowExtendedDetails = showExtendedDetails
mStoredHideExtendedDetails = hideExtendedDetails
mStoredExtendedDetails = extendedDetails
mStoredBottomActions = bottomActions
+ mStoredRememberLastVideoPosition = rememberLastVideoPosition
+ mStoredLastVideoPath = lastVideoPath
+ mStoredLastVideoPosition = lastVideoPosition
}
}
private fun setupPlayer() {
- if (activity == null)
+ if (activity == null || mConfig.openVideosOnSeparateScreen || mIsPanorama) {
return
+ }
- mView!!.video_play_outline.setOnClickListener { togglePlayPause() }
+ mView.video_surface_frame.setOnClickListener { toggleFullscreen() }
+ mTextureView.surfaceTextureListener = this
- mTextureView = mView!!.video_surface
- mTextureView!!.setOnClickListener { toggleFullscreen() }
- mTextureView!!.surfaceTextureListener = this
- mView!!.video_holder.setOnClickListener { toggleFullscreen() }
-
- initTimeHolder()
checkExtendedDetails()
+
+ mExoPlayer = ExoPlayerFactory.newSimpleInstance(context)
+ mExoPlayer!!.seekParameters = SeekParameters.CLOSEST_SYNC
+ initExoPlayerListeners()
+ }
+
+ private fun saveVideoProgress() {
+ if (!videoEnded()) {
+ mStoredLastVideoPosition = mExoPlayer!!.currentPosition.toInt() / 1000
+ mStoredLastVideoPath = mMedium.path
+ }
+
+ mConfig.apply {
+ lastVideoPosition = mStoredLastVideoPosition
+ lastVideoPath = mStoredLastVideoPath
+ }
+ }
+
+ private fun setLastVideoSavedPosition() {
+ if (mStoredLastVideoPath == mMedium.path && mStoredLastVideoPosition > 0) {
+ setPosition(mStoredLastVideoPosition)
+ }
+ }
+
+ private fun setupTimeHolder() {
+ mSeekBar.max = mDuration
+ mView.video_duration.text = mDuration.getFormattedDuration()
+ setupTimer()
+ }
+
+ private fun setupTimer() {
+ activity!!.runOnUiThread(object : Runnable {
+ override fun run() {
+ if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
+ mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
+ mSeekBar.progress = mCurrTime
+ mCurrTimeView.text = mCurrTime.getFormattedDuration()
+ }
+
+ mTimerHandler.postDelayed(this, 1000)
+ }
+ })
}
private fun initExoPlayer() {
- val isContentUri = medium.path.startsWith("content://")
- val uri = if (isContentUri) Uri.parse(medium.path) else Uri.fromFile(File(medium.path))
+ val isContentUri = mMedium.path.startsWith("content://")
+ val uri = if (isContentUri) Uri.parse(mMedium.path) else Uri.fromFile(File(mMedium.path))
val dataSpec = DataSpec(uri)
val fileDataSource = if (isContentUri) ContentDataSource(context) else FileDataSource()
try {
@@ -229,7 +334,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
val factory = DataSource.Factory { fileDataSource }
val audioSource = ExtractorMediaSource(fileDataSource.uri, factory, DefaultExtractorsFactory(), null, null)
- mExoPlayer!!.audioStreamType = AudioManager.STREAM_MUSIC
+ mExoPlayer!!.audioStreamType = C.STREAM_TYPE_MUSIC
mExoPlayer!!.prepare(audioSource)
}
@@ -241,9 +346,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
- override fun onPlayerError(error: ExoPlaybackException?) {
- mIsExoPlayerInitialized = false
- }
+ override fun onPlayerError(error: ExoPlaybackException?) {}
override fun onLoadingChanged(isLoading: Boolean) {}
@@ -256,7 +359,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
- mIsExoPlayerInitialized = playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED
when (playbackState) {
Player.STATE_READY -> videoPrepared()
Player.STATE_ENDED -> videoCompleted()
@@ -264,7 +366,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
})
- mExoPlayer!!.addVideoListener(object : VideoListener {
+ mExoPlayer!!.addVideoListener(object : SimpleExoPlayer.VideoListener {
override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
mVideoSize.x = width
mVideoSize.y = height
@@ -275,108 +377,162 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
})
}
+ private fun launchVideoPlayer() {
+ listener?.launchViewVideoIntent(mMedium.path)
+ }
+
private fun toggleFullscreen() {
listener?.fragmentClicked()
}
- private fun initTimeHolder() {
- val res = resources
- val left = 0
- val top = 0
- var right = 0
- var bottom = 0
-
- if (hasNavBar()) {
- if (res.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
- bottom += context!!.navigationBarHeight
- } else {
- right += context!!.navigationBarWidth
- bottom += context!!.navigationBarHeight
+ private fun checkExtendedDetails() {
+ if (mConfig.showExtendedDetails) {
+ mView.video_details.apply {
+ beInvisible() // make it invisible so we can measure it, but not show yet
+ text = getMediumExtendedDetails(mMedium)
+ onGlobalLayout {
+ if (isAdded) {
+ val realY = getExtendedDetailsY(height)
+ if (realY > 0) {
+ y = realY
+ beVisibleIf(text.isNotEmpty())
+ alpha = if (!mConfig.hideExtendedDetails || !mIsFullscreen) 1f else 0f
+ }
+ }
+ }
}
+ } else {
+ mView.video_details.beGone()
}
+ }
- if (context!!.config.bottomActions) {
+ private fun initTimeHolder() {
+ var right = 0
+ var bottom = context!!.navigationBarHeight
+ if (mConfig.bottomActions) {
bottom += resources.getDimension(R.dimen.bottom_actions_height).toInt()
}
- mTimeHolder!!.setPadding(left, top, right, bottom)
-
- mCurrTimeView = mView!!.video_curr_time
- mSeekBar = mView!!.video_seekbar
- mSeekBar!!.setOnSeekBarChangeListener(this)
-
- if (mIsFullscreen) {
- mTimeHolder!!.beInvisible()
+ if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && activity?.hasNavBar() == true) {
+ right += activity!!.navigationBarWidth
}
- }
- private fun hasNavBar(): Boolean {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- val display = context!!.windowManager.defaultDisplay
-
- val realDisplayMetrics = DisplayMetrics()
- display.getRealMetrics(realDisplayMetrics)
-
- val realHeight = realDisplayMetrics.heightPixels
- val realWidth = realDisplayMetrics.widthPixels
-
- val displayMetrics = DisplayMetrics()
- display.getMetrics(displayMetrics)
-
- val displayHeight = displayMetrics.heightPixels
- val displayWidth = displayMetrics.widthPixels
-
- realWidth - displayWidth > 0 || realHeight - displayHeight > 0
- } else {
- val hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey()
- val hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK)
- !hasMenuKey && !hasBackKey
+ (mTimeHolder.layoutParams as RelativeLayout.LayoutParams).apply {
+ bottomMargin = bottom
+ rightMargin = right
}
+ mTimeHolder.beInvisibleIf(mIsFullscreen)
}
- private fun setupTimeHolder() {
- mSeekBar!!.max = mDuration
- mView!!.video_duration.text = mDuration.getFormattedDuration()
- setupTimer()
- }
-
- private fun setupTimer() {
- activity!!.runOnUiThread(object : Runnable {
- override fun run() {
- if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
- mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
- mSeekBar!!.progress = mCurrTime
- mCurrTimeView!!.text = mCurrTime.getFormattedDuration()
+ private fun checkIfPanorama() {
+ try {
+ val fis = FileInputStream(File(mMedium.path))
+ fis.use { fis ->
+ context!!.parseFileChannel(mMedium.path, fis.channel, 0, 0, 0) {
+ mIsPanorama = true
}
-
- mTimerHandler.postDelayed(this, 1000)
}
- })
+ } catch (ignored: Exception) {
+ } catch (ignored: OutOfMemoryError) {
+ }
}
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putInt(PROGRESS, mCurrTime)
+ private fun openPanorama() {
+ Intent(context, PanoramaVideoActivity::class.java).apply {
+ putExtra(PATH, mMedium.path)
+ startActivity(this)
+ }
}
- private fun checkFullscreen() {
- if (activity == null) {
+ private fun updateInstantSwitchWidths() {
+ val newWidth = resources.getDimension(R.dimen.instant_change_bar_width) + if (activity?.portrait == false) activity!!.navigationBarWidth else 0
+ mView.instant_prev_item.layoutParams.width = newWidth.toInt()
+ mView.instant_next_item.layoutParams.width = newWidth.toInt()
+ }
+
+ override fun fullscreenToggled(isFullscreen: Boolean) {
+ mIsFullscreen = isFullscreen
+ val newAlpha = if (isFullscreen) 0f else 1f
+ if (!mIsFullscreen) {
+ mTimeHolder.beVisible()
+ }
+
+ mSeekBar.setOnSeekBarChangeListener(if (mIsFullscreen) null else this)
+ arrayOf(mView.video_curr_time, mView.video_duration).forEach {
+ it.isClickable = !mIsFullscreen
+ }
+
+ mTimeHolder.animate().alpha(newAlpha).start()
+ mView.video_details.apply {
+ if (mStoredShowExtendedDetails && isVisible()) {
+ animate().y(getExtendedDetailsY(height))
+
+ if (mStoredHideExtendedDetails) {
+ animate().alpha(newAlpha).start()
+ }
+ }
+ }
+ }
+
+ private fun getExtendedDetailsY(height: Int): Float {
+ val smallMargin = resources.getDimension(R.dimen.small_margin)
+ val fullscreenOffset = smallMargin + if (mIsFullscreen) 0 else context!!.navigationBarHeight
+ var actionsHeight = 0f
+ if (!mIsFullscreen) {
+ actionsHeight += resources.getDimension(R.dimen.video_player_play_pause_size)
+ if (mConfig.bottomActions) {
+ actionsHeight += resources.getDimension(R.dimen.bottom_actions_height)
+ }
+ }
+ return context!!.realScreenSize.y - height - actionsHeight - fullscreenOffset
+ }
+
+ private fun skip(forward: Boolean) {
+ if (mExoPlayer == null || mIsPanorama) {
return
}
- var anim = android.R.anim.fade_in
- if (mIsFullscreen) {
- anim = android.R.anim.fade_out
- mSeekBar!!.setOnSeekBarChangeListener(null)
- } else {
- mSeekBar!!.setOnSeekBarChangeListener(this)
+ val curr = mExoPlayer!!.currentPosition
+ val twoPercents = Math.max((mExoPlayer!!.duration / 50).toInt(), MIN_SKIP_LENGTH)
+ val newProgress = if (forward) curr + twoPercents else curr - twoPercents
+ val roundProgress = Math.round(newProgress / 1000f)
+ val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt(), roundProgress), 0)
+ setPosition(limitedProgress)
+ if (!mIsPlaying) {
+ togglePlayPause()
+ }
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ if (mExoPlayer != null && fromUser) {
+ setPosition(progress)
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+ if (mExoPlayer == null)
+ return
+
+ mExoPlayer!!.playWhenReady = false
+ mIsDragged = true
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+ if (mIsPanorama) {
+ openPanorama()
+ return
}
- AnimationUtils.loadAnimation(activity, anim).apply {
- duration = 150
- fillAfter = true
- mTimeHolder?.startAnimation(this)
+ if (mExoPlayer == null)
+ return
+
+ if (mIsPlaying) {
+ mExoPlayer!!.playWhenReady = true
+ } else {
+ togglePlayPause()
}
+
+ mIsDragged = false
}
private fun togglePlayPause() {
@@ -384,7 +540,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
return
mIsPlaying = !mIsPlaying
- mHidePauseHandler.removeCallbacksAndMessages(null)
if (mIsPlaying) {
playVideo()
} else {
@@ -397,20 +552,39 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
return
}
- if (mView!!.video_preview.isVisible()) {
- mView!!.video_preview.beGone()
+ if (mView.video_preview.isVisible()) {
+ mView.video_preview.beGone()
initExoPlayer()
}
- if (videoEnded()) {
- setProgress(0)
+ val wasEnded = videoEnded()
+ if (wasEnded) {
+ setPosition(0)
}
+ if (mStoredRememberLastVideoPosition) {
+ setLastVideoSavedPosition()
+ clearLastVideoSavedProgress()
+ }
+
+ if (!wasEnded || !mConfig.loopVideos) {
+ mPlayPauseButton.setImageResource(R.drawable.ic_pause_outline)
+ }
+
+ if (!mWasVideoStarted) {
+ mView.video_play_outline.beGone()
+ mPlayPauseButton.beVisible()
+ }
+
+ mWasVideoStarted = true
mIsPlaying = true
mExoPlayer?.playWhenReady = true
- mView!!.video_play_outline.setImageResource(R.drawable.ic_pause)
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
- schedulePlayPauseFadeOut()
+ }
+
+ private fun clearLastVideoSavedProgress() {
+ mStoredLastVideoPosition = 0
+ mStoredLastVideoPath = ""
}
private fun pauseVideo() {
@@ -423,46 +597,35 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mExoPlayer?.playWhenReady = false
}
- mView?.video_play_outline?.setImageResource(R.drawable.ic_play)
- mView?.video_play_outline?.alpha = PLAY_PAUSE_VISIBLE_ALPHA
+ mPlayPauseButton.setImageResource(R.drawable.ic_play_outline)
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
- schedulePlayPauseFadeOut()
}
- private fun schedulePlayPauseFadeOut() {
- mHidePauseHandler.removeCallbacksAndMessages(null)
- mHidePauseHandler.postDelayed({
- mView!!.video_play_outline.animate().alpha(0f).start()
- }, HIDE_PAUSE_DELAY)
+ private fun videoEnded(): Boolean {
+ val currentPos = mExoPlayer?.currentPosition ?: 0
+ val duration = mExoPlayer?.duration ?: 0
+ return currentPos != 0L && currentPos >= duration
}
- private fun videoEnded() = mExoPlayer?.currentPosition ?: 0 >= mExoPlayer?.duration ?: 0
-
- private fun setProgress(seconds: Int) {
- mExoPlayer!!.seekTo(seconds * 1000L)
- mSeekBar!!.progress = seconds
- mCurrTimeView!!.text = seconds.getFormattedDuration()
+ private fun setPosition(seconds: Int) {
+ mExoPlayer?.seekTo(seconds * 1000L)
+ mSeekBar.progress = seconds
+ mCurrTimeView.text = seconds.getFormattedDuration()
}
private fun setupVideoDuration() {
- try {
- val retriever = MediaMetadataRetriever()
- retriever.setDataSource(medium.path)
- mDuration = Math.round(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toInt() / 1000f)
- } catch (ignored: Exception) {
- }
-
+ mDuration = mMedium.path.getVideoDuration()
setupTimeHolder()
- setProgress(0)
+ setPosition(0)
}
private fun videoPrepared() {
if (mDuration == 0) {
mDuration = (mExoPlayer!!.duration / 1000).toInt()
setupTimeHolder()
- setProgress(mCurrTime)
+ setPosition(mCurrTime)
- if (mIsFragmentVisible && (context!!.config.autoplayVideos)) {
+ if (mIsFragmentVisible && (mConfig.autoplayVideos)) {
playVideo()
}
}
@@ -474,23 +637,24 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
- if (listener?.videoEnded() == false && context!!.config.loopVideos) {
+ if (listener?.videoEnded() == false && mConfig.loopVideos) {
playVideo()
} else {
- mSeekBar!!.progress = mSeekBar!!.max
- mCurrTimeView!!.text = mDuration.getFormattedDuration()
+ mSeekBar.progress = mSeekBar.max
+ mCurrTimeView.text = mDuration.getFormattedDuration()
pauseVideo()
}
}
private fun cleanup() {
pauseVideo()
- mCurrTimeView?.text = 0.getFormattedDuration()
releaseExoPlayer()
- mSeekBar?.progress = 0
- mTimerHandler.removeCallbacksAndMessages(null)
- mHidePauseHandler.removeCallbacksAndMessages(null)
- mTextureView = null
+
+ if (mWasFragmentInit) {
+ mCurrTimeView.text = 0.getFormattedDuration()
+ mSeekBar.progress = 0
+ mTimerHandler.removeCallbacksAndMessages(null)
+ }
}
private fun releaseExoPlayer() {
@@ -509,33 +673,28 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
Thread {
- mExoPlayer?.setVideoSurface(Surface(mTextureView!!.surfaceTexture))
+ mExoPlayer?.setVideoSurface(Surface(mTextureView.surfaceTexture))
}.start()
}
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private fun setVideoSize() {
- if (activity == null || mTextureView == null)
+ if (activity == null || mConfig.openVideosOnSeparateScreen) {
return
+ }
val videoProportion = mVideoSize.x.toFloat() / mVideoSize.y.toFloat()
val display = activity!!.windowManager.defaultDisplay
val screenWidth: Int
val screenHeight: Int
- if (isJellyBean1Plus()) {
- val realMetrics = DisplayMetrics()
- display.getRealMetrics(realMetrics)
- screenWidth = realMetrics.widthPixels
- screenHeight = realMetrics.heightPixels
- } else {
- screenWidth = display.width
- screenHeight = display.height
- }
+ val realMetrics = DisplayMetrics()
+ display.getRealMetrics(realMetrics)
+ screenWidth = realMetrics.widthPixels
+ screenHeight = realMetrics.heightPixels
val screenProportion = screenWidth.toFloat() / screenHeight.toFloat()
- mTextureView!!.layoutParams.apply {
+ mTextureView.layoutParams.apply {
if (videoProportion > screenProportion) {
width = screenWidth
height = (screenWidth.toFloat() / videoProportion).toInt()
@@ -543,92 +702,7 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
width = (videoProportion * screenHeight.toFloat()).toInt()
height = screenHeight
}
- mTextureView!!.layoutParams = this
+ mTextureView.layoutParams = this
}
}
-
- private fun checkExtendedDetails() {
- if (context!!.config.showExtendedDetails) {
- mView!!.video_details.apply {
- beInvisible() // make it invisible so we can measure it, but not show yet
- text = getMediumExtendedDetails(medium)
- onGlobalLayout {
- if (isAdded) {
- val realY = getExtendedDetailsY(height)
- if (realY > 0) {
- y = realY
- beVisibleIf(text.isNotEmpty())
- alpha = if (!context!!.config.hideExtendedDetails || !mIsFullscreen) 1f else 0f
- }
- }
- }
- }
- } else {
- mView!!.video_details.beGone()
- }
- }
-
- private fun skip(forward: Boolean) {
- if (mExoPlayer == null) {
- return
- }
-
- val curr = mExoPlayer!!.currentPosition
- val twoPercents = Math.max((mExoPlayer!!.duration / 50).toInt(), MIN_SKIP_LENGTH)
- val newProgress = if (forward) curr + twoPercents else curr - twoPercents
- val roundProgress = Math.round(newProgress / 1000f)
- val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt(), roundProgress), 0)
- setProgress(limitedProgress)
- if (!mIsPlaying) {
- togglePlayPause()
- }
- }
-
- override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
- if (mExoPlayer != null && fromUser) {
- setProgress(progress)
- }
- }
-
- override fun onStartTrackingTouch(seekBar: SeekBar) {
- if (mExoPlayer == null)
- return
-
- mExoPlayer!!.playWhenReady = false
- mIsDragged = true
- }
-
- override fun onStopTrackingTouch(seekBar: SeekBar) {
- if (mExoPlayer == null)
- return
-
- if (!mIsPlaying) {
- togglePlayPause()
- } else {
- mExoPlayer!!.playWhenReady = true
- }
-
- mIsDragged = false
- }
-
- override fun fullscreenToggled(isFullscreen: Boolean) {
- mIsFullscreen = isFullscreen
- checkFullscreen()
- mView!!.video_details.apply {
- if (mStoredShowExtendedDetails && isVisible()) {
- animate().y(getExtendedDetailsY(height))
-
- if (mStoredHideExtendedDetails) {
- animate().alpha(if (isFullscreen) 0f else 1f).start()
- }
- }
- }
- }
-
- private fun getExtendedDetailsY(height: Int): Float {
- val smallMargin = resources.getDimension(R.dimen.small_margin)
- val timeHolderHeight = mTimeHolder!!.height - context!!.navigationBarHeight.toFloat()
- val fullscreenOffset = context!!.navigationBarHeight.toFloat() - smallMargin
- return context!!.usableScreenSize.y - height + if (mIsFullscreen) fullscreenOffset else -(timeHolderHeight + smallMargin)
- }
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/ViewPagerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/ViewPagerFragment.kt
new file mode 100644
index 000000000..2cb3cf893
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/ViewPagerFragment.kt
@@ -0,0 +1,119 @@
+package com.simplemobiletools.gallery.pro.fragments
+
+import android.provider.MediaStore
+import android.view.MotionEvent
+import androidx.fragment.app.Fragment
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.OTG_PATH
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.Medium
+import java.io.File
+
+abstract class ViewPagerFragment : Fragment() {
+ var listener: FragmentListener? = null
+
+ protected var mTouchDownTime = 0L
+ protected var mTouchDownX = 0f
+ protected var mTouchDownY = 0f
+ protected var mCloseDownThreshold = 100f
+ protected var mIgnoreCloseDown = false
+
+ abstract fun fullscreenToggled(isFullscreen: Boolean)
+
+ interface FragmentListener {
+ fun fragmentClicked()
+
+ fun videoEnded(): Boolean
+
+ fun goToPrevItem()
+
+ fun goToNextItem()
+
+ fun launchViewVideoIntent(path: String)
+ }
+
+ fun getMediumExtendedDetails(medium: Medium): String {
+ val file = File(medium.path)
+ if (!file.exists()) {
+ return ""
+ }
+
+ val path = "${file.parent.trimEnd('/')}/"
+ val exif = android.media.ExifInterface(medium.path)
+ val details = StringBuilder()
+ val detailsFlag = context!!.config.extendedDetails
+ if (detailsFlag and EXT_NAME != 0) {
+ medium.name.let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_PATH != 0) {
+ path.let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_SIZE != 0) {
+ file.length().formatSize().let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_RESOLUTION != 0) {
+ file.absolutePath.getResolution()?.formatAsResolution().let { if (it?.isNotEmpty() == true) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_LAST_MODIFIED != 0) {
+ getFileLastModified(file).let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_DATE_TAKEN != 0) {
+ path.getExifDateTaken(exif).let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_CAMERA_MODEL != 0) {
+ path.getExifCameraModel(exif).let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+
+ if (detailsFlag and EXT_EXIF_PROPERTIES != 0) {
+ path.getExifProperties(exif).let { if (it.isNotEmpty()) details.appendln(it) }
+ }
+ return details.toString().trim()
+ }
+
+ fun getPathToLoad(medium: Medium) = if (medium.path.startsWith(OTG_PATH)) medium.path.getOTGPublicPath(context!!) else medium.path
+
+ private fun getFileLastModified(file: File): String {
+ val projection = arrayOf(MediaStore.Images.Media.DATE_MODIFIED)
+ val uri = MediaStore.Files.getContentUri("external")
+ val selection = "${MediaStore.MediaColumns.DATA} = ?"
+ val selectionArgs = arrayOf(file.absolutePath)
+ val cursor = context!!.contentResolver.query(uri, projection, selection, selectionArgs, null)
+ cursor?.use {
+ return if (cursor.moveToFirst()) {
+ val dateModified = cursor.getLongValue(MediaStore.Images.Media.DATE_MODIFIED) * 1000L
+ dateModified.formatDate()
+ } else {
+ file.lastModified().formatDate()
+ }
+ }
+ return ""
+ }
+
+ protected fun handleEvent(event: MotionEvent) {
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ mTouchDownTime = System.currentTimeMillis()
+ mTouchDownX = event.x
+ mTouchDownY = event.y
+ }
+ MotionEvent.ACTION_POINTER_DOWN -> mIgnoreCloseDown = true
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ val diffX = mTouchDownX - event.x
+ val diffY = mTouchDownY - event.y
+
+ val downGestureDuration = System.currentTimeMillis() - mTouchDownTime
+ if (!mIgnoreCloseDown && Math.abs(diffY) > Math.abs(diffX) && diffY < -mCloseDownThreshold && downGestureDuration < MAX_CLOSE_DOWN_GESTURE_DURATION) {
+ activity?.supportFinishAfterTransition()
+ }
+ mIgnoreCloseDown = false
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Config.kt
similarity index 73%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/Config.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Config.kt
index e53e7f6f8..f715686b4 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/Config.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Config.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import android.content.Context
import android.content.res.Configuration
@@ -8,8 +8,8 @@ import com.google.gson.reflect.TypeToken
import com.simplemobiletools.commons.helpers.BaseConfig
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.models.AlbumCover
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.models.AlbumCover
import java.util.*
class Config(context: Context) : BaseConfig(context) {
@@ -59,6 +59,22 @@ class Config(context: Context) : BaseConfig(context) {
fun hasCustomGrouping(path: String) = prefs.contains(GROUP_FOLDER_PREFIX + path.toLowerCase())
+ fun saveFolderViewType(path: String, value: Int) {
+ if (path.isEmpty()) {
+ viewTypeFiles = value
+ } else {
+ prefs.edit().putInt(VIEW_TYPE_PREFIX + path.toLowerCase(), value).apply()
+ }
+ }
+
+ fun getFolderViewType(path: String) = prefs.getInt(VIEW_TYPE_PREFIX + path.toLowerCase(), viewTypeFiles)
+
+ fun removeFolderViewType(path: String) {
+ prefs.edit().remove(VIEW_TYPE_PREFIX + path.toLowerCase()).apply()
+ }
+
+ fun hasCustomViewType(path: String) = prefs.contains(VIEW_TYPE_PREFIX + path.toLowerCase())
+
var wasHideFolderTooltipShown: Boolean
get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false)
set(wasShown) = prefs.edit().putBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, wasShown).apply()
@@ -88,7 +104,10 @@ class Config(context: Context) : BaseConfig(context) {
fun addPinnedFolders(paths: Set) {
val currPinnedFolders = HashSet(pinnedFolders)
currPinnedFolders.addAll(paths)
- pinnedFolders = currPinnedFolders
+ pinnedFolders = currPinnedFolders.filter { it.isNotEmpty() }.toHashSet()
+ if (paths.contains(RECYCLE_BIN)) {
+ showRecycleBinLast = false
+ }
}
fun removePinnedFolders(paths: Set) {
@@ -104,7 +123,7 @@ class Config(context: Context) : BaseConfig(context) {
fun addExcludedFolders(paths: Set) {
val currExcludedFolders = HashSet(excludedFolders)
currExcludedFolders.addAll(paths)
- excludedFolders = currExcludedFolders
+ excludedFolders = currExcludedFolders.filter { it.isNotEmpty() }.toHashSet()
}
fun removeExcludedFolder(path: String) {
@@ -114,23 +133,21 @@ class Config(context: Context) : BaseConfig(context) {
}
var excludedFolders: MutableSet
- get() = prefs.getStringSet(EXCLUDED_FOLDERS, getDataFolder())
+ get() = prefs.getStringSet(EXCLUDED_FOLDERS, HashSet())
set(excludedFolders) = prefs.edit().remove(EXCLUDED_FOLDERS).putStringSet(EXCLUDED_FOLDERS, excludedFolders).apply()
- private fun getDataFolder(): Set {
- val folders = HashSet()
- val dataFolder = context.externalCacheDir?.parentFile?.parent?.trimEnd('/') ?: ""
- if (dataFolder.endsWith("data"))
- folders.add(dataFolder)
- return folders
- }
-
fun addIncludedFolder(path: String) {
val currIncludedFolders = HashSet(includedFolders)
currIncludedFolders.add(path)
includedFolders = currIncludedFolders
}
+ fun addIncludedFolders(paths: Set) {
+ val currIncludedFolders = HashSet(includedFolders)
+ currIncludedFolders.addAll(paths)
+ includedFolders = currIncludedFolders.filter { it.isNotEmpty() }.toHashSet()
+ }
+
fun removeIncludedFolder(path: String) {
val currIncludedFolders = HashSet(includedFolders)
currIncludedFolders.remove(path)
@@ -143,7 +160,7 @@ class Config(context: Context) : BaseConfig(context) {
var autoplayVideos: Boolean
get() = prefs.getBoolean(AUTOPLAY_VIDEOS, false)
- set(autoplay) = prefs.edit().putBoolean(AUTOPLAY_VIDEOS, autoplay).apply()
+ set(autoplayVideos) = prefs.edit().putBoolean(AUTOPLAY_VIDEOS, autoplayVideos).apply()
var animateGifs: Boolean
get() = prefs.getBoolean(ANIMATE_GIFS, false)
@@ -157,6 +174,10 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(CROP_THUMBNAILS, true)
set(cropThumbnails) = prefs.edit().putBoolean(CROP_THUMBNAILS, cropThumbnails).apply()
+ var showThumbnailVideoDuration: Boolean
+ get() = prefs.getBoolean(SHOW_THUMBNAIL_VIDEO_DURATION, false)
+ set(showThumbnailVideoDuration) = prefs.edit().putBoolean(SHOW_THUMBNAIL_VIDEO_DURATION, showThumbnailVideoDuration).apply()
+
var screenRotation: Int
get() = prefs.getInt(SCREEN_ROTATION, ROTATE_BY_SYSTEM_SETTING)
set(screenRotation) = prefs.edit().putInt(SCREEN_ROTATION, screenRotation).apply()
@@ -165,13 +186,17 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(LOOP_VIDEOS, false)
set(loop) = prefs.edit().putBoolean(LOOP_VIDEOS, loop).apply()
+ var openVideosOnSeparateScreen: Boolean
+ get() = prefs.getBoolean(OPEN_VIDEOS_ON_SEPARATE_SCREEN, false)
+ set(openVideosOnSeparateScreen) = prefs.edit().putBoolean(OPEN_VIDEOS_ON_SEPARATE_SCREEN, openVideosOnSeparateScreen).apply()
+
var displayFileNames: Boolean
get() = prefs.getBoolean(DISPLAY_FILE_NAMES, false)
set(display) = prefs.edit().putBoolean(DISPLAY_FILE_NAMES, display).apply()
var blackBackground: Boolean
- get() = prefs.getBoolean(DARK_BACKGROUND, false)
- set(darkBackground) = prefs.edit().putBoolean(DARK_BACKGROUND, darkBackground).apply()
+ get() = prefs.getBoolean(BLACK_BACKGROUND, false)
+ set(blackBackground) = prefs.edit().putBoolean(BLACK_BACKGROUND, blackBackground).apply()
var filterMedia: Int
get() = prefs.getInt(FILTER_MEDIA, TYPE_IMAGES or TYPE_VIDEOS or TYPE_GIFS or TYPE_RAWS or TYPE_SVGS)
@@ -181,10 +206,6 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(getDirectoryColumnsField(), getDefaultDirectoryColumnCount())
set(dirColumnCnt) = prefs.edit().putInt(getDirectoryColumnsField(), dirColumnCnt).apply()
- var oneFingerZoom: Boolean
- get() = prefs.getBoolean(ONE_FINGER_ZOOM, false)
- set(oneFingerZoom) = prefs.edit().putBoolean(ONE_FINGER_ZOOM, oneFingerZoom).apply()
-
var allowInstantChange: Boolean
get() = prefs.getBoolean(ALLOW_INSTANT_CHANGE, false)
set(allowInstantChange) = prefs.edit().putBoolean(ALLOW_INSTANT_CHANGE, allowInstantChange).apply()
@@ -266,10 +287,6 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(SLIDESHOW_INTERVAL, SLIDESHOW_DEFAULT_INTERVAL)
set(slideshowInterval) = prefs.edit().putInt(SLIDESHOW_INTERVAL, slideshowInterval).apply()
- var slideshowIncludePhotos: Boolean
- get() = prefs.getBoolean(SLIDESHOW_INCLUDE_PHOTOS, true)
- set(slideshowIncludePhotos) = prefs.edit().putBoolean(SLIDESHOW_INCLUDE_PHOTOS, slideshowIncludePhotos).apply()
-
var slideshowIncludeVideos: Boolean
get() = prefs.getBoolean(SLIDESHOW_INCLUDE_VIDEOS, false)
set(slideshowIncludeVideos) = prefs.edit().putBoolean(SLIDESHOW_INCLUDE_VIDEOS, slideshowIncludeVideos).apply()
@@ -362,6 +379,18 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(BOTTOM_ACTIONS, true)
set(bottomActions) = prefs.edit().putBoolean(BOTTOM_ACTIONS, bottomActions).apply()
+ var rememberLastVideoPosition: Boolean
+ get() = prefs.getBoolean(REMEMBER_LAST_VIDEO_POSITION, false)
+ set(rememberLastVideoPosition) = prefs.edit().putBoolean(REMEMBER_LAST_VIDEO_POSITION, rememberLastVideoPosition).apply()
+
+ var lastVideoPath: String
+ get() = prefs.getString(LAST_VIDEO_PATH, "")
+ set(lastVideoPath) = prefs.edit().putString(LAST_VIDEO_PATH, lastVideoPath).apply()
+
+ var lastVideoPosition: Int
+ get() = prefs.getInt(LAST_VIDEO_POSITION, 0)
+ set(lastVideoPosition) = prefs.edit().putInt(LAST_VIDEO_POSITION, lastVideoPosition).apply()
+
var visibleBottomActions: Int
get() = prefs.getInt(VISIBLE_BOTTOM_ACTIONS, DEFAULT_BOTTOM_ACTIONS)
set(visibleBottomActions) = prefs.edit().putInt(VISIBLE_BOTTOM_ACTIONS, visibleBottomActions).apply()
@@ -371,10 +400,12 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getStringSet(EVER_SHOWN_FOLDERS, getEverShownFolders())
set(everShownFolders) = prefs.edit().putStringSet(EVER_SHOWN_FOLDERS, everShownFolders).apply()
- fun getEverShownFolders() = hashSetOf(
+ private fun getEverShownFolders() = hashSetOf(
internalStoragePath,
- Environment.DIRECTORY_DCIM,
- Environment.DIRECTORY_PICTURES
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).absolutePath,
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath,
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath,
+ "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath}/Screenshots"
)
var showRecycleBinAtFolders: Boolean
@@ -388,4 +419,52 @@ class Config(context: Context) : BaseConfig(context) {
var lastBinCheck: Long
get() = prefs.getLong(LAST_BIN_CHECK, 0L)
set(lastBinCheck) = prefs.edit().putLong(LAST_BIN_CHECK, lastBinCheck).apply()
+
+ var showHighestQuality: Boolean
+ get() = prefs.getBoolean(SHOW_HIGHEST_QUALITY, false)
+ set(showHighestQuality) = prefs.edit().putBoolean(SHOW_HIGHEST_QUALITY, showHighestQuality).apply()
+
+ var showRecycleBinLast: Boolean
+ get() = prefs.getBoolean(SHOW_RECYCLE_BIN_LAST, false)
+ set(showRecycleBinLast) = prefs.edit().putBoolean(SHOW_RECYCLE_BIN_LAST, showRecycleBinLast).apply()
+
+ var allowDownGesture: Boolean
+ get() = prefs.getBoolean(ALLOW_DOWN_GESTURE, true)
+ set(allowDownGesture) = prefs.edit().putBoolean(ALLOW_DOWN_GESTURE, allowDownGesture).apply()
+
+ var lastEditorCropAspectRatio: Int
+ get() = prefs.getInt(LAST_EDITOR_CROP_ASPECT_RATIO, ASPECT_RATIO_FREE)
+ set(lastEditorCropAspectRatio) = prefs.edit().putInt(LAST_EDITOR_CROP_ASPECT_RATIO, lastEditorCropAspectRatio).apply()
+
+ var lastEditorCropOtherAspectRatioX: Int
+ get() = prefs.getInt(LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_X, 2)
+ set(lastEditorCropOtherAspectRatioX) = prefs.edit().putInt(LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_X, lastEditorCropOtherAspectRatioX).apply()
+
+ var lastEditorCropOtherAspectRatioY: Int
+ get() = prefs.getInt(LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_Y, 1)
+ set(lastEditorCropOtherAspectRatioY) = prefs.edit().putInt(LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_Y, lastEditorCropOtherAspectRatioY).apply()
+
+ var groupDirectSubfolders: Boolean
+ get() = prefs.getBoolean(GROUP_DIRECT_SUBFOLDERS, false)
+ set(groupDirectSubfolders) = prefs.edit().putBoolean(GROUP_DIRECT_SUBFOLDERS, groupDirectSubfolders).apply()
+
+ var showWidgetFolderName: Boolean
+ get() = prefs.getBoolean(SHOW_WIDGET_FOLDER_NAME, true)
+ set(showWidgetFolderName) = prefs.edit().putBoolean(SHOW_WIDGET_FOLDER_NAME, showWidgetFolderName).apply()
+
+ var allowOneToOneZoom: Boolean
+ get() = prefs.getBoolean(ALLOW_ONE_TO_ONE_ZOOM, false)
+ set(allowOneToOneZoom) = prefs.edit().putBoolean(ALLOW_ONE_TO_ONE_ZOOM, allowOneToOneZoom).apply()
+
+ var lastEditorDrawColor: Int
+ get() = prefs.getInt(LAST_EDITOR_DRAW_COLOR, primaryColor)
+ set(lastEditorDrawColor) = prefs.edit().putInt(LAST_EDITOR_DRAW_COLOR, lastEditorDrawColor).apply()
+
+ var lastEditorBrushSize: Int
+ get() = prefs.getInt(LAST_EDITOR_BRUSH_SIZE, 50)
+ set(lastEditorBrushSize) = prefs.edit().putInt(LAST_EDITOR_BRUSH_SIZE, lastEditorBrushSize).apply()
+
+ var showNotch: Boolean
+ get() = prefs.getBoolean(SHOW_NOTCH, true)
+ set(showNotch) = prefs.edit().putBoolean(SHOW_NOTCH, showNotch).apply()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Constants.kt
similarity index 74%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/Constants.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Constants.kt
index 38761a2d2..d3f5588a8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/Constants.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Constants.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import com.simplemobiletools.commons.helpers.MONTH_SECONDS
@@ -6,17 +6,21 @@ import com.simplemobiletools.commons.helpers.MONTH_SECONDS
const val DIRECTORY_SORT_ORDER = "directory_sort_order"
const val SORT_FOLDER_PREFIX = "sort_folder_"
const val GROUP_FOLDER_PREFIX = "group_folder_"
+const val VIEW_TYPE_PREFIX = "view_type_folder_"
const val SHOW_HIDDEN_MEDIA = "show_hidden_media"
const val TEMPORARILY_SHOW_HIDDEN = "temporarily_show_hidden"
const val IS_THIRD_PARTY_INTENT = "is_third_party_intent"
const val AUTOPLAY_VIDEOS = "autoplay_videos"
+const val REMEMBER_LAST_VIDEO_POSITION = "remember_last_video_position"
const val LOOP_VIDEOS = "loop_videos"
+const val OPEN_VIDEOS_ON_SEPARATE_SCREEN = "open_videos_on_separate_screen"
const val ANIMATE_GIFS = "animate_gifs"
const val MAX_BRIGHTNESS = "max_brightness"
const val CROP_THUMBNAILS = "crop_thumbnails"
+const val SHOW_THUMBNAIL_VIDEO_DURATION = "show_thumbnail_video_duration"
const val SCREEN_ROTATION = "screen_rotation"
const val DISPLAY_FILE_NAMES = "display_file_names"
-const val DARK_BACKGROUND = "dark_background"
+const val BLACK_BACKGROUND = "dark_background"
const val PINNED_FOLDERS = "pinned_folders"
const val FILTER_MEDIA = "filter_media"
const val DIR_COLUMN_CNT = "dir_column_cnt"
@@ -43,7 +47,6 @@ const val VIEW_TYPE_FILES = "view_type_files"
const val SHOW_EXTENDED_DETAILS = "show_extended_details"
const val EXTENDED_DETAILS = "extended_details"
const val HIDE_EXTENDED_DETAILS = "hide_extended_details"
-const val ONE_FINGER_ZOOM = "one_finger_zoom"
const val ALLOW_INSTANT_CHANGE = "allow_instant_change"
const val DO_EXTRA_CHECK = "do_extra_check"
const val WAS_NEW_APP_SHOWN = "was_new_app_shown_clock"
@@ -51,6 +54,8 @@ const val LAST_FILEPICKER_PATH = "last_filepicker_path"
const val WAS_OTG_HANDLED = "was_otg_handled"
const val TEMP_SKIP_DELETE_CONFIRMATION = "temp_skip_delete_confirmation"
const val BOTTOM_ACTIONS = "bottom_actions"
+const val LAST_VIDEO_PATH = "last_video_path"
+const val LAST_VIDEO_POSITION = "last_video_position"
const val VISIBLE_BOTTOM_ACTIONS = "visible_bottom_actions"
const val WERE_FAVORITES_PINNED = "were_favorites_pinned"
const val WAS_RECYCLE_BIN_PINNED = "was_recycle_bin_pinned"
@@ -58,13 +63,24 @@ const val USE_RECYCLE_BIN = "use_recycle_bin"
const val GROUP_BY = "group_by"
const val EVER_SHOWN_FOLDERS = "ever_shown_folders"
const val SHOW_RECYCLE_BIN_AT_FOLDERS = "show_recycle_bin_at_folders"
+const val SHOW_RECYCLE_BIN_LAST = "show_recycle_bin_last"
const val ALLOW_ZOOMING_IMAGES = "allow_zooming_images"
const val WAS_SVG_SHOWING_HANDLED = "was_svg_showing_handled"
const val LAST_BIN_CHECK = "last_bin_check"
+const val SHOW_HIGHEST_QUALITY = "show_highest_quality"
+const val ALLOW_DOWN_GESTURE = "allow_down_gesture"
+const val LAST_EDITOR_CROP_ASPECT_RATIO = "last_editor_crop_aspect_ratio"
+const val LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_X = "last_editor_crop_other_aspect_ratio_x"
+const val LAST_EDITOR_CROP_OTHER_ASPECT_RATIO_Y = "last_editor_crop_other_aspect_ratio_y"
+const val GROUP_DIRECT_SUBFOLDERS = "group_direct_subfolders"
+const val SHOW_WIDGET_FOLDER_NAME = "show_widget_folder_name"
+const val ALLOW_ONE_TO_ONE_ZOOM = "allow_one_to_one_zoom"
+const val LAST_EDITOR_DRAW_COLOR = "last_editor_draw_color"
+const val LAST_EDITOR_BRUSH_SIZE = "last_editor_brush_size"
+const val SHOW_NOTCH = "show_notch"
// slideshow
const val SLIDESHOW_INTERVAL = "slideshow_interval"
-const val SLIDESHOW_INCLUDE_PHOTOS = "slideshow_include_photos"
const val SLIDESHOW_INCLUDE_VIDEOS = "slideshow_include_videos"
const val SLIDESHOW_INCLUDE_GIFS = "slideshow_include_gifs"
const val SLIDESHOW_RANDOM_ORDER = "slideshow_random_order"
@@ -73,17 +89,26 @@ const val SLIDESHOW_MOVE_BACKWARDS = "slideshow_move_backwards"
const val SLIDESHOW_LOOP = "loop_slideshow"
const val SLIDESHOW_DEFAULT_INTERVAL = 5
const val SLIDESHOW_SCROLL_DURATION = 500L
+const val SLIDESHOW_START_ON_ENTER = "slideshow_start_on_enter"
const val NOMEDIA = ".nomedia"
const val FAVORITES = "favorites"
const val RECYCLE_BIN = "recycle_bin"
const val SHOW_FAVORITES = "show_favorites"
const val SHOW_RECYCLE_BIN = "show_recycle_bin"
+const val SHOW_NEXT_ITEM = "show_next_item"
+const val SHOW_PREV_ITEM = "show_prev_item"
+const val GO_TO_NEXT_ITEM = "go_to_next_item"
+const val GO_TO_PREV_ITEM = "go_to_prev_item"
const val MAX_COLUMN_COUNT = 20
const val SHOW_TEMP_HIDDEN_DURATION = 300000L
const val CLICK_MAX_DURATION = 150
+const val CLICK_MAX_DISTANCE = 100
+const val MAX_CLOSE_DOWN_GESTURE_DURATION = 300
const val DRAG_THRESHOLD = 8
const val MONTH_MILLISECONDS = MONTH_SECONDS * 1000L
+const val MIN_SKIP_LENGTH = 2000
+const val HIDE_SYSTEM_UI_DELAY = 500L
const val DIRECTORY = "directory"
const val MEDIUM = "medium"
@@ -149,5 +174,20 @@ const val BOTTOM_ACTION_SHOW_ON_MAP = 256
const val BOTTOM_ACTION_TOGGLE_VISIBILITY = 512
const val BOTTOM_ACTION_RENAME = 1024
const val BOTTOM_ACTION_SET_AS = 2048
+const val BOTTOM_ACTION_COPY = 4096
const val DEFAULT_BOTTOM_ACTIONS = BOTTOM_ACTION_TOGGLE_FAVORITE or BOTTOM_ACTION_EDIT or BOTTOM_ACTION_SHARE or BOTTOM_ACTION_DELETE
+
+// aspect ratios used at the editor for cropping
+const val ASPECT_RATIO_FREE = 0
+const val ASPECT_RATIO_ONE_ONE = 1
+const val ASPECT_RATIO_FOUR_THREE = 2
+const val ASPECT_RATIO_SIXTEEN_NINE = 3
+const val ASPECT_RATIO_OTHER = 4
+
+// some constants related to zooming videos
+const val MIN_VIDEO_ZOOM_SCALE = 1f
+const val MAX_VIDEO_ZOOM_SCALE = 5f
+const val ZOOM_MODE_NONE = 0
+const val ZOOM_MODE_DRAG = 1
+const val ZOOM_MODE_ZOOM = 2
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/FilterThumbnailsManager.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/FilterThumbnailsManager.kt
similarity index 86%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/FilterThumbnailsManager.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/FilterThumbnailsManager.kt
index 204f54f3b..ae043ddde 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/FilterThumbnailsManager.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/FilterThumbnailsManager.kt
@@ -1,7 +1,7 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import android.graphics.Bitmap
-import com.simplemobiletools.gallery.models.FilterItem
+import com.simplemobiletools.gallery.pro.models.FilterItem
import java.util.*
class FilterThumbnailsManager {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/GlideRotateTransformation.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/GlideRotateTransformation.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/GlideRotateTransformation.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/GlideRotateTransformation.kt
index 5f34c6ced..a0dabc09d 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/GlideRotateTransformation.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/GlideRotateTransformation.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import android.graphics.Bitmap
import android.graphics.Matrix
@@ -8,8 +8,9 @@ import java.security.MessageDigest
class GlideRotateTransformation(val rotateRotationAngle: Int) : BitmapTransformation() {
override fun transform(pool: BitmapPool, bitmap: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
- if (rotateRotationAngle % 360 == 0)
+ if (rotateRotationAngle % 360 == 0) {
return bitmap
+ }
val matrix = Matrix()
matrix.postRotate(rotateRotationAngle.toFloat())
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/IsoTypeReader.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/IsoTypeReader.kt
new file mode 100644
index 000000000..d8c33e72d
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/IsoTypeReader.kt
@@ -0,0 +1,27 @@
+package com.simplemobiletools.gallery.pro.helpers
+
+import java.io.UnsupportedEncodingException
+import java.nio.ByteBuffer
+import java.nio.charset.Charset
+
+// file taken from the https://github.com/sannies/mp4parser project, used at determining if a video is a panoramic one
+object IsoTypeReader {
+ fun readUInt32(bb: ByteBuffer): Long {
+ var i = bb.int.toLong()
+ if (i < 0) {
+ i += 1L shl 32
+ }
+ return i
+ }
+
+ fun read4cc(bb: ByteBuffer): String? {
+ val codeBytes = ByteArray(4)
+ bb.get(codeBytes)
+
+ return try {
+ String(codeBytes, Charset.forName("ISO-8859-1"))
+ } catch (e: UnsupportedEncodingException) {
+ null
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MediaFetcher.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MediaFetcher.kt
index 7b7a08997..a5912edce 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaFetcher.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MediaFetcher.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import android.content.Context
import android.database.Cursor
@@ -7,33 +7,39 @@ import android.provider.MediaStore
import android.text.format.DateFormat
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.extensions.*
-import com.simplemobiletools.gallery.models.Medium
-import com.simplemobiletools.gallery.models.ThumbnailItem
-import com.simplemobiletools.gallery.models.ThumbnailSection
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.models.Medium
+import com.simplemobiletools.gallery.pro.models.ThumbnailItem
+import com.simplemobiletools.gallery.pro.models.ThumbnailSection
import java.io.File
import java.util.*
class MediaFetcher(val context: Context) {
var shouldStop = false
- fun getFilesFrom(curPath: String, isPickImage: Boolean, isPickVideo: Boolean, getProperDateTaken: Boolean, favoritePaths: ArrayList): ArrayList {
+ fun getFilesFrom(curPath: String, isPickImage: Boolean, isPickVideo: Boolean, getProperDateTaken: Boolean, favoritePaths: ArrayList,
+ getVideoDurations: Boolean, sortMedia: Boolean = true): ArrayList {
val filterMedia = context.config.filterMedia
if (filterMedia == 0) {
return ArrayList()
}
val curMedia = ArrayList()
- if (curPath.startsWith(OTG_PATH)) {
- val newMedia = getMediaOnOTG(curPath, isPickImage, isPickVideo, filterMedia, favoritePaths)
- curMedia.addAll(newMedia)
+ if (curPath.startsWith(OTG_PATH, true)) {
+ if (context.hasOTGConnected()) {
+ val newMedia = getMediaOnOTG(curPath, isPickImage, isPickVideo, filterMedia, favoritePaths, getVideoDurations)
+ curMedia.addAll(newMedia)
+ }
} else {
- val newMedia = getMediaInFolder(curPath, isPickImage, isPickVideo, filterMedia, getProperDateTaken, favoritePaths)
+ val newMedia = getMediaInFolder(curPath, isPickImage, isPickVideo, filterMedia, getProperDateTaken, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
}
- sortMedia(curMedia, context.config.getFileSorting(curPath))
+ if (sortMedia) {
+ sortMedia(curMedia, context.config.getFileSorting(curPath))
+ }
+
return curMedia
}
@@ -122,7 +128,7 @@ class MediaFetcher(val context: Context) {
val foldersToIgnore = arrayListOf("/storage/emulated/legacy")
val config = context.config
val includedFolders = config.includedFolders
- var foldersToScan = config.everShownFolders.toMutableList() as ArrayList
+ var foldersToScan = config.everShownFolders.filter { it == FAVORITES || it == RECYCLE_BIN || context.getDoesFilePathExist(it) }.toMutableList() as ArrayList
cursor.use {
if (cursor.moveToFirst()) {
@@ -167,7 +173,7 @@ class MediaFetcher(val context: Context) {
}
private fun getMediaInFolder(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int, getProperDateTaken: Boolean,
- favoritePaths: ArrayList): ArrayList {
+ favoritePaths: ArrayList, getVideoDurations: Boolean): ArrayList {
val media = ArrayList()
val deletedMedia = if (folder == RECYCLE_BIN) {
@@ -176,27 +182,27 @@ class MediaFetcher(val context: Context) {
ArrayList()
}
+ val doExtraCheck = context.config.doExtraCheck
+ val showHidden = context.config.shouldShowHidden
+ val dateTakens = if (getProperDateTaken && folder != FAVORITES && folder != RECYCLE_BIN) getFolderDateTakens(folder) else HashMap()
+
val files = when (folder) {
- FAVORITES -> favoritePaths.map { File(it) }.toTypedArray()
+ FAVORITES -> favoritePaths.filter { showHidden || !it.contains("/.") }.map { File(it) }.toTypedArray()
RECYCLE_BIN -> deletedMedia.map { File(it.path) }.toTypedArray()
else -> File(folder).listFiles() ?: return media
}
- val doExtraCheck = context.config.doExtraCheck
- val showHidden = context.config.shouldShowHidden
- val dateTakens = if (getProperDateTaken) getFolderDateTakens(folder) else HashMap()
-
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()
- val isRaw = if (isImage || isVideo || isGif) false else filename.isRawFast()
- val isSvg = if (isImage || isVideo || isGif || isRaw) false else filename.isSvg()
+ val path = file.absolutePath
+ val isImage = path.isImageFast()
+ val isVideo = if (isImage) false else path.isVideoFast()
+ val isGif = if (isImage || isVideo) false else path.isGif()
+ val isRaw = if (isImage || isVideo || isGif) false else path.isRawFast()
+ val isSvg = if (isImage || isVideo || isGif || isRaw) false else path.isSvg()
if (!isImage && !isVideo && !isGif && !isRaw && !isSvg)
continue
@@ -216,6 +222,7 @@ class MediaFetcher(val context: Context) {
if (isSvg && filterMedia and TYPE_SVGS == 0)
continue
+ val filename = file.name
if (!showHidden && filename.startsWith('.'))
continue
@@ -223,7 +230,6 @@ class MediaFetcher(val context: Context) {
if (size <= 0L || (doExtraCheck && !file.exists()))
continue
- val path = file.absolutePath
if (folder == RECYCLE_BIN) {
deletedMedia.firstOrNull { it.path == path }?.apply {
media.add(this)
@@ -231,6 +237,7 @@ class MediaFetcher(val context: Context) {
} else {
val lastModified = file.lastModified()
var dateTaken = lastModified
+ val videoDuration = if (getVideoDurations && isVideo) path.getVideoDuration() else 0
if (getProperDateTaken) {
dateTaken = dateTakens.remove(filename) ?: lastModified
@@ -245,14 +252,15 @@ class MediaFetcher(val context: Context) {
}
val isFavorite = favoritePaths.contains(path)
- val medium = Medium(null, filename, path, file.parent, lastModified, dateTaken, size, type, isFavorite, 0L)
+ val medium = Medium(null, filename, path, file.parent, lastModified, dateTaken, size, type, videoDuration, isFavorite, 0L)
media.add(medium)
}
}
return media
}
- private fun getMediaOnOTG(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int, favoritePaths: ArrayList): ArrayList {
+ private fun getMediaOnOTG(folder: String, isPickImage: Boolean, isPickVideo: Boolean, filterMedia: Int, favoritePaths: ArrayList,
+ getVideoDurations: Boolean): ArrayList {
val media = ArrayList()
val files = context.getDocumentFile(folder)?.listFiles() ?: return media
val doExtraCheck = context.config.doExtraCheck
@@ -307,8 +315,9 @@ class MediaFetcher(val context: Context) {
}
val path = Uri.decode(file.uri.toString().replaceFirst("${context.config.OTGTreeUri}/document/${context.config.OTGPartition}%3A", OTG_PATH))
+ val videoDuration = if (getVideoDurations) path.getVideoDuration() else 0
val isFavorite = favoritePaths.contains(path)
- val medium = Medium(null, filename, path, folder, dateModified, dateTaken, size, type, isFavorite, 0L)
+ val medium = Medium(null, filename, path, folder, dateModified, dateTaken, size, type, videoDuration, isFavorite, 0L)
media.add(medium)
}
@@ -344,6 +353,11 @@ class MediaFetcher(val context: Context) {
}
fun sortMedia(media: ArrayList, sorting: Int) {
+ if (sorting and SORT_BY_RANDOM != 0) {
+ media.shuffle()
+ return
+ }
+
media.sortWith(Comparator { o1, o2 ->
o1 as Medium
o2 as Medium
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MyWidgetProvider.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MyWidgetProvider.kt
new file mode 100644
index 000000000..2af024791
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/MyWidgetProvider.kt
@@ -0,0 +1,85 @@
+package com.simplemobiletools.gallery.pro.helpers
+
+import android.app.PendingIntent
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.widget.RemoteViews
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.request.RequestOptions
+import com.simplemobiletools.commons.extensions.setBackgroundColor
+import com.simplemobiletools.commons.extensions.setText
+import com.simplemobiletools.commons.extensions.setVisibleIf
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.activities.MediaActivity
+import com.simplemobiletools.gallery.pro.extensions.*
+import com.simplemobiletools.gallery.pro.models.Widget
+
+class MyWidgetProvider : AppWidgetProvider() {
+ private fun setupAppOpenIntent(context: Context, views: RemoteViews, id: Int, widget: Widget) {
+ val intent = Intent(context, MediaActivity::class.java).apply {
+ putExtra(DIRECTORY, widget.folderPath)
+ }
+
+ val pendingIntent = PendingIntent.getActivity(context, widget.widgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+ views.setOnClickPendingIntent(id, pendingIntent)
+ }
+
+ override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
+ super.onUpdate(context, appWidgetManager, appWidgetIds)
+ Thread {
+ val config = context.config
+ context.widgetsDB.getWidgets().filter { appWidgetIds.contains(it.widgetId) }.forEach {
+ val views = RemoteViews(context.packageName, R.layout.widget).apply {
+ setBackgroundColor(R.id.widget_holder, config.widgetBgColor)
+ setVisibleIf(R.id.widget_folder_name, config.showWidgetFolderName)
+ setTextColor(R.id.widget_folder_name, config.widgetTextColor)
+ setText(R.id.widget_folder_name, context.getFolderNameFromPath(it.folderPath))
+ }
+
+ val path = context.directoryDB.getDirectoryThumbnail(it.folderPath) ?: return@forEach
+ val options = RequestOptions()
+ .signature(path.getFileSignature())
+ .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
+ if (context.config.cropThumbnails) options.centerCrop() else options.fitCenter()
+
+ val density = context.resources.displayMetrics.density
+ val appWidgetOptions = appWidgetManager.getAppWidgetOptions(appWidgetIds.first())
+ val width = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
+ val height = appWidgetOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
+
+ val widgetSize = (Math.max(width, height) * density).toInt()
+ try {
+ val image = Glide.with(context)
+ .asBitmap()
+ .load(path)
+ .apply(options)
+ .submit(widgetSize, widgetSize)
+ .get()
+ views.setImageViewBitmap(R.id.widget_imageview, image)
+ } catch (e: Exception) {
+ }
+
+ setupAppOpenIntent(context, views, R.id.widget_holder, it)
+ appWidgetManager.updateAppWidget(it.widgetId, views)
+ }
+ }.start()
+ }
+
+ override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle) {
+ super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
+ onUpdate(context, appWidgetManager, intArrayOf(appWidgetId))
+ }
+
+ override fun onDeleted(context: Context, appWidgetIds: IntArray) {
+ super.onDeleted(context, appWidgetIds)
+ Thread {
+ appWidgetIds.forEach {
+ context.widgetsDB.deleteWidgetId(it)
+ }
+ }.start()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/PicassoDecoder.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/PicassoDecoder.kt
similarity index 83%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/PicassoDecoder.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/PicassoDecoder.kt
index 16dd85760..978106763 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/PicassoDecoder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/PicassoDecoder.kt
@@ -1,9 +1,9 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
-import com.davemorrissey.labs.subscaleview.decoder.ImageDecoder
+import com.davemorrissey.labs.subscaleview.ImageDecoder
import com.squareup.picasso.MemoryPolicy
import com.squareup.picasso.Picasso
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/PicassoRegionDecoder.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/PicassoRegionDecoder.kt
similarity index 83%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/PicassoRegionDecoder.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/PicassoRegionDecoder.kt
index 69c758009..85b0e1133 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/PicassoRegionDecoder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/PicassoRegionDecoder.kt
@@ -1,16 +1,17 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.helpers
import android.content.Context
import android.graphics.*
import android.net.Uri
-import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder
+import com.davemorrissey.labs.subscaleview.ImageRegionDecoder
class PicassoRegionDecoder : ImageRegionDecoder {
private var decoder: BitmapRegionDecoder? = null
private val decoderLock = Any()
override fun init(context: Context, uri: Uri): Point {
- val inputStream = context.contentResolver.openInputStream(uri)
+ val newUri = Uri.parse(uri.toString().replace("%", "%25").replace("#", "%23"))
+ val inputStream = context.contentResolver.openInputStream(newUri)
decoder = BitmapRegionDecoder.newInstance(inputStream, false)
return Point(decoder!!.width, decoder!!.height)
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/DirectoryDao.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/DirectoryDao.kt
similarity index 73%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/DirectoryDao.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/DirectoryDao.kt
index 6ca318f4d..57d6807c2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/DirectoryDao.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/DirectoryDao.kt
@@ -1,11 +1,11 @@
-package com.simplemobiletools.gallery.interfaces
+package com.simplemobiletools.gallery.pro.interfaces
-import android.arch.persistence.room.Dao
-import android.arch.persistence.room.Insert
-import android.arch.persistence.room.OnConflictStrategy.REPLACE
-import android.arch.persistence.room.Query
-import com.simplemobiletools.gallery.helpers.RECYCLE_BIN
-import com.simplemobiletools.gallery.models.Directory
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy.REPLACE
+import androidx.room.Query
+import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
+import com.simplemobiletools.gallery.pro.models.Directory
@Dao
interface DirectoryDao {
@@ -29,4 +29,7 @@ interface DirectoryDao {
@Query("DELETE FROM directories WHERE path = \'$RECYCLE_BIN\' COLLATE NOCASE")
fun deleteRecycleBin()
+
+ @Query("SELECT thumbnail FROM directories WHERE path = :path")
+ fun getDirectoryThumbnail(path: String): String?
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/DirectoryOperationsListener.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/DirectoryOperationsListener.kt
similarity index 67%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/DirectoryOperationsListener.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/DirectoryOperationsListener.kt
index 760ed78f4..b4f265969 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/DirectoryOperationsListener.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/DirectoryOperationsListener.kt
@@ -1,6 +1,6 @@
-package com.simplemobiletools.gallery.interfaces
+package com.simplemobiletools.gallery.pro.interfaces
-import com.simplemobiletools.gallery.models.Directory
+import com.simplemobiletools.gallery.pro.models.Directory
import java.io.File
interface DirectoryOperationsListener {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/MediaOperationsListener.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/MediaOperationsListener.kt
similarity index 81%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/MediaOperationsListener.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/MediaOperationsListener.kt
index 77e877fd8..7e384003c 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/MediaOperationsListener.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/MediaOperationsListener.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.interfaces
+package com.simplemobiletools.gallery.pro.interfaces
import com.simplemobiletools.commons.models.FileDirItem
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/MediumDao.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/MediumDao.kt
similarity index 63%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/MediumDao.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/MediumDao.kt
index ac653ccd4..1ce2bb641 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/interfaces/MediumDao.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/MediumDao.kt
@@ -1,24 +1,24 @@
-package com.simplemobiletools.gallery.interfaces
+package com.simplemobiletools.gallery.pro.interfaces
-import android.arch.persistence.room.Dao
-import android.arch.persistence.room.Delete
-import android.arch.persistence.room.Insert
-import android.arch.persistence.room.OnConflictStrategy.REPLACE
-import android.arch.persistence.room.Query
-import com.simplemobiletools.gallery.models.Medium
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy.REPLACE
+import androidx.room.Query
+import com.simplemobiletools.gallery.pro.models.Medium
@Dao
interface MediumDao {
- @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, is_favorite, deleted_ts FROM media WHERE deleted_ts = 0 AND parent_path = :path COLLATE NOCASE")
+ @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, video_duration, is_favorite, deleted_ts FROM media WHERE deleted_ts = 0 AND parent_path = :path COLLATE NOCASE")
fun getMediaFromPath(path: String): List
- @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, is_favorite, deleted_ts FROM media WHERE deleted_ts = 0 AND is_favorite = 1")
+ @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, video_duration, is_favorite, deleted_ts FROM media WHERE deleted_ts = 0 AND is_favorite = 1")
fun getFavorites(): List
@Query("SELECT full_path FROM media WHERE deleted_ts = 0 AND is_favorite = 1")
fun getFavoritePaths(): List
- @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, is_favorite, deleted_ts FROM media WHERE deleted_ts != 0")
+ @Query("SELECT filename, full_path, parent_path, last_modified, date_taken, size, type, video_duration, is_favorite, deleted_ts FROM media WHERE deleted_ts != 0")
fun getDeletedMedia(): List
@Insert(onConflict = REPLACE)
@@ -33,7 +33,7 @@ interface MediumDao {
@Query("DELETE FROM media WHERE full_path = :path COLLATE NOCASE")
fun deleteMediumPath(path: String)
- @Query("DELETE FROM media WHERE deleted_ts < :timestmap")
+ @Query("DELETE FROM media WHERE deleted_ts < :timestmap AND deleted_ts != 0")
fun deleteOldRecycleBinItems(timestmap: Long)
@Query("UPDATE OR REPLACE media SET filename = :newFilename, full_path = :newFullPath, parent_path = :newParentPath WHERE full_path = :oldPath COLLATE NOCASE")
@@ -42,8 +42,8 @@ interface MediumDao {
@Query("UPDATE media SET is_favorite = :isFavorite WHERE full_path = :path COLLATE NOCASE")
fun updateFavorite(path: String, isFavorite: Boolean)
- @Query("UPDATE media SET deleted_ts = :deletedTS WHERE full_path = :path COLLATE NOCASE")
- fun updateDeleted(path: String, deletedTS: Long)
+ @Query("UPDATE OR REPLACE media SET full_path = :newPath, deleted_ts = :deletedTS WHERE full_path = :oldPath COLLATE NOCASE")
+ fun updateDeleted(newPath: String, deletedTS: Long, oldPath: String)
@Query("UPDATE media SET date_taken = :dateTaken WHERE full_path = :path COLLATE NOCASE")
fun updateFavoriteDateTaken(path: String, dateTaken: Long)
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/WidgetsDao.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/WidgetsDao.kt
new file mode 100644
index 000000000..b6ecb2492
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/interfaces/WidgetsDao.kt
@@ -0,0 +1,19 @@
+package com.simplemobiletools.gallery.pro.interfaces
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.simplemobiletools.gallery.pro.models.Widget
+
+@Dao
+interface WidgetsDao {
+ @Query("SELECT * FROM widgets")
+ fun getWidgets(): List
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insertOrUpdate(widget: Widget): Long
+
+ @Query("DELETE FROM widgets WHERE widget_id = :widgetId")
+ fun deleteWidgetId(widgetId: Int)
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/models/AlbumCover.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/AlbumCover.kt
similarity index 54%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/models/AlbumCover.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/AlbumCover.kt
index f5937ad08..c4afd11b1 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/models/AlbumCover.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/AlbumCover.kt
@@ -1,3 +1,3 @@
-package com.simplemobiletools.gallery.models
+package com.simplemobiletools.gallery.pro.models
data class AlbumCover(val path: String, val tmb: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/models/Directory.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Directory.kt
similarity index 64%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/models/Directory.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Directory.kt
index 3c748c50e..fd320050e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/models/Directory.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Directory.kt
@@ -1,20 +1,16 @@
-package com.simplemobiletools.gallery.models
+package com.simplemobiletools.gallery.pro.models
-import android.arch.persistence.room.ColumnInfo
-import android.arch.persistence.room.Entity
-import android.arch.persistence.room.Index
-import android.arch.persistence.room.PrimaryKey
+import androidx.room.*
import com.simplemobiletools.commons.extensions.formatDate
import com.simplemobiletools.commons.extensions.formatSize
import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_BY_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_PATH
import com.simplemobiletools.commons.helpers.SORT_BY_SIZE
-import com.simplemobiletools.gallery.helpers.FAVORITES
-import com.simplemobiletools.gallery.helpers.RECYCLE_BIN
-import java.io.Serializable
+import com.simplemobiletools.gallery.pro.helpers.FAVORITES
+import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
-@Entity(tableName = "directories", indices = [Index(value = "path", unique = true)])
+@Entity(tableName = "directories", indices = [Index(value = ["path"], unique = true)])
data class Directory(
@PrimaryKey(autoGenerate = true) var id: Long?,
@ColumnInfo(name = "path") var path: String,
@@ -24,12 +20,14 @@ data class Directory(
@ColumnInfo(name = "last_modified") var modified: Long,
@ColumnInfo(name = "date_taken") var taken: Long,
@ColumnInfo(name = "size") var size: Long,
- @ColumnInfo(name = "location") val location: Int,
- @ColumnInfo(name = "media_types") var types: Int) : Serializable {
+ @ColumnInfo(name = "location") var location: Int,
+ @ColumnInfo(name = "media_types") var types: Int,
- companion object {
- private const val serialVersionUID = -6553345863555455L
- }
+ // used with "Group direct subfolders" enabled
+ @Ignore var subfoldersCount: Int = 0,
+ @Ignore var subfoldersMediaCount: Int = 0) {
+
+ constructor() : this(null, "", "", "", 0, 0L, 0L, 0L, 0, 0, 0, 0)
fun getBubbleText(sorting: Int) = when {
sorting and SORT_BY_NAME != 0 -> name
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/models/FilterItem.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/FilterItem.kt
similarity index 75%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/models/FilterItem.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/FilterItem.kt
index f2dd7f2d6..ce826e57e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/models/FilterItem.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/FilterItem.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.models
+package com.simplemobiletools.gallery.pro.models
import android.graphics.Bitmap
import com.zomato.photofilters.imageprocessors.Filter
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/models/Medium.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Medium.kt
similarity index 87%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/models/Medium.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Medium.kt
index 14d804178..e6d95da66 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/models/Medium.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Medium.kt
@@ -1,9 +1,9 @@
-package com.simplemobiletools.gallery.models
+package com.simplemobiletools.gallery.pro.models
-import android.arch.persistence.room.ColumnInfo
-import android.arch.persistence.room.Entity
-import android.arch.persistence.room.Index
-import android.arch.persistence.room.PrimaryKey
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
import com.simplemobiletools.commons.extensions.formatDate
import com.simplemobiletools.commons.extensions.formatSize
import com.simplemobiletools.commons.extensions.getFilenameExtension
@@ -11,11 +11,11 @@ import com.simplemobiletools.commons.helpers.SORT_BY_DATE_MODIFIED
import com.simplemobiletools.commons.helpers.SORT_BY_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_PATH
import com.simplemobiletools.commons.helpers.SORT_BY_SIZE
-import com.simplemobiletools.gallery.helpers.*
+import com.simplemobiletools.gallery.pro.helpers.*
import java.io.Serializable
import java.util.*
-@Entity(tableName = "media", indices = [(Index(value = "full_path", unique = true))])
+@Entity(tableName = "media", indices = [(Index(value = ["full_path"], unique = true))])
data class Medium(
@PrimaryKey(autoGenerate = true) var id: Long?,
@ColumnInfo(name = "filename") var name: String,
@@ -25,6 +25,7 @@ data class Medium(
@ColumnInfo(name = "date_taken") var taken: Long,
@ColumnInfo(name = "size") val size: Long,
@ColumnInfo(name = "type") val type: Int,
+ @ColumnInfo(name = "video_duration") val videoDuration: Int,
@ColumnInfo(name = "is_favorite") var isFavorite: Boolean,
@ColumnInfo(name = "deleted_ts") var deletedTS: Long) : Serializable, ThumbnailItem() {
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/PaintOptions.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/PaintOptions.kt
new file mode 100644
index 000000000..fc1cacb2a
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/PaintOptions.kt
@@ -0,0 +1,5 @@
+package com.simplemobiletools.gallery.pro.models
+
+import android.graphics.Color
+
+data class PaintOptions(var color: Int = Color.BLACK, var strokeWidth: Float = 5f)
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/ThumbnailItem.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/ThumbnailItem.kt
new file mode 100644
index 000000000..4d2deaa31
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/ThumbnailItem.kt
@@ -0,0 +1,3 @@
+package com.simplemobiletools.gallery.pro.models
+
+open class ThumbnailItem
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/models/ThumbnailSection.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/ThumbnailSection.kt
similarity index 57%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/models/ThumbnailSection.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/ThumbnailSection.kt
index fc501383f..1655cad89 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/models/ThumbnailSection.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/ThumbnailSection.kt
@@ -1,3 +1,3 @@
-package com.simplemobiletools.gallery.models
+package com.simplemobiletools.gallery.pro.models
data class ThumbnailSection(val title: String) : ThumbnailItem()
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Widget.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Widget.kt
new file mode 100644
index 000000000..2ef525334
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Widget.kt
@@ -0,0 +1,12 @@
+package com.simplemobiletools.gallery.pro.models
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.Index
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "widgets", indices = [(Index(value = ["widget_id"], unique = true))])
+data class Widget(
+ @PrimaryKey(autoGenerate = true) var id: Int?,
+ @ColumnInfo(name = "widget_id") var widgetId: Int,
+ @ColumnInfo(name = "folder_path") var folderPath: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/receivers/RefreshMediaReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/receivers/RefreshMediaReceiver.kt
similarity index 75%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/receivers/RefreshMediaReceiver.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/receivers/RefreshMediaReceiver.kt
index d9ece1f20..3e3f35ec9 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/receivers/RefreshMediaReceiver.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/receivers/RefreshMediaReceiver.kt
@@ -1,13 +1,13 @@
-package com.simplemobiletools.gallery.receivers
+package com.simplemobiletools.gallery.pro.receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.REFRESH_PATH
-import com.simplemobiletools.gallery.extensions.galleryDB
-import com.simplemobiletools.gallery.helpers.*
-import com.simplemobiletools.gallery.models.Medium
+import com.simplemobiletools.gallery.pro.extensions.galleryDB
+import com.simplemobiletools.gallery.pro.helpers.*
+import com.simplemobiletools.gallery.pro.models.Medium
import java.io.File
class RefreshMediaReceiver : BroadcastReceiver() {
@@ -16,7 +16,7 @@ class RefreshMediaReceiver : BroadcastReceiver() {
Thread {
val medium = Medium(null, path.getFilenameFromPath(), path, path.getParentPath(), System.currentTimeMillis(), System.currentTimeMillis(),
- File(path).length(), getFileType(path), false, 0L)
+ File(path).length(), getFileType(path), 0, false, 0L)
context.galleryDB.MediumDao().insert(medium)
}.start()
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgDecoder.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgDecoder.kt
similarity index 94%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgDecoder.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgDecoder.kt
index 3765db0e3..7bc47c9f8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgDecoder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgDecoder.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.svg
+package com.simplemobiletools.gallery.pro.svg
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.ResourceDecoder
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgDrawableTranscoder.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgDrawableTranscoder.kt
similarity index 93%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgDrawableTranscoder.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgDrawableTranscoder.kt
index 96dbe3492..e447136e9 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgDrawableTranscoder.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgDrawableTranscoder.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.svg
+package com.simplemobiletools.gallery.pro.svg
import android.graphics.drawable.PictureDrawable
import com.bumptech.glide.load.Options
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgModule.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgModule.kt
similarity index 93%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgModule.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgModule.kt
index a6e4f3efb..ee15a2274 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgModule.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgModule.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.svg
+package com.simplemobiletools.gallery.pro.svg
import android.content.Context
import android.graphics.drawable.PictureDrawable
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgSoftwareLayerSetter.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgSoftwareLayerSetter.kt
similarity index 95%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgSoftwareLayerSetter.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgSoftwareLayerSetter.kt
index 043d26e27..c766ab651 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/svg/SvgSoftwareLayerSetter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/svg/SvgSoftwareLayerSetter.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.svg
+package com.simplemobiletools.gallery.pro.svg
import android.graphics.drawable.PictureDrawable
import android.widget.ImageView
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/EditorDrawCanvas.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/EditorDrawCanvas.kt
new file mode 100644
index 000000000..592320b33
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/EditorDrawCanvas.kt
@@ -0,0 +1,147 @@
+package com.simplemobiletools.gallery.pro.views
+
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.config
+import com.simplemobiletools.gallery.pro.models.PaintOptions
+import java.util.*
+
+class EditorDrawCanvas(context: Context, attrs: AttributeSet) : View(context, attrs) {
+ private var mCurX = 0f
+ private var mCurY = 0f
+ private var mStartX = 0f
+ private var mStartY = 0f
+ private var mColor = 0
+ private var mWasMultitouch = false
+
+ private var mPaths = LinkedHashMap()
+ private var mPaint = Paint()
+ private var mPath = Path()
+ private var mPaintOptions = PaintOptions()
+
+ private var backgroundBitmap: Bitmap? = null
+
+ init {
+ mColor = context.config.primaryColor
+ mPaint.apply {
+ color = mColor
+ style = Paint.Style.STROKE
+ strokeJoin = Paint.Join.ROUND
+ strokeCap = Paint.Cap.ROUND
+ strokeWidth = 40f
+ isAntiAlias = true
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ canvas.save()
+
+ if (backgroundBitmap != null) {
+ canvas.drawBitmap(backgroundBitmap!!, 0f, 0f, null)
+ }
+
+ for ((key, value) in mPaths) {
+ changePaint(value)
+ canvas.drawPath(key, mPaint)
+ }
+
+ changePaint(mPaintOptions)
+ canvas.drawPath(mPath, mPaint)
+ canvas.restore()
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ val x = event.x
+ val y = event.y
+
+ when (event.action and MotionEvent.ACTION_MASK) {
+ MotionEvent.ACTION_DOWN -> {
+ mWasMultitouch = false
+ mStartX = x
+ mStartY = y
+ actionDown(x, y)
+ }
+ MotionEvent.ACTION_MOVE -> {
+ if (event.pointerCount == 1 && !mWasMultitouch) {
+ actionMove(x, y)
+ }
+ }
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> actionUp()
+ MotionEvent.ACTION_POINTER_DOWN -> mWasMultitouch = true
+ }
+
+ invalidate()
+ return true
+ }
+
+ private fun actionDown(x: Float, y: Float) {
+ mPath.reset()
+ mPath.moveTo(x, y)
+ mCurX = x
+ mCurY = y
+ }
+
+ private fun actionMove(x: Float, y: Float) {
+ mPath.quadTo(mCurX, mCurY, (x + mCurX) / 2, (y + mCurY) / 2)
+ mCurX = x
+ mCurY = y
+ }
+
+ private fun actionUp() {
+ if (!mWasMultitouch) {
+ mPath.lineTo(mCurX, mCurY)
+
+ // draw a dot on click
+ if (mStartX == mCurX && mStartY == mCurY) {
+ mPath.lineTo(mCurX, mCurY + 2)
+ mPath.lineTo(mCurX + 1, mCurY + 2)
+ mPath.lineTo(mCurX + 1, mCurY)
+ }
+ }
+
+ mPaths[mPath] = mPaintOptions
+ mPath = Path()
+ mPaintOptions = PaintOptions(mPaintOptions.color, mPaintOptions.strokeWidth)
+ }
+
+ private fun changePaint(paintOptions: PaintOptions) {
+ mPaint.color = paintOptions.color
+ mPaint.strokeWidth = paintOptions.strokeWidth
+ }
+
+ fun updateColor(newColor: Int) {
+ mPaintOptions.color = newColor
+ }
+
+ fun updateBrushSize(newBrushSize: Int) {
+ mPaintOptions.strokeWidth = resources.getDimension(R.dimen.full_brush_size) * (newBrushSize / 100f)
+ }
+
+ fun updateBackgroundBitmap(bitmap: Bitmap) {
+ backgroundBitmap = bitmap
+ invalidate()
+ }
+
+ fun getBitmap(): Bitmap {
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ canvas.drawColor(Color.WHITE)
+ draw(canvas)
+ return bitmap
+ }
+
+ fun undo() {
+ if (mPaths.isEmpty()) {
+ return
+ }
+
+ val lastKey = mPaths.keys.lastOrNull()
+ mPaths.remove(lastKey)
+ invalidate()
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/views/InstantItemSwitch.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/InstantItemSwitch.kt
similarity index 80%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/views/InstantItemSwitch.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/InstantItemSwitch.kt
index bdfe1264a..18f2f154e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/views/InstantItemSwitch.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/InstantItemSwitch.kt
@@ -1,12 +1,13 @@
-package com.simplemobiletools.gallery.views
+package com.simplemobiletools.gallery.pro.views
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.RelativeLayout
-import com.simplemobiletools.gallery.helpers.CLICK_MAX_DURATION
-import com.simplemobiletools.gallery.helpers.DRAG_THRESHOLD
+import com.simplemobiletools.gallery.pro.helpers.CLICK_MAX_DISTANCE
+import com.simplemobiletools.gallery.pro.helpers.CLICK_MAX_DURATION
+import com.simplemobiletools.gallery.pro.helpers.DRAG_THRESHOLD
// handle only one finger clicks, pass other events to the parent view and ignore it when received again
class InstantItemSwitch(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {
@@ -40,7 +41,9 @@ class InstantItemSwitch(context: Context, attrs: AttributeSet) : RelativeLayout(
mTouchDownTime = System.currentTimeMillis()
}
MotionEvent.ACTION_UP -> {
- if (System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION) {
+ val diffX = mTouchDownX - event.x
+ val diffY = mTouchDownY - event.y
+ if (Math.abs(diffX) < CLICK_MAX_DISTANCE && Math.abs(diffY) < CLICK_MAX_DISTANCE && System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION) {
performClick()
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaSideScroll.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/MediaSideScroll.kt
similarity index 80%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaSideScroll.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/MediaSideScroll.kt
index 91c5f0a3c..52ebde06e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/MediaSideScroll.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/MediaSideScroll.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.helpers
+package com.simplemobiletools.gallery.pro.views
import android.app.Activity
import android.content.Context
@@ -10,9 +10,12 @@ import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.TextView
-import com.simplemobiletools.gallery.R
-import com.simplemobiletools.gallery.activities.ViewPagerActivity
-import com.simplemobiletools.gallery.extensions.audioManager
+import com.simplemobiletools.commons.extensions.onGlobalLayout
+import com.simplemobiletools.gallery.pro.R
+import com.simplemobiletools.gallery.pro.extensions.audioManager
+import com.simplemobiletools.gallery.pro.helpers.CLICK_MAX_DISTANCE
+import com.simplemobiletools.gallery.pro.helpers.CLICK_MAX_DURATION
+import com.simplemobiletools.gallery.pro.helpers.DRAG_THRESHOLD
// allow horizontal swipes through the layout, else it can cause glitches at zoomed in images
class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {
@@ -23,6 +26,7 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
private var mTouchDownValue = -1
private var mTempBrightness = 0
private var mLastTouchY = 0f
+ private var mViewHeight = 0
private var mIsBrightnessScroll = false
private var mPassTouches = false
private var dragThreshold = DRAG_THRESHOLD * context.resources.displayMetrics.density
@@ -30,8 +34,8 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
private var mSlideInfoText = ""
private var mSlideInfoFadeHandler = Handler()
private var mParentView: ViewGroup? = null
+ private var activity: Activity? = null
- private lateinit var activity: Activity
private lateinit var slideInfoView: TextView
private lateinit var callback: (Float, Float) -> Unit
@@ -42,6 +46,9 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
mParentView = parentView
mIsBrightnessScroll = isBrightness
mSlideInfoText = activity.getString(if (isBrightness) R.string.brightness else R.string.volume)
+ onGlobalLayout {
+ mViewHeight = height
+ }
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
@@ -55,7 +62,7 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
}
override fun onTouchEvent(event: MotionEvent): Boolean {
- if (mPassTouches) {
+ if (mPassTouches && activity == null) {
return false
}
@@ -78,7 +85,7 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
val diffY = mTouchDownY - event.y
if (Math.abs(diffY) > dragThreshold && Math.abs(diffY) > Math.abs(diffX)) {
- var percent = ((diffY / ViewPagerActivity.screenHeight) * 100).toInt() * 3
+ var percent = ((diffY / mViewHeight) * 100).toInt() * 3
percent = Math.min(100, Math.max(-100, percent))
if ((percent == 100 && event.y > mLastTouchY) || (percent == -100 && event.y < mLastTouchY)) {
@@ -100,7 +107,9 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
mLastTouchY = event.y
}
MotionEvent.ACTION_UP -> {
- if (System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION) {
+ val diffX = mTouchDownX - event.x
+ val diffY = mTouchDownY - event.y
+ if (Math.abs(diffX) < CLICK_MAX_DISTANCE && Math.abs(diffY) < CLICK_MAX_DISTANCE && System.currentTimeMillis() - mTouchDownTime < CLICK_MAX_DURATION) {
callback(event.rawX, event.rawY)
}
@@ -112,11 +121,11 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
return true
}
- private fun getCurrentVolume() = activity.audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
+ private fun getCurrentVolume() = activity!!.audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
private fun getCurrentBrightness(): Int {
return try {
- Settings.System.getInt(activity.contentResolver, Settings.System.SCREEN_BRIGHTNESS)
+ Settings.System.getInt(activity!!.contentResolver, Settings.System.SCREEN_BRIGHTNESS)
} catch (e: Settings.SettingNotFoundException) {
70
}
@@ -132,11 +141,11 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
private fun volumePercentChanged(percent: Int) {
val stream = AudioManager.STREAM_MUSIC
- val maxVolume = activity.audioManager.getStreamMaxVolume(stream)
+ val maxVolume = activity!!.audioManager.getStreamMaxVolume(stream)
val percentPerPoint = 100 / maxVolume
val addPoints = percent / percentPerPoint
val newVolume = Math.min(maxVolume, Math.max(0, mTouchDownValue + addPoints))
- activity.audioManager.setStreamVolume(stream, newVolume, 0)
+ activity!!.audioManager.setStreamVolume(stream, newVolume, 0)
val absolutePercent = ((newVolume / maxVolume.toFloat()) * 100).toInt()
showValue(absolutePercent)
@@ -156,9 +165,9 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
val absolutePercent = ((newBrightness / maxBrightness) * 100).toInt()
showValue(absolutePercent)
- val attributes = activity.window.attributes
+ val attributes = activity!!.window.attributes
attributes.screenBrightness = absolutePercent / 100f
- activity.window.attributes = attributes
+ activity!!.window.attributes = attributes
mSlideInfoFadeHandler.removeCallbacksAndMessages(null)
mSlideInfoFadeHandler.postDelayed({
diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/views/MySquareImageView.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/MySquareImageView.kt
similarity index 92%
rename from app/src/main/kotlin/com/simplemobiletools/gallery/views/MySquareImageView.kt
rename to app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/MySquareImageView.kt
index 39a2f74be..24cde1c2a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/gallery/views/MySquareImageView.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/views/MySquareImageView.kt
@@ -1,4 +1,4 @@
-package com.simplemobiletools.gallery.views
+package com.simplemobiletools.gallery.pro.views
import android.content.Context
import android.util.AttributeSet
diff --git a/app/src/main/res/drawable-hdpi/ic_draw.png b/app/src/main/res/drawable-hdpi/ic_draw.png
new file mode 100644
index 000000000..b2c513174
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_draw.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_next_outline.png b/app/src/main/res/drawable-hdpi/ic_next_outline.png
new file mode 100644
index 000000000..88c0a34aa
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_next_outline.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_panorama.png b/app/src/main/res/drawable-hdpi/ic_panorama.png
deleted file mode 100644
index 513420277..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_panorama.png and /dev/null differ
diff --git a/app/src/main/res/drawable-hdpi/ic_panorama_outline.png b/app/src/main/res/drawable-hdpi/ic_panorama_outline.png
new file mode 100644
index 000000000..c932dcd78
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_panorama_outline.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_pause_outline.png b/app/src/main/res/drawable-hdpi/ic_pause_outline.png
new file mode 100644
index 000000000..d6d51bfc9
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_pause_outline.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_play_outline.png b/app/src/main/res/drawable-hdpi/ic_play_outline.png
new file mode 100644
index 000000000..12be0724f
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_play_outline.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_prev_outline.png b/app/src/main/res/drawable-hdpi/ic_prev_outline.png
new file mode 100644
index 000000000..898c74d78
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_prev_outline.png differ
diff --git a/app/src/main/res/drawable-hdpi/img_widget_preview.png b/app/src/main/res/drawable-hdpi/img_widget_preview.png
new file mode 100644
index 000000000..625e39531
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/img_widget_preview.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_draw.png b/app/src/main/res/drawable-xhdpi/ic_draw.png
new file mode 100644
index 000000000..534bf7180
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_draw.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_next_outline.png b/app/src/main/res/drawable-xhdpi/ic_next_outline.png
new file mode 100644
index 000000000..e92ce5556
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_next_outline.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_panorama.png b/app/src/main/res/drawable-xhdpi/ic_panorama.png
deleted file mode 100644
index c7f48d89c..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_panorama.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_panorama_outline.png b/app/src/main/res/drawable-xhdpi/ic_panorama_outline.png
new file mode 100644
index 000000000..70e0274ec
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_panorama_outline.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_pause_outline.png b/app/src/main/res/drawable-xhdpi/ic_pause_outline.png
new file mode 100644
index 000000000..11d8d1698
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_pause_outline.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_play_outline.png b/app/src/main/res/drawable-xhdpi/ic_play_outline.png
new file mode 100644
index 000000000..f99911f6c
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_play_outline.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_prev_outline.png b/app/src/main/res/drawable-xhdpi/ic_prev_outline.png
new file mode 100644
index 000000000..8c2c6774b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_prev_outline.png differ
diff --git a/app/src/main/res/drawable-xhdpi/img_widget_preview.png b/app/src/main/res/drawable-xhdpi/img_widget_preview.png
new file mode 100644
index 000000000..481681a63
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/img_widget_preview.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_draw.png b/app/src/main/res/drawable-xxhdpi/ic_draw.png
new file mode 100644
index 000000000..3e42d0923
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_draw.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_next_outline.png b/app/src/main/res/drawable-xxhdpi/ic_next_outline.png
new file mode 100644
index 000000000..f88b8d317
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_next_outline.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_panorama.png b/app/src/main/res/drawable-xxhdpi/ic_panorama.png
deleted file mode 100644
index 17e7cde47..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_panorama.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_panorama_outline.png b/app/src/main/res/drawable-xxhdpi/ic_panorama_outline.png
new file mode 100644
index 000000000..8ba832ca8
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_panorama_outline.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_pause_outline.png b/app/src/main/res/drawable-xxhdpi/ic_pause_outline.png
new file mode 100644
index 000000000..d6f0c72ce
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_pause_outline.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_play_outline.png b/app/src/main/res/drawable-xxhdpi/ic_play_outline.png
new file mode 100644
index 000000000..156d6ca62
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_play_outline.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_prev_outline.png b/app/src/main/res/drawable-xxhdpi/ic_prev_outline.png
new file mode 100644
index 000000000..b92feff7d
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_prev_outline.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/img_widget_preview.png b/app/src/main/res/drawable-xxhdpi/img_widget_preview.png
new file mode 100644
index 000000000..c400e88e9
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/img_widget_preview.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_draw.png b/app/src/main/res/drawable-xxxhdpi/ic_draw.png
new file mode 100644
index 000000000..6c1924bdf
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_draw.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_next_outline.png b/app/src/main/res/drawable-xxxhdpi/ic_next_outline.png
new file mode 100644
index 000000000..34fe17702
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_next_outline.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_panorama.png b/app/src/main/res/drawable-xxxhdpi/ic_panorama.png
deleted file mode 100644
index deaad2e1c..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/ic_panorama.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_panorama_outline.png b/app/src/main/res/drawable-xxxhdpi/ic_panorama_outline.png
new file mode 100644
index 000000000..c7a48896b
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_panorama_outline.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_pause_outline.png b/app/src/main/res/drawable-xxxhdpi/ic_pause_outline.png
new file mode 100644
index 000000000..162ed84d1
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_pause_outline.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_play_outline.png b/app/src/main/res/drawable-xxxhdpi/ic_play_outline.png
new file mode 100644
index 000000000..77370d426
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_play_outline.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_prev_outline.png b/app/src/main/res/drawable-xxxhdpi/ic_prev_outline.png
new file mode 100644
index 000000000..2e0a23b8b
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_prev_outline.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/img_widget_preview.png b/app/src/main/res/drawable-xxxhdpi/img_widget_preview.png
new file mode 100644
index 000000000..ffe566b2d
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/img_widget_preview.png differ
diff --git a/app/src/main/res/drawable/circle_black_background_with_inset.xml b/app/src/main/res/drawable/circle_black_background_with_inset.xml
deleted file mode 100644
index 6bab48721..000000000
--- a/app/src/main/res/drawable/circle_black_background_with_inset.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
diff --git a/app/src/main/res/drawable/actionbar_gradient_background.xml b/app/src/main/res/drawable/gradient_background_flipped.xml
similarity index 100%
rename from app/src/main/res/drawable/actionbar_gradient_background.xml
rename to app/src/main/res/drawable/gradient_background_flipped.xml
diff --git a/app/src/main/res/drawable/gradient_background_lighter.xml b/app/src/main/res/drawable/gradient_background_lighter.xml
deleted file mode 100644
index ba3477dc5..000000000
--- a/app/src/main/res/drawable/gradient_background_lighter.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/layout/activity_edit.xml b/app/src/main/res/layout/activity_edit.xml
index 7e0465221..dcf322124 100644
--- a/app/src/main/res/layout/activity_edit.xml
+++ b/app/src/main/res/layout/activity_edit.xml
@@ -11,24 +11,32 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_editor_crop_rotate_actions"
- android:layout_marginBottom="@dimen/activity_margin"/>
+ android:layout_marginBottom="@dimen/bottom_filters_height_with_margin"/>
+
+
+ android:background="@drawable/gradient_background"/>
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 5946f7e31..84912ef58 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,5 @@
-
@@ -49,8 +49,8 @@
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:paddingLeft="@dimen/normal_margin"
android:paddingStart="@dimen/normal_margin"
+ android:paddingLeft="@dimen/normal_margin"
android:visibility="gone">
@@ -61,9 +61,9 @@
android:id="@+id/directories_horizontal_fastscroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
android:paddingTop="@dimen/normal_margin"
android:visibility="gone">
@@ -71,4 +71,4 @@
-
+
diff --git a/app/src/main/res/layout/activity_manage_folders.xml b/app/src/main/res/layout/activity_manage_folders.xml
index b1256ecc8..e4526f204 100644
--- a/app/src/main/res/layout/activity_manage_folders.xml
+++ b/app/src/main/res/layout/activity_manage_folders.xml
@@ -20,8 +20,8 @@
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:paddingLeft="@dimen/big_margin"
- android:paddingRight="@dimen/big_margin"
android:paddingTop="@dimen/activity_margin"
+ android:paddingRight="@dimen/big_margin"
android:text="@string/excluded_activity_placeholder"
android:visibility="gone"/>
diff --git a/app/src/main/res/layout/activity_media.xml b/app/src/main/res/layout/activity_media.xml
index 629861b53..222ca5826 100644
--- a/app/src/main/res/layout/activity_media.xml
+++ b/app/src/main/res/layout/activity_media.xml
@@ -1,5 +1,5 @@
-
@@ -49,8 +49,8 @@
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:paddingLeft="@dimen/normal_margin"
android:paddingStart="@dimen/normal_margin"
+ android:paddingLeft="@dimen/normal_margin"
android:visibility="gone">
@@ -61,9 +61,9 @@
android:id="@+id/media_horizontal_fastscroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
android:paddingTop="@dimen/normal_margin"
android:visibility="gone">
@@ -71,4 +71,4 @@
-
+
diff --git a/app/src/main/res/layout/activity_medium.xml b/app/src/main/res/layout/activity_medium.xml
index 63559ebf0..8de070d68 100644
--- a/app/src/main/res/layout/activity_medium.xml
+++ b/app/src/main/res/layout/activity_medium.xml
@@ -14,4 +14,10 @@
android:id="@+id/bottom_actions"
layout="@layout/bottom_actions"/>
+
+
diff --git a/app/src/main/res/layout/activity_panorama.xml b/app/src/main/res/layout/activity_panorama_photo.xml
similarity index 78%
rename from app/src/main/res/layout/activity_panorama.xml
rename to app/src/main/res/layout/activity_panorama_photo.xml
index ab146f09e..199fb8d8f 100644
--- a/app/src/main/res/layout/activity_panorama.xml
+++ b/app/src/main/res/layout/activity_panorama_photo.xml
@@ -4,7 +4,7 @@
android:id="@+id/panorama_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#FF000000">
+ android:background="@color/md_grey_black">
+
+
@@ -25,8 +32,8 @@
android:id="@+id/explore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_explore"/>
diff --git a/app/src/main/res/layout/activity_panorama_video.xml b/app/src/main/res/layout/activity_panorama_video.xml
new file mode 100644
index 000000000..343496d24
--- /dev/null
+++ b/app/src/main/res/layout/activity_panorama_video.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index b543aaa68..bf10bce55 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -12,46 +12,24 @@
android:layout_height="wrap_content"
android:orientation="vertical">
-
-
-
-
-
-
+ android:paddingBottom="@dimen/activity_margin">
@@ -62,10 +40,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
-
-
-
-
-
-
+ android:paddingBottom="@dimen/activity_margin">
@@ -150,18 +104,18 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -172,18 +126,18 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -194,10 +148,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -218,10 +172,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -247,8 +201,8 @@
android:id="@+id/videos_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/videos"
android:textAllCaps="true"
@@ -260,10 +214,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
+
+
+
+
+
+
+ android:paddingBottom="@dimen/activity_margin">
+
+
+
+
+
+
+ android:paddingBottom="@dimen/activity_margin">
@@ -337,8 +339,8 @@
android:id="@+id/thumbnails_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/thumbnails"
android:textAllCaps="true"
@@ -350,10 +352,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -374,10 +376,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
+
+
+
+
+
+
+ android:paddingBottom="@dimen/activity_margin">
@@ -427,8 +453,8 @@
android:id="@+id/scrolling_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/scrolling"
android:textAllCaps="true"
@@ -440,10 +466,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -464,10 +490,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -488,10 +514,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -517,8 +543,8 @@
android:id="@+id/fullscreen_media_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/fullscreen_media"
android:textAllCaps="true"
@@ -530,10 +556,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -554,10 +580,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -578,10 +604,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
-
-
-
-
-
-
-
-
-
-
-
-
+ android:paddingBottom="@dimen/activity_margin">
@@ -674,10 +652,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
+
+
+
+
+
+
+
+
+
+
+
+
+ android:paddingBottom="@dimen/bigger_margin">
@@ -727,6 +753,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:paddingBottom="@dimen/activity_margin">
@@ -774,10 +890,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -797,18 +913,18 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -824,32 +940,32 @@
android:id="@+id/security_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/security"
android:textAllCaps="true"
android:textSize="@dimen/smaller_text_size"/>
+ android:paddingBottom="@dimen/activity_margin">
@@ -861,10 +977,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
+
+
+
+
+
+
+ android:paddingBottom="@dimen/activity_margin">
@@ -927,10 +1067,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -951,10 +1091,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -980,8 +1120,8 @@
android:id="@+id/bottom_actions_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/bottom_actions"
android:textAllCaps="true"
@@ -993,10 +1133,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -1017,18 +1157,18 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -1044,8 +1184,8 @@
android:id="@+id/recycle_bin_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginStart="@dimen/bigger_margin"
+ android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginTop="@dimen/activity_margin"
android:text="@string/recycle_bin"
android:textAllCaps="true"
@@ -1057,10 +1197,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
@@ -1081,10 +1221,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
- android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
+ android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/normal_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingBottom="@dimen/activity_margin">
+
+
+
+
+
+
+ android:paddingBottom="@dimen/bigger_margin">
@@ -1133,5 +1297,67 @@
android:clickable="false"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_video_player.xml b/app/src/main/res/layout/activity_video_player.xml
new file mode 100644
index 000000000..d7962a628
--- /dev/null
+++ b/app/src/main/res/layout/activity_video_player.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_widget_config.xml b/app/src/main/res/layout/activity_widget_config.xml
new file mode 100644
index 000000000..ae762ab01
--- /dev/null
+++ b/app/src/main/res/layout/activity_widget_config.xml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/bottom_actions.xml b/app/src/main/res/layout/bottom_actions.xml
index c3329af25..48bacf38d 100644
--- a/app/src/main/res/layout/bottom_actions.xml
+++ b/app/src/main/res/layout/bottom_actions.xml
@@ -1,19 +1,19 @@
-
-
+
+
+
diff --git a/app/src/main/res/layout/bottom_actions_aspect_ratio.xml b/app/src/main/res/layout/bottom_actions_aspect_ratio.xml
index e2ccf6e00..8663bf358 100644
--- a/app/src/main/res/layout/bottom_actions_aspect_ratio.xml
+++ b/app/src/main/res/layout/bottom_actions_aspect_ratio.xml
@@ -1,5 +1,5 @@
-
-
+
+
+
diff --git a/app/src/main/res/layout/bottom_editor_crop_rotate_actions.xml b/app/src/main/res/layout/bottom_editor_crop_rotate_actions.xml
index da48247b9..76af37131 100644
--- a/app/src/main/res/layout/bottom_editor_crop_rotate_actions.xml
+++ b/app/src/main/res/layout/bottom_editor_crop_rotate_actions.xml
@@ -1,5 +1,5 @@
-
-
+
diff --git a/app/src/main/res/layout/bottom_editor_draw_actions.xml b/app/src/main/res/layout/bottom_editor_draw_actions.xml
new file mode 100644
index 000000000..fb12e40b2
--- /dev/null
+++ b/app/src/main/res/layout/bottom_editor_draw_actions.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/bottom_editor_primary_actions.xml b/app/src/main/res/layout/bottom_editor_primary_actions.xml
index 2acfec635..20d8035fd 100644
--- a/app/src/main/res/layout/bottom_editor_primary_actions.xml
+++ b/app/src/main/res/layout/bottom_editor_primary_actions.xml
@@ -1,5 +1,5 @@
-
-
+
+
+
diff --git a/app/src/main/res/layout/bottom_set_wallpaper_actions.xml b/app/src/main/res/layout/bottom_set_wallpaper_actions.xml
index 1c8015458..707c72ca7 100644
--- a/app/src/main/res/layout/bottom_set_wallpaper_actions.xml
+++ b/app/src/main/res/layout/bottom_set_wallpaper_actions.xml
@@ -1,18 +1,18 @@
-
+ android:background="@drawable/gradient_background">
-
+
diff --git a/app/src/main/res/layout/bottom_video_time_holder.xml b/app/src/main/res/layout/bottom_video_time_holder.xml
new file mode 100644
index 000000000..ec34b6501
--- /dev/null
+++ b/app/src/main/res/layout/bottom_video_time_holder.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_change_grouping.xml b/app/src/main/res/layout/dialog_change_grouping.xml
index 21292ec5d..d4e6fc02f 100644
--- a/app/src/main/res/layout/dialog_change_grouping.xml
+++ b/app/src/main/res/layout/dialog_change_grouping.xml
@@ -11,8 +11,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingRight="@dimen/activity_margin">
@@ -84,16 +84,16 @@
android:id="@+id/grouping_dialog_radio_ascending"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="@dimen/medium_margin"
android:paddingTop="@dimen/medium_margin"
+ android:paddingBottom="@dimen/medium_margin"
android:text="@string/ascending"/>
@@ -105,8 +105,8 @@
android:id="@+id/grouping_dialog_use_for_this_folder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin"
android:text="@string/use_for_this_folder"/>
diff --git a/app/src/main/res/layout/dialog_change_sorting.xml b/app/src/main/res/layout/dialog_change_sorting.xml
index e0e642acc..4ac35ce0f 100644
--- a/app/src/main/res/layout/dialog_change_sorting.xml
+++ b/app/src/main/res/layout/dialog_change_sorting.xml
@@ -11,8 +11,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingRight="@dimen/activity_margin">
+
+
@@ -97,8 +105,8 @@
android:id="@+id/sorting_dialog_use_for_this_folder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin"
android:text="@string/use_for_this_folder"/>
diff --git a/app/src/main/res/layout/dialog_change_view_type.xml b/app/src/main/res/layout/dialog_change_view_type.xml
new file mode 100644
index 000000000..b6e1f342c
--- /dev/null
+++ b/app/src/main/res/layout/dialog_change_view_type.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_custom_aspect_ratio.xml b/app/src/main/res/layout/dialog_custom_aspect_ratio.xml
new file mode 100644
index 000000000..3f9180ae0
--- /dev/null
+++ b/app/src/main/res/layout/dialog_custom_aspect_ratio.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_delete_with_remember.xml b/app/src/main/res/layout/dialog_delete_with_remember.xml
index df4ea5519..2f9fc47d5 100644
--- a/app/src/main/res/layout/dialog_delete_with_remember.xml
+++ b/app/src/main/res/layout/dialog_delete_with_remember.xml
@@ -5,8 +5,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/big_margin"
- android:paddingRight="@dimen/big_margin"
- android:paddingTop="@dimen/big_margin">
+ android:paddingTop="@dimen/big_margin"
+ android:paddingRight="@dimen/big_margin">
-
+ android:layout_height="match_parent">
-
-
-
-
-
-
-
-
-
+ android:paddingTop="@dimen/activity_margin">
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_filter_media.xml b/app/src/main/res/layout/dialog_filter_media.xml
index f2ccc2559..b90fc9aeb 100644
--- a/app/src/main/res/layout/dialog_filter_media.xml
+++ b/app/src/main/res/layout/dialog_filter_media.xml
@@ -6,47 +6,47 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
- android:paddingTop="@dimen/activity_margin">
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingRight="@dimen/activity_margin">
diff --git a/app/src/main/res/layout/dialog_manage_bottom_actions.xml b/app/src/main/res/layout/dialog_manage_bottom_actions.xml
index 6e58322e4..39fc8555d 100644
--- a/app/src/main/res/layout/dialog_manage_bottom_actions.xml
+++ b/app/src/main/res/layout/dialog_manage_bottom_actions.xml
@@ -11,104 +11,112 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
- android:paddingTop="@dimen/medium_margin">
+ android:paddingTop="@dimen/medium_margin"
+ android:paddingRight="@dimen/activity_margin">
+
+
diff --git a/app/src/main/res/layout/dialog_manage_extended_details.xml b/app/src/main/res/layout/dialog_manage_extended_details.xml
index 129adf95e..4b42ab9b2 100644
--- a/app/src/main/res/layout/dialog_manage_extended_details.xml
+++ b/app/src/main/res/layout/dialog_manage_extended_details.xml
@@ -11,71 +11,71 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
- android:paddingRight="@dimen/activity_margin"
- android:paddingTop="@dimen/medium_margin">
+ android:paddingTop="@dimen/medium_margin"
+ android:paddingRight="@dimen/activity_margin">
diff --git a/app/src/main/res/layout/dialog_medium_picker.xml b/app/src/main/res/layout/dialog_medium_picker.xml
index 087ca553c..c9b59d2d8 100644
--- a/app/src/main/res/layout/dialog_medium_picker.xml
+++ b/app/src/main/res/layout/dialog_medium_picker.xml
@@ -20,8 +20,8 @@
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:paddingLeft="@dimen/normal_margin"
- android:paddingStart="@dimen/normal_margin">
+ android:paddingStart="@dimen/normal_margin"
+ android:paddingLeft="@dimen/normal_margin">
@@ -31,9 +31,9 @@
android:id="@+id/media_horizontal_fastscroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
android:paddingTop="@dimen/normal_margin">
diff --git a/app/src/main/res/layout/dialog_other_aspect_ratio.xml b/app/src/main/res/layout/dialog_other_aspect_ratio.xml
new file mode 100644
index 000000000..61487c688
--- /dev/null
+++ b/app/src/main/res/layout/dialog_other_aspect_ratio.xml
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/resize_image.xml b/app/src/main/res/layout/dialog_resize_image.xml
similarity index 92%
rename from app/src/main/res/layout/resize_image.xml
rename to app/src/main/res/layout/dialog_resize_image.xml
index 13a3d5d1a..1f1300337 100644
--- a/app/src/main/res/layout/resize_image.xml
+++ b/app/src/main/res/layout/dialog_resize_image.xml
@@ -1,11 +1,13 @@
+ android:paddingLeft="@dimen/activity_margin"
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingRight="@dimen/activity_margin">
@@ -38,9 +40,9 @@
android:id="@+id/image_height"
android:layout_width="100dp"
android:layout_height="wrap_content"
- android:layout_alignLeft="@+id/image_height_label"
- android:layout_alignStart="@+id/image_height_label"
android:layout_below="@+id/image_width_label"
+ android:layout_alignStart="@+id/image_height_label"
+ android:layout_alignLeft="@+id/image_height_label"
android:inputType="number"
android:maxLength="6"
android:maxLines="1"
@@ -54,8 +56,8 @@
android:layout_below="@+id/image_height"
android:layout_marginTop="@dimen/activity_margin"
android:checked="true"
- android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin"
android:text="@string/keep_aspect_ratio"/>
diff --git a/app/src/main/res/layout/dialog_save_as.xml b/app/src/main/res/layout/dialog_save_as.xml
index 7fa48abe4..61033e0bc 100644
--- a/app/src/main/res/layout/dialog_save_as.xml
+++ b/app/src/main/res/layout/dialog_save_as.xml
@@ -18,10 +18,10 @@
android:id="@+id/save_as_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/activity_margin"
android:layout_marginLeft="@dimen/activity_margin"
- android:paddingRight="@dimen/small_margin"
- android:paddingTop="@dimen/small_margin"/>
+ android:layout_marginBottom="@dimen/activity_margin"
+ android:paddingTop="@dimen/small_margin"
+ android:paddingRight="@dimen/small_margin"/>
-
-
-
-
-
-
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin">
+ android:paddingLeft="@dimen/medium_margin"
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin">
+ android:paddingLeft="@dimen/medium_margin"
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin">
+ android:paddingLeft="@dimen/medium_margin"
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin">
+ android:paddingLeft="@dimen/medium_margin"
+ android:paddingTop="@dimen/activity_margin"
+ android:paddingBottom="@dimen/activity_margin">
-
@@ -17,9 +17,10 @@
android:id="@+id/dir_check"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
+ android:layout_alignRight="@+id/dir_shadow_holder"
+ android:layout_alignParentTop="true"
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"
@@ -30,8 +31,8 @@
android:id="@+id/dir_pin"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_margin="@dimen/small_margin"
android:background="@drawable/circle_black_background"
@@ -44,8 +45,8 @@
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:layout_alignParentBottom="true"
android:background="@drawable/gradient_background"/>
+ android:paddingBottom="@dimen/small_margin">
diff --git a/app/src/main/res/layout/directory_item_list.xml b/app/src/main/res/layout/directory_item_list.xml
index 7e125cf5c..4a0036d7f 100644
--- a/app/src/main/res/layout/directory_item_list.xml
+++ b/app/src/main/res/layout/directory_item_list.xml
@@ -9,7 +9,7 @@
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/small_margin">
-
@@ -18,9 +18,9 @@
android:id="@+id/dir_check"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
+ android:layout_alignParentTop="true"
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"
@@ -43,8 +43,8 @@
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_alignLeft="@+id/dir_name"
android:layout_marginRight="@dimen/activity_margin"
android:alpha="0.4"
android:ellipsize="end"
@@ -67,9 +67,9 @@
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_alignParentBottom="true"
android:layout_marginRight="@dimen/small_margin"
android:gravity="end"
android:orientation="horizontal"
diff --git a/app/src/main/res/layout/fragment_holder.xml b/app/src/main/res/layout/fragment_holder.xml
index d10a3a193..77f5953e7 100644
--- a/app/src/main/res/layout/fragment_holder.xml
+++ b/app/src/main/res/layout/fragment_holder.xml
@@ -10,6 +10,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+
+
diff --git a/app/src/main/res/layout/pager_photo_item.xml b/app/src/main/res/layout/pager_photo_item.xml
index 96cb2b6c3..7484532eb 100644
--- a/app/src/main/res/layout/pager_photo_item.xml
+++ b/app/src/main/res/layout/pager_photo_item.xml
@@ -6,11 +6,26 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
+
+
+
+
+
+
-
+ android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"/>
-
-
-
+
-
+
+
+
+
-
-
-
+ android:padding="20dp"
+ android:src="@drawable/ic_play_outline"/>
-
+
-
+
+
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="120dp"
+ android:alpha="0"
+ android:background="@drawable/black_rounded_background"
+ android:gravity="center"
+ android:paddingLeft="@dimen/activity_margin"
+ android:paddingTop="@dimen/medium_margin"
+ android:paddingRight="@dimen/activity_margin"
+ android:paddingBottom="@dimen/medium_margin"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/extra_big_text_size"/>
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/photo_video_item_grid.xml b/app/src/main/res/layout/photo_video_item_grid.xml
index fb1a67e51..14c85348d 100644
--- a/app/src/main/res/layout/photo_video_item_grid.xml
+++ b/app/src/main/res/layout/photo_video_item_grid.xml
@@ -8,7 +8,7 @@
android:focusable="true"
android:padding="1px">
-
@@ -17,9 +17,10 @@
android:id="@+id/medium_check"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
+ android:layout_alignRight="@+id/medium_name"
+ android:layout_alignParentTop="true"
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"
@@ -30,27 +31,44 @@
android:id="@+id/play_outline"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
- android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_margin="@dimen/small_margin"
android:src="@drawable/img_play_outline"
android:visibility="gone"/>
+
+
diff --git a/app/src/main/res/layout/photo_video_item_list.xml b/app/src/main/res/layout/photo_video_item_list.xml
index 8e8046e0a..46f772e8a 100644
--- a/app/src/main/res/layout/photo_video_item_list.xml
+++ b/app/src/main/res/layout/photo_video_item_list.xml
@@ -1,6 +1,7 @@
-
@@ -18,9 +19,9 @@
android:id="@+id/medium_check"
android:layout_width="@dimen/selection_check_size"
android:layout_height="@dimen/selection_check_size"
+ android:layout_alignParentTop="true"
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"
@@ -28,11 +29,11 @@
android:visibility="gone"/>
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/cab_media.xml b/app/src/main/res/menu/cab_media.xml
index 26033fad3..5f3a83a8b 100644
--- a/app/src/main/res/menu/cab_media.xml
+++ b/app/src/main/res/menu/cab_media.xml
@@ -16,6 +16,23 @@
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom"/>
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index b1a4035e7..5d6ee89c7 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -1,6 +1,12 @@
Filtrar medios
@@ -87,8 +88,8 @@
VoltearVoltear horizontalmenteVoltear verticalmente
- Editar con
- Free
+ Free
+ OtherFondo de pantalla
@@ -115,6 +116,7 @@
Reproducir en bucleRematou a presentaciónNon se atoparon medios para a presentación
+ Use crossfade animationsCambiar o tipo de vista
@@ -131,13 +133,19 @@
Tipo de ficheiorExtensión
+
+ Folder shown on the widget:
+ Show folder name
+
Reproducir vídeos automticamente
+ Remember last video playback positionMudar a visibilidade do ficheirovideos en bucleAnimar os GIFs na iconaBrillo ao máximo cando mire mediosRecortar iconas a cadrados
+ Show video durationsRotar medios a pantalla completa aAxuste do sistemaRotación do dispositivo
@@ -149,7 +157,6 @@
Permitir controlar o brillo da foto con xestos verticaisPermitir controlar o volume do vídeo e o brillo con xestos verticáisMostrar a conta de medios do cartafol na vista principal
- Substituír Compartir con Rotar no menú de pantalla completaMostrar información pormenorizada sobre medios a pantalla completaXestionar información polo miúdoPermitir zoom con un dedo a pantalla completa
@@ -159,6 +166,13 @@
Facer unha comprobación extra para evitar mostrar ficheiros non válidosShow some action buttons at the bottom of the screenShow the Recycle Bin at the folders screen
+ Deep zoomable images
+ Show images in the highest possible quality
+ Show the Recycle Bin as the last item on the main screen
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableIconas
@@ -180,7 +194,7 @@
Cómo podo facer que un álbume apareza sempre arriba de todo?Pode manter premido o álbume e escoller a icona de Fixar no menú de accións, esto fixarao arriba. Pode fixar varios cartafoles tambén, os elementos fixados estarán ordenados polo criterio por omisión.Cómo podo aumentar a velocidade de reprodución de vídeo?
- Pode pulsar no texto de duración actual ou máxima preto da barra de busca, esto moverá ou vídeo hacia adiante ou atrás.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.Cal é a diferenza entre agochar e excluír un cartafol?A Exclusión prevén que se mostre o cartafol só en Simple Gallery, mentras Agochar funciona para todo o sistema e agocha o cartafol para outras galerías tamén. Esto funciona creando un ficheiro baldeiro de nome \".nomedia\" no cartafol, que tamén pode quitar con calquer xestor de ficheiros.Por qué aparecen cartafoles de música con portadas ou pegatinas?
@@ -190,28 +204,46 @@
E qué pasa si só quero que sexan visibles certos cartafolesEngadir un cartafol a Cartafoles incluídos non exclúe nada de xeito automático. O que pode facer é ir a Axustes -> Xestionar cartafoles incluídos, excluír o cartafol root \"/\", e despóis engadir os cartafoles desexados con Axustes -> Xestionar Cartafoles Incluídos.
Esto fará visibles só aos cartafoles escollidos, como tanto excluír e incluír son recursivos e si está excluído e logo incluído, será mostrado.
- As imaxes a pantalla completa teñen píxeles extranos, podo mellorar a calidade da imaxe?
- Si, hai unha opción en Axustes que di \"Substituír imaxes con un gran zoom con imaxes de mellor calidade\", pode usar eso. mellorará a calidade das imaxes, mais farase máis borrosaxa si intenta facer moito zoom.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- Unha galería para ver fotos e videos, pero non publicidade.
+ An offline gallery for managing your files without ads, respecting your privacy.
- Unha simple ferramenta para ver fotos e vídeos. Pode organizar os elementos por data, tamaño, nome tanto ascendentes como descendentes, pode facer zoom nas fotografías. Os ficheiros de medios móstranse en múltiples columnas dependendo do tamaño da pantalla, pode mudar o número de columnas con xestos de belisco. Poden ser renomeados, compartidos, eliminados, copiados, movidos. As imaxes tamén se poden recortar, rotar, voltear ou establecer como fondo de pantalla directamente no aplicativo.
-
- A Galería tamén se ofrece como aplicación de terceiros para vista previa de imaxes / vídeos, engadir anexos en correos etc. É perfecta para o uso diario.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- O permiso de pegada é preciso para bloquear a visibilidade de elementos ocultos ou o aplicativo completo.
+ It is open source, contains no ads or unnecessary permissions.
- Non contén anuncios nin solicita permisos innecesarios. É de código aberto, con cores personalizadas.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- Este aplicativo é só unha das pezas de unha grande familia. Pode atopar o resto en https://www.simplemobiletools.com
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
+
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
Filtriranje medija
@@ -87,8 +88,8 @@
OkreniOkreni horizontalnoOkreni vertikalno
- Uredi pomoću
- Slobodan odabir
+ Slobodan odabir
+ OstaloJednostavna pozadina
@@ -115,6 +116,7 @@
Prikaži dijaprojekciju kao petljuKraj dijaprojekcijeNema datoteka za dijaprojekciju
+ Koristi prijelazne animacijePromijeni vrstu prikaza
@@ -131,13 +133,19 @@
Tip datotekeVrsta datoteke
+
+ Mapa prikazana na widgetu:
+ Prikaži naziv mape
+
Automatsko pokretanje videa
+ Zapamti poziciju zadnjeg reproduciranog videozapisaUključi prikaz naziva datotekaPonavljanje videaPrikaz animacije GIF-ova na sličicamaMaksimalna svjetlina pri pregledu datotekaIzreži sličice u kvadrate
+ Prikaži trajanje videozapisaRotiraj datoteku u punom zaslonu zaPostavke sustavaRotacija uređaja
@@ -149,16 +157,22 @@
Omogućite kontrolu svjetline fotografije vertikalnim pokretimaOmogući kontrolu glasnoće videa i svjetline pomoću vertikalnih pokretaPrikaz broja medija mapa u glavnom prikazu
- Zamjeni Dijeli s Rotiraj pri pregledu datotekaPrikaži proširene pojedinosti preko medija na cijelom zaslonuUpravljaj proširenim pojedinostimaOmogući zumiranje jednim prstom na mediju cijelog zaslonaDopusti trenutačno mijenjanje medija dodirom na stranice zaslona
- Allow deep zooming images
+ Omogućite duboko zumiranje slikaSakrij proširene pojedinosti kada je traka statusa skrivenaNapravite dodatnu provjeru da biste izbjegli prikazivanje nevažećih datoteka
- Pokažite neke gumbe za radnju pri dnu zaslona
+ Prikaži neke gumbe za radnju pri dnu zaslonaPrikažite koš za smeće na zaslonu mapa
+ Duboko zumirane slike
+ Prikaži slike u najvišoj mogućoj kvaliteti
+ Prikaži koš za smeće kao posljednju stavku na glavnom zaslonu
+ Omogućite zatvaranje prikaza preko cijelog zaslona pokretom prema dolje
+ Dopusti zumiranje 1: 1 s dva dvostruka dodira
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableSličice
@@ -180,7 +194,7 @@
Kako postići da je album uvijek na vrhu?Dugo pritisnute željeni album i odaberite ikonu igle na akcijskom izborniku, koji će ga pričvrstiti na vrh. Možete prikvačiti više mapa odjednom, prikvačene stavke će biti razvrstane prema zadanom načinu razvrstavanja.Kako mogu ubrzati video?
- Možete pritisnuti trenutačno vrijeme ili ukupno trajanje videozapisa na traci napretka, videozapis će se prema Vašem izboru pomicati unatrag ili prema naprijed.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.Koja je razlika između skrivanja i izuzimanja mape?Izuzimanje sprječava prikaz mape samo u Jednostavnoj galeriji, a skrivanje radi na razini sustava i skriva mapu iz drugih galerija. Djeluje stvaranjem praznih \".nomedia\" datoteka u zadanoj mapi, koju možete ukloniti pomoću bilo kojeg upraviteljem datoteka.Zašto se prikazuju mape s naslovnicama albuma i minijaturama slika?
@@ -190,26 +204,44 @@
Što ako želim vidjeti samo nekoliko određenih mapa?Dodavanje direktorija u uključene mape ne izuzima ništa automatski. Da biste to učinili, idite na "Postavke", a zatim "Upravljanje izuzetim mapama", izuzmite korijenski direktorij \"/\", a zatim dodajte željene direktorije u "Postavke" i "Upravljanje uključenim mapama".
Samo će odabrane mape biti vidljive, jer su izuzimanja i uključivanja rekurzivni, a ako je mapa isključena i uključena, bit će prikazana.
- Slike na cijelom zaslonu imaju čudne artefakte, mogu li nekako poboljšati kvalitetu?
- Da, u "Postavkama" postoji opcija "Zamjena duboko zumiranih slika s kvalitetnijim slikama", ali slike će biti zamućene ako zumirate previše.Mogu li izrezati slike pomoću ove aplikacije?Da, možete obrezati slike u uređivaču povlačenjem uglova. Možete doći do uređivača dugim pritiskom na minijaturu slike i odabirom Uređivanje ili odabirom Uredi iz prikaza preko cijelog zaslona.Mogu li nekako grupirati sličice medijskih datoteka?Naravno, koristeći stavku izbornika \"Grupiraj po\" u prikazu sličica. Možete grupirati datoteke prema više kriterija, uključujući datum snimanja. Ako koristite funkciju "Prikaži sve sadržaje mape", možete ih grupirati i mapama.Sortiranje po datumu nije točno, kako to mogu ispraviti?Vjerojatno uzrok tome da su Vaše slike kopirane s drugih mjesta. U tom slučaju trebali biste dugo dodirnuti sličice prikazanih slika, a zatim u izborniku u gornjem desnom kutu, odabrati funkciju \'Ispravi vrijednost datuma snimanja\'.
+ Vidim nekakvno preklapanje boja na slikama. Kako mogu poboljšati kvalitetu?
+ Trenutno rješenje za prikazivanje slika dobro funkcionira u velikoj većini slučajeva, ali ako želite još bolju kvalitetu slike, možete omogućiti prikazivanje slika u najvišoj mogućoj kvaliteti u postavkama aplikacije, u odjeljku "Duboko zumirane slike".
+ Sakrio sam datoteku/mapu. Kako je mogu otkriti?
+ Možete pritisnuti stavku izbornika "Privremeno prikazati skrivene stavke" na glavnom zaslonu ili uključiti "Prikaži skrivene stavke" u postavkama aplikacije da biste vidjeli skrivenu stavku. Ako je želite otkriti, samo ju dugo pritisnite i odaberite "Otkrij". Mape su skrivene dodavanjem skrivene datoteke ".Nomedia", možete i izbrisati navedenu datoteku.
- Galerija za gledanje fotografija i videozapisa bez oglasa.
+ Izvanmrežna galerija za upravljanje datotekama,bez oglasa,poštujući privatnost.
- Jednostavan alat za pregled slika, GIFova i videa. Datoteke možete sortirati po datumu, veličini, imenu i to uzlazno i silazno. Također možete zumirati slike. Medijski sadržaj se prikazuje u višestrukim stupcima ovisno o veličini ekrana, a vi samo možete birati broj stupaca s gestom štipkanja. Možete preimenovati, dijeliti, brisati, kopirati, premještati datoteke. Slike također možete izrezati, rotirati ili postaviki kao pozadinu ekrana, odmah iz aplikacije.
+ Vrlo prilagodljiva galerija koja može prikazivati različite vrste slika i videozapisa, uključujući SVG-ove, RAW-ove, panoramske fotografije i videozapise.
- Galerija se također može koristiti za pregledavanje slika i videa u drugim aplikacijama, prikačivanja datoteka u e-mail aplikacije itd. Savršeno za svakodnevno korištenje.
+ Otvorenog je koda, ne sadrži oglase ili nepotrebna dopuštenja.
- Dopuštenje otiska prsta je potrebno za zaključavanje prikaza skrivene stavke ili cijele aplikacije.
+ Navedimo neke od značajki koje vrijedi spomenuti:
+ 1. Pretraživanje
+ 2. Dijaprojekcija
+ 3. Podrška za notch
+ 4. Pričvršćivanje mapa na vrh
+ 5. Filtriranje medijskih datoteka prema vrsti
+ 6. Koš za smeće za jednostavan oporavak datoteke
+ 7. Zaključavanje prikaza na cijelom zaslonu
+ 8. Označavanje omiljenih datoteka radi lakog pristupa
+ 9. Brzi mediji na cijelom zaslonu zatvaraju se pokretom prema dolje
+ 10. Urednik za izmjenu slika i primjenu filtara
+ 11. Zaštita lozinkom za zaštitu skrivenih stavki ili cijele aplikacije
+ 12. Promjena broja stupaca minijatura pomoću pokreta ili gumba izbornika
+ 13. Prilagodljive akcije na dnu cijelog zaslona za brzi pristup
+ 14. Prikazivanje proširenih detalja preko medija, preko cijelog zaslona sa željenim svojstvima datoteke
+ 15. Nekoliko različitih načina razvrstavanja ili grupiranja stavki, kako rastućih tako i silaznih
+ 16. Skrivanje mapa (utječe i na druge aplikacije), izuzimanje mapa (utječe samo na jednostavnu galeriju)
- Ne sadrži oglase ili nepotrebne dozvole. Aplikacije je otvorenog koda, pruža prilagodljive boje.
+ Dopuštenje za otisak prsta potreban je za zaključavanje vidljivosti skrivenih stavki, cijele aplikacije ili za zaštitu datoteka od brisanja.
Ova je aplikacija samo dio većeg broja aplikacija. Možete pronaći ostatak na https://www.simplemobiletools.com
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 6be796647..2838734a1 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -1,217 +1,250 @@
Simple Gallery
- Gallery
- Edit
- Open camera
- (hidden)
- (excluded)
- Pin folder
- Unpin folder
- Pin to the top
- Show all folders content
- All folders
- Switch to folder view
- Other folder
- Show on map
- Unknown location
- Increase column count
- Reduce column count
- Change cover image
- Select photo
- Use default
- Volume
- Brightness
- Lock orientation
- Unlock orientation
- Change orientation
- Force portrait
- Force landscape
- Use default orientation
- Fix Date Taken value
- Fixing…
- Dates fixed successfully
+ Galéria
+ Szerkesztés
+ Kamera megnyitása
+ (rejtett)
+ (kizárva)
+ Mappa kitűzés
+ Mappa kitűzés megszüntetése
+ Kitűzés felülre
+ Mutassa az összes mappa tartalmát
+ Összes mappa
+ Váltás mappa nézetre
+ Egyéb mappa
+ Mutassa a térképen
+ Ismeretlen helyszín
+ Növelje az oszlopok számát
+ Csökkentse az oszlopok számát
+ Borítókép változtatása
+ Válasszon fotót
+ Alapértelmezett használata
+ Hangerő
+ Fényerő
+ Tájolás zárolása
+ Tájolás feloldása
+ Tájolás változtatása
+ Álló mód kényszerítése
+ Fekvő mód kényszerítése
+ Alapértelmezett tájolás használata
+ Dátum javítása
+ Javítás…
+ Sikeres dátum javítás
+ Átméretezett verzió megosztása
- Filter media
- Images
- Videos
- GIFs
- RAW images
- SVGs
- No media files have been found with the selected filters.
- Change filters
+ Média szűrő
+ Kép
+ Videó
+ GIF
+ RAW kép
+ SVG
+ A kiválasztott szűrők nem találtak médiafájlokat.
+ Szűrők változtatása
- 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?
- Exclude
- Excluded folders
- Manage excluded folders
- This will exclude the selection together with its subfolders from Simple Gallery only. You can manage excluded folders in Settings.
- Exclude a parent instead?
- 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.
- Remove all
- Remove all folders from the list of excluded? This will not delete the folders.
- Hidden folders
- Manage hidden folders
- Seems like you don\'t have any folders hidden with a \".nomedia\" file.
+ Ez a funkció elrejti a mappát egy \'.nomedia\' fájl hozzáadásával, és elrejti az almappákat is. Láthatóvá teheti ezeket a Beállítások \"Mutassa a rejtett elemeket\" menüpontban. Folytatja?
+ Kizárás
+ Kizárt mappák
+ Kizárt mappák kezelése
+ Ez kizárja a kijelölést és az alkönyvtárakat a Simple Gallery alkalmazásból. A kizárt mappákat a Beállításokban kezelheti.
+ Kizárja a szülő mappát?
+ A mappák kizárásával az almappákkal együtt elrejti a Simple Gallery alkalmazásban, de továbbra is láthatóak maradnak más alkalmazásokban.\n\nHa el szeretné rejteni őket más alkalmazásokban is, használja az Elrejtés funkciót.
+ Összes eltávolítása
+ Összes mappa eltávolítása a Kizárás listából. Ez nem törli a mappákat.
+ Rejtett mappák
+ Rejtett mappák kezelése
+ Úgy tűnik, a mappái nincsenek elrejtve egy \".nomedia\" fájllal.
- Included folders
- Manage included folders
- Add folder
- If you have some folders which contain media, but were not recognized by the app, you can add them manually here.\n\nAdding some items here will not exclude any other folder.
+ Befoglalt mappák
+ Befoglalt mappák kezelése
+ Mappa hozzáadása
+ Ha vannak olyan mappák, amelyek média fájlokat tartalmaznak, de az alkalmazás nem ismerte fel, akkor kézzel is hozzáadhatja ezeket.\n\nAz elemek hozzáadása nem zár ki más mappákat.
- Resize
- Resize selection and save
- Width
- Height
- Keep aspect ratio
- Please enter a valid resolution
+ Átméretezés
+ Kiválasztás átméretezése és mentés
+ Szélesség
+ Magasság
+ Képarány megtartása
+ Írjon be érvényes felbontást
- Editor
- Save
- Rotate
- Path
- Invalid image path
- Image editing failed
- Edit image with:
- No image editor found
- Unknown file location
- Could not overwrite the source file
- Rotate left
- Rotate right
- Rotate by 180º
- Flip
- Flip horizontally
- Flip vertically
- Edit with
- Free
+ Szerkesztő
+ Mentés
+ Forgatás
+ Elérési útvonal
+ Érvénytelen kép elérési útvonal
+ Sikertelen kép szerkesztés
+ Kép szerkesztés ezzel:
+ Nem található kép szerkesztő
+ Ismeretlen fájl hely
+ Nem lehet felülírni a forrás fájlt
+ Forgatás balra
+ Forgatás jobbra
+ 180º-os forgatás
+ Tükrözés
+ Tükrözés vízszintesen
+ Tükrözés függőlegesen
+ Kötetlen
+ Egyéb
+
Simple Wallpaper
- Set as Wallpaper
- Setting as Wallpaper failed
- Set as wallpaper with:
- Setting wallpaper…
- Wallpaper set successfully
- Portrait aspect ratio
- Landscape aspect ratio
- Home screen
- Lock screen
- Home and lock screen
+ Beállítás háttérképként
+ Nem sikerült a beállítás háttérképként
+ Beállítás háttérképként ezzel:
+ Beállítás háttérképként…
+ Sikeresen beállítva háttérképnek
+ Álló képarány
+ Fekvő képarány
+ Kezdő képernyő
+ Zárolás képernyő
+ Kezdő és zárolás képernyő
- Slideshow
- Interval (seconds):
- Include photos
- Include videos
- Include GIFs
- Random order
- Use fade animations
- Move backwards
- Loop slideshow
- The slideshow ended
- No media for the slideshow have been found
+ Diavetítés
+ Időköz (másodperc):
+ Fotók befoglalása
+ Videók befoglalása
+ GIF befoglalása
+ Véletlen sorrend
+ Halványuló animáció használat
+ Áthelyezés hátra
+ Diavetítés ismétlése
+ A diavetítés vége
+ A diavetítéshez nem található média
+ Animáció átmenet használat
- Change view type
- Grid
- List
- Group direct subfolders
+ Nézet típus változtatása
+ Rács
+ Lista
+ Közvetlen almappa csoport
- Group by
- Do not group files
- Folder
- Last modified
- Date taken
- File type
- Extension
+ Csoportosítás
+ Nincs csoportosítás
+ Mappa
+ Utolsó módosítás
+ Dátum
+ Fájl típus
+ Kiterjesztés
+
+
+ Mappa mutatása a widgeten:
+ Mutassa a mappa nevét
- Play videos automatically
- Toggle filename visibility
- Loop videos
- Animate GIFs at thumbnails
- Max brightness when viewing media
- Crop thumbnails into squares
- Rotate fullscreen media by
- System setting
- Device rotation
- Aspect ratio
- Black background and status bar at fullscreen media
- Scroll thumbnails horizontally
- Automatically hide system UI at fullscreen media
- Delete empty folders after deleting their content
- Allow controlling photo brightness with vertical gestures
- Allow controlling video volume and brightness with vertical gestures
- Show folder media count on the main view
- Replace Share with Rotate at fullscreen menu
- Show extended details over fullscreen media
- Manage extended details
- Allow one finger zoom at fullscreen media
- Allow instantly changing media by clicking on screen sides
- Allow deep zooming images
- Hide extended details when status bar is hidden
- Do an extra check to avoid showing invalid files
- Show some action buttons at the bottom of the screen
- Show the Recycle Bin at the folders screen
+ Automatikus videó lejátszás
+ Emlékezzen a videó utolsó lejátszási pozícióra
+ Fájlnév láthatóság módosítása
+ Videók ismétlése
+ Animált GIF miniatűr
+ Maximális fényerő a teljes képernyős médiánál
+ Miniatűrök négyzet alakúra vágva
+ Mutassa a videó időtartamát
+ Teljes képernyős média forgatása
+ Rendszer beállítások
+ Eszköz elforgatás
+ Képarány
+ Fekete háttérszín teljes képernyős médiánál
+ Miniatűrök görgetése vízszintesen
+ Automatikusan elrejti a rendszer UI-t teljes képernyőn
+ Az üres mappák törlése a tartalom törlése után
+ Engedélyezi a kép fényerő módosítást függőleges gesztusokkal
+ Engedélyezi a videó hangerő és fényerő módosítást függőleges gesztusokkal
+ Mutassa a fájlok számát a mappákban
+ Mutassa a kiterjesztett adatokat a teljes képernyős médián keresztül
+ Bővített részletek kezelése
+ Engedélyezi az egy ujjas nagyítást a teljes képernyős médiában
+ Engedélyezi a azonnali média váltást a képernyő oldalára kattintva
+ Engedélyezi a képek mély nagyítását
+ Bővített részletek elrejtése az állapotsor rejtett állapotában
+ Végezzen extra ellenőrzést, hogy elkerülje az érvénytelen fájlok mutatását
+ Mutassa a művelet gombokat a képernyő alján
+ Mutassa a Lomtárat a mappák képernyőjén
+ Mély nagyítású képek
+ Mutassa a képeket a lehető legjobb minőségben
+ Mutassa a Lomtárat a fő képernyő utolsó elemeként
+ Engedélyezi a teljes képernyős nézetet a lefelé mozdulattal
+ Engedélyezi az 1:1 nagyítást két dupla érintéssel
+ Mindig külön képernyőn nyissa meg a videókat új vízszintes mozdulattal
+ Notch mutatása, ha elérhető
- Thumbnails
- Fullscreen media
- Extended details
- Bottom actions
+ Miniatűrök
+ Teljes képernyős média
+ Bővített részletek
+ Gomb műveletek
- Manage visible bottom actions
- Toggle favorite
- Toggle file visibility
+ Látható gomb műveletek kezelése
+ Kedvencek módosítása
+ Fájl láthatóság módosítása
- How can I make Simple Gallery the default device gallery?
- First you have to find the currently default gallery in the Apps section of your device settings, look for a button that says something like \"Open by default\", click on it, then select \"Clear defaults\".
- The next time you will try opening an image or video you should see an app picker, where you can select Simple Gallery and make it the default app.
- I locked the app with a password, but I forgot it. What can I do?
- You can solve it in 2 ways. You can either reinstall the app, or find the app in your device settings and select \"Clear data\". It will reset all your settings, it will not remove any media files.
- How can I make an album always appear at the top?
- You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.
- How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
- What is the difference between hiding and excluding a folder?
- Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.
- Why do folders with music cover art or stickers show up?
- It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
- That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.
- What if I want just a few particular folders visible?
- Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
- That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.
- Can I crop images with this app?
- Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.
- Can I somehow group media file thumbnails?
- Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.
- Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?
- It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ Hogyan tudom beállítani a Simple Gallery-t alapértelmezett galériának?
+ Először meg kell találnia az alapértelmezett galériát az eszköz beállításainak Alkalmazások részében. Keressen egy olyan gombot, amely valami olyasmit, mint az \"Legyen alapértelmezett\", kattintson rá, majd válassza a \"Alapértelmezések törlése\" pontot.
+A következő alkalommal, amikor megpróbál megnyitni egy képet vagy videót, megjelenik egy alkalmazásválasztó, ahol kiválaszthatja a Simple Gallery lehetőséget, és beállíthatja alapértelmezett alkalmazásnak.
+ Zároltam az alkalmazást jelszóval, de elfelejtettem. Mit tehetek?
+ 2 módon is megoldhatja. Újratelepítheti az alkalmazást, vagy megkeresi az alkalmazást az eszköz beállításai között, és válassza az \"Adatok törlése\" lehetőséget. Minden beállítást visszaállít alapértelmezettre. Ez nem távolítja el a média fájlokat.
+ Hogyan állíthatok be egy albumot úgy, hogy mindig felül legyen?
+ Hosszan nyomja meg a kívánt albumot, és válassza ki a Kitűzés ikont a művelet menüben, ami rögzíti felülre. Többféle mappát is kitűzhet, ezeket az elemeket az alapértelmezett rendezési mód szerint rendezi.
+ Hogyan tudom előre tekerni a videókat?
+ Húzhatja az ujját vízszintesen a videolejátszón, vagy kattintson az aktuális vagy a max. időtartam szövegekre a keresősáv közelében. Ez visszafelé vagy előre mozgatja a videót.
+ Mi a különbség a mappa elrejtése és kizárása között?
+ A Kizárás megakadályozza, hogy a mappát a Simple Gallery megjelenítse, az Elrejtés pedig rendszer szinten működik, és elrejti a mappát más galériákból is. Úgy működik, hogy létrehoz egy üres \". nomedia\" nevű fájlt az adott mappában, amelyet bármikor eltávolíthat bármilyen fájlkezelővel is.
+ Miért jelennek meg a zenei borítóval vagy matricával rendelkező mappák?
+ Lehet, hogy látni fog néhány szokatlan album megjelenést. Könnyen kizárhatja a hosszú megnyomással és a Kizárás kiválasztásával. A következő párbeszédablakban kiválaszthatja a szülő mappát, és valószínűleg megakadályozza, hogy a többi kapcsolódó album is megjelenjen.
+ A képekkel nem rendelkező mappa nem jelenik meg, vagy nem jeleníti meg az összes elemet. Mit tehetek?
+ Ennek több oka lehet, de megoldása egyszerű. Menjen a Beállítások -> Befoglalt mappák kezelése lehetőségre, válassza a plusz jelet, és keresse meg a kívánt mappát.
+ Mi van, ha csak néhány különleges mappát szeretnék látni?
+ A Befoglalt mappákhoz tartozó mappák hozzáadása nem zár ki automatikusan semmit. Amit tehetünk, menjünk a Beállítások -> Kizárt mappák kezelése, kizárjuk a gyökérmappát \"/ \", utána hozzáadjuk a kívánt mappákat a Beállítások -> Befoglalt mappák kezelése menüpontban.
+Ezzel csak a kiválasztott mappák láthatók, mivel a kizárás és a befoglalás rekurzív. Ha egy mappát hozzáadunk mindkettőhöz, akkor megjelenik.
+ Tudom vágni a képeket ezzel az alkalmazással?
+ Igen, megvághatja a képeket a szerkesztőben a kép sarkainak húzásával. A szerkesztőhöz eljuthat egy miniatűr kép hosszú megnyomásával és a Szerkesztés választásával, vagy a Szerkesztés választásával a teljes képernyős nézetben.
+ Valamilyen módon össze tudom csoportosítani a médiafájl bélyegképeit?
+ Persze, a miniatűr nézetben használja a \"Csoport\" menüpontot. A fájlokat többféle kritérium alapján csoportosíthatja, beleértve a dátumot is. Ha a \"Mutassa az összes mappa tartalmát\" funkciót választja, akkor mappákba is csoportosíthatja ezeket.
+ A dátum szerinti rendezés nem működik megfelelően, hogyan tudom megjavítani?
+ Valószínűleg a fájlok másolásából származik. Ezt a fájl bélyegképének kiválasztásával és a \"Dátum javítása\" lehetőség kiválasztásával oldhatja meg.
+ Néhány színcsíkot látok a képeken. Hogyan javíthatom a minőséget?
+ Az esetek többségében a kép megjelenítés jelenlegi megoldása jól működik. Ha még jobb képminőséget szeretne, engedélyezheti a \"Mutassa a képeket a lehető legjobb minőségben\" opcióval az alkalmazás beállításaiban, a \"Mély nagyítású képek\" szakaszban.
+ Elrejtettem egy fájlt/mappát. Hogyan tudom látni?
+ A rejtett elemek megtekintéséhez nyomja meg a \"Rejtettek ideiglenes mutatása\" elemet a fő képernyőn, vagy válassza a \"Mutassa a rejtett elemeket\" az alkalmazás beállításaiban. Ha meg akarja szüntetni, csak hosszan nyomja meg, és válassza a \"Elrejtés megszüntetés\" lehetőséget. A mappák elrejtése egy rejtett \". nomedia\" fájl hozzáadásával történik. Ezt a fájlt bármelyik fájlkezelővel is törölheti.
- A gallery for viewing photos and videos without ads.
+ Egy offline galéria a fájlok hirdetés nélküli kezelésére.
- A simple tool usable for viewing photos and videos. Items can be sorted by date, size, name both ascending or descending, photos can be zoomed in. Media files are shown in multiple columns depending on the size of the display, you can change the column count by pinch gestures. They can be renamed, shared, deleted, copied, moved. Images can also be cropped, rotated or set as Wallpaper directly from the app.
+ Nagyon testreszabható galéria, amely alkalmas számos különböző kép- és videotípus megjelenítésére, beleértve az SVG-ket, RAW-t, panorámaképeket és videókat.
- The Gallery is also offered for third party usage for previewing images / videos, adding attachments at email clients etc. It\'s perfect for everyday usage.
+ Nyitott forráskódú, nem tartalmaz hirdetéseket vagy szükségtelen engedélyeket.
- The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
+ Lista néhány fontosabb funkcióról
+ 1. Keresés
+ 2. Diavetítés
+ 3. Notch támogatás
+ 4. Mappa kitűzés felülre
+ 5. Médiafájlok szűrése típus szerint
+ 6. Lomtár a törölt fájlok könnyű helyreállításához
+ 7. Teljes képernyős nézet tájolás zárolása
+ 8. Kedvenc fájlok megjelölése az egyszerű eléréshez
+ 9. Teljes képernyős média gyors bezárása lefelé mozdulattal
+ 10. Szerkesztő a képek módosításához és szűrők alkalmazásához
+ 11. Jelszavas védelem a rejtett elemekhez vagy az egész alkalmazáshoz
+ 12. Az indexkép oszlop módosítása mozdulatokkal vagy a menüben
+ 13. Testreszabható gomb műveletek a teljes képernyős nézetben a gyors elérés érdekében
+ 14. Bővített részletek mutatása a teljes képernyős nézetben a kívánt fájlok tulajdonságainál
+ 15. Az elemek rendezése vagy csoportosítása különböző növekvő és csökkenő módon
+ 16. Mappák elrejtése (más alkalmazásokra is hatással van), kivéve a mappákat (csak a Simple Gallery-t érinti)
- Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
+ Az ujjlenyomat engedély szükséges a rejtett elemek láthatóságának, az egész alkalmazásnak és a fájlok törlésének védelme érdekében.
- This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
+ Ez az alkalmazás csak egy része egy nagyobb alkalmazás sorozatnak. A többi megtalálható a https://www.simplemobiletools.com címen.
+ Filter media
+ Gambar
+ Video
+ GIF
+ Gambar RAW
+ SVG
+ Tidak ada file media ditemukan dari filter ini.
+ Ubah filter
+
+
+ Fungsi ini menyembunyikan folder dengan menambahkan file \'.nomedia\' ke dalamnya, juga akan menyembunyikan semua subfolder. Anda bisa melihatnya dari opsi \'Tampilkan file tersembunyi\' di Setelan. Lanjutkan?
+ Kecualikan
+ Folder yang dikecualikan
+ Atur folder yang dikecualikan
+ Ini hanya akan mengecualikan pilihan bersama dengan subfoldernya di Simple Gallery. Anda bisa mengatur pengecualian di Setelan.
+ Kecualikan folder induk?
+ Mengecualikan folder akan membuatnya bersama subfoldernya tersembunyi hanya di Simple Gallery, namun masih bisa dilihat di aplikasi lain.\n\nJika Anda ingin menyembunyikannya dari aplikasi lain juga, gunakan fitur Sembunyikan.
+ Bersihkan daftar
+ Hapus semua folder dari daftar pengecualian? Ini tidak akan menghapus folder tersebut.
+ Folder tersembunyi
+ Atur folder tersembunyi
+ Sepertinya tidak ada folder tersembunyi dengan file \".nomedia\" didalamnya.
+
+
+ Folder yang disertakan
+ Atur folder yang disertakan
+ Tambah folder
+ Jika ada folder yang berisi file media, namun tidak dikenali oleh aplikasi ini, Anda bisa menambahkannya disini secara manual.\n\nMenambah beberapa item disini tidak akan mengecualikan folder yang lain.
+
+
+ Ubah ukuran
+ Ubah ukuran terpilih dan simpan
+ Lebar
+ Tinggi
+ Jaga aspek rasio
+ Harap masukkan resolusi dengan benar
+
+
+ Editor
+ Simpan
+ Rotasi
+ Jalur
+ Jalur gambar tidak valid
+ Gagal mengedit gambar
+ Edit dengan:
+ Tidak ada aplikasi editor gambar
+ Lokasi file tidak diketahui
+ Tidak dapat mengganti file sumber
+ Putar ke kiri
+ Putar ke kanan
+ Putar 180º
+ Balik
+ Balik horizontal
+ Balik vertikal
+ Bebas
+ Lainnya
+
+
+ Simple Wallpaper
+ Setel wallpaper
+ Gagal menyetel sebagai wallpaper
+ Setel wallpaper dengan:
+ Menyetel wallpaper…
+ Wallpaper berhasil disetel
+ Aspek rasio potret
+ Aspek rasio landscape
+ Layar beranda
+ Layar kunci
+ Beranda dan layar kunci
+
+
+ Slideshow
+ Interval (detik):
+ Sertakan foto
+ Sertakan video
+ Sertakan GIF
+ Urutan acak
+ Animasi memudar
+ Mundur
+ Slideshow tanpa henti
+ Slideshow berakhir
+ Tidak ditemukan media untuk slideshow
+ Use crossfade animations
+
+
+ Ubah jenis tampilan
+ Kotak
+ Daftar
+ Kelompokkan langsung subfolder
+
+
+ Kelompokkan menurut
+ Jangan kelompokkan file
+ Folder
+ Terakhir diubah
+ Tanggal diambil
+ Jenis file
+ Ekstensi
+
+
+ Folder shown on the widget:
+ Show folder name
+
+
+ Putar video otomatis
+ Ingat posisi pemutaran terakhir
+ Tampil/sembunyikan nama file
+ Ulangi video
+ Animasi GIF di thumbnail
+ Kecerahan maksimal saat melihat di layar penuh
+ Pangkas thumbnail menjadi persegi
+ Tampilkan durasi video
+ Rotasi layar penuh dari
+ Pengaturan sistem
+ Rotasi perangkat
+ Aspek rasio
+ Background dan status bar hitam saat layar penuh
+ Gulir thumbnail secara horizontal
+ Otomatis sembunyikan sistem UI saat layar penuh
+ Hapus folder kosong setelah menghapus isinya
+ Izinkan mengontrol kecerahan foto dengan gerakan vertikal
+ Izinkan mengontrol kecerahan dan volume video dengan gerakan vertikal
+ Tampilkan jumlah folder media di tampilan utama
+ Tampilkan detail tambahan saat layar penuh
+ Atur detail tambahan
+ Izinkan zoom satu jari di layar penuh
+ Izinkan mengganti media dengan mengklik sisi layar
+ Izinkan zoom gambar lebih dalam
+ Sembunyikan detail tambahan ketika status bar disembunyikan
+ Lakukan pemeriksaan ulang untuk menghindari file yang tidak valid
+ Tampilkan beberapa tombol tindakan dibawah layar
+ Tampilkan Sampah di layar folder
+ Zoom gambar mendalam
+ Tampilkan gambar dalam kualitas tertinggi
+ Tampilkan Sampah sebagai item terakhir di layar utama
+ Izinkan keluar dari layar penuh dengan menggeser kebawah
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if available
+
+
+ Thumbnail
+ Media layar penuh
+ Detail tambahan
+ Tindakan di bawah
+
+
+ Sesuaikan tombol tindakan bawah
+ Favorit
+ Tampil/sembunyikan file
+
+
+ Bagaimana cara menjadikan Simple Gallery sebagai aplikasi galeri default?
+ Pertama Anda harus menemukan galeri default saat ini di bagian Aplikasi di Setelan perangkat, lihatlah tombol yang seperti \"Buka secara default\", klik itu, lalu pilih \"Hapus default\".
+ Lain kali Anda mencoba membuka gambar atau video, Anda akan disuruh memilih aplikasi, dimana Anda bisa memilih Simple Gallery dan menjadikannya default.
+ Saya mengunci aplikasi dengan password, tapi saya lupa. Apa yang harus dilakukan?
+ Anda bisa menyelesaikannya dengan 2 cara. Anda bisa instal ulang aplikasi, atau cari aplikasi ini di Setelan perangkat dan pilih \"Hapus data\". Ini akan menyetel ulang semua setelan Anda, dan tidak akan menghapus file media apapun.
+ Bagaimana agar sebuah album selalu muncul paling atas di daftar?
+ Anda bisa menekan lama album tersebut dan pilih ikon Pin di menu tindakan, itu akan menaruhnya di atas daftar. Anda juga bisa menyematkan beberapa folder, item yang di-pin akan diurutkan berdasarkan metode urutan default.
+ Bagaimana cara mempercepat laju video?
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.
+ Apa perbedaan antara menyembunyikan dan mengecualikan folder?
+ Mengecualikan tidak akan menampilkan folder di Simple Gallery, sedangkan Sembunyikan bekerja sesuai aturan sistem dan akan menyembunyikan folder juga dari aplikasi galeri yang lain. Cara kerjanya dengan membuat file \".nomedia\" kosong pada folder yang diinginkan, yang bisa Anda hapus juga dengan aplikasi file manager.
+ Mengapa folder dengan gambar album musik atau stiker muncul?
+ Kadang Anda melihat beberapa album yang tidak biasa muncul. Anda bisa dengan mudah menyembunyikannya dengan menekan lama dan pilih Kecualikan. Pada dialog berikutnya, Anda lalu bisa memilih folder induk, yang akan mencegah album terkait muncul kembali.
+ Ada folder berisi gambar namun tidak muncul, apa yang harus dilakukan?
+ Itu bisa disebabkan berbagai alasan, namun solusinya mudah. Pergi ke Setelan -> Atur folder yang disertakan, pilih Tambah dan cari folder yang diinginkan.
+ Bagaimana jika saya hanya ingin beberapa folder saja yang terlihat?
+ Menambahkan folder di Folder yang Disertakan tidak otomatis mengecualikan folder yang lain. Yang bisa Anda lakukan adalah pergi ke Setelan -> Atur Folder yang Dikecualikan, lalu kecualikan folder root \"/\", lalu tambahkan folder yang diinginkan di Setelan -> Atur Folder yang Disertakan.
+ Itu akan membuat folder yang dipilih saja yang muncul, dan jika sebuah folder disertakan dan dikecualikan secara bersamaan, folder tersebut akan muncul.
+ Bisakah saya meng-crop gambar dengan aplikasi ini?
+ Ya, Anda bisa melakukannya di Editor, dengan menyeret sudut gambar. Anda bisa masuk ke editor dengan menekan lama thumbnail gambar dan memilih Edit, atau pilih Edit dari tampilan layar penuh.
+ Bisakah saya mengelompokkan thumbnail file media?
+ Bisa, gunakan menu \"Kelompokkan menurut\" pada tampilan thumbnail. Anda bisa mengelompokkan file dengan berbagai kriteria, termasuk Tanggal Diambil. Jika Anda menggunakan fungsi \"Tampilkan semua isi folder\", Anda juga bisa mengelompokkan berdasarkan foldernya.
+ Tidak dapat mengurutkan berdasarkan Tanggal Diambil, bagaimana cara memperbaikinya?
+ Itu umumnya disebabkan karena file yang disalin dari tempat lain. Anda bisa memperbaikinya dengan memilih thumbnail file dan pilih \"Perbaiki Tanggal Diambil\".
+ Saya melihat beberapa pita warna pada gambar. Bagaimana saya meningkatkan kualitasnya?
+ Solusi saat ini untuk menampilkan gambar berfungsi dengan baik dalam sebagian besar kasus, namun jika Anda ingin kualitas gambar yang lebih baik, Anda bisa mengaktifkan \"Tampilkan gambar dalam kualitas tertinggi\" di setelan aplikasi, pada bagian \"Zoom gambar mendalam\".
+ Saya punya file/folder tersembunyi. Bagaimana cara memunculkannya?
+ Anda bisa memilih menu \"Tampilkan sementara file tersembunyi\" di layar utama, atau \"Tampilkan file tersembunyi\" di setelan aplikasi untuk menampilkannya. Jika Anda tidak ingin menyembunyikannya, tekan lama dan pilih \"Jangan sembunyikan\". Folder disembunyikan dengan menambahkan file \".nomedia\" di dalamnya, Anda bisa menghapus file tersebut dengan aplikasi file manager.
+
+
+
+ An offline gallery for managing your files without ads, respecting your privacy.
+
+ Aplikasi galeri dengan banyak kustomisasi dan mampu menampilkan banyak jenis gambar dan video termasuk SVG, RAW, panorama foto dan video.
+
+ Aplikasi ini open source, tidak berisi iklan atau izin yang tidak diperlukan.
+
+ Beberapa fiturnya antara lain:
+ 1. Pencarian
+ 2. Slideshow
+ 3. Dukungan notch (layar berponi)
+ 4. Pin folder di atas
+ 5. Filter media berdasarkan jenis
+ 6. Keranjang sampah untuk memulihkan file
+ 7. Kunci orientasi layar penuh
+ 8. Tandai file favorit agar mudah diakses
+ 9. Keluar dari layar penuh dengan menggeser ke bawah
+ 10. Editor bawaan untuk mengedit dan menambahkan filter
+ 11. Perlindungan password untuk item tersembunyi atau mengunci aplikasi
+ 12. Ubah jumlah kolom thumbnail lewat gerakan atau menu
+ 13. Sesuaikan tombol tindakan di bawah layar penuh untuk akses cepat
+ 14. Menampilkan detail tambahan di layar penuh dengan properti file yang diinginkan
+ 15. Berbagai cara untuk mengurutkan atau mengelompokkan item, dengan naik atau turun
+ 16. Sembunyikan folder (berpengaruh di aplikasi lain), kecualikan folder (hanya berpengaruh di Simple Gallery)
+
+ Izin sidik jari diperlukan untuk mengunci item tersembunyi, mengunci aplikasi, atau melindungi agar file tidak dihapus.
+
+ Aplikasi ini hanyalah bagian dari rangkaian aplikasi saya. Anda bisa menemukan aplikasi saya lainnya di https://www.simplemobiletools.com
+
+
+
+
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 0ab2f220c..0d2b39653 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1,6 +1,6 @@
- Simple Gallery
+ Semplice GalleriaGalleriaModificaApri fotocamera
@@ -29,38 +29,39 @@
Forza orizzontaleUsa l\'orientamento predefinitoCorreggi valore Data acquisizione
- Correzione…
+ Correzione in corso…Date aggiornate correttamente
+ Condividi una versione ridimensionata
- Filtra i media
+ Filtra i fileImmaginiVideoGIFImmagini RAWSVG
- Nessun file trovato per il filtro selezionato.
+ Nessun file trovato con il filtro selezionato.Cambia filtro
- 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?
+ Questa funzione nasconde la cartella aggiungendo un file \'.nomedia\' all\'interno, nasconderà anche tutte le sottocartelle. Si possono vedere attivando l\'opzione \'Mostra cartelle nascoste\' nelle impostazioni. Continuare?EscludiCartelle escluseGestisci le cartelle escluse
- Questo escluderà la selezione e le relative sottocartelle solo da Simple Gallery. Puoi gestire le cartelle escluse nelle impostazioni.
+ Questo escluderà la selezione e le relative sottocartelle solo da Semplice Galleria. Si possono gestire le cartelle escluse nelle impostazioni.Vuoi invece escluderne una superiore?
- L\'esclusione delle cartelle e delle sottocartelle le renderà nascoste solo in Simple Gallery, saranno ancora visibili in altre applicazioni.\\n\\nSe desideri nasconderle anche nelle altre app, usa la funzione Nascondi.
+ L\'esclusione delle cartelle e delle sottocartelle le renderà nascoste solo in Semplice Galleria, saranno ancora visibili in altre applicazioni.\\n\\nSe si desidera nasconderle anche nelle altre app, usa la funzione Nascondi.Rimuovi tutteRimuovere tutte le cartelle dalla lista delle esclusioni? Ciò non eliminerà le cartelle.Cartelle nascosteGestisci le cartelle nascoste
- Pare che tu non abbia alcuna cartella nascosta con un file \".nomedia\".
+ Non si ha alcuna cartella nascosta con un file \".nomedia\".Includi cartelleGestisci le cartelle incluseAggiungi cartella
- Se hai alcune cartelle che contengono media, ma non sono state riconosciute dall\'app, puoi aggiungerle manualmente qui.
+ Se si hanno alcune cartelle che contengono media, ma non sono state riconosciute dall\'app, si possono aggiungerle manualmente qui.Ridimensiona
@@ -75,11 +76,11 @@
SalvaRuotaPercorso
- Percorso immagine non valido
- Modifica immagine fallita
+ Percorso dell\'immagine non valido
+ Modifica dell\'immagine fallitaModifica immagine con:
- Editor immagini non trovato
- Posizione file sconosciuta
+ Editor delle immagini non trovato
+ Posizione del file sconosciutaImpossibile sovrascrivere il file originaleRuota a sinistraRuota a destra
@@ -87,8 +88,8 @@
CapovolgiCapovolgi orizzontalmenteCapovolgi verticalmente
- Modifica con
- Libero
+ Libero
+ AltroSfondo semplice
@@ -114,7 +115,8 @@
Scorri al contrarioRipeti presentazioneLa presentazione è terminata
- Nessun media trovato per la presentazione
+ Nessun file trovato per la presentazione
+ Usa le animazioni in dissolvenzaCambia modalità visualizzazione
@@ -131,25 +133,30 @@
Tipo di fileEstensione
+
+ Cartella mostrata nel widget:
+ Mostra il nome della cartella
+
- Riproduci i video automaticamente
+ Riproduci automaticamente i video
+ Ricorda l\'ultimo stato di riproduzione dei videoVisibilità nome del fileRipeti i videoAnima le GIF in miniaturaLuminosità max durante la visualizzazioneRitaglia le miniature in quadrati
+ Mostra la durata del videoRuota schermo perImpostazione di sistemaRotazione dispositivoProporzioni
- Sfondo e barra di stato neri con media a schermo intero
+ Sfondo e barra di stato neri con i file a schermo interoScorri le miniature orizzontalmente
- Nascondi UI di sistema con media a schermo intero
+ Nascondi l\'interfaccia utente di sistema con i file a schermo interoElimina cartelle vuote dopo averne eliminato il contenutoControlla la luminosità delle foto con gesti verticaliGestisci il volume e la luminosità dei video con gesti verticaliMostra numero elementi nella cartella
- Sostituisci Condividi con Ruota a schermo interoMostra informazioni estese su media a schermo interoGestisci le informazioni esteseAbilita zoom con un dito su media a schermo intero
@@ -159,9 +166,16 @@
Fai un controllo ulteriore per evitare di mostrare file non validiMostra alcuni pulsanti azione in fondo allo schermoMostra il cestino nella schermata delle cartelle
+ Immagini ingrandibili a fondo
+ Mostra le immagini alla massima qualità possibile
+ Mostra il cestino come ultimo elemento nella schermata principale
+ Chiudi la visuale a schermo intero con un gesto verso il basso
+ Permetti l\'ingrandimento 1:1 con un doppio tasto
+ Apri sempre i video su uno schermo separato con i nuovi movimenti orizzontali
+ Mostra un notch se disponibile
- Miniature
+ AnteprimeMedia a schermo interoDettagli estesiAzioni inferiori
@@ -172,48 +186,65 @@
Attiva / disattiva la visibilità dei file
- Come posso impostare Simple Gallery come galleria predefinita del dispositivo?
- Prima devi trovare l\'attuale galleria predefinita nella sezione App delle impostazioni di sistema, cerca un pulsante che dice qualcosa come \"App predefinite\", cliccalo, poi seleziona \"Ripristina predefiniti\".
- La prossima volta che proverai ad aprire un\'immagine o video dovresti vedere un selettore di app, dove puoi scegliere Simple Gallery e renderla l\'app predefinita.
+ Come posso impostare Semplice Galleria come la galleria predefinita del dispositivo?
+ Si deve prima trovare l\'attuale galleria predefinita nella sezione App delle impostazioni di sistema, cercare un pulsante che dice qualcosa come \"App predefinite\", cliccarlo e poi selezionare \"Ripristina predefiniti\".
+ La prossima volta che proverai ad aprire un\'immagine o video si dovrebbe vedere un selettore di app, dove si può scegliere Semplice Galleria e renderla l\'app predefinita.Ho bloccato l\'app con una password, ma l\'ho dimenticata. Cosa posso fare?
- Puoi risolvere in 2 modi. Puoi reinstallare l\'app, oppure trova l\'app nelle impostazioni del dispositivo e seleziona \"Cancella dati\". Tutte le impostazioni verranno ripristinate, nessun file multimediale verrà rimosso.
+ Si può risolvere in 2 modi: reinstallare l\'app, oppure trovare l\'app nelle impostazioni del dispositivo e seleziona \"Cancella dati\". Tutte le impostazioni verranno ripristinate, nessun file verrà rimosso.Come posso fare apparire un album sempre in cima?
- Puoi toccare a lungo l\'album desiderato e selezionare l\'icona puntina nel menù azioni, ciò lo fisserà in cima. Puoi anche fissare varie cartelle, gli elementi fissati verranno ordinati dal metodo di ordinamento predefinito.
+ Si può toccare a lungo l\'album desiderato e selezionare l\'icona puntina nel menù azioni, ciò lo fisserà in cima. Si possono anche fissare varie cartelle, gli elementi fissati saranno ordinati dal metodo di ordinamento predefinito.Come avanzo velocemente nei video?
- Puoi cliccare sui testi di durata attuale o massima vicino alla barra di avanzamento, ciò avanzerà o riavvolgerà il video.
+ Si possono trascinare le proprie dita orrizontalmente sul video, oppure cliccando i testi accanto alla barra di avanzamento. In questo modo il video andrà avanti o indietro.Che differenza c\'è tra nascondere ed escludere una cartella?
- Escludere impedisce la visualizzazione della cartella solo in Simple Gallery, mentre nascondere ha effetto in tutto il sistema e nasconde la cartella anche alle altre gallerie. Funziona creando un file vuoto \".nomedia\" nella cartella in questione, che puoi anche rimuovere successivamente con qualsiasi gestore di file.
+ Escludere impedisce la visualizzazione della cartella solo in Semplice Galleria, mentre nascondere ha effetto in tutto il sistema e nasconde la cartella anche alle altre gallerie. Funziona creando un file vuoto \".nomedia\" nella cartella in questione, si possono anche rimuovere successivamente con qualsiasi gestore dei file.Perchè vengono mostrate cartelle con copertine o adesivi di musica?
- Può succedere che tu veda apparire alcuni album insoliti. Puoi escluderli facilmente toccandoli a lungo e selezionando Escludi. Nella finestra successiva puoi quindi selezionare la cartella superiore, con la possibilità di impedire la visualizzazione anche di altri album correlati.
+ Può succedere che si vedano apparire alcuni album insoliti. Si possono escluderli facilmente toccandoli a lungo e selezionando Escludi. Nella finestra successiva si possono quindi selezionare la cartella superiore, con la possibilità di impedire la visualizzazione anche di altri album correlati.Una cartella con immagini non viene mostrata, cosa posso fare?Può succedere per vari motivi, ma la soluzione è semplice. Vai in Impostazioni -> Gestisci le cartelle incluse, tocca il tasto Più e naviga verso la cartella desiderata.Che fare se voglio rendere visibili solo poche particolari cartelle?
- Aggiungere una cartella nelle Cartelle Incluse non esclude automaticamente nulla. Quello che puoi fare è andare in Impostazioni -> Gestisci le cartelle escluse, escludi la cartella root \"/\", poi aggiungi le cartelle desiderate in Impostazioni -> Gestisci le cartelle incluse.
+ Aggiungere una cartella nelle Cartelle Incluse non esclude automaticamente nulla. Quello che puoi fare è andare in Impostazioni → Gestisci le cartelle escluse, escludi la cartella root \"/\", poi aggiungi le cartelle desiderate in Impostazioni → Gestisci le cartelle incluse.
Ciò renderà visibili solo le cartelle selezionate, dato che sia l\'esclusione che l\'inclusione sono ricorsive e se una cartella è sia esclusa che inclusa, verrà mostrata.
- Le immagini a schermo intero hanno strani artefatti, posso migliorarne la qualità in qualche modo?
- Sì, c\'è un\'opzione nelle impostazioni che dice \"Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità\", puoi usare quella. Ciò migliorerà la qualità delle immagini, ma saranno sfuocate quando proverai a ingrandirle troppo.Posso ritagliare le immagini con questa app?
- Sì, puoi ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Puoi usare l\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, o selezionando Modifica mentre la vedi a schermo intero.
+ Sì, si possono ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Si può accedere all\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, oppure selezionando Modifica mentre si vede una foto a schermo intero.Posso raggruppare in qualche modo le miniature dei file?
- Certo, usa il menu \"Raggruppa per\" mentre visualizzi le miniature. Puoi raggruppare i file con diversi criteri, incluso la data di creazione. Se utilizzi la funzione \"Mostra tutti i contenuti\" puoi anche raggrupparli per cartelle.
+ Certo, si può usare il menu \"Raggruppa per\" mentre si visualizzano le miniature. Il raggruppamento può avvenire per diversi criteri, incluso la data di creazione. Se si utilizza la funzione \"Mostra tutti i contenuti\" si può anche raggrupparli per cartelle.L\'ordinamento per data acquisizione sembra non funzionare bene, come posso risolvere?
- Probabilmente è causato dai file copiati da altre parti. Puoi risolvere selezionando le miniature dei file e scegliendo \"Correggi valore Data acquisizione\".
+ Probabilmente è causato dai file copiati da altre parti. Si può risolvere selezionando le miniature dei file e scegliendo \"Correggi valore Data acquisizione\".
+ Vedo curvature di colore nelle immagini. Come posso migliorarne la qualità?
+ L\'attuale soluzione per visualizzare immagini funziona bene nella maggior parte dei casi, ma se si vuole una qualità ancora maggiore, si può attivare \"Mostra le immagini alla massima qualità possibile\" nelle impostazioni dell\'app, nella sezione \"Immagini ingrandibili a fondo\".
+ Ho nascosto un file/una cartella. Come posso mostrarlo/a di nuovo?
+ Si può premere \"Mostra temporaneamente nascosti\" nel menu della schermata principale, oppure attivare \"Mostra gli elementi nascosti\" nelle impostazioni dell\'app per vedere l\'elemento nascosto. Per farla rimanere visibile, premere a lungo e selezionare \"Non nascondere\". Le cartelle vengono nascoste aggiungendo un file nascosto \".nomedia\" all\'interno di esse, si può eliminare il file con qualsiasi gestore di file.
- Una galleria per visualizzare foto e video senza pubblicità.
+ Una galleria per gestire i propri file senza pubblicità che rispetta la privacy.
- Un semplice strumento per visualizzare foto e video. Gli elementi possono essere ordinati per data, dimensioni, nome sia ascendente che discendente; le foto possono essere ingrandite. I file sono mostrati in colonne multiple a seconda delle dimensioni dello schermo, puoi modificare il numero di colonne con il tocco. Possono essere rinominate, condivise, eliminate, copiate, spostate. Le immagini possono anche essere ritagliate, ruotate o impostate come sfondo direttamente dalla app.
+ Una galleria altamente personalizzabile e capace di visualizzare tipi di file immagini e video differenti, fra cui SVG, RAW, foto panoramiche e video.
- Simple Gallery è anche offerta per utilizzo di terze parti per anteprime di immagini / video, aggiunta di allegati ai client email, ecc. È perfetta per un uso quotidiano.
+ L\'applicazione non contiene pubblicità o permessi non necessari; è completamente opensource e la si può personalizzare con i propri colori preferiti.
- L\'autorizzazione per le impronte è necessaria per bloccare la visibilità di alcuni elementi o dell\'intera app.
+ Alcune funzionalità che vale la pena accennare:
+ 1. Ricerca
+ 2. Presentazione
+ 3. Supporto al notch
+ 4. Fissare le cartelle in alto
+ 5. Filtro dei file per tipologia
+ 6. Cestino per un recupero facile dei file
+ 7. Blocco dell\'orientamento nella vista a schermo intero
+ 8. Selezione dei file preferiti per un accesso immediato
+ 9. Chisura rapida della vista a schermo intero con un movimento verso il basso
+ 10. Un editor per modificare le immagini e applicare filtri
+ 11. Protezione con password per proteggere elementi nascosti o l\'intera applicazione
+ 12. Cambio delle colonne delle anteprime con un movimento o tramite dei pulsanti nel menu
+ 13. Pulsanti rapidi per azioni personalizzabili nella vista a schermo intero
+ 14. Visualizzazione di determinati dettagli aggiuntivi nella vista a schermo intero
+ 15. Molti modi per ordinare o raggruppare gli elementi, sia in ordine crescente che decrescente
+ 16. Cartelle nascoste (anche per altre applicazioni), cartelle escluse (solo per Semplice Galleria)
- Non contiene pubblicità o autorizzazioni non necessarie. È completamente opensource, offre colori personalizzabili.
+ L\'autorizzazione per leggere le impronte digitali è necessaria per il blocco della visibilità, dell\'intera applicazione o per proteggere alcuni file dalla loro eliminazione.
- Questa app è solo una piccola parte di una grande serie di altre app. Puoi trovarle tutte su https://www.simplemobiletools.com
+ Questa è solamente una delle tante applicazioni della serie Simple Mobile Tools. Si possono trovare le altre su https://www.simplemobiletools.com
-
表示する形式
@@ -87,8 +88,8 @@
反転水平方向に反転垂直方向に反転
- 他のアプリで編集
- Free
+ Free
+ Otherシンプル壁紙
@@ -115,6 +116,7 @@
スライドショーをリピート再生するスライドショーが終了しましたスライドショーに表示するメディアがありません
+ Use crossfade animations表示形式の変更
@@ -131,34 +133,46 @@
ファイル形式拡張子
+
+ Folder shown on the widget:
+ Show folder name
+
- ビデオを自動再生する
+ ビデオを自動再生
+ Remember last video playback positionファイル名の表示を切り替え
- ビデオを繰り返し再生する
- アニメーションGIFのサムネイルを動かす
- メディア再生時に明るさを最大にする
+ ビデオを繰り返し再生
+ アニメーションGIFを動かす
+ 再生時には明るさを最大にするサムネイルを正方形に切り取る
- メディア再生時のフルスクリーン表示切り替え
+ Show video durations
+ フルスクリーン再生の表示切り替えシステム設定に従う端末の向きに従うメディアの縦横比に従う
- 表示の背景色とステータスバーの背景色を黒にする
+ メディア表示の背景色を黒にするサムネイル画面を横方向にスクロール
- フルスクリーン時にシステムUIを非表示にする
- フォルダが空になったらフォルダも削除する
- 垂直のジェスチャーで写真の明るさを制御する
- 音量と明るさを縦のジェスチャーで変更する
- フォルダ内のメディアの数を表示する
- フルスクリーン時の「共有」を「回転」に置き換える
+ 全画面ではシステムUIを非表示
+ 空になったフォルダは削除する
+ 垂直の動作で写真の明るさを制御
+ 音量と明るさを縦の動作で変更
+ フォルダ内のメディア数を表示するフルスクリーンに詳細を重ねて表示する詳細表示を管理する
- メディアを指ひとつでズーム可能にする
- 画面の端を押してメディアをスライドする
- Allow deep zooming images
+ メディアを指だけでズームする
+ 端を押してメディアをスライド
+ 深いズームを許可ステータスバーが非表示の時は詳細を隠す
- 無効なファイルを見せない調整を行う
- 画面下部にアクションボタンを表示する
- フォルダ画面にごみ箱を表示する
+ 無効なファイルは見せない
+ 画面の下にはアクションを表示
+ フォルダ画面にごみ箱を表示
+ 画像のズームを深くする
+ 可能な限り高品質で画像を表示
+ ごみ箱をメイン画面の最後に表示
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableサムネイル設定
@@ -180,38 +194,56 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.動画を早送りするにはどうすればよいですか?
- シークバーの隣にある経過時間または最大時間の表示を押すごとに早送り、または巻き戻しが作動します。
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- 写真やビデオを見るためのギャラリー。広告はありません。
+ An offline gallery for managing your files without ads, respecting your privacy.
- 写真やビデオを見るためのシンプルなツール。 日付、サイズ、名前で、昇順または降順にアイテムを並べ替えることができ、写真は拡大表示できます。 メディアファイルは、画面のサイズに応じて最適な列数で表示されます。 名前の変更、共有、削除、コピー、移動が可能です。 画像をトリミング、回転、または壁紙としてアプリから直接設定することもできます。
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- ギャラリーは、画像やビデオのプレビュー、メールクライアントで添付ファイルの追加など、サードパーティの用途にも提供されます。 日々の使用には最適です。
+ It is open source, contains no ads or unnecessary permissions.
- 隠しアイテムを表示したりアプリそのものをロックするには指紋認証の許可が必要です。
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- 広告や不要なアクセス許可は含まれていません。 完全にオープンソースで、ダークテーマも提供しています。
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
- このアプリは、大きな一連のアプリの一つです。 他のアプリは https://www.simplemobiletools.com で見つけることができます
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
미디어 필터 설정
@@ -87,8 +88,8 @@
반전가로 반전세로 반전
- 이미지편집 프로그램 연결
- Free
+ Free
+ OtherSimple Wallpaper
@@ -115,6 +116,7 @@
슬라이드 쇼 반복슬라이드 쇼 종료슬라이드 쇼를 위한 미디어를 찾을 수 없음
+ Use crossfade animations보기방식 변경
@@ -131,13 +133,19 @@
File typeExtension
+
+ Folder shown on the widget:
+ Show folder name
+
비디오 자동재생
+ Remember last video playback position파일이름 보기비디오 반복섬네일에서 GIFs 애니메이션 활성화미디어 최대 밝기미리보기 사각형으로 자름
+ Show video durations전체화면으로 회전기준시스템 설정디바이스 회전
@@ -149,7 +157,6 @@
상하 제스처로 사진 밝기 제어수직 제스처로 비디오 볼륨 및 밝기 제어폴더에 포함된 미디어파일 수 표시
- 전체화면 메뉴의 공유 아이콘을 회전 아이콘으로 변경전체화면 모드에서 세부정보 표시확장된 세부정보 관리전체화면 모드에서 한 손가락으로 확대 및 축소
@@ -159,6 +166,13 @@
잘못된 파일 표시를 방지하기 위해 추가 검사 수행Show some action buttons at the bottom of the screenShow the Recycle Bin at the folders screen
+ Deep zoomable images
+ Show images in the highest possible quality
+ Show the Recycle Bin as the last item on the main screen
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if available섬네일
@@ -180,42 +194,56 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- 광고없이 사진과 동영상을 볼 수 있는 갤러리.
+ An offline gallery for managing your files without ads, respecting your privacy.
- 사진과 동영상을 간편하게 관리 할 수 있는 툴입니다.
-
- 날짜, 크기, 이름을 기준으로 정렬 할 수 있으며 사진을 확대 할 수 있습니다. 미디어 파일은 디스플레이의 크기에 맞춰 여러 열로 표시되며 핀치 제스처를 이용하여 변경 할 수도 있습니다.
-
- 이름변경, 공유, 삭제, 복사, 이동, 이미지편집, 배경화면 설정 등의 기능을 제공합니다.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- 갤러리는 \'이미지/비디오 미리보기\', \'이메일 클라이언트에서 첨부파일 추가하기\' 등의 기능을 서드파티 애플리케이션에 제공합니다. 언제나 완벽하게 사용할 수 있습니다.
+ It is open source, contains no ads or unnecessary permissions.
- 앱을 잠그거나 숨김파일을 보기위하여 지문인식 기능을 사용하는 경우 지문사용 권한이 필요합니다.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- 광고가 포함되어 있거나, 불필요한 권한을 요청하지 않습니다. 이 앱의 모든 소스는 오픈소스이며, 사용자가 직접 애플리케이션의 컬러를 설정 할 수 있습니다.
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
- 이 앱은 다양한 시리즈의 모바일앱 중 하나입니다. 나머지는 https://www.simplemobiletools.com 에서 찾아보실 수 있습니다.
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
Filtruoti mediją
@@ -87,8 +88,8 @@
ApverstiApversti horizontaliaiApversti vertikaliai
- Redaguoti su
- Free
+ Free
+ OtherPaprastas darbalaukio fonas
@@ -115,6 +116,7 @@
Klipuoti skaidrių demonstracijąSkaidrių demonstracija pasibaigėNerasta medijos skaidrių demonstracijai
+ Use crossfade animationsKeisti peržiūros tipą
@@ -131,13 +133,19 @@
File typeExtension
+
+ Folder shown on the widget:
+ Show folder name
+
Groti vaizdo įrašus automatiškai
+ Remember last video playback positionPerjungti bylos pavadinimo matomumąKlipuoti vaizdo įrašusAnimuoti GIF\'us miniatiūroseMaksimalus ryškumas, kai medija peržiūrima viso ekrano rėžimuApkirpti miniatiūras kvadratu
+ Show video durationsSukti viso ekrano mediją pagalSistemos nustatymaiĮrenginio sukimas
@@ -149,7 +157,6 @@
Leisti valdyti nuotraukų ryškumą vertikaliais gestaisLeisti kontroliuoti vaizdo įrašo garsumą ir ryškumą vertikaliais gestaisRodyti aplanko bylų skaičių pagrindiniame rodinyje
- Pakeisti bendrinti su rotacija viso ekrano meniuRodyti išsamią informaciją per visą ekranąTvarkykite išsamią informacijąLeisti vienu pirštu pritraukti viso ekrano rėžime
@@ -159,6 +166,13 @@
Atlikti papildomą patikrinimą, kad nebūtų rodomos sugadintos bylosShow some action buttons at the bottom of the screenShow the Recycle Bin at the folders screen
+ Deep zoomable images
+ Show images in the highest possible quality
+ Show the Recycle Bin as the last item on the main screen
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableMiniatiūros
@@ -180,7 +194,7 @@
Kaip aš galiu padaryti albumą visada rodomą viršuje?Galite ilgai paspausti norimą albumą ir pasirinkti "Prisegti" piktogramą, esančią meniu "Veiksmo meniu", viršuje. Galite prisegti kelis aplankus, prisegti elementai bus rūšiuojami pagal numatytąjį rūšiavimo metodą.Kaip galėčiau greitai prasukti vaizdo įrašus?
- Galite spustelėti tekstus šalia slinkties juostos, kad būtų perkeltas vaizdo įrašas atgal arba į priekį.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.Koks skirtumas tarp slėpimo ir išskyrimo iš aplanko?Išskyrimas neleidžia rodyti aplanko tik paprastoje galerijoje, tuo tarpu slėpimas slepia aplanką iš kitų galerijų. Tai veikia, sukuriant tuščią \ ". Nomedia \" bylą tam tikrame aplanke, kurį vėliau galite pašalinti bet kuria bylų tvarkykle.Kodėl pasirodo aplankai su muzikos viršeliu ar lipdukais?
@@ -190,28 +204,46 @@
Ką daryti, jei noriu matyti tik keletą konkrečių aplankų?Pridėjus aplanką į "Įtraukti aplankai", automatiškai neįtraukiama nieko. Ką jūs galite padaryti, eikite į Nustatymai -> Tvarkyti išskirtus aplankus, išskirkite šakninį aplanką \ "/ \", tada pridėkite norimus aplankus, esančius Nustatymai -> Tvarkyti įtrauktas aplankas.
Tai leis matyti tik tuos pasirinktus aplankus, nes abu atmetami ir įtraukti yra rekursyvūs, ir jei aplankas yra išskirtas ir įtrauktas, jis bus rodomas.
- Viso ekrano atvaizdai turi keistus artefaktus, galiu kokiu nors būdu pagerinti kokybę?
- Taip, "Nustatymuose" perjunkite \ "Pakeisti giliuosius, patobulintus vaizdus geresnės kokybės vaizdais \", galite tai naudoti. Tai pagerins vaizdų kokybę, tačiau kai jūs pabandysite per daug padidinti vaizdą, jie bus neryškūs.Ar galiu apkarpyti vaizdus naudojant šią programėlę?Taip, redaguodami vaizdus, galite juos apkarpyti, vilkdami vaizdo kampus. Galite patekti į redaktorių ilgai paspaudę vaizdo miniatiūrą ir pasirinkę "Redaguoti" arba iš viso ekrano rodinio pasirinkę "Redaguoti".Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- Galerija, skirta peržiūrėti nuotraukas ir vaizdo įrašus be reklamų.
+ An offline gallery for managing your files without ads, respecting your privacy.
- Paprastas įrankis, naudojamas peržiūrėti nuotraukas ir vaizdo įrašus. Elementus galima suskirstyti pagal datą, dydį, pavadinimą tiek didėjančia, tiek mažėjančia tvarka, nuotraukos gali būti priartintos. Vaizdo bylos rodomos keliuose stulpeliuose, priklausomai nuo ekrano dydžio, kolonėlių skaičių galite keisti naudodami gesinimo gestus. Jas galima pervardyti, bendrinti, ištrinti, kopijuoti, perkelti. Vaizdus taip pat galima apkarpyti, pasukti, apversti arba nustatyti kaip "Darbalaukio paveikslėliu" tiesiai iš programėlės.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- Galerija taip pat siūloma trečiosioms šalims peržiūrėti atvaizdus / vaizdo įrašus, pridėti priedus el. Pašto klientams ir pan. Tai idealu kasdieniam naudojimui.
+ It is open source, contains no ads or unnecessary permissions.
- Pirštų atspaudų leidimas reikalingas norint užblokuoti paslėptą elemento matomumą arba visą programą.
-
- Neturi reklamų ar nereikalingų leidimų. Programėlė visiškai atviro kodo, yra galimybė keisti spalvas.
-
- Ši programėle yra vienina iš keletos mūsų programėlių. Likusias Jūs galite rasti čia http://www.simplemobiletools.com
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
+
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
+
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
Filtrer media
@@ -38,7 +39,7 @@
VideoerGIF-bilderRAW-format-bilder
- SVGs
+ SVG-bilderIngen media-filer er funnet med de valgte filtrene.Endre filtere
@@ -87,8 +88,8 @@
SpeilvendSpeilvend horisontaltSpeilvend vertikalt
- Rediger med
- Fri
+ Fri
+ AnnenBakgrunnsbilde
@@ -115,12 +116,13 @@
Gjenta lysbildeshowLysbildeshowet er sluttIngen media for lysbildeshowet er funnet
+ Bruk krysstoningsanimasjonEndre visningstypeRutenettListe
- Group direct subfolders
+ Grupper direkte undermapperGrupper etter
@@ -131,13 +133,19 @@
FiltypeEndelse
+
+ Mappe vist på modulen:
+ Vis mappenavn
+
Avspill videoer automatisk
+ Husk siste videoavspillingsposisjonVis/skjul filnavnGjenta videoerAnimert GIF i minibildevisningMaks lysstyrke ved mediavisningBeskjær minibilder i kvadrater
+ Vis videolengdeRoter media etterSysteminnstillingEnhetsrotasjon
@@ -149,7 +157,6 @@
Tillat å styre fotolysstyrke med vertikale bevegelserTillat å styre videovolum og lysstyrke med vertikale bevegelserVis mediaantallet i mapper på hovedvisningen
- Erstatt Del med Roter i meny ved mediavisningVis flere detaljer i mediavisningenVelg detaljerTillat en-finger-zoom i mediavisningen
@@ -159,6 +166,13 @@
Gjør en ekstra sjekk for å unngå visning av ugyldige filerVis noen handlingsknapper nederst på skjermenVis papirkurven på mappeskjermen
+ Dyp zoombare bilder
+ Vis bilder i høyest mulig kvalitet
+ Vis papirkurven som siste element på hovedskjermen
+ Tillat lukking av mediavisningen med en nedoverbevegelse
+ Tillat å zoome 1:1 med to dobbeltrykk
+ Åpne alltid videoer på en separat skjerm med nye horisontale bevegelser
+ Vis flik hvis tilgjengeligMinibilder
@@ -180,36 +194,54 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- A gallery for viewing photos and videos without ads.
+ An offline gallery for managing your files without ads, respecting your privacy.
- A simple tool usable for viewing photos and videos. Items can be sorted by date, size, name both ascending or descending, photos can be zoomed in. Media files are shown in multiple columns depending on the size of the display, you can change the column count by pinch gestures. They can be renamed, shared, deleted, copied, moved. Images can also be cropped, rotated, flipped or set as Wallpaper directly from the app.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- The Gallery is also offered for third party usage for previewing images / videos, adding attachments at email clients etc. It\'s perfect for everyday usage.
+ It is open source, contains no ads or unnecessary permissions.
- The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 68b177cad..974f83c41 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -31,6 +31,7 @@
Datum opname corrigerenCorrigeren…Datums zijn gecorrigeerd
+ Verkleinde versie delenMedia filteren
@@ -87,8 +88,8 @@
KantelenHorizontaal kantelenVerticaal kantelen
- Bewerken met
- Vrij
+ Vrij
+ AndersAchtergrond
@@ -115,6 +116,7 @@
Voorstelling herhalenDe diavoorstelling is beëindigdGeen media gevonden voor diavoorstelling
+ Crossfade-animaties gebruikenWeergave
@@ -131,13 +133,19 @@
BestandstypeExtensie
+
+ Map tonen in de widget:
+ Mapnaam tonen
+
Video\'s automatisch afspelen
+ Laatste positie in video\'s onthoudenBestandsnamen tonenVideo\'s herhalenGIF-bestanden afspelen in overzichtMaximale helderheid in volledig schermMiniatuurvoorbeelden bijsnijden
+ Lengte van video\'s tonenMedia in volledig scherm roteren volgensSysteeminstellingOriëntatie van apparaat
@@ -146,10 +154,9 @@
Horizontaal scrollenStatusbalk automatisch verbergen in volledig schermLege mappen verwijderen na leegmaken
- Helderheid voor afbeeldingen aanpassen met verticale gebaren
- Volume en helderheid voor video\'s aanpassen met verticale gebaren
+ Helderheid voor afbeeldingen aanpassen met verticale veeggebaren
+ Volume en helderheid voor video\'s aanpassen met verticale veeggebarenAantallen in mappen tonen
- Menu-item Draaien vastzetten in volledig scherm (in plaats van Delen)Uitgebreide informatie tonen in volledig schermUitgebreide informatieMet één vinger zoomen in volledig scherm
@@ -157,14 +164,21 @@
Verder inzoomen mogelijk makenUitgebreide informatie niet tonen als de statusbalk is verborgenOngeldige bestanden verbergen
- Enkele actieknoppen onderaan het scherm tonen
+ Enkele actieknoppen onder aan het scherm tonenPrullenbak weergeven in de mapweergave
+ Afbeeldingen ver inzoomen
+ Afbeeldingen in de hoogst mogelijke kwaliteit weergeven
+ Prullenbak als laatste item tonen
+ Naar beneden vegen om volledig scherm af te sluiten
+ 1:1 zoomen na 2x dubbelklikken
+ Video\'s altijd in apart scherm met horizontale veeggebaren openen
+ Inkeping scherm tonen indien aanwezigMiniatuurvoorbeeldenVolledig schermUitgebreide informatie
- Acties onderaan het scherm
+ Acties onder aan het schermActieknoppen onderaan beheren
@@ -172,46 +186,64 @@
Bestand tonen/verbergen
- How can I make Simple Gallery the default device gallery?
- First you have to find the currently default gallery in the Apps section of your device settings, look for a button that says something like \"Open by default\", click on it, then select \"Clear defaults\".
- The next time you will try opening an image or video you should see an app picker, where you can select Simple Gallery and make it the default app.
- I locked the app with a password, but I forgot it. What can I do?
- You can solve it in 2 ways. You can either reinstall the app, or find the app in your device settings and select \"Clear data\". It will reset all your settings, it will not remove any media files.
- How can I make an album always appear at the top?
- You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.
- How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
- What is the difference between hiding and excluding a folder?
- Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.
- Why do folders with music cover art or stickers show up?
- It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
- That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.
- What if I want just a few particular folders visible?
- Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
- That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.
- Can I crop images with this app?
- Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.
- Can I somehow group media file thumbnails?
- Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.
- Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?
- It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ Hoe kan ik Eenvoudige Galerij instellen als standaard-app voor foto\'s en video\'s?
+ Zoek eerst de huidige standaard-app voor foto\'s en video\'s in de Android-instellingen (via \"Apps\" of "\Apps en meldingen\"). Klik bij de App-info op \"Standaardwaarden wissen\" (soms onder \"Standaard openen\").
+ Bij het openen van een foto of video zal de volgende keer een keuzescherm verschijnen, waarin Eenvoudige Galerij als standaard-app kan worden ingesteld.
+ Ik heb de app beveiligd met een wachtwoord, maar ben het wachtwoord nu vergeten. Wat kan ik doen?
+ Deïnstalleer en herinstalleer de app, of ga naar de app in de Android-instellingen (via \"Apps (en meldingen)\" -> \"App-info\") en kies via \"Opslagruimte\" voor \"Gegevens wissen\". Hiermee worden alleen de instellingen van deze app gewist.
+ Hoe kan ik een map bovenaan vastzetten?
+ Druk lang op het map en kies vervolgens de punaise in het actiemenu. Als er meerdere mappen zijn vastgezet, zullen deze worden weergeven op basis van de standaardsortering.
+ Hoe kan ik terug- of vooruitspoelen in video\'s?
+ Sleep horizontaal over de videospeler, of klik bij de zoekbalk op de cijfers die de huidige voortgang of de lengte weergeven. Hierbij zal de video respectievelijk terug- of vooruitspringen.
+ Wat is het verschil tussen het verbergen en het uitsluiten van mappen?
+ Met \"Uitsluiten\" wordt het tonen van de map alleen binnen deze app voorkomen, terwijl \"Verbergen\" de map ook zal verbergen voor andere galerij-apps. Met \"Verbergen\" wordt een bestand genaamd \".nomedia\" in de te verbergen map aangemaakt (het verwijderen van dit bestand uit de map maakt het verbergen ongedaan).
+ Waarom zie ik mappen met stickers of covers van muziekalbums?
+ Soms worden er wat ongebruikelijke afbeeldingen van andere apps getoond. Deze zijn gemakkelijk uit het overzicht te halen door lang te drukken op de map en vervolgens te kiezen voor \"Uitsluiten\". In het daaropvolgende venster is ook de bovenliggende map te kiezen; dit zou het tonen van soortgelijke ongewenste items kunnen voorkomen.
+ Een bepaalde map met afbeeldingen wordt niet getoond. Wat kan ik doen?
+ Dit kan verschillende redenen hebben, maar is eenvoudig op te lossen. Ga naar \"Instellingen\" -> \"Toegevoegde mappen beheren\", klik vervolgens op het plus-symbool en navigeer naar de gewenste map.
+ Wat als ik slechts een beperkt aantal mappen wil laten zien?
+ Het toevoegen van mappen aan de lijst bij \"Toegevoegde mappen beheren\" sluit niet automatisch alle andere mappen uit. Sluit daarom de hoofdmap \"/\" uit via \"Instellingen\" -> \"Uitgesloten mappen beheren\" en voeg vervolgens de te tonen mappen toe bij \"Instellingen\" -> \"Toegevoegde mappen beheren\".
+ Dit zal ervoor zorgen dat alleen de toegevoegde mappen worden getoond (mappen toevoegen of uitsluiten gebeurt recursief, maar een toegevoegde map zal altijd worden getoond).
+ Kan ik afbeeldingen bijsnijden met deze app?
+ Ja, dat kan bij \"Bewerken\" door de hoeken te verslepen. Druk lang op een afbeelding in het overzicht of ga naar het volledig scherm en klik vervolgens op \"Bewerken\".
+ Hoe kan ik mediabestanden groeperen?
+ Kies het menu-item \"Groeperen op\" om op basis van verschillende criteria te groeperen. Dit kan ook ongeacht de mappenstructuur als voor de functie \"Alles weergeven\" is gekozen.
+ Sorteren op Datum opname lijkt niet te werken. Hoe kan ik dit oplossen?
+ Waarschijnlijk zijn de bestanden gekopieerd vanaf een andere locatie. Selecteer de bestanden en kies \"Datum opname corrigeren\".
+ Ik zie \"color banding\" op de afbeeldingen. Hoe kan ik de kwaliteit verbeteren?
+ In de meeste gevallen werkt de huidige methode voor het weergeven van afbeeldingen prima, maar met de instelling \"Afbeeldingen in de hoogst mogelijke kwaliteit weergeven\" onder \"Afbeeldingen ver inzoomen\" kan een nog betere kwaliteit worden bewerkstelligd.
+ Ik heb een bestand of map verborgen. Hoe kan ik dit ongedaan maken?
+ Kies het menu-item \"Verborgen items tijdelijk tonen\", of schakel de instelling \"Verborgen items tonen\" in om het verborgen item te kunnen zien. Druk vervolgens lang op het item en kies \"Tonen\" om het verbergen ongedaan te maken. Mappen worden verborgen door het bestand \".nomedia\" in de map te plaatsen; dit bestand kan ook handmatig in een andere app worden verwijderd.
- Een galerij voor afbeeldingen en video\'s, zonder advertenties.
+ Een privacyvriendelijke advertentievrije galerij voor afbeeldingen en video\'s.
- Een eenvoudige galerij voor afbeeldingen en video\'s. Bestanden kunnen worden gesorteerd op datum, grootte en naam. Afbeeldingen kunnen in- en uitgezoomd worden. Bestanden worden afhankelijk van de schermgrootte weergegeven in kolommen, waarbij het aantal kolommen kan worden aangepast via knijpgebaren. Bestanden kunnen worden gedeeld, hernoemd, gekopieerd, verplaatst en verwijderd. Afbeeldingen kunnen ook worden bijgesneden, gedraaid, gekanteld of direct vanuit de app als achtergrond worden ingesteld.
+ Een zeer goed aan te passen galerij voor afbeeldingen en video\'s in vele bestandsformaten, waaronder SVG, RAW, panoramafoto\'s en -video\'s.
- De galerij kan ook worden gebruikt voor het bekijken van afbeeldingen of video\'s vanuit andere apps, om bijlagen toe te voegen in e-mail, etc. Perfect voor dagelijks gebruik.
+ Deze app is open-source, bevat geen advertenties en vraagt niet om onnodige machtigingen.
- De machtiging voor het uitlezen van vingerafdrukken is benodigd voor het vergendelen van verborgen items of de gehele app.
+ Een lijst met de belangrijkste mogelijkheden:
+ 1. Zoeken
+ 2. Diavoorstelling
+ 3. Ondersteuning voor schermen met een inkeping
+ 4. Mappen bovenaan vastzetten
+ 5. Media filteren op bestandstype
+ 6. Een prullenbak
+ 7. Oriëntatie vastzetten voor volledig scherm
+ 8. Favorieten
+ 9. Veeggebaren in volledig scherm
+ 10. Afbeeldingen bewerken en filters toepassen
+ 11. Verborgen items of de gehele app beveiligen met een wachtwoord
+ 12. Het aantal kolommen aanpassen via veeggebaren of menuknoppen
+ 13. De acties op de werkbalk in volledig scherm aanpassen
+ 14. Uitgebreide informatie tonen over de bestanden in volledig scherm
+ 15. Items sorteren en groeperen op verschillende manieren
+ 16. Mappen verbergen (ook voor andere apps), of mappen uitsluiten (alleen voor deze app)
- Bevat geen advertenties of onnodige machtigingen. Volledig open-source. Kleuren van de app kunnen worden aangepast.
+ De machtiging voor vingerafdrukken is benodigd voor het beveiligen van verborgen items, of de hele app, of om te voorkomen dat bestanden kunnen worden verwijderd.
- Deze app is onderdeel van een grotere verzameling. Vind de andere apps op https://www.simplemobiletools.com
+ Deze app is onderdeel van een grotere collectie. De andere apps zijn te vinden op https://www.simplemobiletools.com
Filtruj multimedia
@@ -38,8 +39,8 @@
FilmyGIFyObrazy RAW
- SVGs
- Nie znalazłem multimediów z wybranymi filtrami.
+ Obrazy SVG
+ Nie znaleziono multimediów zgodnych z zastosowanymi filtrami.Zmień filtry
@@ -87,8 +88,8 @@
PrzewróćPrzewróć w poziomiePrzewróć w pionie
- Edytuj w:
- Wolne
+ Wolne
+ InneTapeta
@@ -115,6 +116,7 @@
ZapętlajPokaz slajdów zakończonyNie znalazłem multimediów do pokazu slajdów
+ Używaj animacji przejściaZmień typ widoku
@@ -131,13 +133,19 @@
TypuRozszerzenia
+
+ Folder wyświetlany na widżecie:
+ Pokaż nazwę folderu
+
Odtwarzaj filmy automatycznie
+ Pamiętaj ostatni moment odtwarzania filmówPokazuj / ukrywaj nazwy plikówZapętlaj odtwarzanie filmówAnimowane miniatury GIFówMaksymalna jasność podczas wyświetlania multimediówPrzycinaj miniatury do kwadratów
+ Pokazuj czas trwania filmówObracaj pełnoekranowe multimedia wedługUstawień systemowychOrientacji urządzenia
@@ -149,7 +157,6 @@
Zezwalaj na kontrolowanie jasności zdjęcia pionowymi gestamiZezwalaj na kontrolowanie jasności i głośności filmów pionowymi gestamiPokazuj liczbę elementów w folderach w głównym widoku
- Zamień funkcję udostępniania na obracanie w menu pełnoekranowymDodatkowe szczegóły przy podglądzie pełnoekranowymZarządzaj dodatkowymi szczegółamiZezwalaj na powiększanie jednym palcem w widoku pełnoekranowym
@@ -159,6 +166,13 @@
Dodatkowe sprawdzenie w celu uniknięcia pokazywania niewłaściwych plikówPokazuj niektóre przyciski akcji na dole ekranuPokazuj kosz w widoku folderów
+ Duże powiększanie obrazów
+ Pokazuj obrazy w najwyższej możliwej rozdzielczości
+ Pokazuj kosz jako ostatni element na głównym ekranie
+ Zezwalaj na zamykanie pełnoekranowego widoku gestem pociągnięcia w dół
+ Zezwalaj na powiększanie 1:1 dwoma podwójnymi dotknięciami
+ Zawsze otwieraj filmy na osobnym ekranie z nowymi poziomymi gestami
+ Pokazuj wcięcie (jeśli dostępne)Miniatury
@@ -179,7 +193,7 @@
Jak sprawić, aby album(y) zawsze pojawiał(y) się na górze?Przytrzymaj album(y) i wybierz ikonę przypięcia w pasku akcji.Jak mogę przwijać filmy?
- Kliknij na napisie z czasem trwania filmu, bądź tym z obecnym momentem filmu.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.Jaka jest różnica między ukryciem, a wykluczeniem folderu?Wykluczenie działa tylko w obrębie niniejszej aplikacji (wszędzie indziej pliki są normalnie widoczne), ukrywanie - w obrębie całego systemu (nie widać ich nigdzie), dodawany jest wtedy do folderu pusty plik \'.nomedia\', który możesz usunąć w dowolnym menedżerze plików.Dlaczego pokazują mi się foldery z okładkami do piosenek i tym podobne rzeczy?
@@ -188,26 +202,46 @@
Wejdź do ustawień aplikacji i w sekcji z dołączonymi folderami dodaj tenże folder do listy.Co jeśli chcę widzieć tylko wybrane foldery?Przejdź do sekcji z wykluczonymi folderami w ustawieniach aplikacji, dodaj tam folder główny (\"/\"), a następnie dodaj pożądane foldery w sekcji z dołączonymi folderami.
- Zdjęcia w widoku pełnoekranowym mają dziwne artefakty. Jak mogę to naprawić?
- W ustawieniach aplikacji włącz opcję \'Zamieniaj powiększalne obrazy na te o lepszej jakości\'. Poprawi ona jakość zdjęć, jednak przy bardzo dużych powiększeniach mogą się stać one zbyt rozmazane.Czy mogę w tej aplikacji przycinać obrazy?Tak, możesz to zrobić w edytorze, przeciągając krawędzie obrazu. Edytor otworzysz przytrzymując miniaturę obrazu i wybierając opcję \'Edytuj\', bądź wybierając tą samą opcję w menu pełnoekranowym.Czy mogę jakoś grupować miniatury plików?Tak. Użyj opcji \'Grupuj według\', gdy jesteś w widoku miniatur. Grupować je możesz według wielu kryteriów, włącznie z datą ich utworzenia. Ponadto, jeśli użyjesz opcji \'Pokazuj całą zawartość folderów\', możesz je także grupować według folderów.Sortowanie według daty utworzenia nie działa poprawnie. Dlaczego tak się dzieje i jak mogę to naprawić?Dzieje się tak, gdyż prawdopodobnie pliki zostały skądś do urządzenia skopiowane. Naprawić to można wybierając miniatury plików, a następnie opcję \'Napraw datę utworzenia\'.
+ Na obrazach widzę wyraźne zmiany w kolorach. Jak mogę to naprawić?
+ Obecne rozwiązanie służące wyświetlaniu obrazów działa jak powinno w większości w przypadków. Jeśli jednak tak nie jest, pomocna może okazać się opcja \'Pokazuj obrazy w najwyższej możliwej jakości\' w sekcji \'Duże powiększanie obrazów\'.
+ Mam ukryte pliki i / lub foldery. Jak mogę zobaczyć?
+ Możesz to zrobić albo wybierając opcję \'Tymczasowo pokaż ukryte multimedia\' w menu na ekranie głównym, lub \'Pokazuj ukryte elementy\' w ustawieniach. Foldery są ukrywane poprzez dodanie do nich pustego, ukrytego pliku \'.nomedia\'. Usunąć go możesz dowolnym menedżerem plików.
- Prosta galeria bez reklam do przeglądania obrazów i filmów.
+ Galeria pozwalająca na zarządzanie Twoimi plikami bez reklam, szanująca Twoją prywatność.
- Prosta aplikacja galerii do oglądania obrazów i filmów. Pliki mogą być sortowane według daty, rozmiaru i nazwy, zarówno w porządku rosnącym, jak i malejącym. W zależności od wielkości ekranu, wyświetlane mogą być w wielu kolumnach. Liczbę kolumn można zmieniać za pomocą gestów, a zdjęcia mogą być powiększane, przycinane, obracane lub ustawiane jako tapeta bezpośrednio z poziomu aplikacji. Jej kolorystykę można dowolnie modyfikować.
+ Wysoce konfigurowalna galeria obsługująca wiele formatów obrazów i filmów, w tym SVG, RAW oraz multimedia panoramiczne.
+
+ Jest otwartoźródłowa, nie zawiera reklam i nie potrzebuje masy uprawnień.
- Uprawnienie od odcisków palców potrzebne jest w celu blokowania widoczności elementów, bądź też całej aplikacji.
+ Oto lista wartych wspomnienia funkcji:
+ 1. Wyszukiwanie
+ 2. Pokazy slajdów
+ 3. Wsparcie dla wcięć w ekranach
+ 4. Przypinanie folderów
+ 5. Filtrowanie plików według typu
+ 6. Kosz dla łatwego odzyskiwania plików
+ 7. Blokowanie orientacji ekranu w widoku pełnoekranowym
+ 8. Oznaczanie plików jako ulubione dla łatwiejszego do nich dostępu
+ 9. Szybkie zamykanie pełnoekranowego widoku gestem pociągnięcia w dół
+ 10. Edytor do szybkich modyfikacji i poprawek
+ 11. Ochrona plików i/lub całej aplikacji hasłem
+ 12. Zmiana ilości kolumn w widoku miniatur gestami lub w menu
+ 13. Konfigurowalne przyciski akcji w widoku pełnoekranowym
+ 14. Rozszerzone informacje o multimediach w widoku pełnoekranowym
+ 15. Wiele sposobów sortowania i grupowania plików i folderów, rosnąco i malejąco
+ 16. Ukrywanie folderów (wszędzie) i ich wykluczanie (tylko w obrębie tej aplikacji)
- Nie zawiera żadnych reklam, nie potrzebuje wielu uprawnień i jest w pełni otwartoźródłowa.
+ Uprawnienie odnośnie odcisków palców potrzebne jest do blokowania widoczności plików i folderów, do ochrony przed ich usunięciem i do blokowania dostępu do aplikacji.
- Jest ona tylko częścią naszej kolekcji prostych narzędzi. Ta, jak i pozostałe, dostępne są na stronie https://www.simplemobiletools.com
+ Aplikacja ta jest tylko częścią serii. Pozostałe znajdziesz tutaj: https://www.simplemobiletools.com
Filtrar mídia
@@ -87,8 +88,8 @@
InverterHorizontalmenteVerticalmente
- Editar com
- Free
+ Free
+ OtherSimple Wallpaper
@@ -115,6 +116,7 @@
Apresentação em cicloFim da apresentaçãoNenhuma mídia encontrada para a apresentação
+ Use crossfade animationsAlterar modo de visualização
@@ -131,13 +133,19 @@
File typeExtension
+
+ Folder shown on the widget:
+ Show folder name
+
Reproduzir vídeos automaticamente
+ Remember last video playback positionMostrar/ocultar nome do arquivoReproduzir vídeos em cicloAnimação de GIFs nas miniaturasBrilho máximo ao visualizar mídiaRecortar miniaturas em quadrados
+ Show video durationsCritério para rotação de telaPadrão do sistemaSensor do aparelho
@@ -149,7 +157,6 @@
Permitir controle do brilho com gestos na verticalPermitir controle do volume e brilho com gestos na verticalMostrar quantidade de arquivos das pastas
- Substituir botão "Compartilhar" por "Rotação de tela" quando em tela cheiaExibir detalhes extendidos quando em tela cheiaGerenciar detalhes extendidosPermitir zoom com um dedo quando em exibição de tela cheia
@@ -159,6 +166,13 @@
Realizar verificação extra para evitar mostrar arquivos inválidosShow some action buttons at the bottom of the screenShow the Recycle Bin at the folders screen
+ Deep zoomable images
+ Show images in the highest possible quality
+ Show the Recycle Bin as the last item on the main screen
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableMiniaturas
@@ -180,38 +194,56 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- Um aplicativo para visualizar fotos e vídeos.
+ An offline gallery for managing your files without ads, respecting your privacy.
- Uma ferramenta simples para ver fotos e vídeos. Pode organizar os itens por data, tamanho, nome e ampliar as fotografias. As pastas são mostradas em colunas, e a sua exibição dependerá do tamanho da tela, aonde o usuário poderá alterar o número de colunas através de gestos. Você pode renomear, compartilhar, apagar, copiar e mover os arquivos. Também pode recortar, girar e definir as imagens como fundo de tela.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- Também pode ser utilizada para pré-visualizar imagens e vídeos, ou para adicionar como anexos ao e-mail, entre outros. É perfeita para a utilização diária.
+ It is open source, contains no ads or unnecessary permissions.
- The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- Não contém anúncios, nem permissões desnecessárias. Disponibiliza um tema escuro, e é totalmente \'open source\'.
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
- Este aplicativo é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em https://www.simplemobiletools.com
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
Filtrar multimédia
@@ -43,7 +44,7 @@
Alterar filtros
- Esta opção oculta uma pasta com a adição de um ficheiro \'.nomedia\' na pasta, e irá ocultar todas as subpastas existentes. Pode ver as pastas com a opção \'Mostrar pastas ocultas\'. Continuar?
+ Esta opção oculta uma pasta com a adição de um ficheiro \'.nomedia\' e irá ocultar todas as sub-pastas existentes. Pode ver as pastas com a opção \'Mostrar pastas ocultas\'. Continuar?ExclusãoPastas excluídasGerir pastas excluídas
@@ -87,8 +88,8 @@
InverterHorizontalmenteVerticalmente
- Editar com
- Livre
+ Livre
+ OutroSimple Wallpaper
@@ -110,11 +111,12 @@
Incluir vídeosIncluir GIFOrdem aleatória
- Usar animações
+ Utilizar animaçõesMover para trásApresentação em cicloApresentação terminadaNão foram encontrados ficheiros para a apresentação
+ Utilizar animação de transiçãoTipo de exibição
@@ -131,13 +133,19 @@
Tipo de ficheiroExtensão
+
+ Pasta mostrada no widget:
+ Mostrar nome da pasta
+
Reproduzir vídeos automaticamente
+ Memorizar posição da reproduçãoMostrar/ocultar nome do ficheiroVídeos em cicloAnimação de GIF nas miniaturasBrilho máximo permitidoRecortar miniaturas em quadrados
+ Mostrar duração do vídeoRodar em ecrã completo porDefinições do sistemaRotação do dispositivo
@@ -149,16 +157,22 @@
Permitir controlo do brilho das fotos com gestos verticaisPermitir controlo do volume e do brilho dos vídeos através de gestos verticaisMostrar número de ficheiros na vista principal
- Substituir a opção Partilhar pela opção Rodar se em ecrã completoMostrar detalhes se em ecrã completoGerir detalhes exibidosPermitir ampliação com um dedo se em ecrã completoPermitir troca imediata de ficheiro ao tocar nas margens do ecrã
- Perimitir ampliação profunda de imagens
+ Permitir ampliação profunda de imagensOcultar detalhes extra se a barra de estado estiver oculta
- Efetuar uma dupla verificação para evitar mostrar os ficheiros inválidos
+ Dupla verificação para evitar mostrar os ficheiros inválidosMostrar alguns botões de ação na base do ecrãMostrar reciclagem no ecrã de pastas
+ Ampliação de imagens
+ Mostrar fotos com a melhor qualidade possível
+ Mostrar a reciclagem como o último item do ecrã principal
+ Sair de ecrã completo com um gesto para baixo
+ Permitir ampliação 1:1 com dois toques
+ Abrir vídeos em ecrã distinto com os novos toques horizontais
+ Mostrar \"notch\", se disponívelMiniaturas
@@ -180,38 +194,56 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- Uma aplicação para ver fotografias e vídeos.
+ Aplicação para gerir os seus ficheiros, sem anúncios e com total privacidade.
- Uma ferramenta simples para ver fotos e vídeos. Pode organizar os itens por data, tamanho, nome e ampliar as fotografias. Os ficheiros multimédia são mostrados em colunas e a sua exibição está dependente do tamanho do ecrã, podendo alterar o número de colunas através de gestos. Pode renomear, partilhar, apagar, copiar e mover as fotografias. Também pode recortar, rodar e definir as imagens como fundo do ecrã a partir da aplicação.
+ Um aplicação capaz de mostrar diversos tipos de imagens e vídeos incluíndo SVG, RAW, fotos panorâmicas e vídeos.
- Também pode ser utilizada para pré-visualizar imagens e vídeos ou para adicionar como anexos ao e-mail, entre outros. É perfeita para a utilização diária.
+ É totalmente open source, não tem anúncios nem requer permissões desnecessárias.
- A permissão Impressão digital é necessária para bloquear a visualização dos itens ocultos ou de toda a aplicação.
+ Algumas das suas funcionalidades:
+ 1. Pesquisa
+ 2. Apresentações
+ 3. Suporte notch
+ 4. Fixação de pastas
+ 5. Filtro de ficheiros por tipo
+ 6. Reciclagem para recuperação de ficheiros
+ 7. Possibilidade de bloquer a orientação da vista
+ 8. Possibilidade de marcar ficheiros como favoritos
+ 9. Possibilidade de sair de ecrã completo com um gesto
+ 10. Editor para modificar imagens e aplicar filtros
+ 11. Possibilidade de proteger ficheiros com palavra-passe
+ 12. Possibilidade de alterar o número de colunas com gestos ou botões de menu
+ 13. Botões de ação personalizados
+ 14. Possibilidade de mostrar detalhes extra em ecrã completo bem como as propriedades dos ficheiros
+ 15. Diversas formas para organizar e agrupar itens
+ 16. Proteção de pastas (com efeito nas outras aplicações) e exclusão de pastas (apenas Simple Gallery)
- Não contém anúncios nem permissões desnecessárias. Disponibiliza um tema escuro e é totalmente \'open source\'.
+ A permissão Impressão digital é necessária para proteger a visibilidade dos itens ocultos, a aplicação e/ou para impedir a eliminação dos ficheiros.
- Esta aplicação é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em https://www.simplemobiletools.com
+ Esta aplicação é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em http://www.simplemobiletools.com
Фильтр медиаИзображенияВидеоGIF
- Изображения RAW
- Изображения SVG
+ RAW
+ SVGПри заданных фильтрах медиафайлы не найденыИзменить фильтры
@@ -64,7 +65,7 @@
Изменить размер
- Изменить выбранное и сохранить
+ Изменить и сохранитьШиринаВысотаСохранять соотношение сторон
@@ -87,8 +88,8 @@
ОтразитьПо горизонталиПо вертикали
- Редактировать в…
- Произвольно
+ Произвольно
+ ДругоеПростые обои
@@ -106,40 +107,47 @@
СлайдшоуИнтервал (в секундах):
- Использовать изображения
- Использовать видео
- Использовать GIF
+ Изображения
+ Видео
+ GIFВ случайном порядкеЭффект затуханияВ обратном порядкеЗациклить
- Слайдшоу завершилось
- Медиафайлов для слайдшоу не было найдено
+ Слайдшоу завершено
+ Медиафайлов для слайдшоу не найдено
+ Эффект плавного переходаВидСеткаСписок
- Группировать вложенные папки
+ Объединять вложенные папкиГруппировать по…
- Не группировать файлы
+ Не группироватьПапкаПоследнее изменение
- Дата
+ Дата съёмкиТип файлаРасширение
+
+ Папка, отображаемая в виджете:
+ Показывать имя папки
+
Воспроизводить видео автоматически
+ Запоминать позицию воспроизведения последнего видеоПереключить отображение имени файлаПовтор видеоАнимировать эскизы GIFМаксимальная яркость при просмотре файлов
- Нарезать миниатюры в квадраты
+ Нарезать миниатюры на квадраты
+ Показывать длительность видеоПоворот экрана при просмотре изображения
- Использовать системные настройки
+ Системные настройкиПри повороте устройстваПо размеру изображенияЧёрные фон и строка состояния при просмотре изображения
@@ -149,16 +157,22 @@
Управление яркостью при просмотре изображения вертикальными жестамиУправление громкостью и яркостью при просмотре видео вертикальными жестамиПоказывать количество файлов в папках
- Заменить \"Поделиться\" на \"Повернуть\" в меню при просмотре изображенияПоказывать сведения о файлеВыбор сведений о файлеМасштабирование одним пальцем при просмотре изображения
- Мгновенное переключение медиафайлов нажатием по краю экрана
+ Переключение медиафайлов нажатием по краю экранаВключить полное масштабированиеСкрывать сведения о файле при скрытой строке состоянияДополнительная проверка, чтобы избежать показа неподдерживаемых файловПоказывать кнопки действий в нижней части экранаПоказывать корзину вместе с папками
+ Масштабирование изображения
+ Показывать изображения с максимально высоким качеством
+ Показывать корзину как последний элемент на главном экране
+ Отключать полноэкранный режим жестом вниз
+ Масштаб 1:1 двумя двойными нажатиями
+ Воспроизводить видео на отдельном экране с управлением позицией горизонтальными жестами
+ Показывать метку при наличииМиниатюры
@@ -180,7 +194,7 @@
Как я могу сделать альбом всегда отображающимся сверху?Вы можете длительным нажатием на желаемый альбом открыть меню действий в нём выбрать пункт \"Закрепить\". Можно закрепить несколько альбомов (папок); прикреплённые элементы будут отсортированы по методу сортировки по умолчанию.Как ускорить перемотку видео?
- Вы можете нажать на цифры текущего положения или максимальной длительности видео рядом с панелью поиска, что приведёт к перемещению позиции воспроизведения либо назад, либо вперёд.
+ Вы можете либо перемещать пальцем по горизонтали над видеоплеером, либо нажать на цифры текущего положения или максимальной длительности видео рядом с панелью поиска, что приведёт к перемещению позиции воспроизведения назад или вперёд.В чём разница между скрытием и исключением папки?Исключение запрещает отображение папки только в Simple Gallery, в то время как скрытие работает системно и скрывает папку из других галерей. Это достигается путём создания пустого файла \".nomedia\" в данной папке, который впоследствии можно удалить любым файловым менеджером.Почему отображаются папки с музыкальными обложками?
@@ -190,28 +204,46 @@
Что делать, если я хочу видеть только несколько конкретных папок?Добавление папки во включённые не исключает автоматически остальные. Что вы можете сделать это через \"Настройки\" -> \"Управление исключёнными папками\". Исключите корневую папку \"/\", затем добавьте нужные папки в \"Настройки\" -> \"Управление включёнными папками\".
Это сделает видимыми только выбранные папки, так как исключение и включение являются рекурсивными, и если папка исключена и включена, то она будет отображаться.
- Полноэкранные изображения имеют странные артефакты, можно как-то улучшить качество?
- Да, в настройках есть переключатель \"Заменять масштабируемые изображения высококачественными\", использовуйте его. Это улучшит качество изображений, но они будут размыты, если вы попытаетесь сильно увеличить масштаб отображения.Можно ли обрезать изображения с помощью данного приложения?Да, вы можете обрезать изображения в редакторе, перетаскивая за углы. К редактированию можно перейти, выбрав соответсвующий пункт в меню, открывающемуся длительным нажатием на миниатюру или изображение в полноэкранном режиме.Могу ли я как-то сгруппировать миниатюры медиафайлов?Конечно, просто используйте пункт меню \"Группировать по…\" во время просмотра миниатюр. Вы можете группировать файлы по нескольким критериям, включая дату съёмки. Если вы используете функцию \"Отобразить все медиафайлы\", то также можете группировать их по папкам.Сортировка по дате, похоже, не работает должным образом. Как это можно исправить?Скорее всего, проблема вызвано тем, что файлы были откуда-то скопированы. Это можно исправить, выделив миниатюры файлов и выбрав \"Исправить дату\".
+ Я вижу какие-то цветовые полосы на изображениях. Как я могу улучшить качество?
+ Используемый в настоящее время метод вывода изображений работает отлично в подавляющем большинстве случаев, но если вы хотите получить ещё более высокое качество изображения, можете включить \"Показывать изображения с максимально высоким качеством\" в настройках приложения в разделе \"Масштабируемые изображения\".
+ Я скрыл файл/папку. Как я могу его увидеть его снова?
+ Вы можете либо нажать кнопку \"Временно показать скрытые элементы\" на главном экране, либо переключить \"Показывать скрытые папки\" в настройках приложения, чтобы его увидеть. Если вы хотите, чтобы скрытый элемент отображался, используйте длительное нажатие и выберите \"Показать\". Папки скрываются добавлением в них файла \".nomedia\", который можно удалить любым файловым менеджером.
- Галерея для просмотра изображений и видео. Без рекламы.
+ Автономная галерея для управления файлами. Конфиденциальная. Без рекламы.
- Простое приложение для просмотра изображений и видеозаписей. Отображаемые файлы могут быть отсортированы как по возрастанию, так и по убыванию даты, размера или имени. Фотографии можно масштабировать. В зависимости от размера экрана, медиафайлы располагаются в несколько столбцов, можно изменять число столбцов щипком двумя пальцами. Можно переименовывать, удалять, копировать, перемещать и делиться файлами из галереи. В приложении есть возможность обрезать и поворачивать изображения или устанавливать их в качестве обоев.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- Галерея идеальна для повседневных задач (предпросмотр фото/видео, добавление вложений в почтовых клиентах и т.д.).
+ It is open source, contains no ads or unnecessary permissions.
- Разрешение \"Отпечаток пальца\" необходимо для блокировки видимости скрытых объектов или всего приложения.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- Это приложение не будет показывать рекламу или запрашивать ненужные разрешения. У него полностью открытый исходный код и настраиваемые цвета оформления.
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
- Simple Gallery — это приложение из серии Simple Mobile Tools. Остальные приложения из этой серии можно найти здесь: https://www.simplemobiletools.com
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
Filter médií
@@ -87,8 +88,8 @@
PreklopiťPreklopiť vodorovnePreklopiť zvisle
- Upraviť s
- Voľný
+ Voľný
+ InýJednoduchá tapeta
@@ -115,6 +116,7 @@
Automaticky reštartovať prezentáciuPrezentácia skončilaPre prezentáciu sa nenašli žiadne vhodné súbory
+ Použiť prelínacie animácieZmeniť typ zobrazenia
@@ -131,25 +133,30 @@
Typu súboruPrípony
+
+ Priečinok zobrazený vo widgete:
+ Zobraziť názov priečinka
+
Spúšťať videá automaticky
+ Zapamätať si pozíciu posledného prehraného videaPrepnúť viditeľnosť názvov súborovAutomaticky reštartovať videáAnimovať GIF súbory pri náhľadeMaximálny jas pri prezeraní médiíOrezať náhľady na štvorce
+ Zobraziť dĺžku videíOtáčať obrazovku podľa Rotate fullscreen media bySystémového nastaveniaOtočenia zariadeniaPomeru strán
- Čierne pozadie a stavová lišta pri médiách na celú obrazovku
+ Čierne pozadie pri médiách na celú obrazovkuPrehliadať miniatúry vodorovneAutomaticky skrývať systémové lišty pri celoobrazovkových médiáchOdstrániť prázdne priečinky po vymazaní ich obsahu
- Allow controlling photo brightness with vertical gestures
+ Povoliť ovládanie jasu vertikálnymi ťahmiPovoliť ovládanie hlasitosti a jasu videí vertikálnymi ťahmiZobraziť počet médií v priečinku na hlavnej obrazovke
- Nahradiť Zdieľanie s Otočením v celoobrazovkovom menuZobraziť rozšírené vlastnosti ponad celoobrazovkové médiáSpravovať rozšírené vlastnostiPovoliť približovanie jedným prstom v celoobrazovkovom režime
@@ -159,6 +166,13 @@
Predísť zobrazovaniu neplatných súborov dodatočnou kontrolouZobraziť niektoré akčné tlačidlá na spodku obrazovkyZobraziť odpadkový kôš na obrazovke s priečinkami
+ Hlboko priblížiteľné obrázky
+ Zobrazovať obrázky v najlepšej možnej kvalite
+ Zobraziť odpadkový kôš ako poslednú položku na hlavnej obrazovke
+ Povoliť zatváranie celoobrazovkového režimu potiahnutím prsta dole
+ Povoliť 1:1 priblíženie dvojnásobným dvojklikom
+ Vždy otvárať videá na vlastnej obrazovke s novými vodorovnými gestami
+ Zobraziť výrez obrazovky, ak je dostupnýNáhľady
@@ -180,36 +194,54 @@
Ako môžem dosiahnuť, aby bol daný album stále zobrazený prvý?Môžete označiť daný priečinok dlhým podržaním a zvoliť tlačidlo s obrázkom pripinačky, to ho pripne na vrch. Môžete pripnúť aj viacero priečinkov, budú zoradené podľa zvoleného radenia.Ako viem posunúť video vpred?
- Môžete kliknúť na texty súčasnej, alebo maximálnej dĺžky videa, ktoré sú vedľa indikátora súčasného progresu. To posunie video buď vpred, alebo vzad.
+ Môžete to dosiahnuť vodorovným potiahnutím prsta cez video prehrávač, alebo kliknúť na texty súčasnej, alebo maximálnej dĺžky videa, ktoré sú vedľa indikátora súčasného progresu. To posunie video buď vpred, alebo vzad.Aký je rozdiel medzi Skrytím a Vylúčením priečinka?Kým vylúčenie predíde zobrazeniu priečinka iba vrámci Jednoduchej Galérie, skrytie ho ukryje vrámci celého systému, teda to ovplyvní aj ostatné galérie. Skrytie funguje pomocou vytvorenia prázdneho \".nomedia\" súboru v danom priečinku, ktorý viete vymazať aj nejakým správcom súborov.Prečo sa mi zobrazujú priečinky s obalmi hudobných albumov, alebo nálepkami?Môže sa stať, že sa vám zobrazia aj nezvyčajné priečinky. Viete ich ale jednoducho ukryť pomocou ich zvolenia dlhým podržaním a zvolením Vylúčiť. Ak na nasledovnom dialógu zvolíte vylúčenie rodičovského priečinka, pravdepodobne budú vylúčené aj ostatné, podobné priečinky.
- Priečinok s fotkami sa mi nezobrazuje, čo môžem urobiť?
+ Priečinok s fotkami sa mi nezobrazuje, alebo priečinok nezobrazuje všetky položky. Čo s tým môžem urobiť?Môže to mať viacero dôvodov, riešenie je ale jednoduché. Choďte do Nastavenia -> Spravovať pridané priečinky, zvoľte Plus a zvoľte vytúžený priečinok.Čo v prípade, ak chcem mať zobrazených iba pár priečinkov?Pridanie priečinka medzi Pridané Priečinky automaticky nevylúči ostatné. Môžete ale ísť do Nastavenia -> Spravovať vylúčené priečinky a zvoliť Koreňový priečinok \"/\", následne pridať žiadané priečinky v Nastavenia -> Spravovať pridané priečinky.
To spôsobí, že budú zobrazené iba vyžiadané priečinky, keďže aj vylúčenie, aj pridanie fungujú rekurzívne a ak je priečinok vylúčený, aj pridaný, bude viditeľný.
- Fotky na celú obrazovku majú zhoršenú kvalitu, viem ju nejak zlepšiť?
- Áno, v nastaveniach je prepínač s textom \"Nahradiť hlboko priblížiteľné obrázky s obrázkami s lepšou kvalitou\", môžete ho skúsiť. Spôsobí to vyššiu kvalitu obrázkov, po priblížení sa ale budú rozmazávať oveľa skôr.Dá sa s touto aplikáciou orezať obrázky?Áno, orezanie je možné v editore, potiahnutím rohov obrázkov. Do editoru sa môžete dostať buď dlhým podržaním náhľadu obrázku a zvolením menu položky Upraviť, alebo zvolením Upraviť pri celoobrazovkovom režime.Viem nejakým spôsobom zoskupiť náhľady súborov?Áno, použitím funkcie \"Zoskupiť podľa\" na menu obrazovky s náhľadmi. Zoskupenie je možné na základe rozličných kritérií vrátane Dátumu vytvorenia. Ak použijete funkciu \"Zobraziť obsah všetkých priečinkov\", viete ich zoskupiť aj podľa priečinkov.Radenie podľa dátumu vytvorenia nefunguje správne, ako ho viem opraviť?Je to pravdepodobne spôsobené kopírovaním súborov. Viete to opraviť označením jednotlivých náhľadov súborov a zvoliť \"Opraviť dátum vytvorenia\".
+ Na obrázkoch vidno nejaké farebné pásy. Ako viem zlepšiť kvalitu obrázkov?
+ Súčasné riešenie funguje správne v drvivej väčšine prípadov, ak ale chcete zobraziť obrázky v lepšej kvalite, môžete povoliť možnosť \"Zobraziť obrázky v najlepšej možnej kvalite\" v nastaveniach aplikácie, v sekcií \"Hlboko priblížiteľné obrázky\".
+ Skryl som súbor/priečinok, ako ho viem odkryť?
+ Môžete buď použiť menu tlačidlo \"Dočasne zobraziť skryté položky\" na hlavnej obrazovke, alebo v nastaveniach aplikácie zapnúť možnosť \"Zobraziť skryté položky\", tým sa skryté položky zobrazia. Ak ich chcete odkryť, stačí ich dlho podržať a zvoliť možnosť \"Odkryť\". Priečinky sú skrývané pridaním skrytého súboru \".nomedia\", ten viete vymazať aj ľubovoľným správcom súborov.
- Galéria na prezeranie obrázkov a videí bez reklám.
+ Offline galéria na správu vašich súborov, rešpektujúca vaše súkromie.
- Jednoduchá nástroj použiteľný na prezeranie obrázkov a videí. Položky môžu byť zoradené podľa dátumu, veľkosti, názvu oboma smermi, obrázky je možné aj priblížiť. Položky sú zobrazované vo viacerých stĺpcoch v závislosti od veľkosti displeja, počet stĺpcov je možné meniť pomocou gesta prstami. Súbory môžete premenovať, zdieľať, mazať, kopírovať, premiestňovať. Obrázky môžete orezať, otočiť, alebo nastaviť ako tapeta priamo v aplikácií.
+ Nastaviteľná galéria na zobrazovanie množstva rozličných druhov obrázkov a videí, vrátane SVG, RAW súborov, panoramatických fotiek a videí.
- Galéria je tiež poskytovaná pre použitie treťou stranou pre prehliadanie fotiek a videí, pridávanie príloh v emailových klientoch. Je perfektná na každodenné použitie.
+ Je open source, neobsahuje reklamy, ani nepotrebné oprávnenia.
- Prístup ku odtlačkom prstov je potrebný pre uzamykanie skrytých položiek, alebo celej apky.
+ Tu je niekoľko funkcií hodných spomenutia:
+ 1. Vyhľadávanie
+ 2. Slideshow
+ 3. Podpora pre zárez v displeji
+ 4. Pripínanie priečinkov na vrch
+ 5. Filtrovanie médií podľa typu
+ 6. Odpadkový kôš pre jednoduchú obnovu súborov
+ 7. Uzamykanie orientácie celoobrazovkového režimu
+ 8. Označovanie obľúbených položiek pre jednoduchý prístup
+ 9. Rýchle ukončovanie celoobrazovkového režimu pomocou potiahnutia prstom dole
+ 10. Editor na úpravu obrázkov a aplikáciu filtrov
+ 11. Ochrana heslom pre zobrazovanie skrytých súborov, alebo celej aplikácie
+ 12. Zmena počtu stĺpcov s náhľadmi buď gestúrami, alebo pomocou menu tlačidiel
+ 13. Nastaviteľné spodné akcie na celoobrazovkovom režime pre rýchly prístup
+ 14. Zobrazenie nastaviteľných rozšírených vlastností ponad celoobrazovkové médiá
+ 15. Niekoľko rozličných spôsobov radenia a zoskupovania položiek vzostupne, alebo zostupne
+ 16. Ukrývanie priečinkov (ovplyvňuje aj iné aplikácie), alebo ich vylúčenie (ovplyvní iba Jednoduchú galériu)
- Neobsahuje žiadne reklamy a nepotrebné oprávnenia. Je opensource, poskytuje možnosť zmeny farieb.
+ Prístup k odtlačkom prstov je potrebný pre ochranu zobrazenia skrytých položiek, celej aplikácie, alebo ochranu súborov pred ich odstránením.
Táto aplikácia je iba jednou zo skupiny aplikácií. Ostatné viete nájsť na https://www.simplemobiletools.com
diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml
new file mode 100644
index 000000000..d30849de5
--- /dev/null
+++ b/app/src/main/res/values-sl/strings.xml
@@ -0,0 +1,252 @@
+
+
+ Simple Gallery
+ Galerija
+ Uredi
+ Zaženi fotoaparat
+ (skrito)
+ (izključeno)
+ Pripni mapo
+ Odpni mapo
+ Pripni na vrh
+ Prikaži vso vsebino mape
+ Vse mape
+ Preklopi na pogled map
+ Druga mapa
+ Prikaži na zemljevidu
+ Neznana lokacija
+ Povečaj število stolpcev
+ Zmanjšaj število stolpcev
+ Spremeni naslovno fotografijo
+ Izberi fotografijo
+ Uporabi privzeto
+ Glasnost
+ Svetlost
+ Zakleni usmerjenost
+ Odkleni usmerjenost
+ Spremeni usmerjenost
+ Vsili pokončen format
+ Vsili ležeč format
+ Uporabi privzeto usmerjenost
+ Popravi datum posnetka
+ Popravljam…
+ Datumi uspešno popravljeni
+ Share a resized version
+
+
+ Filtriranje datotek
+ Slike
+ Videoposnetki
+ GIFi
+ RAW slike
+ SVGji
+ Na podlagi izbranih filtrov ne najdem nobenih medijskih datotek.
+ Spremeni filtre
+
+
+ Ta funkcija skrije mapo tako, da vanjo doda \'.nomedia\' datoteko, kar skrije tudi vse podmape. Ponovno jih lahko prikažete z uporabo možnosti \'Prikaži skrite elemente\' v Nastavitvah. Nadaljujem?
+ Izključi
+ Izključene mape
+ Urejaj izključene mape
+ Izbira bo skupaj s podmapami izključena zgolj iz te Galerije. Izključene mape lahko urejate v Nastavitvah.
+ Namesto tega izključim nadredno mapo?
+ Izključevanje map jih bo skupaj s podmapami skrilo zgolj v tej Galeriji, v ostalih aplikacijah bodo normalno vidne.\n\nČe jih želite skriti tudi v drugih aplikacijah, uporabite funkcijo Skrij.
+ Odstrani vse
+ Odstranim vse mape iz seznama izključenih? Mape ne bodo izbrisane.
+ Skrite mape
+ Urejaj skrite mape
+ Kot kaže skrite mape z \".nomedia\" datoteko ne obstajajo.
+
+
+ Vključene mape
+ Urejaj vključene mape
+ Dodaj mapo
+ Če imate mape, ki vsebujejo medijske datoteke, ki jih aplikacija ni prepoznala, jih lahko ročno dodate tukaj.\n\nDodajanje novih elementov ne bo izključilo drugih.
+
+
+ Spremeni velikost
+ Spremeni velikost izbora in shrani
+ Širina
+ Višina
+ Obdrži razmerje stranic
+ Vnesite veljavno ločljivost
+
+
+ Urejevalnik
+ Shrani
+ Zavrti
+ Pot
+ Napačna pot
+ Urejanje slike ni uspelo
+ Uredi sliko z:
+ Ne najdem urejevalnika slik
+ Neznana lokacija datoteke
+ Ne morem prepisati izvorne datoteke
+ Zavrti levo
+ Zavrti desno
+ Zavrti za 180º
+ Zrcaljenje
+ Zrcali horizontalno
+ Zrcali vertikalno
+ Prosto
+ Drugo
+
+
+ Simple ozadje
+ Nastavi kot ozadje
+ Nastavljanje ozadja ni uspelo
+ Nastavi kot ozadje z:
+ Nastavljam ozadje…
+ Ozadje uspešno nastavljeno
+ Pokončno razmerje stranic
+ Ležeče razmerje stranic
+ Domači zaslon
+ Zaklenjeni zaslon
+ Domači in zaklenjeni zaslon
+
+
+ Diaprojekcija
+ Interval (sekund):
+ Vključi fotografije
+ Vključi videoposnetke
+ Vključi GIFe
+ Naključni vrstni red
+ Uporabi zameglitev animacij
+ Premik nazaj
+ Ponavljaj diaprojekcijo
+ Diaprojekcija se je zaključila
+ Ne najdem datotek za diaprojekcijo
+ Uporabi križanje animacij
+
+
+ Spremeni tip pogleda
+ Mreža
+ Seznam
+ Združi neposredne podmape
+
+
+ Združi po
+ Ne združuj datotek
+ Mapa
+ Zadnjič spremenjeno
+ Posneto
+ Tip datoteke
+ Končnica
+
+
+ Mapa uporabljena na pripomočku:
+ Prikaži ime mape
+
+
+ Avtomatično predvajaj videoposnetke
+ Zapomni si zadnji položaj predvajanja
+ Preklopi vidljivost imen datotek
+ Ponavljaj videoposnetke
+ Animiraj GIFe v predogledu
+ Najvišja svetlost pri celozaslonskem predvajanju
+ Obreži predoglede slik v kvadrate
+ Prikaži trajanje posnetkov
+ Zavrti celozaslonski medij za
+ Sistemska nastavitev
+ Obračanje naprave
+ Razmerje stranic
+ Črno ozadje in statusna vrstica pri celozaslonskem mediju
+ Horizontalno pomikanje sličic
+ Avtomatično skrij sistemski UI v celozaslonskem načinu
+ Izbriši prazne mape po brisanju njihove vsebine
+ Dovoli nadzor svetlosti fotografije z vertikalnimi gestami
+ Dovoli nadzor glasnosti in svetlosti videoposnetka z vertikalnimi gestami
+ Pokaži število elementov v glavnem pogledu
+ Prikaži razširjene podrobnosti nad celozaslonskim prikazom
+ Urejaj razširjene podrobnosti
+ Dovoli enoprstno povečavo v celozaslonskem načinu
+ Dovoli takojšnje spremembe medija s klikanjem na robove zaslona
+ Dovoli globoko povečavo slik
+ Skrij razširjene podrobnosti, ko je statusna vrstica skrita
+ Dvojna kontrola za izogibanje prikazovanja napačnih datotek
+ Prikaži določene akcijske gumbe na dnu zaslona
+ Prikaži Koš na zaslonih map
+ Globoko povečljive slike
+ Prikaži slike v največji možni kvaliteti
+ Prikaži Koš kot zadnji element na glavnem zaslonu
+ Dovoli zapiranje celozaslonskega načina z gesto navzdol
+ Dovoli 1:1 povečavo z dvojnim pritiskom
+ Vedno odpri videoposnetke na ločenem zaslonu z novimi horizontalnimi gestami
+ Show a notch if available
+
+
+ Sličice
+ Celozaslonski prikaz
+ Razširjene podrobnosti
+ Akcije na dnu
+
+
+ Urejaj vidne akcije na dnu
+ Preklopi priljubljene
+ Preklopi vidljivost datotek
+
+
+ Kako naredim Simple galerijo za privzeto aplikacijo na napravi?
+ Najprej morate najti trenutno privzeto aplikacijo za prikaz slik med aplikacijami v nastavitvah naprave, kjer poiščete gumb v smislu \"Odpri kot privzeto\", kliknite nanj in izberite \"Izbriši privzeto\".
+ Ko boste naslednjič želeli odpreti fotografijo ali videoposnetek, bi se moral pokazati izbirnik, v katerem lahko izberete Simple galerijo in jo določite kot privzeto aplikacijo.
+ Aplikacijo sem zaklenil z geslom, ki se ga ne spomnim več. Kaj lahko naredimo?
+ To lahko rešite na 2 načina. Lahko ali ponovno namestite aplikacijo ali pa jo poiščete v nastavitvah in kliknete \"Počisti podatke\". To bo ponastavilo vaše nastavitve, ne bo pa izbrisalo nobenih datotek.
+ Kako nastaviti, da se določen album vedno prikaže na vrhu?
+ Z dolgim pritiskom na album se vam prikaže meni, v katerem je na voljo bucika, s katero pripnete album na željeno mesto. Na ta način lahko pripnete več albumov, ki bodo razvrščeni v skladu s privzetim načinom razvrščanja.
+ Ali lahko hitro predvajam videoposnetke?
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ Kakšna je razlika med skrivanjem in izključevanjem mape?
+ Izključevanje mape jo skrije le v Simple galeriji, medtem ko jo skrivanje skrije tudi v ostalih aplikacijah oz. galerijah. Deluje tako, da kreira prazno \".nomedia\" datoteko v izbrani mapi, katero lahko odstranite tudi s katerimkoli urejevalnikom datotek.
+ Zakaj se v galeriji prikažejo datoteke z naslovnicami glasbenih map ali nalepk?
+ Lahko se zgodi, da se vam prikažejo nepoznani albumi, ki vsebujejo tovrstne datoteke. Lahko jih preprosto skrijete tako, da z dolgim pritiskom na album prikličete meni in tam izberete Izključi. V naslednjem oknu lahko izberete tudi nadrejeno mapo, obstaja pa verjetnost, da bo to preprečilo tudi prikazovanje ostalih povezanih albumov.
+ Mapa s slikami se ne prikaže. Kaj naj naredim?
+ Razlogi so lahko različni, rešitev je vseeno preprosta. Pojdite v Nastavitve in izberite Urejaj vključene mape, izberite plus in poiščite željeno mapo.
+ Kaj, če želim prikazati le nekaj izbranih map?
+ Dodajanje mape med vključene mape avtomatično ne izključi ničesar. Lahko pa naredite sledeče: v Nastavitvah -> Urejaj izključene mape izključite korensko mapo \"/\", željene mape pa dodajte v Nastavitvah -> Urejaj vključene mape.
+ To bo naredilo vidne le vključene mape, saj sta tako vključevanje kot tudi izključevanje rekurzivna, kar pomeni, da je mapa, ki je istočasno izključena in vključena, vidna.
+ Ali lahko obrezujem slike s to aplikacijo?
+ Da, slike lahko obrezujete z vlečenjem kotov slike. Do urejevalnika lahko pridete z dolgim pritiskom na sličico fotografije in izborom opcije Uredi ali izborom funkcije Uredi iz celozaslonskega načina prikaza.
+ Ali lahko združujem sličice medijskih datotek?
+ Seveda, uporabite \"Združi po\" opcijo v meniju, ki je na voljo v pregledu sličic. Datoteke lahko združujete po različnih kriterijih, vključujoč po datumu posnetka. Če uporabite funkcijo \"Prikaži vso vsebino map\" jih lahko združujete tudi po mapah.
+ Kot kaže razvrščanje po datumu posnetka ne deluje pravilno. Kako lahko to popravim?
+ To se najverjetneje zgodi po kopiranju datotek iz nekje drugje. To lahko popravite tako, da izberete sličice in uporabite funkcijo \"Popravi datum posnetka\".
+ Opaziti je slabo barvno povezovanje na slikah. Kako lahko izboljšam kvaliteto?
+ Trenutna rešitev prikazovanja slik deluje dobro v veliki večini primerov, če pa vseeno želite višjo kvaliteto, lahko uporabite funkcijo \"Prikaži slike v najvišji možni kvaliteti\" v Nastavitvah v razdelku \"Globoko povečljive slike\".
+ Skril sem mapo/datoteko. Kako jo lahko zopet prikažem?
+ Lahko uporabite funkcijo \"Začasno prikaži skrite elemente\", ki se nahaja v meniju na glavnem zaslonu ali preklopite \"Prikaži skrite elemente\" v Nastavitvah aplikacije. Če želite element označiti kot viden, z dolgim pritiskom nanj prikličite meni in izberite \"Prikaži\". Skrivanje map deluje tako, da se kreira prazno \".nomedia\" datoteko v izbrani mapi, ki jo lahko odstranite tudi s katerimkoli urejevalnikom datotek.
+
+
+ Galerija brez obvezne internetne povezave za urejanje vaših datotek brez prikazovanja oglasov in z upoštevanjem vaše zasebnosti.
+
+ Visoko prilagodljiva galerija, zmožna prikazovanja različnih tipov fotografij in videoposnetkov, vključno s SVGji, RAWi, panoramskimi fotografijami in videoposnetki.
+
+ Temelji na odprtokodnem principu, ne vsebuje reklam in ne zahteva nepotrebnih dovoljenj.
+
+ Poglejmo nekaj glavnih funkcij vrednih omembe:
+ 1. Iskanje
+ 2. Diaprojekcija
+ 3. Podpora za notch
+ 4. Pripenjanje map na vrh
+ 5. Filtriranje medijskih datotek po tipu
+ 6. Koš za preprosto vračanje izbrisanih datotek
+ 7. Zaklepanje orientacije zaslona v celozaslonskem načinu
+ 8. Označevanje priljubljenih datotek za lažji dostop
+ 9. Hitro zapiranje celozaslonskega načina z gestami
+ 10. Urejevalnih slik za urejanje in apliciranje različnih filtrov
+ 11. Zaščita skritih datotek ali celotne aplikacije z geslom
+ 12. Spreminjanje števila stolpcev z gestami ali gumbi v meniju
+ 13. Nastavljive akcije na dnu zaslona v celozaslonskem načinu
+ 14. Prikaz razširjenih podrobnosti nad prikazom v celozaslonskem načinu
+ 15. Različni načini razvrščanja in združevanja datotek, tako naraščajoče kot tudi padajoče
+ 16. Skrivanje map (vpliva tudi na druge aplikacije), izključevanje map (vpliva zgolj na Simple galerijo)
+
+ Dovoljenje za prstni odtis je potrebno za zaklepanje vidljivosti skritih elementov, celotne aplikacije ali zaščito datotek pred brisanjem.
+
+ Ta aplikacija je le del večje zbirke aplikacij. Ostale lahko najdete na https://www.simplemobiletools.com
+
+
+
+
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 7f9a97d74..ab7104932 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -31,6 +31,7 @@
Korrigera fotodatumKorrigerar…Datumen har korrigerats
+ Dela en version med ändrad storlekFiltrera media
@@ -87,8 +88,8 @@
VändVänd horisontelltVänd vertikalt
- Redigera med
- Fritt
+ Fritt
+ AnnatBakgrund
@@ -115,6 +116,7 @@
Spela upp i en slingaBildspelet har avslutatsIngen media hittades för bildspelet
+ Använd övertoningsanimationerÄndra vy
@@ -131,13 +133,19 @@
FiltypFilnamnstillägg
+
+ Mapp som visas i widgeten:
+ Visa mappnamnet
+
Spela upp videor automatiskt
+ Kom ihåg senaste videouppspelningspositionVisa/dölj filnamnSpela upp videor om och om igenAnimera GIF-bilders miniatyrerMaximal ljusstyrka när media visas i helskärmslägeBeskär miniatyrer till kvadrater
+ Visa videolängderRotera media i helskärmslägeSysteminställningEnhetens rotation
@@ -149,7 +157,6 @@
Tillåt styrning av fotoljusstyrka med vertikala gesterTillåt styrning av videovolym och videoljusstyrka med vertikala gesterVisa antalet mediefiler i varje mapp i huvudvyn
- Ersätt Dela med Rotera i helskärmsmenynVisa utökad information över media i helskärmslägeHantera utökad informationTillåt zoomning med ett finger när media visas i helskärmsläge
@@ -159,6 +166,13 @@
Gör en extra kontroll för att hindra ogiltiga filer från att visasVisa några åtgärdsknappar längst ned på skärmenVisa Papperskorgen i mappvyn
+ Djupt zoombara bilder
+ Visa bilder i högsta möjliga kvalitet
+ Visa Papperskorgen som det sista objektet i huvudvyn
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableMiniatyrer
@@ -180,38 +194,56 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- Ett galleri för att visa foton och videor utan reklam.
+ Ett offlinegalleri för dina filer utan reklam, respekterar din integritet.
- Ett enkelt verktyg för att visa foton och videor. Det kan zooma in på foton och sortera objekt i stigande eller fallande ordning efter datum, storlek eller namn. Mediefiler visas i flera kolumner. Antalet kolumner beror på skärmens storlek och kan ändras med nypgester. Mediefiler kan döpas om, delas, tas bort, kopieras eller flyttas. Bilder kan även beskäras, roteras, vändas eller anges som bakgrundsbilder direkt i appen.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- Galleriet kan även användas av tredjepartsappar för att förhandsgranska bilder och videor, lägga till bilagor i e-postklienter etc. Det är perfekt för daglig användning.
+ It is open source, contains no ads or unnecessary permissions.
- Fingeravtrycksbehörigheten behövs för att låsa hela appen eller synligheten för dolda objekt.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- Innehåller ingen reklam eller onödiga behörigheter. Det har helt öppen källkod och anpassningsbara färger.
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
- Denna app är bara en del av en större serie appar. Du hittar resten av dem på https://www.simplemobiletools.com
+ This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
- Filter media
- Images
- Videos
- GIFs
- RAW images
- SVGs
- No media files have been found with the selected filters.
- Change filters
+ Medyayı filtrele
+ Resimler
+ Videolar
+ GIF\'ler
+ RAW resimler
+ SVG\'ler
+ Seçilen filtrelerle hiçbir medya dosyası bulunamadı.
+ Filtreleri değiştir
- Bu işlev, klasöre\'.medya yok\'dosyası ekleyerek gizler; tüm alt klasörleri de gizler. Bunları Ayarlar\'da\'Gizli klasörleri göster\'seçeneğine basarak görebilirsiniz. Devam et?
- Dışlama
+ Bu işlev, klasöre bir \'.nomedia\' dosyası ekleyerek gizler, tüm alt klasörleri de gizler. Ayarlar\'dan \'Gizli öğeleri göster\' seçeneğini değiştirerek onları görebilirsiniz. Devam edilsin mi?
+ Hariç tutHariç tutulan klasörlerHariç tutulan klasörleri yönet
- Bu, seçimi alt klasörleriyle birlikte yalnızca Basit Galeri\'den hariç tutacaktır. Dışlanan klasörleri Ayarlar\'dan yönetebilirsiniz.
- Bunun yerine ebeveynleri hariç tutun?
- Klasörler hariç tutulduğunda, onları Basit Galeri\'de gizli olan alt klasörleriyle bir araya getirirler, ancak yine de diğer uygulamalarda görünür olurlar.\n\nBunları diğer uygulamalardan gizlemek isterseniz, Gizle işlevini kullanın.
- Hepsini sil
- Hariç tutulanlar listesinden tüm klasörleri kaldırmak mı istiyorsunuz? Bu, klasörler silinmez.
- Hidden folders
- Manage hidden folders
- Seems like you don\'t have any folders hidden with a \".nomedia\" file.
+ Bu, alt klasörleriyle birlikte seçimi yalnızca Basit Galeriden hariç tutacaktır. Hariç tutulan klasörleri Ayarlar\'dan yönetebilirsiniz.
+ Bunun yerine bir üst dizin hariç tutulsun mu?
+ Klasörleri hariç tutmak, onları yalnızca Basit Galeri\'de gizlenen alt klasörleriyle bir araya getirecek, diğer uygulamalarda görünmeye devam edecektir.\n\nBunları diğer uygulamalardan gizlemek istiyorsanız, Gizle işlevini kullanın.
+ Tümünü kaldır
+ Tüm klasörler hariç tutulanlar listesinden kaldırılsın mı? Bu klasörleri silmez.
+ Gizli klasörler
+ Gizli klasörleri yönet
+ \".nomedia\" dosyasıyla gizlenmiş herhangi bir klasörünüz yok gibi görünüyor.Dahil edilen klasörlerDahil edilen klasörleri yönetKlasör ekle
- Ortam içeren, ancak uygulama tarafından tanınmayan bazı klasörleriniz varsa, bunları el ile buradan ekleyebilirsiniz.
+ Medya içeren, ancak uygulama tarafından tanınmayan bazı klasörleriniz varsa, bunları elle ekleyebilirsiniz.\n\nBuraya bazı öğeler eklemek başka bir klasörü hariç tutmaz.Yeniden boyutlandır
@@ -68,150 +69,181 @@
GenişlikYükseklikEn-boy oranını koru
- Lütfen geçerli bir çözüm önerisi girin
+ Lütfen geçerli bir çözünürlük girin
- Editör
+ DüzenleyiciKaydetDöndürYol
- Görüntü yolu geçersiz
+ Geçersiz resim yoluResim düzenleme başarısız
- İle resmi düzenle:
- Resim editörü bulunamadı
+ Resmi şununla düzenle:
+ Resim düzenleyici bulunamadıBilinmeyen dosya konumuKaynak dosyanın üzerine yazılamadıSola döndürSağa döndür
- Ters çevir 180º
+ 180º döndürÇevir
- Yatay
- Dikey
- Edit with
- Free
+ Yatay olarak çevir
+ Dikey olarak çevir
+ Serbest
+ OtherBasit Duvar KağıdıDuvar kağıdı olarak ayarla
- Duvar Kağıdı Olarak Ayarlanılamıyor
- İle duvar kağıdı olarak ayarla:
- Duvar kağıdını ayarlama…
+ Duvar kağıdı olarak ayarlanmadı
+ Şununla duvar kağıdı olarak ayarla:
+ Duvar kağıdı ayarlanıyor…Duvar kağıdı başarıyla ayarlandı
- Portrait aspect ratio
- Landscape aspect ratio
- Home screen
- Lock screen
- Home and lock screen
+ Dikey en boy oranı
+ Yatay en boy oranı
+ Ana ekran
+ Kilit ekranı
+ Ana ve kilit ekranı
- Slideshow
- Interval (seconds):
- Include photos
- Include videos
- Include GIFs
- Random order
- Use fade animations
- Move backwards
- Loop slideshow
- The slideshow ended
- No media for the slideshow have been found
+ Slayt gösterisi
+ Süre (saniye):
+ Fotoğrafları dahil et
+ Videoları dahil et
+ GIF\'leri dahil et
+ Rastgele sırala
+ Soldurma animasyonlarını kullan
+ Geriye doğru git
+ Slayt gösterisini tekrarla
+ Slayt gösterisi sona erdi
+ Slayt gösterisi için medya bulunamadı
+ Use crossfade animations
- Change view type
- Grid
- List
- Group direct subfolders
+ Görünüm türünü değiştir
+ Izgara
+ Liste
+ Doğrudan alt klasörleri gruplandır
- Group by
- Do not group files
- Folder
- Last modified
- Date taken
- File type
- Extension
+ Gruplandırma
+ Dosyaları gruplandırma
+ Klasör
+ Son değiştirilme
+ Çekildiği tarih
+ Dosya türü
+ Uzantı
+
+
+ Folder shown on the widget:
+ Show folder name
- Videoları otomatik olarak oynat
- Dosya adı görünürlüğünü değiştir
- Videolar döngüsü
- Küçük resimlerde GIF\'leri canlandırın
- Ortam görüntülerken azami parlaklık
+ Videoları otomatik oynat
+ Son video oynatma konumunu hatırla
+ Dosya adı görünürlüğünü aç/kapat
+ Videoları tekrarla
+ Küçük resimlerdeki GIF\'leri hareketlendir
+ Tam ekran medya görüntülerken maksimum parlaklıkKüçük resimleri karelere kırp
- Tarafından tam ekran medyayı döndür
- Sistem ayarı
- Cihaz döndürme
- En-boy oranı
- Black background and status bar at fullscreen media
- Scroll thumbnails horizontally
- Automatically hide system UI at fullscreen media
- Delete empty folders after deleting their content
- Allow controlling photo brightness with vertical gestures
- Allow controlling video volume and brightness with vertical gestures
- Show folder media count on the main view
- Replace Share with Rotate at fullscreen menu
- Show extended details over fullscreen media
- Manage extended details
- Allow one finger zoom at fullscreen media
- Allow instantly changing media by clicking on screen sides
- Allow deep zooming images
- Hide extended details when status bar is hidden
- Do an extra check to avoid showing invalid files
- Show some action buttons at the bottom of the screen
- Show the Recycle Bin at the folders screen
+ Show video durations
+ Tam ekran medyayı döndür
+ Sistem ayarları
+ Cihaz yönü
+ En boy oranı
+ Tam ekran medyada siyah arka plan ve durum çubuğu
+ Küçük resimleri yatay olarak kaydır
+ Tam ekran medyada sistem arayüzünü otomatik gizle
+ İçeriğini sildikten sonra boş klasörleri sil
+ Dikey hareketlerle fotoğraf parlaklığının kontrolüne izin ver
+ Video sesini ve parlaklığını dikey hareketlerle kontrol etmeye izin ver
+ Ana görünümde klasör medya sayısını göster
+ Tam ekran medya üzerinde genişletilmiş ayrıntıları göster
+ Genişletilmiş ayrıntıları yönet
+ Tam ekran medyalarda tek parmakla yakınlaştırmaya izin ver
+ Ekran kenarlarına tıklayarak anında medya değiştirmeye izin ver
+ Derin yakınlaştırma resimlerine izin ver
+ Durum çubuğu gizlendiğinde genişletilmiş ayrıntıları gizle
+ Geçersiz dosyaları göstermemek için ekstra kontrol yap
+ Ekranın alt kısmındaki bazı eylem düğmelerini göster
+ Geri dönüşüm kutusu\'nu klasörler ekranında gösterme
+ Derin yakınlaştırılabilir resimler
+ Resimleri mümkün olan en yüksek kalitede göster
+ Geri dönüşüm kutusu\'nu ana ekranda son öğe olarak göster
+ Tam ekran görünümünü aşağı hareketi ile kapatmaya izin ver
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if available
- Thumbnails
- Fullscreen media
- Extended details
- Bottom actions
+ Küçük resimler
+ Tam ekran medya
+ Genişletilmiş ayrıntılar
+ Alt eylemler
- Manage visible bottom actions
- Toggle favorite
- Toggle file visibility
+ Görünür alt eylemleri yönet
+ Favoriyi göster/gizle
+ Dosya görünürlüğünü aç/kapat
- How can I make Simple Gallery the default device gallery?
- First you have to find the currently default gallery in the Apps section of your device settings, look for a button that says something like \"Open by default\", click on it, then select \"Clear defaults\".
- The next time you will try opening an image or video you should see an app picker, where you can select Simple Gallery and make it the default app.
- I locked the app with a password, but I forgot it. What can I do?
- You can solve it in 2 ways. You can either reinstall the app, or find the app in your device settings and select \"Clear data\". It will reset all your settings, it will not remove any media files.
- How can I make an album always appear at the top?
- You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.
- How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
- What is the difference between hiding and excluding a folder?
- Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.
- Why do folders with music cover art or stickers show up?
- It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
- That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.
- What if I want just a few particular folders visible?
- Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
- That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.
- Can I crop images with this app?
- Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.
- Can I somehow group media file thumbnails?
- Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.
- Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?
- It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ Basit Galeri\'yi nasıl varsayılan cihaz galerisi yapabilirim?
+ Önce cihaz ayarlarınızın Uygulamalar bölümünde varsayılan galeriyi bulmanız, \"Varsayılan olarak aç\" gibi bir şey söyleyen bir düğme aramanız, üzerine tıklamanız ve ardından \"Varsayılanları Temizle\"yi seçmeniz gerekir.
+ Gelecek sefer video veya resim açmayı denediğinizde, Basit Galeri\'yi seçip varsayılan uygulama haline getirebileceğiniz bir uygulama seçicisini görmelisiniz.
+ Uygulamayı bir şifre ile kilitledim ama unuttum. Ne yapabilirim?
+ Bunu 2 şekilde çözebilirsiniz. Uygulamayı yeniden yükleyebilir veya uygulamayı cihaz ayarlarınızdan bulabilir ve \"Verileri temizle\"yi seçebilirsiniz. Tüm ayarlarınızı sıfırlar, herhangi bir medya dosyasını kaldırmaz.
+ Bir albümün her zaman en üstte görünmesini nasıl sağlayabilirim?
+ İstediğiniz albüme uzunca basabilir ve eylem menüsündeki Sabitle simgesini seçebilirsiniz. Birden çok klasörü de sabitleyebilirsiniz, sabitlenmiş öğeler varsayılan sıralama yöntemine göre sıralanır.
+ Videoları nasıl hızlıca ileri sarabilirim?
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ Klasörün gizlenmesi ve hariç tutulması arasındaki fark nedir?
+ Hariç tut, klasörü yalnızca Basit Galeri\'de görüntülemeyi engellerken, Gizle sistem genelinde çalışır ve klasörü diğer galerilerden de gizler. Verilen klasörde boş bir \".nomedia\" dosyası oluşturarak çalışır, daha sonra herhangi bir dosya yöneticisi ile kaldırabilirsiniz.
+ Neden albüm resimlerini içeren klasörler görünüyor?
+ Bazı olağandışı albümlerin ortaya çıktığını göreceksiniz. Onları uzun süre basarak ve Hariç tut\'u seçerek kolayca hariç tutabilirsiniz. Bir sonraki iletişim kutusunda, üst klasörü seçebilirsiniz, ancak diğer ilgili albümlerin de gösterilmesini önleyecektir.
+ Resimler içeren bir klasör görünmüyor, ne yapabilirim?
+ Bunun birden fazla nedeni olabilir, ancak bunu çözmek kolaydır. Sadece Ayarlar -> Dahil Edilen Klasörleri Yönet\'e gidin, Artı\'yı seçin ve gerekli klasöre gidin.
+ Sadece birkaç belirli klasörün görünmesini istersem ne olur?
+ Dahil Edilen Klasörler\'e bir klasör eklemek otomatik olarak hiçbir şeyi hariç tutmaz. Yapabilecekleriniz Ayarlar -> Hariç Tutulan Klasörleri Yönet, kök klasörü \"/\" hariç tut, ardından Ayarlar -> Dahil Edilen Klasörleri Yönet.
+ Bu, hem hariç tutulan hem de dahil edilen olmak üzere, yalnızca seçilen klasörleri görünür hale getirir ve bir klasör her ikisi de hariç tutulur ve dahil edilirse, görünür.
+ Bu uygulamayla görüntüleri kırpabilir miyim?
+ Evet, görüntü köşelerini sürükleyerek resimleri düzenleyicide kırpabilirsiniz. Düzenleyiciye, bir resim küçük resmine uzun basıp Düzenle\'yi seçerek veya tam ekran görünümünden Düzenle\'yi seçerek ulaşabilirsiniz.
+ Medya dosyası küçük resimlerini bir şekilde gruplayabilir miyim?
+ Elbette, küçük resimler görünümünde \"Gruplandırma\" menü öğesini kullanın. Çekildiği Tarih de dahil olmak üzere dosyaları birçok kritere göre gruplayabilirsiniz. \"Tüm klasörleri göster\" işlevini kullanırsanız, bunları klasörlere göre de gruplayabilirsiniz.
+ Çekildiği Tarihe Göre Sıralama düzgün çalışmıyor gibi görünüyor, nasıl düzeltebilirim?
+ Büyük olasılıkla bir yerden kopyalanan dosyalardan kaynaklanır. Dosya küçük resimlerini seçip \"Çekilen tarih değerini düzelt\" seçeneğini seçerek düzeltebilirsiniz.
+ Görüntülerde renk şeritleri görüyorum. Kaliteyi nasıl arttırabilirim?
+ Görüntüleri görüntülemek için geçerli çözüm, vakaların büyük çoğunluğunda iyi çalışır, ama daha iyi görüntü kalitesi istiyorsanız, \"Derin yakınlaştırılabilir resimler\" bölümündeki uygulama ayarlarında \"Resimleri mümkün olan en yüksek kalitede göster\" seçeneğini etkinleştirebilirsiniz.
+ Bir dosya/klasör gizledim. Nasıl gösterebilirim?
+ Ana ekranda \"Geçici olarak gizli öğeleri göster\" menü öğesine veya gizli öğeyi görmek için uygulama ayarlarında \"Gizli öğeleri göster\" seçeneğine tıklayabilirsiniz. Göstermek isterseniz, sadece uzun basın ve \"Göster\"i seçin. Klasörler gizlenmiş bir \".nomedia\" dosyası ekleyerek gizlenir, dosyayı herhangi bir dosya yöneticisi ile de silebilirsiniz.
- Fotoğrafları ve videoları reklamsız görüntülemek için kullanılan bir galeri.
+ An offline gallery for managing your files without ads, respecting your privacy.
- Fotoğrafları ve videoları görüntülemek için kullanılabilecek basit bir araç. Öğeler tarihine, boyutuna, adına göre artan veya azalan olarak sıralanabilir, fotoğraflar yakınlaştırılabilir. Medya dosyaları, ekranın boyutuna bağlı olarak birden fazla sütunda gösterilir, sıkıştırma hareketleriyle sütun sayısını değiştirebilirsiniz. Yeniden adlandırılabilir, paylaşılabilir, silinebilir, kopyalanabilir, taşınabilirler. Resimler ayrıca kırpılabilir, döndürülebilir veya doğrudan uygulama\'dan Duvar kağıdı olarak ayarlanabilir.
+ SVG\'ler, RAW\'lar, panoramik fotoğraflar ve videolar dahil olmak üzere birçok farklı resim ve video türünü gösterebilen son derece özelleştirilebilir bir galeri.
- Galeri, görüntüleri/videoları önizlemek, e-posta istemcilerine ek ekler yapmak için üçüncü taraf kullanımı için de önerilir. Bu\'s günlük kullanım için mükemmel.
+ Açık kaynaktır, hiçbir reklam veya gereksiz izinler içermez.
- The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
+ Bahsetmeye değer özelliklerinden bazılarını listeleyelim:
+ 1. Arama
+ 2. Slayt gösterisi
+ 3. Çentik desteği
+ 4. Klasörleri en üste sabitleme
+ 5. Medya dosyalarını türüne göre filtreleme
+ 6. Kolayca dosya kurtarma için geri dönüşüm kutusu
+ 7. Tam ekran görünüm yönünü kilitleme
+ 8. Kolay erişim için favori dosyaları işaretleme
+ 9. Aşağı hareket ile hızlıca tam ekran medyayı kapatma
+ 10. Görüntüleri değiştirmek ve filtreleri uygulamak için bir düzenleyici
+ 11. Gizli öğeleri veya tüm uygulamayı korumak için parola koruması
+ 12. Küçük resim sütun sayısını hareketlerle veya menü düğmelerini kullanarak değiştirme
+ 13. Hızlı erişim için tam ekran görünümünde özelleştirilebilir alt eylemler
+ 14. İstenen dosya özellikleriyle tam ekran medya üzerinden genişletilmiş ayrıntıları gösterme
+ 15. Hem artan hem de azalan öğeleri sıralamak veya gruplandırmak için birkaç farklı yol
+ 16. Klasörleri gizleme (diğer uygulamaları da etkiler), klasörleri hariç tutma (yalnızca Basit Galeri\'yi etkiler)
- Reklam içermeyen veya gereksiz izinler. Tamamen açık kaynaktır, özelleştirilebilir renkler sağlar.
+ Parmak izi izni, gizli öğe görünürlüğünü, tüm uygulamayı kilitlemek veya dosyaların silinmesini önlemek için gereklidir.
- Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanını şu adresten bulabilirsiniz https://www.simplemobiletools.com
+ Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanı http://www.simplemobiletools.com adresinde bulabilirsiniz
+ Фільтр мультимедійних файлів
+ Зображення
+ Відео
+ GIF-зображення
+ RAW-зображення
+ SVG-зображення
+ З вибраними фільтрами мультимедійні файли не знайдено.
+ Змінити фільтри
+
+
+ Ця функція приховує теку шляхом додавання в неї файлу \".nomedia\"; це також приховає і підтеки. Ви можете побачити приховані теки, обравши опцію \'Показати приховані елементи\' в \"Налаштуваннях\". Продовжити?
+ Виключити
+ Виключені теки
+ Керування виключеними теками
+ Ця дія виключить обрані теки з їх підтеками тільки з Simple Gallery. Керувати виключеними теками можна у \"Налаштуваннях\".
+ Виключити батьківську теку натомість?
+ Виключення тек приховає їх разом з їх підтеками тільки у Simple Gallery, але вони все ще будуть видимі у інших додатках.\n\nЯкщо ви хочете приховати їх і від інших додатків, використовуйте функцію \"Приховати\".
+ Видалити все
+ Видалити всі теки зі списку виключених? Це не видалить теки з пристрою.
+ Приховані теки
+ Керування прихованими теками
+ Схоже, у вас немає тек, прихованих за допомогою файлу \".nomedia\".
+
+
+ Включені теки
+ Керування включеними теками
+ Додати теку
+ Якщо у вас є теки з медіафайлами, але вони не були розпізнані додатком, ви можете додати їх тут вручну.\n\nДодавання елементів сюди не виключить будь-яку іншу теку.
+
+
+ Змінити розмір
+ Змінити розмір вибраного і зберегти
+ Ширина
+ Висота
+ Зберігати співвідношення сторін
+ Введіть допустиму роздільну здатність
+
+
+ Редактор
+ Зберегти
+ Обернути
+ Шлях
+ Неприпустимий шлях до зображення
+ Не вдалося редагувати зображення
+ Редагувати зображення за допомогою:
+ Не знайдено редакторів зображень
+ Невідоме розташування файлу
+ Не вдалося перезаписати вихідний файл
+ Обернути ліворуч
+ Обернути праворуч
+ Обернути на 180º
+ Віддзеркалити
+ Віддзеркалити горизонтально
+ Віддзеркалити вертикально
+ Вільне
+ Інше
+
+
+ Simple Wallpaper
+ Встановити як шпалери
+ Не вдалося встановити шпалери
+ Встановити шпалери за допомогою:
+ Встановлення шпалер…
+ Шпалери успішно встановлено
+ Співвідношення сторін в портретній орієнтації
+ Співвідношення сторін в ландшафтній орієнтації
+ Домашній екран
+ Екран блокування
+ Домашній екран і екран блокування
+
+
+ Слайдшоу
+ Інтервал (секунди):
+ Включити фото
+ Включити відео
+ Включити GIF-зображення
+ Випадковий порядок
+ Використовувати анімацію затухання
+ Рухатися назад
+ Зациклити показ слайдів
+ Слайдшоу закінчено
+ Не знайдено медіафайлів для показу у слайдшоу
+ Анімувати перехід між елементами
+
+
+ Змінити тип перегляду
+ Сітка
+ Список
+ Групувати безпосередні підтеки
+
+
+ Групувати за…
+ Не групувати файли
+ текою
+ останньою зміною
+ датою зйомки
+ типом файлу
+ розширенням
+
+
+ Тека, що відображається на віджеті:
+ Показувати ім\'я теки
+
+
+ Відтворювати відео автоматично
+ Запам\'ятовувати місце зупинки перегляду
+ Перемкнути відображення імені файлу
+ Зациклити відео
+ Анімувати ескізи GIF-файлів
+ Максимальна яскравість екрану при повноекранному перегляді медіафайлу
+ Обрізати ескізи у квадрат
+ Показувати тривалість відео
+ При повноекранному перегляді обертати за…
+ системними налаштуваннями
+ поворотом пристрою
+ співвідношенням сторін
+ Чорне тло і рядок стану при повноекранному перегляді
+ Гортати ескізи горизонтально
+ Автоматично приховувати системний інтерфейс при повноекранному перегляді
+ Видаляти порожні теки після видалення їх вмісту
+ Дозволити керування яскравістю фотографій вертикальними жестами
+ Дозволити керування яскравістю та гучністю відео вертикальними жестами
+ Показувати кількість файлів у теці на головному екрані
+ Показувати розширені подробиці при повноекранному перегляді
+ Керування розширеними подробицями
+ Дозволити масштабування одним пальцем при повноекранному перегляді
+ Дозволити миттєво змінювати медіафайл натисканням на сторони екрану
+ Дозволити глибоке масштабування зображень
+ Приховати розширені подробиці, коли рядок стану прихований
+ Робити додаткову перевірку для запобігання відображенню недійсних файлів
+ Показати деякі кнопки дій внизу екрану
+ Показувати \"Кошик\" на головному екрані
+ Глибокомасштабовані зображення
+ Показувати зображення в найвищій можливій якості
+ Показувати \"Кошик\" останнім елементом на головному екрані
+ Дозволити закриття повноекранного перегляду свайпом згори вниз
+ Дозволити масштабування до 1:1 подвійним тапом
+ Завжди відкривати відео на окремому екрані з новими горизонтальними жестами
+ Show a notch if available
+
+
+ Ескізи
+ Повноекранний перегляд
+ Розширені подробиці
+ Кнопки дій внизу екрану
+
+
+ Керування видимими кнопками дій внизу екрану
+ Перемкнути улюблене
+ Перемкнути видимість файлу
+
+
+ Як зробити Simple Gallery галереєю за-замовчуванням?
+ Спочатку необхідно знайти поточну галерею за-замовчуванням в розділі \"Додатки\" налаштувань вашого пристрою. Знайдіть і натисніть на кнопку \"Використовувати за-замовчуванням\" абощо, потім оберіть \"Очистити замовчування\".
+ Наступного разу коли ви намагатиметеся відкрити зображення або відео, ви побачите вікно з вибором додатків для цього. Оберіть Simple Gallery та зробіть його додатком за-замовчуванням.
+ Я заблокував додаток за допомогою паролю і забув його. Що я можу зробити?
+ Ви можете вирішити цю проблему двома способами: перевстановити додаток або знайти його в розділі \"Додатки\" налаштувань вашого пристрою та обрати \"Очистити дані\". Це скине усі ваші налаштування додатка, але не видалить жодного медіафайлу.
+ Як зробити альбом завжди доступним у верхній частині?
+ Ви можете виконати довге натискання на бажаному альбомі і вибрати піктограму \"Закріпити\" у меню дій, що закріпить його вгорі. Ви також можете закріпити декілька тек; закріплені елементи будуть відсортовані за методом сортування за-замовчуванням.
+ Як я можу швидко прокручувати відео?
+ Ви можете або провести пальцем горизонтально під час відтворення відео, або натиснути на текст поточної або максимальної тривалості відео біля прогрес-бару, що прокрутить відео або назад, або вперед.
+ В чому полягає різниця між приховуванням та виключенням теки?
+ \"Виключити\" запобігає відображенню теки тільки в додатку Simple Gallery, в той час як \"Приховати\" працює на системному рівні і приховує теку і в інших галереях також. Це здійснюється шляхом створення порожнього файлу \".nomedia\" в заданій теці, який може бути видалений пізніше будь-яким файловим менеджером.
+ Чому відображаються теки з музичними обкладинками або стікерами?
+ Може так трапитися, що з\'являться деякі незвичні альбоми. Їх можна легко виключити довгим натисканням і вибором \"Виключити\". В наступному діалозі у вас буде можливість обрати бітьківську теку. Швидше за все, це дозволить також запобігти появі інших пов\'язаних альбомів.
+ Тека з зображеннями не відображається, що мені робити?
+ У цього може бути кілька причин, проте вирішення доволі просте. Перейдіть в \"Налаштування\" -> \"Керування виключеними теками\", оберіть \"Плюс\" та перейдіть до потрібної теки.
+ Що робити, якщо я хочу, щоб лише кілька окремих тек були видимими?
+ Додавання теки до \"Включених тек\" автоматично не виключає нічого. Що ви можете зробити, то це перейти в \"Налаштування\" -> \"Керування виключеними теками\", виключити кореневу теку \"/\", потім додати бажані теки через \"Налаштування\" -> \"Керування включеними теками\".
+ Це зробить видимими лише обрані теки. Якщо тека є одночасно і виключеною, і включеною, вона відобразиться, оскільки і виключення, і включення є рекурсивним.
+ Чи можу я обрізати зображення у цьому додатку?
+ Так, обрізати зображення можна у редакторі шляхом перетягування кутів зображення. Відкрити редактор можна або довгим натисканням на ескіз зображення і наступним вибором \"Редагувати\", або вибором \"Редагувати\" при повноекранному перегляді.
+ Чи можу я якимось чином згрупувати ескізи медіафайлів?
+ Звісно, просто скористайтеся пунктом меню \"Групувати за…\", знаходячись на екрані ескізів. Групувати файли можна за кількома критеріями, включаючи дату зйомки. Якщо ви використовуєте функцію \"Показати вміст усіх тек\", ви також можете групувати їх за теками.
+ Сортування за датою зйомки, здається, працює некоректно. Як я можу це виправити?
+ Найбільш вірогідна причина цього - копіювання фалів з іншого місця. Це можна виправити, обравши ескізи файлів і потім - \"Виправити дату зйомки\".
+ Я бачу деякі кольорові нашарування на зображенні. Як я можу покращити якість?
+ Поточне рішення для показу зображень відмінно працює в переважній більшості випадків, але якщо вам потрібна ще краща якість зображень, ви можете увімкнути опцію \"Показувати зображення в найвищій можливій якості\" в розділі \"Глибокомасштабовані зображення\" налаштувань додатку.
+ Я приховав файл / теку. Як я можу відмінити цю дію?
+ Щоб побачити приховані елементи, ви можете або натиснути пункт меню \"Тимчасово показати приховані елементи\" на головному екрані, або перемкнути опцію \"Показати приховані елементи\" в налаштуваннях додатку. Якщо ви більше не хочете приховувати елемент, довго натисніть на нього і оберіть \"Не приховувати\". Теки приховуються шляхом створення прихованого файлу \".nomedia\" в них, тож ви також можете видалити цей файл будь-яким файловим менеджером.
+
+
+
+ Офлайн-галерея для керування файлами: без реклами та з повагою до приватності.
+
+ Тонко налаштовувана галерея, здатна відображати зображення та відео різноманітних типів, включаючи SVG-зображення, RAW-зображення,панорамні фото і відео.
+
+ Джерельний код галереї відкритий, вона не містить реклами та не вимагає зайвих дозволів.
+
+ Ось кілька її переваг, які варто згадати:
+ 1. Пошук
+ 2. Слайдшоу
+ 3. Підтримка закладок (Notch)
+ 4. Закріплення тек вгорі екрану
+ 5. Фільтрування медіафайлів за типом
+ 6. Кошик для легкого відновлення файлів
+ 7. Фіксація повороту екрану при повноекранному перегляді
+ 8. Позначення улюблених файлів для швидкого доступу
+ 9. Швидке закриття повноекранного перегляду жестом згори вниз
+ 10. Редактор для редагування зображень та накладання фільтрів
+ 11. Захист паролем для захисту прихованих елементів або всього додатку
+ 12. Зміна кількості колонок піктограм жестом чи через меню
+ 13. Налаштовувані кнопки швидких дій внизу екрану при повноекранному перегляді
+ 14. Показ детальної інформації з обраними властивостями файлу при повноекранному перегляді
+ 15. Кілька різних способів сортування або групування елементів - як за зростанням, так і за спаданням
+ 16. Приховування тек (і в інших додатках також), виключення тек (тільки в Simple Gallery)
+
+ Дозвіл на доступ до відбитку пальця потрібен для блокування або видимості прихованих елементів, або всього додатку, або для захисту файлів від видалення.
+
+ Цей додаток входить в велику серію додатків. Ви можете знайти їх на https://www.simplemobiletools.com
+
+
+
+
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
deleted file mode 100644
index 7f1ac50b0..000000000
--- a/app/src/main/res/values-v21/styles.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 710d0ef86..4252b5daf 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -5,10 +5,10 @@
编辑打开相机(隐藏)
- (排除)
+ (排除)锁定目录解除锁定目录
- 锁定到顶部
+ 固定到顶部文件视图所有目录目录视图
@@ -24,13 +24,14 @@
亮度锁定方向解锁方向
- Change orientation
- Force portrait
- Force landscape
- Use default orientation
- Fix Date Taken value
- Fixing…
- Dates fixed successfully
+ 改变方向
+ 强制竖屏
+ 强制横屏
+ 默认
+ 修复拍摄日期
+ 正在修复…
+ 日期修复成功
+ 调整图像尺寸并分享要显示的媒体文件
@@ -75,20 +76,20 @@
保存旋转路径
- 无效图片路径
+ 无效的图片路径图片编辑失败编辑方式:未找到可用图片编辑器未知的文件路径
- 不能覆盖源文件
+ 无法覆盖源文件向左旋转向右旋转旋转 180º翻转水平翻转垂直翻转
- 编辑方式
- Free
+ 自由
+ 其他简约壁纸
@@ -115,6 +116,7 @@
循环幻灯片幻灯片结束未发现可用媒体
+ 使用淡入淡出动画更改视图类型
@@ -131,45 +133,57 @@
文件类型扩展
+
+ 要在小部件上显示的文件夹:
+ 显示文件夹名称
+
自动播放
+ 记住上次视频播放位置显示文件名循环播放视频GIF 缩略图浏览时最大亮度裁剪缩略图
+ 显示视频时长全屏方向系统设置设备方向根据长宽比
- 全屏时使用黑色背景和状态栏
+ 全屏时使用黑色背景和状态栏水平滚动缩略图全屏时自动隐藏状态栏删除没有内容的空文件夹使用纵向滑动手势控制照片亮度使用纵向滑动手势控制视频音量和亮度在主界面显示文件夹媒体计数
- 替换全屏时菜单栏的“分享”为“旋转”全屏浏览媒体时显示详细信息要显示的详细信息项目单指缩放通过单击屏幕边缘来切换媒体
- Allow deep zooming images
+ 允许深度放大图像当状态栏隐藏时隐藏扩展详情额外检查以避免显示无效的文件
- 在屏幕底部显示一些操作按钮
- Show the Recycle Bin at the folders screen
+ 显示底栏
+ 在文件夹界面显示回收站
+ 深度放大图像
+ 以最高质量显示图像
+ 在主屏幕界面的最后一项显示回收站
+ 使用下滑手势关闭全屏视图
+ 双击两次后 1:1 放大图像
+ 使用新的水平手势在独立页面播放视频
+ Show a notch if available缩略图全屏显示媒体扩展详情
- Bottom actions
+ 底栏选项
- Manage visible bottom actions
- Toggle favorite
- Toggle file visibility
+ 管理底栏选项
+ 收藏
+ 隐藏文件如何把简约图库设置为设备的默认图库?
@@ -179,7 +193,7 @@
如何让某个相册始终显示在最上面?你可以长按该相册并在操作栏中点击图钉图标,这样 就可以将其固定在顶部了。你也可以固定多个文件夹,固定项目将按照默认排序方法排序。如何快进/快退视频?
- 可以点击底栏进度条两侧的时间文本,或拖动进度条。
+ 只需在视频播放器上左右滑动,或点击底栏进度条两侧的时间文本,即可前进或后退视频。文件夹的隐藏和排除有什么区别?排除功能只是防止其在简约图库中显示,而隐藏功能则使用的是系统的方法,这样做也会在其他图库中隐藏。它的工作原理是在给定的文件夹中创建一个空的.nomedia文件,你可以使用任何文件管理器删除它。为什么会出现音乐艺术家封面或贴纸文件夹?
@@ -188,28 +202,46 @@
原因可能有很多,但解决方法很简单。只需进入设置 -> 管理包含目录,点击+号并选择到所需的文件夹。如果我只想显示几个特定的文件夹,该如何操作?在包含目录中添加文件夹不会自动排除其他的内容。你可以做的:进入设置 -> 管理排除目录,排除根目录\"/\",然后在设置 -> 管理包含目录中添加所需的文件夹。这样就只显示选定的文件夹了,由于排除和包含都是递归的,所以排除并包含的文件夹是会显示的。
- 全屏图像有些奇怪的东西,该如何提高画面质量?
- 在设置中有一个“用质量更好的图像替换可深度缩放的图像”开关,打开它后就可以提高图像的显示质量,但是一旦尝试放大太多次,它们就又会变得模糊不清。可以裁剪图片吗?当然,通过长按图片缩略图并选择编辑,或在全屏视图中选择编辑来打开编辑器。你可以通过拖动图片边角来剪裁图像。我能将媒体文件缩略图分组吗?当然,只需在缩略图视图中使用\"分组依据\"菜单项即可。您可以依据多个条件对文件进行分组,包括拍摄日期。如果您使用了\"显示所有文件夹内容\"功能,则可以按文件夹对它们进行分组。
- Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?
- It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ 按拍摄日期排序似乎有异常,我该如何解决?
+ 很可能是因为图片是从某处复制过来的。您可以长按文件缩略图并选择\"修复拍摄日期\"来修复它。
+ 我看到图像上有一些色带。如何提高提高质量?
+ 目前显示图像的方案在绝大多数情况下都能正常工作,如果您想要更好的图像质量,您可以在设置中启用\"以最高质量显示图像\"。
+ 我隐藏了某个文件/文件夹。如何取消隐藏?
+ 您可以点击主界面上的\"暂时显示隐藏的项目\"选项,或在设置中开启\"显示隐藏的项目\"。 如果你想取消隐藏它,长按它并选择\"取消隐藏\"即可。 我们是通过向文件夹中添加\".nomedia\"文件来隐藏文件夹的,使用文件管理器删除该文件也可以取消隐藏。
- 一个没有广告,用来观看照片及视频的相册。
+ 一个没有广告,尊重隐私,便于管理文件的离线图库。
- 一个观看照片和视频的简单实用工具。项目可以根据日期、大小、名称来递增或递减排序,照片可以缩放。媒体文件根据屏幕的大小排列在多个方格中,您可以使用缩放手势来调整每一列的方格数量。媒体文件可以被重命名、分享、删除、复制以及移动。照片亦可被剪切、旋转或是直接在应用中设为壁纸。
+ 一个高度可定制的图库,支持很多的图像和视频类型,包括SVG,RAW,全景照片和视频。
- 相册亦提供能让第三方应用预览图片/视频、向电子邮件客户端添加附件等的功能。非常适合日常使用。
+ 本应用是开源的,没有广告以及不必要的权限。
- 这个应用需要指纹权限来锁定隐藏的项目或是整个应用。
+ 列举一些值得一提的功能:
+ 1.搜索
+ 2.幻灯片
+ 3.支持留海屏
+ 4.固定文件夹到顶部
+ 5.按类型过滤媒体文件
+ 6.回收站(便于恢复文件)
+ 7.全屏视图方向锁定
+ 8.标记收藏文件以便于访问
+ 9.使用下滑手势关闭全屏视图
+ 10.图片编辑器,可用于修改图像和应用滤镜
+ 11.密码保护,可用于保护应用和隐藏项目
+ 12.可使用手势或菜单按钮来更改缩略图列数
+ 13.可自定义全屏视图中的底栏操作按钮,方便快速
+ 14.在全屏视图下显示文件属性具有的扩展详细信息
+ 15.按照不同的方式对项目进行排序或分组,包括升序和降序
+ 16.隐藏文件夹(会影响其他应用),排除文件夹(仅影响本应用)
- 应用不包含广告与不必要的权限。它是完全开放源代码的,并内置自定义颜色主题。
+ 如需锁定隐藏项目的可见性,或是锁定本应用,或是保护文件不被删除,则需要指纹权限。
- 这个应用只是一系列应用中的一小部份。您可以在 https://www.simplemobiletools.com 找到其余的应用。
+ 这个应用只是一个更大的应用系列的一小部分。您可以在https://www.simplemobiletools.com找到其余的应用。
篩選媒體檔案
@@ -38,7 +39,7 @@
影片GIFRAW圖檔
- SVGs
+ SVG選擇的篩選條件未發現媒體檔案。更改篩選條件
@@ -87,8 +88,8 @@
翻轉水平翻轉垂直翻轉
- 用其他程式編輯
- 自由
+ 自由
+ 其它簡易桌布
@@ -115,6 +116,7 @@
投影片循環投影片結束找不到投影片的媒體檔案
+ 使用淡入淡出動畫改變瀏覽類型
@@ -131,13 +133,19 @@
檔案類型副檔名
+
+ 在小工具顯示資料夾:
+ 顯示資料夾名稱
+
自動播放影片
+ 記住影片上次播放位置顯示檔案名稱影片循環播放縮圖顯示GIF動畫瀏覽時最大亮度縮圖裁剪成正方形
+ 顯示影片長度全螢幕時旋轉方向系統設定方向裝置實際方向
@@ -149,16 +157,22 @@
允許用上下手勢來控制相片的亮度允許用上下手勢來控制影片的音量和亮度主畫面顯示資料夾內的媒體檔案數量
- 將全螢幕選單的分享取代為旋轉全螢幕時顯示詳細資訊管理詳細資訊全螢幕時允許單指縮放允許點擊螢幕邊緣來快速切換媒體檔案
- Allow deep zooming images
+ 允許深度縮放圖片狀態欄隱藏時,同時隱藏詳細資訊進行額外檢查,避免顯示無效的檔案在螢幕底部顯示一些操作按鈕在資料夾畫面顯示回收桶
+ 可深度縮放的圖片
+ 以最高品質顯示圖片
+ 回收桶顯示在主畫面最後一項
+ 允許用下滑手勢來關閉全螢幕檢視
+ 允許用兩次雙擊來1:1縮放
+ 總是用新的水平手勢在獨立畫面開啟影片
+ 如果可以,顯示瀏海螢幕縮圖
@@ -180,7 +194,7 @@
我如何讓某個相冊總是出現在頂端?你可以長按想要的相冊,然後在操作選單中選擇[圖釘]圖示,就會釘選於頂端。你也能釘選多個資料夾,釘選的項目會依預設的排序方法來排序。我如何快轉影片?
- 你可以點擊進度條附近的當前或總時長文字,影片就會快轉或倒轉。
+ 你可以在影片播放器上水平滑動你的手指,或者點擊進度條附近的當前或總時長文字。這會使影片快轉或倒轉。隱藏和排除資料夾,兩者有什麼不同?[排除]只在簡易相簿中避免顯示出來;而[隱藏]則作用於整個系統,資料夾也會被其他相簿隱藏。這是藉由在指定資料夾內建立一個\".nomedia\"空白檔案來進行隱藏,你之後也能用任何檔案管理器移除。為什麼有些音樂專輯封面或貼圖的資料夾會出現?
@@ -190,28 +204,46 @@
如果我只想看到幾個特定的資料夾,怎麼做?在[包含資料夾]內添加資料夾並不會自動排除任何東西。你能做的是到[設定] -> [管理排除資料夾],排除根目錄 \"/\",然後在[設定] -> [管理包含資料夾]添加想要的資料夾。
那樣的話就只有選擇的資料夾可見。因為排除和包含都是遞迴的,如果一個資料夾被排除又被包含,則會顯示出來。
- 全螢幕圖片有雜訊,我有辦法提高品質嗎?
- 可啊,[設定]內有個開關叫做「可深度縮放的圖片用品質更佳的來取代」,你能用用看。這會提高圖片的品質,不過一旦你放大太多就會模糊掉。我可以用這程式裁減圖片嗎?是的,你能夠在編輯器內拉動圖片角落來裁剪圖片。要進入編輯器,你可以長按圖片縮圖然後選擇[編輯],或是在全螢幕檢視下選擇[編輯]。我可以歸類媒體檔案的縮圖嗎?當然,只要在縮圖瀏覽中使用[歸類]選單項目就可以了。你能依多種條件歸類檔案,包含拍照日期。如果你使用了[資料夾內容全部顯示]功能,你還能以資料夾來歸類。依拍照日期排序似乎沒正確運作,我該如何修復?那很可能是由於檔案從某處複製過來所造成的。你可以選擇檔案縮圖,然後選擇\"修復拍照日期數值\"來進行修復。
+ 我在圖片上看到一些色彩條紋。我如何提升品質?
+ 目前顯示圖片的處理方法,在大部分情況下都能正常運行。但如果你想要更好的圖片品質,你可以在程式設定中[可深度縮放的圖片]部分,啟用[以最高品質顯示圖片]。
+ 我隱藏了一個檔案/資料夾。我如何取消隱藏?
+ 你可以在主畫面的選單項按[暫時顯示隱藏的項目],或者在程式設定中切換[顯示隱藏的項目]來看隱藏項目。如果你想要取消隱藏,只要長按然後選擇[取消隱藏]。以添加\".nomedia\"檔案進行隱藏的資料夾,你也可以用任何檔案管理器來刪除這檔案。
- 一個用來瀏覽相片和影片,且沒有廣告的相簿。
+ 一個沒有廣告的離線相簿,用來管理你的檔案,並且尊重您的隱私。
- 一個用來瀏覽相片和影片的簡單工具。可以根據日期、大小、名稱來遞增或遞減排序項目,相片能被縮放。媒體檔案會依畫面大小呈現在數個欄位內,你可以使用縮放手勢來改變欄數。媒體檔案可以重新命名、分享、刪除、複製、移動;圖片還能縮放、旋轉、翻轉,或者直接設為桌布。
-
- 這相簿也支援第三方使用,像是預覽圖片/影片、添加電子信箱附件…等功能,日常使用上相當適合。
+ 一個高自訂性的相簿,能夠顯示許多不同的圖片和影片類型,包含SVGs、RAWs、全景相片和影片。
- 指紋權限用來鎖定隱藏的項目或是整個程式。
+ 它是開源的,而且不包含廣告及非必要的權限。
- 不包含廣告及非必要的權限,而且完全開放原始碼,並提供自訂顏色。
-
- 這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 https://www.simplemobiletools.com
+ 讓我們列出一些值得一提的功能:
+ 1. 搜尋
+ 2. 投影片
+ 3. 支援瀏海螢幕
+ 4. 釘選資料夾在頂端
+ 5. 以類型篩選媒體檔案
+ 6. 輕鬆恢復檔案的回收桶
+ 7. 鎖定全螢幕檢視的方向
+ 8. 標記我的最愛檔案以輕鬆存取
+ 9. 下滑手勢來快速關閉全螢幕媒體
+ 10. 一個編輯器來修改圖片和添加濾鏡
+ 11. 用密碼保護隱藏的項目或整個應用程式
+ 12. 用手勢或選單按鈕來改變縮圖欄數
+ 13. 在全螢幕檢視下,自訂底部操作來快速存取
+ 14. 在全螢幕媒體顯示,要求的檔案屬性其詳細資訊
+ 15. 幾種不同的排序或歸類項目的方式,包含遞增和遞減
+ 16. 隱藏資料夾(也影響其他程式),排除資料夾(只影響簡易相簿)
+
+ 指紋權限用來鎖定隱藏的項目、整個應用程式,或保護檔案不被刪除。
+
+ 這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 https://www.simplemobiletools.com
-
- Added Panorama photo support\n
- Allow forcing portrait/landscape orientation at fullscreen view\n
- Allow customizing visible fullscreen bottom actions in the app settings
+
+ Implemented export/importing for app settings and other preferences, like sorting\n
+ Allow hiding Notch on fullscreen view on Android 9+\n
+ Autosave images zoomed at the fullscreen view
-
- Allow grouping media thumbnails by some criteria\n
- Added a Recycle Bin enabled by default, can be disabled in Settings, or from the Bin itself
+
+ Allow drawing in the editor\n
+ Allow batch rotating images from the thumbnails view (proper thumbnail refreshing might need an app restart)\n
+ Allow sharing images directly from the editor\n
+ Allow zooming GIFs and videos
- Allow selecting Favorite items for easy access
- Allow filtering out RAW images separately
- Added sorting by Date Taken
- Allow customizing the app launcher icon color
- Added toggles for disabling Pull-to-refresh and permanent Delete dialog confirmation skipping
-
- Added a toggle for replacing deep zoomable images with better quality ones\n
- Added a toggle for hiding Extended details when the statusbar is hidden\n
- Added a toggle for switching media files by clicking on screen sides
+ Reverted to the old way of opening videos, opening on separate screen can be enabled in the app settings
+
+ Rewrote video playback, use a separate screen + added fast-forwarding with horizontal swiping\n
+ Added optional 1:1 pixel ratio zooming with two double taps at fullscreen view\n
+ Allow adding Copy at the fullscreen bottom actions\n
+ Always include images at slideshows, never videos
- Added optional one-finger drag zoom, disabled by default
-
- Added new options to use english language on non-english devices, to password protect whole app and to lock screen orientation at fullscreen view
+
+ Added an initial widget implementation for creating homescreen folder shortcuts\n
+ Added optional grouping of direct subfolders, as a check at the \"Change view type\" dialog
- Added an option to keep last-modified field at file copy/move/rename
- Added an option to hide folder media count on the main screen
- Added an option to show customizable extended details over fullscreen media
-
- Added fingerprint to hidden item protection\n
- Added a new List view type
-
-
- Added a switch for disabling video gestures\n
- Added a switch for deleting empty folders after deleting content
-
- Moved media type filter from Settings to the Action menu
- Allow changing the screen brightness and volume at videos by vertically dragging the screen sides
- Added slideshow at the fullscreen view
- Added pattern/pin protection for showing hidden items
- Added a toggle for replacing Share with Rotate at fullscreen media
-
- Added an indicator of folders located on SD cards\n
- Improved the way of rotating jpg images on the internal storage by modifying the exif tags + added autosave
-
- Added an option for automatically hiding the system UI at entering fullscreen mode
- Added an option for enabling horizontal scrolling
- Allow setting custom folder covers
-
- Allow selecting multiple items by finger dragging\n
- Added an option to always use black background at fullscreen media
-
- Allow hiding individual files by prepending filenames with a dot
- Added horizontal and vertical image flipping in the image editor
-
- Allow setting portrait wallpapers\n
- Added more fields in photo Properties dialog
-
-
- Improved fullscreen image zooming\n
- Added more settings related to screen autorotating\n
- Split copy and move functions for ease of use
-
-
- Added an option to temporarily show hidden folders\n
- Added a setting for preventing thumbnail croping to square\n
- Added a setting for auto-rotating the screen depending on photo aspect ratio
-
-
- Added an option to use max brightness at viewing fullscreen media\n
- Added an option to manually include folders which contain media, but were not recognized by the app
-
-
- Fixed some sharing and editor issues\n
- Sorry for the frequent updates lately, they should be stopped now.
-
-
- Exclude the subfolders of excluded folders too\n
- Added an easy way of excluding parent folders from the exclude confirmation dialog\n
- Added draggable scrollbars\n
- Allow setting a third party video player as the default
-
- Added an option to toggle gif animation at thumbnails
- Allow setting different sorting per folder
-
- Implement proper folder hiding via .nomedia file\n
- Allow managing excluded folders in Settings
-
- Added menu items for easy image rotating in fullscreen view
- Added menu buttons for changing the column count
- Allow picking colors by hex codes
- Allow showing the photos and videos on a map, if there are available map coordinates
- Show some additional Exif data at photo properties
-
- Allow zooming pngs and gifs\n
- Allow creating new folders at Copy/Move or Save as dialog destinations
-
- Added an option to loop videos automatically
-
- Added more color customization options\n
- Your settings have been cleared, please reset them
-
- Allow changing the column count with pinch gestures
- Added an option to display images or videos only
- Added a Select all button at selecting media and folders
-
- Added an image resizer to the editor\n
- Allow displaying images and videos from all folders together
-
- Allow pinning folders at the top
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e22f466a0..3950ffb4a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -31,6 +31,7 @@
Fix Date Taken valueFixing…Dates fixed successfully
+ Share a resized versionFilter media
@@ -87,8 +88,8 @@
FlipFlip horizontallyFlip vertically
- Edit with
- Free
+ Free
+ OtherSimple Wallpaper
@@ -115,6 +116,7 @@
Loop slideshowThe slideshow endedNo media for the slideshow have been found
+ Use crossfade animationsChange view type
@@ -131,25 +133,30 @@
File typeExtension
+
+ Folder shown on the widget:
+ Show folder name
+
Play videos automatically
+ Remember last video playback positionToggle filename visibilityLoop videosAnimate GIFs at thumbnailsMax brightness when viewing fullscreen mediaCrop thumbnails into squares
+ Show video durationsRotate fullscreen media bySystem settingDevice rotationAspect ratio
- Black background and status bar at fullscreen media
+ Black background and at fullscreen mediaScroll thumbnails horizontallyAutomatically hide system UI at fullscreen mediaDelete empty folders after deleting their contentAllow controlling photo brightness with vertical gesturesAllow controlling video volume and brightness with vertical gesturesShow folder media count on the main view
- Replace Share with Rotate at fullscreen menuShow extended details over fullscreen mediaManage extended detailsAllow one finger zoom at fullscreen media
@@ -159,6 +166,13 @@
Do an extra check to avoid showing invalid filesShow some action buttons at the bottom of the screenShow the Recycle Bin at the folders screen
+ Deep zoomable images
+ Show images in the highest possible quality
+ Show the Recycle Bin as the last item on the main screen
+ Allow closing the fullscreen view with a down gesture
+ Allow 1:1 zooming in with two double taps
+ Always open videos on a separate screen with new horizontal gestures
+ Show a notch if availableThumbnails
@@ -180,36 +194,54 @@
How can I make an album always appear at the top?You can long press the desired album and select the Pin icon at the actionmenu, that will pin it to the top. You can pin multiple folders too, pinned items will be sorted by the default sorting method.How can I fast-forward videos?
- You can click on the current or max duration texts near the seekbar, that will move the video either backward, or forward.
+ You can either drag your finger horizontally over the video player, or click on the current or max duration texts near the seekbar. That will move the video either backward, or forward.What is the difference between hiding and excluding a folder?Exclude prevents displaying the folder only in Simple Gallery, while Hide works system-wise and it hides the folder from other galleries too. It works by creating an empty \".nomedia\" file in the given folder, which you can then remove with any file manager too.Why do folders with music cover art or stickers show up?It can happen that you will see some unusual albums show up. You can easily exclude them by long pressing them and selecting Exclude. In the next dialog you can then select the parent folder, chances are it will prevent the other related albums showing up too.
- A folder with images isn\'t showing up, what can I do?
+ A folder with images isn\'t showing up, or it doesn\'t show all items. What can I do?That can have multiple reasons, but solving it is easy. Just go in Settings -> Manage Included Folders, select Plus and navigate to the required folder.What if I want just a few particular folders visible?Adding a folder at the Included Folders doesn\'t automatically exclude anything. What you can do is go in Settings -> Manage Excluded Folders, exclude the root folder \"/\", then add the desired folders at Settings -> Manage Included Folders.
That will make only the selected folders visible, as both excluding and including are recursive and if a folder is both excluded and included, it will show up.
- Fullscreen images have weird artifacts, can I somehow improve the quality?
- Yea, there is a toggle in Settings saying \"Replace deep zoomable images with better quality ones\", you can use that. It will improve the quality of the images, but they will get blurred once you try zooming in too much.Can I crop images with this app?Yes, you can crop images in the editor, by dragging the image corners. You can get to the editor either by long pressing an image thumbnail and selecting Edit, or selecting Edit from the fullscreen view.Can I somehow group media file thumbnails?Sure, just use the \"Group by\" menu item while at the thumbnails view. You can group files by multiple criteria, including Date Taken. If you use the \"Show all folders content\" function you can group them by folders too.Sorting by Date Taken doesn\'t seem to work properly, how can I fix it?It is most likely caused by the files being copied from somewhere. You can fix it by selecting the file thumbnails and selecting \"Fix Date Taken value\".
+ I see some color banding on the images. How can I improve the quality?
+ The current solution for displaying images works fine in the vast majority of cases, but if you want even better image quality, you can enable the \"Show images in the highest possible quality\" at the app settings, in the \"Deep zoomable images\" section.
+ I have hidden a file/folder. How can I unhide it?
+ You can either press the \"Temporarily show hidden items\" menu item at the main screen, or toggle \"Show hidden items\" in the app settings to see the hidden item. If you want to unhide it, just long press it and select \"Unhide\". Folders are hidden by adding a hidden \".nomedia\" file into them, you can delete the file with any file manager too.
- A gallery for viewing photos and videos without ads.
+ An offline gallery for managing your files without ads, respecting your privacy.
- A simple tool usable for viewing photos and videos. Items can be sorted by date, size, name both ascending or descending, photos can be zoomed in. Media files are shown in multiple columns depending on the size of the display, you can change the column count by pinch gestures. They can be renamed, shared, deleted, copied, moved. Images can also be cropped, rotated, flipped or set as Wallpaper directly from the app.
+ A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
- The Gallery is also offered for third party usage for previewing images / videos, adding attachments at email clients etc. It\'s perfect for everyday usage.
+ It is open source, contains no ads or unnecessary permissions.
- The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
+ Let\'s list some of its features worth mentioning:
+ 1. Search
+ 2. Slideshow
+ 3. Notch support
+ 4. Pinning folders to the top
+ 5. Filtering media files by type
+ 6. Recycle bin for easy file recovery
+ 7. Fullscreen view orientation locking
+ 8. Marking favorite files for easy access
+ 9. Quick fullscreen media closing with down gesture
+ 10. An editor for modifying images and applying filters
+ 11. Password protection for protecting hidden items or the whole app
+ 12. Changing the thumbnail column count with gestures or menu buttons
+ 13. Customizable bottom actions at the fullscreen view for quick access
+ 14. Showing extended details over fullscreen media with desired file properties
+ 15. Several different ways of sorting or grouping items, both ascending and descending
+ 16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
- Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
+ The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 9bb53721a..fdde24e05 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -4,6 +4,7 @@
diff --git a/app/src/main/res/xml/widget_info.xml b/app/src/main/res/xml/widget_info.xml
new file mode 100644
index 000000000..3f70a3268
--- /dev/null
+++ b/app/src/main/res/xml/widget_info.xml
@@ -0,0 +1,11 @@
+
+
diff --git a/build.gradle b/build.gradle
index 88334c771..0be76662a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.2.70'
+ ext.kotlin_version = '1.3.21'
repositories {
google()
@@ -9,7 +9,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.4'
+ classpath 'com.android.tools.build:gradle:3.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index 4f5d71596..74f1472e3 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -1,9 +1,25 @@
-A simple tool usable for viewing photos and videos. Items can be sorted by date, size, name both ascending or descending, photos can be zoomed in. Media files are shown in multiple columns depending on the size of the display, you can change the column count by pinch gestures. They can be renamed, shared, deleted, copied, moved. Images can also be cropped, rotated, flipped or set as Wallpaper directly from the app.
+A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.
-The Gallery is also offered for third party usage for previewing images / videos, adding attachments at email clients etc. It's perfect for everyday usage.
+It is open source, contains no ads or unnecessary permissions.
-The fingerprint permission is needed for locking either hidden item visibility, or the whole app.
+Let's list some of its features worth mentioning:
+1. Search
+2. Slideshow
+3. Notch support
+4. Pinning folders to the top
+5. Filtering media files by type
+6. Recycle bin for easy file recovery
+7. Fullscreen view orientation locking
+8. Marking favorite files for easy access
+9. Quick fullscreen media closing with down gesture
+10. An editor for modifying images and applying filters
+11. Password protection for protecting hidden items or the whole app
+12. Changing the thumbnail column count with gestures or menu buttons
+13. Customizable bottom actions at the fullscreen view for quick access
+14. Showing extended details over fullscreen media with desired file properties
+15. Several different ways of sorting or grouping items, both ascending and descending
+16. Hiding folders (affects other apps too), excluding folders (affects only Simple Gallery)
-Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
+The fingerprint permission is needed for locking either hidden item visibility, the whole app, or protecting files from being deleted.
This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app.jpg
old mode 100644
new mode 100755
index e03971236..c8e5f57e9
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg
old mode 100644
new mode 100755
index 3c79bf959..9d14855a2
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_2.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg
old mode 100644
new mode 100755
index e9778473f..b80672eb4
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_3.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg
old mode 100644
new mode 100755
index 9cd10f484..20ddf6acf
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg and b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_4.jpg differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg
deleted file mode 100644
index 315243832..000000000
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_5.jpg and /dev/null differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_6.jpg b/fastlane/metadata/android/en-US/images/phoneScreenshots/app_6.jpg
deleted file mode 100644
index a0f0752fe..000000000
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/app_6.jpg and /dev/null differ
diff --git a/gallery-release-227.apk b/gallery-release-227.apk
new file mode 100644
index 000000000..80687777f
Binary files /dev/null and b/gallery-release-227.apk differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 000000000..5465fec0e
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,2 @@
+android.enableJetifier=true
+android.useAndroidX=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f4113673a..00616cb8d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Nov 07 16:59:29 CET 2017
+#Wed Jan 16 16:59:58 CET 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
diff --git a/keystore.properties_sample b/keystore.properties_sample
new file mode 100644
index 000000000..569edd736
--- /dev/null
+++ b/keystore.properties_sample
@@ -0,0 +1,4 @@
+storePassword=123456
+keyPassword=abcdef
+keyAlias=myAlias
+storeFile=../keystore.jks
diff --git a/signing.properties_sample b/signing.properties_sample
deleted file mode 100644
index cf8e23968..000000000
--- a/signing.properties_sample
+++ /dev/null
@@ -1,3 +0,0 @@
-STORE_FILE=/path/to/your.keystore
-KEY_ALIAS=projectkeyalias
-PASSWORD=yourpass