diff --git a/app/build.gradle b/app/build.gradle
index b61da4d4..a83d0033 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,8 +1,8 @@
buildscript {
ext {
jacocoVersion = "0.8.7"
- androidXLifecycleVersion = "2.3.1"
- glideVersion = '4.11.0'
+ androidXLifecycleVersion = "2.6.1"
+ glideVersion = '4.16.0'
fragment_version = "1.3.6"
roomVersion = "2.4.0"
billing_version = "6.0.1"
@@ -39,6 +39,7 @@ plugins {
id 'com.starter.easylauncher'
id 'com.mikepenz.aboutlibraries.plugin'
id 'org.jetbrains.kotlin.android'
+ id("com.google.devtools.ksp")
}
android {
@@ -47,12 +48,12 @@ android {
release
}
- compileSdk 33
+ compileSdk 34
defaultConfig {
applicationId "com.amaze.fileutilities"
minSdk 21
- targetSdk 33
+ targetSdk 34
Integer sudoVersionCode = 31
String versionNameText = "1.91"
@@ -220,7 +221,7 @@ android {
}
dependencies {
- implementation 'androidx.core:core:1.7.0'
+ implementation 'androidx.core:core:1.9.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.6.1'
@@ -231,12 +232,15 @@ dependencies {
implementation 'androidx.palette:palette:1.0.0'
//Glide: loads icons seemlessly
implementation "com.github.bumptech.glide:glide:$glideVersion"
+ ksp("com.github.bumptech.glide:ksp:$glideVersion")
implementation ("com.github.bumptech.glide:recyclerview-integration:$glideVersion") {
// Excludes the support library because it's already included by Glide.
transitive = false
}
+ implementation("com.github.bumptech.glide:compose:1.0.0-alpha.5")
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- kapt "com.github.bumptech.glide:compiler:$glideVersion"
+ implementation 'com.github.awxkee:avif-coder-glide:1.5.7'
+// kapt "com.github.bumptech.glide:compiler:$glideVersion"
implementation 'androidx.webkit:webkit:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c43662cd..56e110dd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,7 +3,8 @@
xmlns:tools="http://schemas.android.com/tools">
+ org.opencv, com.mikepenz.aboutlibraries, com.abedelazizshe.lightcompressorlibrary,
+ com.awxkee.avif.glide, com.github.awxkee.avifcoil, com.github.awxkee.avifcoder, androidx.emoji2" />
?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
// do nothing
@@ -274,15 +274,15 @@ interface IAudioPlayerInterfaceHandler : OnPlaybackInfoUpdate, LifecycleOwner {
}
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
+ resource: Drawable,
+ model: Any,
target: Target?,
- dataSource: DataSource?,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
getContextWeakRef().get()?.let {
context ->
- resource?.let {
+ resource.let {
getAudioPlayerHandlerViewModel().getPaletteColor(
it,
context.getColor(R.color.navy_blue_alt_3)
diff --git a/app/src/main/java/com/amaze/fileutilities/audio_player/playlist/M3UWriter.java b/app/src/main/java/com/amaze/fileutilities/audio_player/playlist/M3UWriter.java
index a26863f5..483a5be2 100644
--- a/app/src/main/java/com/amaze/fileutilities/audio_player/playlist/M3UWriter.java
+++ b/app/src/main/java/com/amaze/fileutilities/audio_player/playlist/M3UWriter.java
@@ -37,8 +37,10 @@ public class M3UWriter {
public static File write(File dir, MediaFileInfo.Playlist playlist, List songs)
throws IOException {
- if (!dir.exists()) // noinspection ResultOfMethodCallIgnored
- dir.mkdirs();
+ if (!dir.exists()) {
+ // noinspection ResultOfMethodCallIgnored
+ dir.mkdirs();
+ }
File file = new File(dir, playlist.getName().concat("." + EXTENSION));
if (songs.size() > 0) {
diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt
index df694c43..5d26f08c 100644
--- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt
+++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/FilesViewModel.kt
@@ -39,8 +39,8 @@ import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Transformations
import androidx.lifecycle.liveData
+import androidx.lifecycle.switchMap
import androidx.lifecycle.viewModelScope
import com.abedelazizshe.lightcompressorlibrary.CompressionListener
import com.abedelazizshe.lightcompressorlibrary.VideoCompressor
@@ -305,7 +305,7 @@ class FilesViewModel(val applicationContext: Application) :
fun usedImagesSummaryTransformations():
LiveData>?> {
if (usedImagesSummaryTransformations == null) {
- usedImagesSummaryTransformations = Transformations.switchMap(internalStorageStats()) {
+ usedImagesSummaryTransformations = internalStorageStats().switchMap {
input ->
getImagesSummaryLiveData(input)
}
@@ -316,7 +316,7 @@ class FilesViewModel(val applicationContext: Application) :
fun usedAudiosSummaryTransformations():
LiveData>?> {
if (usedAudiosSummaryTransformations == null) {
- usedAudiosSummaryTransformations = Transformations.switchMap(internalStorageStats()) {
+ usedAudiosSummaryTransformations = internalStorageStats().switchMap {
input ->
getAudiosSummaryLiveData(input)
}
@@ -328,7 +328,7 @@ class FilesViewModel(val applicationContext: Application) :
LiveData>?> {
if (usedPlaylistsSummaryTransformations == null) {
usedPlaylistsSummaryTransformations =
- Transformations.switchMap(internalStorageStats()) {
+ internalStorageStats().switchMap {
input ->
getPlaylistsSummaryLiveData(input)
}
@@ -339,7 +339,7 @@ class FilesViewModel(val applicationContext: Application) :
fun usedVideosSummaryTransformations():
LiveData>?> {
if (usedVideosSummaryTransformations == null) {
- usedVideosSummaryTransformations = Transformations.switchMap(internalStorageStats()) {
+ usedVideosSummaryTransformations = internalStorageStats().switchMap {
input ->
getVideosSummaryLiveData(input)
}
@@ -350,7 +350,7 @@ class FilesViewModel(val applicationContext: Application) :
fun usedDocsSummaryTransformations():
LiveData>?> {
if (usedDocsSummaryTransformations == null) {
- usedDocsSummaryTransformations = Transformations.switchMap(internalStorageStats()) {
+ usedDocsSummaryTransformations = internalStorageStats().switchMap {
input ->
getDocumentsSummaryLiveData(input)
}
@@ -2688,6 +2688,15 @@ class FilesViewModel(val applicationContext: Application) :
}
val metaInfoAndSummaryPair = CursorUtils
.listImages(applicationContext.applicationContext)
+ val sortingPref = MediaFileListSorter.SortingPreference.newInstance(
+ applicationContext
+ .getAppCommonSharedPreferences(),
+ MediaFileAdapter.MEDIA_TYPE_IMAGES
+ )
+ MediaFileListSorter.generateMediaFileListHeadersAndSort(
+ applicationContext,
+ metaInfoAndSummaryPair.second, sortingPref
+ )
setMediaInfoSummary(metaInfoAndSummaryPair.first, storageSummary)
emit(metaInfoAndSummaryPair)
}
@@ -2727,6 +2736,15 @@ class FilesViewModel(val applicationContext: Application) :
it.path
}
)
+ val sortingPref = MediaFileListSorter.SortingPreference.newInstance(
+ applicationContext
+ .getAppCommonSharedPreferences(),
+ MediaFileAdapter.MEDIA_TYPE_AUDIO
+ )
+ MediaFileListSorter.generateMediaFileListHeadersAndSort(
+ applicationContext,
+ metaInfoAndSummaryPair.second, sortingPref
+ )
setMediaInfoSummary(metaInfoAndSummaryPair.first, storageSummary)
emit(metaInfoAndSummaryPair)
metaInfoAndSummaryPair.second.forEach {
@@ -2778,6 +2796,15 @@ class FilesViewModel(val applicationContext: Application) :
}
mediaStorageSummary?.let {
setMediaInfoSummary(it, storageSummary)
+ val sortingPref = MediaFileListSorter.SortingPreference.newInstance(
+ applicationContext
+ .getAppCommonSharedPreferences(),
+ MediaFileAdapter.MEDIA_TYPE_AUDIO
+ )
+ MediaFileListSorter.generateMediaFileListHeadersAndSort(
+ applicationContext,
+ playlistFiles, sortingPref
+ )
emit(Pair(it, playlistFiles))
playlistFiles.forEach {
mediaFileInfo ->
@@ -2803,6 +2830,15 @@ class FilesViewModel(val applicationContext: Application) :
}
val metaInfoAndSummaryPair = CursorUtils
.listVideos(applicationContext.applicationContext)
+ val sortingPref = MediaFileListSorter.SortingPreference.newInstance(
+ applicationContext
+ .getAppCommonSharedPreferences(),
+ MediaFileAdapter.MEDIA_TYPE_VIDEO
+ )
+ MediaFileListSorter.generateMediaFileListHeadersAndSort(
+ applicationContext,
+ metaInfoAndSummaryPair.second, sortingPref
+ )
setMediaInfoSummary(metaInfoAndSummaryPair.first, storageSummary)
emit(metaInfoAndSummaryPair)
}
@@ -2839,6 +2875,15 @@ class FilesViewModel(val applicationContext: Application) :
mediaFiles.add(mediaFileInfo)
}
val docsSummary = StorageSummary(size, 0, longSize)
+ val sortingPref = MediaFileListSorter.SortingPreference.newInstance(
+ applicationContext
+ .getAppCommonSharedPreferences(),
+ MediaFileAdapter.MEDIA_TYPE_DOCS
+ )
+ MediaFileListSorter.generateMediaFileListHeadersAndSort(
+ applicationContext,
+ mediaFiles, sortingPref
+ )
emit(Pair(docsSummary, mediaFiles))
setMediaInfoSummary(docsSummary, storageSummary)
}
diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt
index f4169228..f55eb4b1 100644
--- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt
+++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaAdapterPreloader.kt
@@ -91,7 +91,7 @@ class MediaAdapterPreloader(private val context: Context, private val loadingDra
override fun onLoadFailed(
e: GlideException?,
model: Any?,
- target: Target?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
if (isGrid) {
@@ -101,10 +101,10 @@ class MediaAdapterPreloader(private val context: Context, private val loadingDra
}
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
+ resource: Drawable,
+ model: Any,
+ target: Target,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
// do nothing
diff --git a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt
index 748f5dac..029bc4ed 100644
--- a/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt
+++ b/app/src/main/java/com/amaze/fileutilities/home_page/ui/files/MediaFileAdapter.kt
@@ -83,10 +83,6 @@ class MediaFileAdapter(
preloader.clear()
onlyItemsCounts = 0
headerListItems.clear()
- MediaFileListSorter.generateMediaFileListHeadersAndSort(
- context,
- mediaFileInfoList, sortingPreference
- )
var lastHeader: String? = null
value.add(ListItem(TYPE_BANNER))
preloader.addItem("")
diff --git a/app/src/main/java/com/amaze/fileutilities/image_viewer/GlideModule.kt b/app/src/main/java/com/amaze/fileutilities/image_viewer/GlideModule.kt
index 707822b0..ad56b06f 100644
--- a/app/src/main/java/com/amaze/fileutilities/image_viewer/GlideModule.kt
+++ b/app/src/main/java/com/amaze/fileutilities/image_viewer/GlideModule.kt
@@ -28,9 +28,5 @@ import com.bumptech.glide.module.AppGlideModule
@GlideModule
class GlideModule : AppGlideModule() {
- override fun isManifestParsingEnabled() = false
- override fun applyOptions(context: Context, builder: GlideBuilder) {
- builder.setLogLevel(Log.ERROR)
- }
}
diff --git a/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt b/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt
index 312acfdc..1a62d9a3 100644
--- a/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt
+++ b/app/src/main/java/com/amaze/fileutilities/image_viewer/ImageViewerFragment.kt
@@ -658,7 +658,7 @@ class ImageViewerFragment : AbstractMediaFragment() {
}
_binding?.let {
glide.into(it.imageView)
- Glide.with(this).load(localTypeModel.uri.toString())
+ Glide.with(this).load("https://wh.aimuse.online/preset/jxl_icc_12.bit.jxl")
.thumbnail(
Glide.with(this).load(
resources.getDrawable(R.drawable.ic_outline_image_32)
@@ -678,7 +678,7 @@ class ImageViewerFragment : AbstractMediaFragment() {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
- target: Target?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
// do nothing
@@ -714,13 +714,13 @@ class ImageViewerFragment : AbstractMediaFragment() {
}
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
+ resource: Drawable,
+ model: Any,
+ target: Target,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
- resource?.let {
+ resource.let {
filesViewModel.getPaletteColors(it)
.observe(this@ImageViewerFragment) {
colorPair ->
diff --git a/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt b/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt
index 125aef85..ce79fd8a 100644
--- a/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt
+++ b/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt
@@ -65,6 +65,7 @@ import com.amaze.fileutilities.utilis.share.getShareIntents
import com.amaze.fileutilities.utilis.share.showEditImageDialog
import com.amaze.fileutilities.utilis.share.showShareDialog
import com.amaze.fileutilities.utilis.showFade
+import com.amaze.fileutilities.utilis.showToastOnBottom
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
@@ -201,7 +202,7 @@ class EditImageActivity :
override fun onLoadFailed(
e: GlideException?,
model: Any?,
- target: Target?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
// do nothing
@@ -210,13 +211,13 @@ class EditImageActivity :
}
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
+ resource: Drawable,
+ model: Any,
+ target: Target,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
- loadedBitmap = resource?.toBitmap()
+ loadedBitmap = resource.toBitmap()
addFilterViews()
return false
}
@@ -241,7 +242,7 @@ class EditImageActivity :
override fun onLoadFailed(
e: GlideException?,
model: Any?,
- target: Target?,
+ target: Target,
isFirstResource: Boolean
): Boolean {
// do nothing
@@ -250,13 +251,13 @@ class EditImageActivity :
}
override fun onResourceReady(
- resource: Drawable?,
- model: Any?,
- target: Target?,
- dataSource: DataSource?,
+ resource: Drawable,
+ model: Any,
+ target: Target,
+ dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
- loadedBitmap = resource?.toBitmap()
+ loadedBitmap = resource.toBitmap()
addFilterViews()
return false
}
@@ -314,13 +315,14 @@ class EditImageActivity :
private fun addFilterViews() {
loadedBitmap?.let {
+ val resizedBitmap = Bitmap.createScaledBitmap(loadedBitmap!!, 100, 100, false)
supportedFilters.forEach {
photoFilter ->
val filterItem = layoutInflater.inflate(R.layout.row_filter_view, null)
val photoEditorView = filterItem.findViewById(R.id.imgFilterView)
val txtFilterName = filterItem.findViewById(R.id.txtFilterName)
txtFilterName.text = photoFilter.name.replace("_", " ")
- photoEditorView.source.setImageBitmap(loadedBitmap)
+ photoEditorView.source.setImageBitmap(resizedBitmap)
try {
photoEditorView.setFilterEffect(photoFilter)
} catch (e: Exception) {
@@ -620,7 +622,12 @@ class EditImageActivity :
}
override fun onFilterSelected(photoFilter: PhotoFilter?) {
- mPhotoEditor?.setFilterEffect(photoFilter)
+ try {
+ mPhotoEditor?.setFilterEffect(photoFilter)
+ } catch (e: Exception) {
+ log.error("failed to apply filter on bitmap")
+ this.showToastOnBottom(getString(R.string.operation_failed))
+ }
}
override fun onToolSelected(toolType: ToolType?) {
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt b/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt
index fad43e47..e4b792d3 100644
--- a/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/Extensions.kt
@@ -22,6 +22,7 @@ package com.amaze.fileutilities.utilis
import android.content.ContentResolver
import android.content.Context
+import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Resources
import android.database.Cursor
@@ -30,6 +31,7 @@ import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.ParcelFileDescriptor
+import android.os.TransactionTooLargeException
import android.provider.MediaStore
import android.util.DisplayMetrics
import android.view.Gravity
@@ -42,9 +44,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.documentfile.provider.DocumentFile
import com.afollestad.materialdialogs.MaterialDialog
-import com.afollestad.materialdialogs.files.FileFilter
-import com.afollestad.materialdialogs.files.fileChooser
-import com.afollestad.materialdialogs.files.folderChooser
+import com.amaze.fileutilities.audio_player.AudioPlayerService
import com.amaze.fileutilities.home_page.database.BlurAnalysis
import com.amaze.fileutilities.home_page.database.BlurAnalysisDao
import com.amaze.fileutilities.home_page.database.ImageAnalysis
@@ -59,6 +59,9 @@ import com.amaze.fileutilities.home_page.database.SimilarImagesAnalysis
import com.amaze.fileutilities.home_page.database.SimilarImagesAnalysisDao
import com.amaze.fileutilities.home_page.database.SimilarImagesAnalysisMetadata
import com.amaze.fileutilities.home_page.database.SimilarImagesAnalysisMetadataDao
+import com.amaze.fileutilities.utilis.dialog_picker.FileFilter
+import com.amaze.fileutilities.utilis.dialog_picker.fileChooser
+import com.amaze.fileutilities.utilis.dialog_picker.folderChooser
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -451,6 +454,35 @@ fun Context.getAppCommonSharedPreferences(): SharedPreferences {
)
}
+fun Context.startServiceSafely(intent: Intent, extraName: String) {
+ try {
+ this.startService(intent)
+ } catch (ttle: TransactionTooLargeException) {
+ // decrease by 10% and try recursively
+ val intentUriList: ArrayList? = intent.getParcelableArrayListExtra(extraName)
+ intentUriList?.let {
+ if (intentUriList.isNotEmpty()) {
+ log.warn("failed to start service with extras size ${intentUriList.size}", ttle)
+ val uri = it.take(intentUriList.size - (intentUriList.size / 10))
+ if (uri.isNotEmpty()) {
+ log.warn("trying to start with new size ${uri.size}")
+ intent.putParcelableArrayListExtra(
+ AudioPlayerService.ARG_URI_LIST,
+ ArrayList(uri)
+ )
+ startServiceSafely(intent, extraName)
+ } else {
+ log.error("couldn't start service safely, returning...", ttle)
+ return
+ }
+ } else {
+ log.error("couldn't start service safely, returning...", ttle)
+ return
+ }
+ }
+ }
+}
+
fun ImageAnalysis.invalidate(dao: ImageAnalysisDao): Boolean {
val file = File(filePath)
return if (!file.exists()) {
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/ContextExt.kt b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/ContextExt.kt
new file mode 100644
index 00000000..fc1115e9
--- /dev/null
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/ContextExt.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Utilities.
+ *
+ * Amaze File Utilities is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Designed and developed by Aidan Follestad (@afollestad)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amaze.fileutilities.utilis.dialog_picker
+
+import android.content.Context
+import java.io.File
+
+internal fun Context.getExternalFilesDir(): File? {
+ return this.getExternalFilesDir(null)
+}
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/DialogFileChooserExt.kt b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/DialogFileChooserExt.kt
new file mode 100644
index 00000000..65c992a6
--- /dev/null
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/DialogFileChooserExt.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Utilities.
+ *
+ * Amaze File Utilities is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Designed and developed by Aidan Follestad (@afollestad)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("unused")
+
+package com.amaze.fileutilities.utilis.dialog_picker
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.text.InputFilter
+import android.widget.EditText
+import android.widget.TextView
+import androidx.annotation.CheckResult
+import androidx.annotation.StringRes
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.WhichButton.POSITIVE
+import com.afollestad.materialdialogs.actions.setActionButtonEnabled
+import com.afollestad.materialdialogs.customview.customView
+import com.afollestad.materialdialogs.customview.getCustomView
+import com.afollestad.materialdialogs.files.R
+import com.afollestad.materialdialogs.input.getInputField
+import com.afollestad.materialdialogs.input.input
+import com.afollestad.materialdialogs.internal.list.DialogRecyclerView
+import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
+import java.io.File
+
+typealias FileFilter = ((File) -> Boolean)?
+typealias FileCallback = ((dialog: MaterialDialog, file: File) -> Unit)?
+
+/** Gets the selected file for the current file chooser dialog. */
+@CheckResult
+fun MaterialDialog.selectedFile(): File? {
+ val customView = getCustomView()
+ val list: DialogRecyclerView = customView.findViewById(R.id.list)
+ return (list.adapter as? FileChooserAdapter)?.selectedFile
+}
+
+/**
+ * Shows a dialog that lets the user select a local file.
+ *
+ * @param initialDirectory The directory that is listed initially, defaults to external storage.
+ * @param filter A filter to apply when listing files, defaults to only show non-hidden files.
+ * @param waitForPositiveButton When true, the callback isn't invoked until the user selects a
+ * file and taps on the positive action button. Defaults to true if the dialog has buttons.
+ * @param emptyTextRes A string resource displayed on the empty view shown when a directory is
+ * empty. Defaults to "This folder's empty!".
+ * @param selection A callback invoked when a file is selected.
+ */
+@SuppressLint("CheckResult")
+fun MaterialDialog.fileChooser(
+ context: Context,
+ initialDirectory: File? = context.getExternalFilesDir(),
+ filter: FileFilter = null,
+ waitForPositiveButton: Boolean = true,
+ emptyTextRes: Int = R.string.files_default_empty_text,
+ allowFolderCreation: Boolean = false,
+ @StringRes folderCreationLabel: Int? = null,
+ selection: FileCallback = null
+): MaterialDialog {
+ var actualFilter: FileFilter = filter
+
+ if (allowFolderCreation) {
+ // we already have permissions at app startup
+// check(hasWriteStoragePermission()) {
+// "You must have the WRITE_EXTERNAL_STORAGE permission first."
+// }
+ if (filter == null) {
+ actualFilter = { !it.isHidden && it.canWrite() }
+ }
+ } else {
+ // we already have permissions at app startup
+// check(hasWriteStoragePermission()) {
+// "You must have the WRITE_EXTERNAL_STORAGE permission first."
+// }
+ if (filter == null) {
+ actualFilter = { !it.isHidden && it.canRead() }
+ }
+ }
+
+ check(initialDirectory != null) {
+ "The initial directory is null."
+ }
+
+ customView(R.layout.md_file_chooser_base, noVerticalPadding = true)
+ setActionButtonEnabled(POSITIVE, false)
+
+ val customView = getCustomView()
+ val list: DialogRecyclerView = customView.findViewById(R.id.list)
+ val emptyText: TextView = customView.findViewById(R.id.empty_text)
+ emptyText.setText(emptyTextRes)
+ emptyText.maybeSetTextColor(windowContext, R.attr.md_color_content)
+
+ list.attach(this)
+ list.layoutManager = LinearLayoutManager(windowContext)
+ val adapter = FileChooserAdapter(
+ dialog = this,
+ initialFolder = initialDirectory,
+ waitForPositiveButton = waitForPositiveButton,
+ emptyView = emptyText,
+ onlyFolders = false,
+ filter = actualFilter,
+ allowFolderCreation = allowFolderCreation,
+ folderCreationLabel = folderCreationLabel,
+ callback = selection
+ )
+ list.adapter = adapter
+
+ if (waitForPositiveButton && selection != null) {
+ setActionButtonEnabled(POSITIVE, false)
+ positiveButton {
+ val selectedFile = adapter.selectedFile
+ if (selectedFile != null) {
+ selection.invoke(this, selectedFile)
+ }
+ }
+ }
+
+ return this
+}
+
+internal fun MaterialDialog.showNewFolderCreator(
+ parent: File,
+ @StringRes folderCreationLabel: Int?,
+ onCreation: () -> Unit
+) {
+ val dialog = MaterialDialog(windowContext).show {
+ title(folderCreationLabel ?: R.string.files_new_folder)
+ input(hintRes = R.string.files_new_folder_hint) { _, input ->
+ File(parent, input.toString().trim()).mkdir()
+ onCreation()
+ }
+ }
+ dialog.getInputField()
+ .blockReservedCharacters()
+}
+
+private fun EditText.blockReservedCharacters() {
+ filters += InputFilter { source, _, _, _, _, _ ->
+ if (source.isEmpty()) {
+ return@InputFilter null
+ }
+ val last = source[source.length - 1]
+ val reservedChars = "?:\"*|/\\<>"
+ if (reservedChars.indexOf(last) > -1) {
+ source.subSequence(0, source.length - 1)
+ } else {
+ null
+ }
+ }
+}
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/DialogFolderChooserExt.kt b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/DialogFolderChooserExt.kt
new file mode 100644
index 00000000..086a826b
--- /dev/null
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/DialogFolderChooserExt.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Utilities.
+ *
+ * Amaze File Utilities is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Designed and developed by Aidan Follestad (@afollestad)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("unused")
+
+package com.amaze.fileutilities.utilis.dialog_picker
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.widget.TextView
+import androidx.annotation.CheckResult
+import androidx.annotation.StringRes
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.WhichButton.POSITIVE
+import com.afollestad.materialdialogs.actions.setActionButtonEnabled
+import com.afollestad.materialdialogs.customview.customView
+import com.afollestad.materialdialogs.customview.getCustomView
+import com.afollestad.materialdialogs.files.R
+import com.afollestad.materialdialogs.internal.list.DialogRecyclerView
+import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
+import java.io.File
+
+/** Gets the selected folder for the current folder chooser dialog. */
+@CheckResult
+fun MaterialDialog.selectedFolder(): File? {
+ val list: DialogRecyclerView = getCustomView().findViewById(R.id.list)
+ return (list.adapter as? FileChooserAdapter)?.selectedFile
+}
+
+/**
+ * Shows a dialog that lets the user select a local folder.
+ *
+ * @param initialDirectory The directory that is listed initially, defaults to external storage.
+ * @param filter A filter to apply when listing folders, defaults to only show non-hidden folders.
+ * @param waitForPositiveButton When true, the callback isn't invoked until the user selects a
+ * folder and taps on the positive action button. Defaults to true if the dialog has buttons.
+ * @param emptyTextRes A string resource displayed on the empty view shown when a directory is
+ * empty. Defaults to "This folder's empty!".
+ * @param selection A callback invoked when a folder is selected.
+ */
+@SuppressLint("CheckResult")
+fun MaterialDialog.folderChooser(
+ context: Context,
+ initialDirectory: File? = context.getExternalFilesDir(),
+ filter: FileFilter = null,
+ waitForPositiveButton: Boolean = true,
+ emptyTextRes: Int = R.string.files_default_empty_text,
+ allowFolderCreation: Boolean = false,
+ @StringRes folderCreationLabel: Int? = null,
+ selection: FileCallback = null
+): MaterialDialog {
+ var actualFilter: FileFilter = filter
+
+ if (allowFolderCreation) {
+ // we already have permissions at app startup
+// check(hasWriteStoragePermission()) {
+// "You must have the WRITE_EXTERNAL_STORAGE permission first."
+// }
+ if (filter == null) {
+ actualFilter = { !it.isHidden && it.canWrite() }
+ }
+ } else {
+ // we already have permissions at app startup
+// check(hasWriteStoragePermission()) {
+// "You must have the READ_EXTERNAL_STORAGE permission first."
+// }
+ if (filter == null) {
+ actualFilter = { !it.isHidden && it.canRead() }
+ }
+ }
+
+ check(initialDirectory != null) {
+ "The initial directory is null."
+ }
+
+ customView(R.layout.md_file_chooser_base, noVerticalPadding = true)
+ setActionButtonEnabled(POSITIVE, false)
+
+ val customView = getCustomView()
+ val list: DialogRecyclerView = customView.findViewById(R.id.list)
+ val emptyText: TextView = customView.findViewById(R.id.empty_text)
+ emptyText.setText(emptyTextRes)
+ emptyText.maybeSetTextColor(windowContext, R.attr.md_color_content)
+
+ list.attach(this)
+ list.layoutManager = LinearLayoutManager(windowContext)
+
+ val adapter = FileChooserAdapter(
+ dialog = this,
+ initialFolder = initialDirectory,
+ waitForPositiveButton = waitForPositiveButton,
+ emptyView = emptyText,
+ onlyFolders = true,
+ filter = actualFilter,
+ allowFolderCreation = allowFolderCreation,
+ folderCreationLabel = folderCreationLabel,
+ callback = selection
+ )
+ list.adapter = adapter
+
+ if (waitForPositiveButton && selection != null) {
+ positiveButton {
+ val selectedFile = adapter.selectedFile
+ if (selectedFile != null) {
+ selection.invoke(this, selectedFile)
+ }
+ }
+ }
+
+ return this
+}
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/FileChooserAdapter.kt b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/FileChooserAdapter.kt
new file mode 100644
index 00000000..61786563
--- /dev/null
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/FileChooserAdapter.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Utilities.
+ *
+ * Amaze File Utilities is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Designed and developed by Aidan Follestad (@afollestad)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amaze.fileutilities.utilis.dialog_picker
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.OnClickListener
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.annotation.StringRes
+import androidx.recyclerview.widget.RecyclerView
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.WhichButton.POSITIVE
+import com.afollestad.materialdialogs.actions.hasActionButtons
+import com.afollestad.materialdialogs.actions.setActionButtonEnabled
+import com.afollestad.materialdialogs.callbacks.onDismiss
+import com.afollestad.materialdialogs.files.R
+import com.afollestad.materialdialogs.list.getItemSelector
+import com.afollestad.materialdialogs.utils.MDUtil.isColorDark
+import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
+import com.afollestad.materialdialogs.utils.MDUtil.resolveColor
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.Dispatchers.Main
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import java.io.File
+import java.util.Locale
+
+internal class FileChooserViewHolder(
+ itemView: View,
+ private val adapter: FileChooserAdapter
+) : RecyclerView.ViewHolder(itemView), OnClickListener {
+
+ init {
+ itemView.setOnClickListener(this)
+ }
+
+ val iconView: ImageView = itemView.findViewById(R.id.icon)
+ val nameView: TextView = itemView.findViewById(R.id.name)
+
+ override fun onClick(view: View) = adapter.itemClicked(adapterPosition)
+}
+
+/** @author Aidan Follestad (afollestad */
+internal class FileChooserAdapter(
+ private val dialog: MaterialDialog,
+ initialFolder: File,
+ private val waitForPositiveButton: Boolean,
+ private val emptyView: TextView,
+ private val onlyFolders: Boolean,
+ private val filter: FileFilter,
+ private val allowFolderCreation: Boolean,
+ @StringRes private val folderCreationLabel: Int?,
+ private val callback: FileCallback
+) : RecyclerView.Adapter() {
+
+ var selectedFile: File? = null
+
+ private var currentFolder = initialFolder
+ private var listingJob: Job? = null
+ private var contents: List? = null
+
+ private val isLightTheme =
+ resolveColor(dialog.windowContext, attr = android.R.attr.textColorPrimary).isColorDark()
+
+ init {
+ dialog.onDismiss { listingJob?.cancel() }
+ switchDirectory(initialFolder)
+ }
+
+ fun itemClicked(index: Int) {
+ val parent = currentFolder.betterParent(dialog.context, allowFolderCreation, filter)
+ if (parent != null && index == goUpIndex()) {
+ // go up
+ switchDirectory(parent)
+ return
+ } else if (currentFolder.canWrite() && allowFolderCreation && index == newFolderIndex()) {
+ // New folder
+ dialog.showNewFolderCreator(
+ parent = currentFolder,
+ folderCreationLabel = folderCreationLabel
+ ) {
+ // Refresh view
+ switchDirectory(currentFolder)
+ }
+ return
+ }
+
+ val actualIndex = actualIndex(index)
+ val selected = contents!![actualIndex].jumpOverEmulated(dialog.context)
+
+ if (selected.isDirectory) {
+ switchDirectory(selected)
+ } else {
+ val previousSelectedIndex = getSelectedIndex()
+ this.selectedFile = selected
+ val actualWaitForPositive = waitForPositiveButton && dialog.hasActionButtons()
+
+ if (actualWaitForPositive) {
+ dialog.setActionButtonEnabled(POSITIVE, true)
+ notifyItemChanged(index)
+ notifyItemChanged(previousSelectedIndex)
+ } else {
+ callback?.invoke(dialog, selected)
+ dialog.dismiss()
+ }
+ }
+ }
+
+ private fun switchDirectory(directory: File) {
+ listingJob?.cancel()
+ listingJob = GlobalScope.launch(Main) {
+ if (onlyFolders) {
+ selectedFile = directory
+ dialog.setActionButtonEnabled(POSITIVE, true)
+ }
+
+ currentFolder = directory
+ dialog.title(text = directory.friendlyName(dialog.context))
+
+ val result = withContext(IO) {
+ val rawContents = directory.listFiles() ?: emptyArray()
+ if (onlyFolders) {
+ rawContents
+ .filter { it.isDirectory && filter?.invoke(it) ?: true }
+ .sortedBy { it.name.toLowerCase(Locale.getDefault()) }
+ } else {
+ rawContents
+ .filter { filter?.invoke(it) ?: true }
+ .sortedWith(
+ compareBy({ !it.isDirectory }, {
+ it.nameWithoutExtension.toLowerCase(Locale.getDefault())
+ })
+ )
+ }
+ }
+
+ contents = result.apply {
+ emptyView.setVisible(isEmpty())
+ }
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun getItemCount(): Int {
+ var count = contents?.size ?: 0
+ if (currentFolder.hasParent(dialog.context, allowFolderCreation, filter)) {
+ count += 1
+ }
+ if (allowFolderCreation && currentFolder.canWrite()) {
+ count += 1
+ }
+ return count
+ }
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): FileChooserViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.md_file_chooser_item, parent, false)
+ view.background = dialog.getItemSelector()
+
+ val viewHolder = FileChooserViewHolder(view, this)
+ viewHolder.nameView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
+ return viewHolder
+ }
+
+ override fun onBindViewHolder(
+ holder: FileChooserViewHolder,
+ position: Int
+ ) {
+ val currentParent = currentFolder.betterParent(dialog.context, allowFolderCreation, filter)
+ if (currentParent != null && position == goUpIndex()) {
+ // Go up
+ holder.iconView.setImageResource(
+ if (isLightTheme) R.drawable.icon_return_dark
+ else R.drawable.icon_return_light
+ )
+ holder.nameView.text = currentParent.name
+ holder.itemView.isActivated = false
+ return
+ }
+
+ if (allowFolderCreation && currentFolder.canWrite() && position == newFolderIndex()) {
+ // New folder
+ holder.iconView.setImageResource(
+ if (isLightTheme) R.drawable.icon_new_folder_dark
+ else R.drawable.icon_new_folder_light
+ )
+ holder.nameView.text = dialog.windowContext.getString(
+ folderCreationLabel ?: R.string.files_new_folder
+ )
+ holder.itemView.isActivated = false
+ return
+ }
+
+ val actualIndex = actualIndex(position)
+ val item = contents!![actualIndex]
+ holder.iconView.setImageResource(item.iconRes())
+ holder.nameView.text = item.name
+ holder.itemView.isActivated = selectedFile?.absolutePath == item.absolutePath ?: false
+ }
+
+ private fun goUpIndex() = if (currentFolder.hasParent(
+ dialog.context, allowFolderCreation,
+ filter
+ )
+ ) 0 else -1
+
+ private fun newFolderIndex() = if (currentFolder.hasParent(
+ dialog.context, allowFolderCreation,
+ filter
+ )
+ ) 1 else 0
+
+ private fun actualIndex(position: Int): Int {
+ var actualIndex = position
+ if (currentFolder.hasParent(dialog.context, allowFolderCreation, filter)) {
+ actualIndex -= 1
+ }
+ if (currentFolder.canWrite() && allowFolderCreation) {
+ actualIndex -= 1
+ }
+ return actualIndex
+ }
+
+ private fun File.iconRes(): Int {
+ return if (isLightTheme) {
+ if (this.isDirectory) R.drawable.icon_folder_dark
+ else R.drawable.icon_file_dark
+ } else {
+ if (this.isDirectory) R.drawable.icon_folder_light
+ else R.drawable.icon_file_light
+ }
+ }
+
+ private fun getSelectedIndex(): Int {
+ if (selectedFile == null) return -1
+ else if (contents?.isEmpty() == true) return -1
+ val index = contents?.indexOfFirst { it.absolutePath == selectedFile?.absolutePath } ?: -1
+ return if (index > -1 && currentFolder.hasParent(
+ dialog.context, allowFolderCreation,
+ filter
+ )
+ ) index + 1 else index
+ }
+}
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/FilesUtilExt.kt b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/FilesUtilExt.kt
new file mode 100644
index 00000000..ec6ea53e
--- /dev/null
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/FilesUtilExt.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Utilities.
+ *
+ * Amaze File Utilities is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Designed and developed by Aidan Follestad (@afollestad)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("SpellCheckingInspection")
+
+package com.amaze.fileutilities.utilis.dialog_picker
+
+import android.Manifest.permission
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.core.content.ContextCompat
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.files.FileFilter
+import java.io.File
+
+internal fun File.hasParent(
+ context: Context,
+ writeable: Boolean,
+ filter: FileFilter
+) = betterParent(context, writeable, filter) != null
+
+internal fun File.isExternalStorage(context: Context) =
+ absolutePath == context.getExternalFilesDir()?.absolutePath
+
+internal fun File.isRoot() = absolutePath == "/"
+
+internal fun File.betterParent(
+ context: Context,
+ writeable: Boolean,
+ filter: FileFilter
+): File? {
+ val parentToUse = (
+ if (isExternalStorage(context)) {
+ // Emulated external storage's parent is empty so jump over it
+ context.getExternalFilesDir()?.parentFile?.parentFile
+ } else {
+ parentFile
+ }
+ ) ?: return null
+
+ if ((writeable && !parentToUse.canWrite()) || !parentToUse.canRead()) {
+ // We can't access this folder
+ return null
+ }
+
+ val folderContent =
+ parentToUse.listFiles()?.filter { filter?.invoke(it) ?: true } ?: emptyList()
+ if (folderContent.isEmpty()) {
+ // There is nothing in this folder most likely because we can't access files inside of it.
+ // We don't want to get stuck here.
+ return null
+ }
+
+ return parentToUse
+}
+
+internal fun File.jumpOverEmulated(context: Context): File {
+ val externalFileDir = context.getExternalFilesDir()
+ externalFileDir?.parentFile?.let { externalParentFile ->
+ if (absolutePath == externalParentFile.absolutePath) {
+ return externalFileDir
+ }
+ }
+ return this
+}
+
+internal fun File.friendlyName(context: Context) = when {
+ isExternalStorage(context) -> "External Storage"
+ isRoot() -> "Root"
+ else -> name
+}
+
+internal fun Context.hasPermission(permission: String): Boolean {
+ return ContextCompat.checkSelfPermission(this, permission) ==
+ PackageManager.PERMISSION_GRANTED
+}
+
+internal fun MaterialDialog.hasReadStoragePermission(): Boolean {
+ return windowContext.hasPermission(permission.READ_EXTERNAL_STORAGE)
+}
+
+internal fun MaterialDialog.hasWriteStoragePermission(): Boolean {
+ return windowContext.hasPermission(permission.WRITE_EXTERNAL_STORAGE)
+}
diff --git a/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/ViewExt.kt b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/ViewExt.kt
new file mode 100644
index 00000000..56443fd1
--- /dev/null
+++ b/app/src/main/java/com/amaze/fileutilities/utilis/dialog_picker/ViewExt.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021-2023 Arpit Khurana , Vishal Nehra ,
+ * Emmanuel Messulam, Raymond Lai and Contributors.
+ *
+ * This file is part of Amaze File Utilities.
+ *
+ * Amaze File Utilities is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Designed and developed by Aidan Follestad (@afollestad)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amaze.fileutilities.utilis.dialog_picker
+
+import android.view.View
+
+internal fun T.setVisible(visible: Boolean) {
+ visibility = if (visible) View.VISIBLE else View.INVISIBLE
+}
diff --git a/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt b/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt
index a5498727..4ad39931 100644
--- a/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt
+++ b/app/src/main/java/com/amaze/fileutilities/video_player/BaseVideoPlayerActivity.kt
@@ -62,7 +62,6 @@ import androidx.core.view.updateLayoutParams
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.afollestad.materialdialogs.files.FileFilter
import com.amaze.fileutilities.PermissionsActivity
import com.amaze.fileutilities.R
import com.amaze.fileutilities.audio_player.AudioPlayerService
@@ -75,6 +74,7 @@ import com.amaze.fileutilities.home_page.ui.transfer.TransferFragment
import com.amaze.fileutilities.utilis.PreferencesConstants
import com.amaze.fileutilities.utilis.Utils
import com.amaze.fileutilities.utilis.Utils.Companion.showProcessingDialog
+import com.amaze.fileutilities.utilis.dialog_picker.FileFilter
import com.amaze.fileutilities.utilis.getAppCommonSharedPreferences
import com.amaze.fileutilities.utilis.getExternalStorageDirectory
import com.amaze.fileutilities.utilis.getFileFromUri
diff --git a/app/src/main/res/layout/adapter_donation.xml b/app/src/main/res/layout/adapter_donation.xml
index 2a9b5ead..316b300c 100644
--- a/app/src/main/res/layout/adapter_donation.xml
+++ b/app/src/main/res/layout/adapter_donation.xml
@@ -1,7 +1,8 @@
-
+
-
\ No newline at end of file
diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-af-rZA/strings.xml
+++ b/app/src/main/res/values-af-rZA/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml
index 86dd0426..b1ddc05c 100644
--- a/app/src/main/res/values-ar-rSA/strings.xml
+++ b/app/src/main/res/values-ar-rSA/strings.xml
@@ -35,6 +35,8 @@
يحتاج التطبيق لأذونات قراءة الذاكرة لتحقيق الأداء الأمثل.
+ App needs notification permissions for optimal performance.
+
منح الأذن
منح
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-ca-rES/strings.xml
+++ b/app/src/main/res/values-ca-rES/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-da-rDK/strings.xml
+++ b/app/src/main/res/values-da-rDK/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml
index 0848e15c..1313fe1d 100644
--- a/app/src/main/res/values-de-rDE/strings.xml
+++ b/app/src/main/res/values-de-rDE/strings.xml
@@ -35,6 +35,8 @@
Die App benötigt Leserechte für eine optimale Performance.
+ App needs notification permissions for optimal performance.
+
Zugriff gewähren
Erlauben
@@ -681,6 +683,8 @@ Wir sind ein Team von Open-Source-Entwicklern, die hart daran arbeiten, ein werb
Datenübertragung zwischen Geräten über WLAN\nHohe Übertragungsgeschwindigkeiten mit Android WiFi-Direct
Wir benötigen Speicherrechte, um Zugriff auf Mediendateien zu erhalten
+
+ We need notification permissions to show app operations
Bevor wir beginnen…
@@ -960,6 +964,10 @@ Wir sind ein Team von Open-Source-Entwicklern, die hart daran arbeiten, ein werb
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-el-rGR/strings.xml
+++ b/app/src/main/res/values-el-rGR/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-en-rUS/strings.xml b/app/src/main/res/values-en-rUS/strings.xml
index eacef777..4cdb019e 100644
--- a/app/src/main/res/values-en-rUS/strings.xml
+++ b/app/src/main/res/values-en-rUS/strings.xml
@@ -5,7 +5,7 @@
Amaze Utilities
- File Utility app with inbuilt media players for all your needs.
+ File. Utility app with inbuilt media players for all your needs.
Amaze Image Viewer
@@ -15,7 +15,7 @@
Amaze Video Player (Dialog)
- Amaze Video Player (Full Screen)
+
Amaze Music Player
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index 1f436b89..14646e0f 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -35,6 +35,8 @@
La aplicación necesita permisos de lectura de almacenamiento para un rendimiento óptimo.
+ App needs notification permissions for optimal performance.
+
Otorgar permiso
Otorgar
@@ -75,7 +77,7 @@
Audios
- Videos
+ Video
Documentos
@@ -220,7 +222,7 @@
Luz Baja
- Memes
+ Español
Entrecejo
@@ -682,6 +684,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -961,6 +965,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-fr-rFR/strings.xml
+++ b/app/src/main/res/values-fr-rFR/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml
index 1619894b..7a0430f6 100644
--- a/app/src/main/res/values-hi-rIN/strings.xml
+++ b/app/src/main/res/values-hi-rIN/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-hu-rHU/strings.xml
+++ b/app/src/main/res/values-hu-rHU/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml
index ec67a04e..7eaa45ec 100644
--- a/app/src/main/res/values-it-rIT/strings.xml
+++ b/app/src/main/res/values-it-rIT/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml
index 99e6a89e..0d2977d0 100644
--- a/app/src/main/res/values-iw-rIL/strings.xml
+++ b/app/src/main/res/values-iw-rIL/strings.xml
@@ -35,6 +35,8 @@
ליישומון דרושה גישה לקרוא מהאחסון לביצועים מיטביים.
+ App needs notification permissions for optimal performance.
+
הענקת הרשאה
הענקה
@@ -679,6 +681,8 @@
העברת נתונים בין מכשירים דרך רשת אלחוטית\nמהירויות במהירות גבוהה עם Android Wi-Fi Direct
אנחנו צריכים הרשאות לאחסון כדי לגשת לקובצי המדיה שלך
+
+ We need notification permissions to show app operations
לפני שנתחיל…
@@ -958,6 +962,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml
index c0626769..e9b71b2c 100644
--- a/app/src/main/res/values-ja-rJP/strings.xml
+++ b/app/src/main/res/values-ja-rJP/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
アクセス許可を付与する
付与
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
始める前に…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index 9f36902a..2b4f7e33 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ml-rIN/strings.xml b/app/src/main/res/values-ml-rIN/strings.xml
index 3d60489e..0ddefe36 100644
--- a/app/src/main/res/values-ml-rIN/strings.xml
+++ b/app/src/main/res/values-ml-rIN/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
അനുമതി നൽകുക
നല്കുക
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-nl-rNL/strings.xml
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-no-rNO/strings.xml
+++ b/app/src/main/res/values-no-rNO/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 5b93b3c9..64866fd7 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -35,6 +35,8 @@
O aplicativo precisa de acesso ao armazenamento para ter um desempenho ideal.
+ App needs notification permissions for optimal performance.
+
Conceder permissão
Conceder
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 60232df9..d179c527 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -35,6 +35,8 @@
A necessidade de aplicação de permissões de leitura de armazenamento para um ótimo desempenho.
+ App needs notification permissions for optimal performance.
+
Conceder permissão
concedente
@@ -643,6 +645,8 @@
Transfira dados entre dispositivos por wifi\nAltas velocidades de transferência usando o android wifi direct
Precisamos de permissões de armazenamento para acessar seus arquivos de mídia
+
+ We need notification permissions to show app operations
Antes de começarmos…
@@ -917,6 +921,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-ro-rRO/strings.xml
+++ b/app/src/main/res/values-ro-rRO/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml
index 5475cfc5..1e43e300 100644
--- a/app/src/main/res/values-ru-rRU/strings.xml
+++ b/app/src/main/res/values-ru-rRU/strings.xml
@@ -35,6 +35,8 @@
Приложению требуется разрешение для чтения хранилища для оптимальной производительности.
+ App needs notification permissions for optimal performance.
+
Предоставьте доступ
Предоставить
@@ -476,7 +478,7 @@
Рейтинг
- CD
+ КД
Загрузчик
@@ -681,6 +683,8 @@
Передавайте данные между устройствами через Wi-Fi\nВысокая скорость передачи данных с помощью android Wi-Fi Direct
Нам нужно разрешение к хранилищу для доступа к вашим медиа-файлам
+
+ We need notification permissions to show app operations
До начала…
@@ -918,7 +922,7 @@
Apps newly installed (in days)
- Recently updated apps
+
Apps recently updated (in days)
@@ -959,6 +963,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-sr-rSP/strings.xml
+++ b/app/src/main/res/values-sr-rSP/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-sv-rSE/strings.xml
+++ b/app/src/main/res/values-sv-rSE/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml
index be7e1e47..56968918 100644
--- a/app/src/main/res/values-tr-rTR/strings.xml
+++ b/app/src/main/res/values-tr-rTR/strings.xml
@@ -35,6 +35,8 @@
Uygulamanın optimum performans için depolama okuma izinlerine ihtiyacı vardır.
+ App needs notification permissions for optimal performance.
+
İzin ver
Ver
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-uk-rUA/strings.xml
+++ b/app/src/main/res/values-uk-rUA/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml
index 944e6162..31b28db2 100644
--- a/app/src/main/res/values-vi-rVN/strings.xml
+++ b/app/src/main/res/values-vi-rVN/strings.xml
@@ -35,6 +35,8 @@
App needs storage read permissions for optimal performance.
+ App needs notification permissions for optimal performance.
+
Grant permission
Grant
@@ -681,6 +683,8 @@
Transfer data between devices through wifi\nHigh transfer speeds using android wifi direct
We need storage permissions to access your media files
+
+ We need notification permissions to show app operations
Before we start…
@@ -960,6 +964,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index f0e5c654..c35233c7 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -35,6 +35,8 @@
為提供最佳效能,App需要授予儲存空間存取權限。
+ App needs notification permissions for optimal performance.
+
授予權限
授權
@@ -683,6 +685,8 @@
在裝置之間以 wifi傳送資料\n利用 Android wifi direct嘅高速傳輸
我們需要授予儲存空間存取權限以讀取你嘅媒體檔案
+
+ We need notification permissions to show app operations
喺開始之前…
@@ -962,6 +966,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index e7c2bdab..8d0d3a52 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -35,6 +35,8 @@
為提供最佳效能,App需要授予儲存空間存取權限。
+ App needs notification permissions for optimal performance.
+
授予權限
授權
@@ -682,6 +684,8 @@
在裝置之間以 wifi傳送資料\n利用 Android wifi direct的高速傳輸
我們需要授予儲存空間存取權限以讀取你的媒體檔案
+
+ We need notification permissions to show app operations
在開始之前…
@@ -961,6 +965,10 @@
Retention by data size
Maximum data (in MBs) trash bin can store
+
+ Cleanup interval
+
+ Trigger auto-cleanup interval (hours)
Retention by days
diff --git a/build.gradle b/build.gradle
index 3dfb816a..69e53081 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,7 @@ buildscript {
repositories {
google()
mavenCentral()
+ gradlePluginPortal()
}
dependencies {
classpath "com.android.tools.build:gradle:7.3.1"
@@ -18,6 +19,7 @@ buildscript {
plugins {
id "com.diffplug.spotless" version "5.14.2"
+ id("com.google.devtools.ksp") version "1.8.10-1.0.9" apply false
}
allprojects {
diff --git a/gradle.properties b/gradle.properties
index b54dc1e4..40378899 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,7 +25,7 @@ kotlin.code.style=official
org.gradle.parallel=true
abiFilters=x86;x86_64;armeabi-v7a;arm64-v8a
# for macs, omit for other operating systems
-#org.gradle.java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
+org.gradle.java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
# https://github.com/usefulness/easylauncher-gradle-plugin/issues/408
android.disableResourceValidation=true
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 15e2be75..da886ee2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,6 +3,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
+ gradlePluginPortal()
jcenter() // Warning: this repository is going to shut down soon
maven { url "https://jitpack.io" }
maven { url "https://jcenter.bintray.com" }