diff --git a/app/src/main/java/com/amaze/fileutilities/audio_player/AudioPlayerService.kt b/app/src/main/java/com/amaze/fileutilities/audio_player/AudioPlayerService.kt index 07de4e1a..a8cb47f4 100644 --- a/app/src/main/java/com/amaze/fileutilities/audio_player/AudioPlayerService.kt +++ b/app/src/main/java/com/amaze/fileutilities/audio_player/AudioPlayerService.kt @@ -49,6 +49,7 @@ import com.amaze.fileutilities.utilis.ObtainableServiceBinder import com.amaze.fileutilities.utilis.PreferencesConstants import com.amaze.fileutilities.utilis.Utils import com.amaze.fileutilities.utilis.getAppCommonSharedPreferences +import com.amaze.fileutilities.utilis.startServiceSafely import com.google.android.exoplayer2.C import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem @@ -89,7 +90,7 @@ class AudioPlayerService : Service(), ServiceOperationCallback, OnPlayerRepeatin intent.putParcelableArrayListExtra(ARG_URI_LIST, ArrayList(uriList)) } intent.action = action - context.startService(intent) + context.startServiceSafely(intent, ARG_URI_LIST) } fun sendCancelBroadcast(context: Context) { 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/image_viewer/editor/EditImageActivity.kt b/app/src/main/java/com/amaze/fileutilities/image_viewer/editor/EditImageActivity.kt index 125aef85..91a6347c 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 @@ -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 @@ - + 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/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