diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3397d71f2..c99bcbd1e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -98,24 +98,21 @@ - - + - - + - --> - + @@ -294,6 +291,26 @@ android:autoRemoveFromRecents="true" android:theme="@style/Theme.MaterialFiles.Translucent" /> + + + + + + + + + + + + + + * All Rights Reserved. + */ + +package me.zhanghai.android.files.filelist + +import android.app.Dialog +import android.os.Bundle +import androidx.appcompat.app.AppCompatDialogFragment +import androidx.fragment.app.Fragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import kotlinx.parcelize.Parcelize +import me.zhanghai.android.files.R +import me.zhanghai.android.files.file.FileItem +import me.zhanghai.android.files.util.ParcelableArgs +import me.zhanghai.android.files.util.args +import me.zhanghai.android.files.util.putArgs +import me.zhanghai.android.files.util.show + +class ConfirmReplaceFileDialogFragment : AppCompatDialogFragment() { + private val args by args() + + private val listener: Listener + get() = requireParentFragment() as Listener + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val file = args.file + return MaterialAlertDialogBuilder(requireContext(), theme) + .setMessage(getString(R.string.file_replace_message_format, file.name)) + .setPositiveButton(android.R.string.ok) { _, _ -> listener.replaceFile(file) } + .setNegativeButton(android.R.string.cancel, null) + .create() + } + + companion object { + fun show(file: FileItem, fragment: Fragment) { + ConfirmReplaceFileDialogFragment().putArgs(Args(file)).show(fragment) + } + } + + @Parcelize + class Args(val file: FileItem) : ParcelableArgs + + interface Listener { + fun replaceFile(file: FileItem) + } +} diff --git a/app/src/main/java/me/zhanghai/android/files/filelist/FileListActivity.kt b/app/src/main/java/me/zhanghai/android/files/filelist/FileListActivity.kt index cb14fcdbb..982342861 100644 --- a/app/src/main/java/me/zhanghai/android/files/filelist/FileListActivity.kt +++ b/app/src/main/java/me/zhanghai/android/files/filelist/FileListActivity.kt @@ -50,22 +50,41 @@ class FileListActivity : AppActivity() { .apply { extraPath = path } } - class PickDirectoryContract : ActivityResultContract() { - override fun createIntent(context: Context, input: Path?): Intent = + class OpenFileContract : ActivityResultContract, Path?>() { + override fun createIntent(context: Context, input: List): Intent = FileListActivity::class.createIntent() - .setAction(Intent.ACTION_OPEN_DOCUMENT_TREE) - .apply { input?.let { extraPath = it } } + .setAction(Intent.ACTION_OPEN_DOCUMENT) + .setType(MimeType.ANY.value) + .addCategory(Intent.CATEGORY_OPENABLE) + .putExtra(Intent.EXTRA_MIME_TYPES, input.map { it.value }.toTypedArray()) override fun parseResult(resultCode: Int, intent: Intent?): Path? = if (resultCode == RESULT_OK) intent?.extraPath else null } - class PickFileContract : ActivityResultContract, Path?>() { - override fun createIntent(context: Context, input: List): Intent = + class CreateFileContract : ActivityResultContract, Path?>() { + override fun createIntent( + context: Context, + input: Triple + ): Intent = FileListActivity::class.createIntent() - .setAction(Intent.ACTION_OPEN_DOCUMENT) - .setType(MimeType.ANY.value) - .putExtra(Intent.EXTRA_MIME_TYPES, input.map { it.value }.toTypedArray()) + .setAction(Intent.ACTION_CREATE_DOCUMENT) + .setType(input.first.value) + .addCategory(Intent.CATEGORY_OPENABLE) + .apply { + input.second?.let { putExtra(Intent.EXTRA_TITLE, it) } + input.third?.let { extraPath = it } + } + + override fun parseResult(resultCode: Int, intent: Intent?): Path? = + if (resultCode == RESULT_OK) intent?.extraPath else null + } + + class OpenDirectoryContract : ActivityResultContract() { + override fun createIntent(context: Context, input: Path?): Intent = + FileListActivity::class.createIntent() + .setAction(Intent.ACTION_OPEN_DOCUMENT_TREE) + .apply { input?.let { extraPath = it } } override fun parseResult(resultCode: Int, intent: Intent?): Path? = if (resultCode == RESULT_OK) intent?.extraPath else null diff --git a/app/src/main/java/me/zhanghai/android/files/filelist/FileListAdapter.kt b/app/src/main/java/me/zhanghai/android/files/filelist/FileListAdapter.kt index 8ab0360b6..0d300aed3 100644 --- a/app/src/main/java/me/zhanghai/android/files/filelist/FileListAdapter.kt +++ b/app/src/main/java/me/zhanghai/android/files/filelist/FileListAdapter.kt @@ -134,10 +134,11 @@ class FileListAdapter( private fun isFileSelectable(file: FileItem): Boolean { val pickOptions = pickOptions ?: return true - return if (pickOptions.pickDirectory) { - file.attributes.isDirectory - } else { - !file.attributes.isDirectory && pickOptions.mimeTypes.any { it.match(file.mimeType) } + return when (pickOptions.mode) { + PickOptions.Mode.OPEN_FILE, PickOptions.Mode.CREATE_FILE -> + !file.attributes.isDirectory && + pickOptions.mimeTypes.any { it.match(file.mimeType) } + PickOptions.Mode.OPEN_DIRECTORY -> file.attributes.isDirectory } } diff --git a/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt b/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt index 24825720a..6050c495d 100644 --- a/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt +++ b/app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt @@ -25,6 +25,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.widget.EditText import android.widget.ProgressBar import android.widget.TextView import androidx.activity.OnBackPressedCallback @@ -38,6 +39,7 @@ import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat import androidx.core.view.GravityCompat +import androidx.core.view.isVisible import androidx.core.view.updatePaddingRelative import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment @@ -64,6 +66,7 @@ import me.zhanghai.android.files.databinding.FileListFragmentSpeedDialIncludeBin import me.zhanghai.android.files.file.FileItem import me.zhanghai.android.files.file.MimeType import me.zhanghai.android.files.file.asMimeTypeOrNull +import me.zhanghai.android.files.file.extension import me.zhanghai.android.files.file.fileProviderUri import me.zhanghai.android.files.file.isApk import me.zhanghai.android.files.file.isImage @@ -100,6 +103,8 @@ import me.zhanghai.android.files.util.Stateful import me.zhanghai.android.files.util.Success import me.zhanghai.android.files.util.addOnBackPressedCallback import me.zhanghai.android.files.util.args +import me.zhanghai.android.files.util.asFileName +import me.zhanghai.android.files.util.asFileNameOrNull import me.zhanghai.android.files.util.checkSelfPermission import me.zhanghai.android.files.util.copyText import me.zhanghai.android.files.util.create @@ -116,6 +121,7 @@ import me.zhanghai.android.files.util.getQuantityString import me.zhanghai.android.files.util.hasSw600Dp import me.zhanghai.android.files.util.isOrientationLandscape import me.zhanghai.android.files.util.putArgs +import me.zhanghai.android.files.util.setOnEditorConfirmActionListener import me.zhanghai.android.files.util.showToast import me.zhanghai.android.files.util.startActivitySafe import me.zhanghai.android.files.util.supportsExternalStorageManager @@ -127,11 +133,11 @@ import me.zhanghai.android.files.viewer.image.ImageViewerActivity import kotlin.math.roundToInt class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter.Listener, - OpenApkDialogFragment.Listener, ConfirmDeleteFilesDialogFragment.Listener, - CreateArchiveDialogFragment.Listener, RenameFileDialogFragment.Listener, - CreateFileDialogFragment.Listener, CreateDirectoryDialogFragment.Listener, - NavigateToPathDialogFragment.Listener, NavigationFragment.Listener, - ShowRequestAllFilesAccessRationaleDialogFragment.Listener, + ConfirmReplaceFileDialogFragment.Listener, OpenApkDialogFragment.Listener, + ConfirmDeleteFilesDialogFragment.Listener, CreateArchiveDialogFragment.Listener, + RenameFileDialogFragment.Listener, CreateFileDialogFragment.Listener, + CreateDirectoryDialogFragment.Listener, NavigateToPathDialogFragment.Listener, + NavigationFragment.Listener, ShowRequestAllFilesAccessRationaleDialogFragment.Listener, ShowRequestNotificationPermissionRationaleDialogFragment.Listener, ShowRequestNotificationPermissionInSettingsRationaleDialogFragment.Listener, ShowRequestStoragePermissionRationaleDialogFragment.Listener, @@ -280,23 +286,40 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. when (val action = intent.action ?: Intent.ACTION_VIEW) { Intent.ACTION_GET_CONTENT, Intent.ACTION_OPEN_DOCUMENT, Intent.ACTION_CREATE_DOCUMENT -> { - val readOnly = action == Intent.ACTION_GET_CONTENT + val mode = if (action == Intent.ACTION_CREATE_DOCUMENT) { + PickOptions.Mode.CREATE_FILE + } else { + PickOptions.Mode.OPEN_FILE + } val mimeType = intent.type?.asMimeTypeOrNull() ?: MimeType.ANY - val extraMimeTypes = intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES) - ?.mapNotNull { it.asMimeTypeOrNull() }?.takeIfNotEmpty() + val fileName = if (mode == PickOptions.Mode.CREATE_FILE) { + intent.getStringExtra(Intent.EXTRA_TITLE)?.asFileNameOrNull()?.value + ?: mimeType.extension?.let { "file.$it" } ?: "file" + } else { + null + } + val readOnly = action == Intent.ACTION_GET_CONTENT + val extraMimeTypes = if (mode == PickOptions.Mode.OPEN_FILE) { + intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES) + ?.mapNotNull { it.asMimeTypeOrNull() }?.takeIfNotEmpty() + } else { + null + } val mimeTypes = extraMimeTypes ?: listOf(mimeType) val localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false) - val allowMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) - // TODO: Actually support ACTION_CREATE_DOCUMENT. - pickOptions = PickOptions(readOnly, false, mimeTypes, localOnly, allowMultiple) + val allowMultiple = mode != PickOptions.Mode.CREATE_FILE && + intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false) + pickOptions = + PickOptions(mode, fileName, readOnly, mimeTypes, localOnly, allowMultiple) } Intent.ACTION_OPEN_DOCUMENT_TREE -> { val localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false) - pickOptions = PickOptions(false, true, emptyList(), localOnly, false) + pickOptions = PickOptions( + PickOptions.Mode.OPEN_DIRECTORY, null, false, emptyList(), localOnly, false + ) } ACTION_VIEW_DOWNLOADS -> path = Paths.get( - @Suppress("DEPRECATION") Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS ).path @@ -768,13 +791,14 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. val title = if (pickOptions == null) { getString(R.string.file_list_title) } else { - val titleRes = if (pickOptions.pickDirectory) { - R.plurals.file_list_title_pick_directory - } else { - R.plurals.file_list_title_pick_file - } val count = if (pickOptions.allowMultiple) Int.MAX_VALUE else 1 - getQuantityString(titleRes, count) + when (pickOptions.mode) { + PickOptions.Mode.OPEN_FILE -> + getQuantityString(R.plurals.file_list_title_open_file, count) + PickOptions.Mode.CREATE_FILE -> getString(R.string.file_list_title_create_file) + PickOptions.Mode.OPEN_DIRECTORY -> + getQuantityString(R.plurals.file_list_title_open_directory, count) + } } requireActivity().title = title updateSelectAllMenuItem() @@ -813,7 +837,7 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. flags = flags or (Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) } - if (pickOptions.pickDirectory) { + if (pickOptions.mode == PickOptions.Mode.OPEN_DIRECTORY) { flags = flags or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION } addFlags(flags) @@ -871,27 +895,27 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. AppBarLayoutExpandHackListener(binding.recyclerView) ) overlayActionMode.start(object : ToolbarActionMode.Callback { - override fun onToolbarActionModeStarted(toolbarActionMode: ToolbarActionMode) {} - - override fun onToolbarActionModeItemClicked( + override fun onToolbarActionModeMenuItemClicked( toolbarActionMode: ToolbarActionMode, item: MenuItem - ): Boolean = onOverlayActionModeItemClicked(toolbarActionMode, item) + ): Boolean = onOverlayActionModeMenuItemClicked(item) override fun onToolbarActionModeFinished(toolbarActionMode: ToolbarActionMode) { - onOverlayActionModeFinished(toolbarActionMode) + onOverlayActionModeFinished() } }) } } - private fun onOverlayActionModeItemClicked( - toolbarActionMode: ToolbarActionMode, - item: MenuItem - ): Boolean = + private fun onOverlayActionModeMenuItemClicked(item: MenuItem): Boolean = when (item.itemId) { R.id.action_pick -> { - pickFiles(viewModel.selectedFiles) + when (viewModel.pickOptions!!.mode) { + PickOptions.Mode.OPEN_FILE, + PickOptions.Mode.OPEN_DIRECTORY -> pickFiles(viewModel.selectedFiles) + PickOptions.Mode.CREATE_FILE -> + confirmReplaceFile(viewModel.selectedFiles.single()) + } true } R.id.action_cut -> { @@ -921,10 +945,25 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. else -> false } - private fun onOverlayActionModeFinished(toolbarActionMode: ToolbarActionMode) { + private fun onOverlayActionModeFinished() { viewModel.clearSelectedFiles() } + private fun confirmReplaceFile(file: FileItem, setFileName: Boolean = true) { + if (setFileName) { + val fileName = file.name + binding.bottomCreateFileNameEdit.setText(fileName) + binding.bottomCreateFileNameEdit.setSelection( + 0, fileName.asFileName().baseName.length + ) + } + ConfirmReplaceFileDialogFragment.show(file, this) + } + + override fun replaceFile(file: FileItem) { + pickFiles(fileItemSetOf(file)) + } + private fun cutFiles(files: FileItemSet) { viewModel.addToPasteState(false, files) viewModel.selectFiles(files, false) @@ -978,18 +1017,48 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. private fun updateBottomToolbar() { val pickOptions = viewModel.pickOptions if (pickOptions != null) { - if (!pickOptions.pickDirectory) { - if (bottomActionMode.isActive) { - bottomActionMode.finish() + bottomActionMode.setMenuResource(R.menu.file_list_pick_bottom) + val menu = bottomActionMode.menu + when (pickOptions.mode) { + PickOptions.Mode.CREATE_FILE -> { + bottomActionMode.setNavigationIcon( + R.drawable.check_icon_control_normal_24dp, R.string.save + ) + bottomActionMode.title = null + binding.bottomCreateFileNameEdit.isVisible = true + binding.bottomCreateFileNameEdit.setOnEditorConfirmActionListener { + onBottomActionModeNavigationIconClicked() + } + if (!viewModel.isCreateFileNameEditInitialized) { + val fileName = pickOptions.fileName!! + binding.bottomCreateFileNameEdit.setText(fileName) + binding.bottomCreateFileNameEdit.setSelection( + 0, fileName.asFileName().baseName.length + ) + binding.bottomCreateFileNameEdit.requestFocus() + viewModel.isCreateFileNameEditInitialized = true + } + menu.findItem(R.id.action_reset).isVisible = true + } + PickOptions.Mode.OPEN_DIRECTORY -> { + bottomActionMode.setNavigationIcon( + R.drawable.check_icon_control_normal_24dp, R.string.select + ) + val path = viewModel.currentPath + val navigationRoot = NavigationRootMapLiveData.valueCompat[path] + val name = navigationRoot?.getName(requireContext()) ?: path.name + bottomActionMode.title = + getString(R.string.file_list_open_current_directory_format, name) + binding.bottomCreateFileNameEdit.isVisible = false + menu.findItem(R.id.action_reset).isVisible = false + } + else -> { + if (bottomActionMode.isActive) { + bottomActionMode.finish() + } + return } - return } - bottomActionMode.setNavigationIcon(R.drawable.check_icon_control_normal_24dp) - val path = viewModel.currentPath - val navigationRoot = NavigationRootMapLiveData.valueCompat[path] - val name = navigationRoot?.getName(requireContext()) ?: path.name - bottomActionMode.title = - getString(R.string.file_list_select_current_directory_format, name) } else { val pasteState = viewModel.pasteState val files = pasteState.files @@ -999,7 +1068,9 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. } return } - bottomActionMode.setNavigationIcon(R.drawable.close_icon_control_normal_24dp) + bottomActionMode.setNavigationIcon( + R.drawable.close_icon_control_normal_24dp, R.string.close + ) val areAllFilesArchivePaths = files.all { it.path.isArchivePath } bottomActionMode.title = getString( if (pasteState.copy) { @@ -1012,6 +1083,7 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. R.string.file_list_paste_move_title_format }, files.size ) + binding.bottomCreateFileNameEdit.isVisible = false bottomActionMode.setMenuResource(R.menu.file_list_paste) val isCurrentPathReadOnly = viewModel.currentPath.fileSystem.isReadOnly bottomActionMode.menu.findItem(R.id.action_paste) @@ -1022,25 +1094,62 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. } if (!bottomActionMode.isActive) { bottomActionMode.start(object : ToolbarActionMode.Callback { - override fun onToolbarActionModeStarted(toolbarActionMode: ToolbarActionMode) {} + override fun onToolbarNavigationIconClicked(toolbarActionMode: ToolbarActionMode) { + onBottomActionModeNavigationIconClicked() + } - override fun onToolbarActionModeItemClicked( + override fun onToolbarActionModeMenuItemClicked( toolbarActionMode: ToolbarActionMode, item: MenuItem - ): Boolean = onBottomActionModeItemClicked(toolbarActionMode, item) + ): Boolean = onBottomActionModeMenuItemClicked(item) override fun onToolbarActionModeFinished(toolbarActionMode: ToolbarActionMode) { - onBottomActionModeFinished(toolbarActionMode) + onBottomActionModeFinished() } }) } } - private fun onBottomActionModeItemClicked( - toolbarActionMode: ToolbarActionMode, - item: MenuItem - ): Boolean = + private fun onBottomActionModeNavigationIconClicked() { + val pickOptions = viewModel.pickOptions + if (pickOptions != null) { + when (pickOptions.mode) { + PickOptions.Mode.CREATE_FILE -> { + val fileName = binding.bottomCreateFileNameEdit.text.toString() + if (fileName.isEmpty()) { + showToast(R.string.file_list_create_file_name_error_empty) + return + } + if (fileName.asFileNameOrNull() == null) { + showToast(R.string.file_list_create_file_name_error_invalid) + return + } + val file = getFileWithName(fileName) + if (file != null) { + confirmReplaceFile(file, false) + } else { + val path = viewModel.currentPath.resolve(fileName) + pickPaths(linkedSetOf(path)) + } + } + PickOptions.Mode.OPEN_DIRECTORY -> pickPaths(linkedSetOf(viewModel.currentPath)) + else -> bottomActionMode.finish() + } + } else { + bottomActionMode.finish() + } + } + + private fun onBottomActionModeMenuItemClicked(item: MenuItem): Boolean = when (item.itemId) { + R.id.action_reset -> { + val fileName = viewModel.pickOptions!!.fileName!! + binding.bottomCreateFileNameEdit.setText(fileName) + binding.bottomCreateFileNameEdit.setSelection( + 0, fileName.asFileName().baseName.length + ) + true + } R.id.action_paste -> { pasteFiles(currentPath) true @@ -1048,13 +1157,9 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. else -> false } - private fun onBottomActionModeFinished(toolbarActionMode: ToolbarActionMode) { + private fun onBottomActionModeFinished() { val pickOptions = viewModel.pickOptions - if (pickOptions != null) { - if (pickOptions.pickDirectory) { - pickPaths(linkedSetOf(viewModel.currentPath)) - } - } else { + if (pickOptions == null) { viewModel.clearPasteState() } } @@ -1097,8 +1202,12 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. if (pickOptions != null) { if (file.attributes.isDirectory) { navigateTo(file.path) - } else if (!pickOptions.pickDirectory) { - pickFiles(fileItemSetOf(file)) + } else { + when (pickOptions.mode) { + PickOptions.Mode.OPEN_FILE -> pickFiles(fileItemSetOf(file)) + PickOptions.Mode.CREATE_FILE -> confirmReplaceFile(file) + PickOptions.Mode.OPEN_DIRECTORY -> {} + } } return } @@ -1219,9 +1328,14 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. RenameFileDialogFragment.show(file, this) } - override fun hasFileWithName(name: String): Boolean { + override fun hasFileWithName(name: String): Boolean = getFileWithName(name) != null + + private fun getFileWithName(name: String): FileItem? { val fileListData = viewModel.fileListStateful - return fileListData is Success && fileListData.value.any { it.name == name } + if (fileListData !is Success) { + return null + } + return fileListData.value.find { it.name == name } } override fun renameFile(file: FileItem, newName: String) { @@ -1558,6 +1672,7 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. val recyclerView: RecyclerView, val bottomBarLayout: ViewGroup, val bottomToolbar: Toolbar, + val bottomCreateFileNameEdit: EditText, val speedDialView: SpeedDialView ) { companion object { @@ -1581,7 +1696,7 @@ class FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter. contentBinding.progress, contentBinding.errorText, contentBinding.emptyView, contentBinding.swipeRefreshLayout, contentBinding.recyclerView, bottomBarBinding.bottomBarLayout, bottomBarBinding.bottomToolbar, - speedDialBinding.speedDialView + bottomBarBinding.bottomCreateFileNameEdit, speedDialBinding.speedDialView ) } } diff --git a/app/src/main/java/me/zhanghai/android/files/filelist/FileListViewModel.kt b/app/src/main/java/me/zhanghai/android/files/filelist/FileListViewModel.kt index b31eccbc1..bafa4c659 100644 --- a/app/src/main/java/me/zhanghai/android/files/filelist/FileListViewModel.kt +++ b/app/src/main/java/me/zhanghai/android/files/filelist/FileListViewModel.kt @@ -139,6 +139,8 @@ class FileListViewModel : ViewModel() { _pickOptionsLiveData.value = value } + var isCreateFileNameEditInitialized: Boolean = false + private val _selectedFilesLiveData = MutableLiveData(fileItemSetOf()) val selectedFilesLiveData: LiveData get() = _selectedFilesLiveData diff --git a/app/src/main/java/me/zhanghai/android/files/filelist/PickOptions.kt b/app/src/main/java/me/zhanghai/android/files/filelist/PickOptions.kt index ab4f72f9a..1082d6fe1 100644 --- a/app/src/main/java/me/zhanghai/android/files/filelist/PickOptions.kt +++ b/app/src/main/java/me/zhanghai/android/files/filelist/PickOptions.kt @@ -8,9 +8,16 @@ package me.zhanghai.android.files.filelist import me.zhanghai.android.files.file.MimeType class PickOptions( + val mode: Mode, + val fileName: String?, val readOnly: Boolean, - val pickDirectory: Boolean, val mimeTypes: List, val localOnly: Boolean, val allowMultiple: Boolean -) +) { + enum class Mode { + OPEN_FILE, + CREATE_FILE, + OPEN_DIRECTORY + } +} diff --git a/app/src/main/java/me/zhanghai/android/files/navigation/EditBookmarkDirectoryDialogFragment.kt b/app/src/main/java/me/zhanghai/android/files/navigation/EditBookmarkDirectoryDialogFragment.kt index d14929423..e91a93958 100644 --- a/app/src/main/java/me/zhanghai/android/files/navigation/EditBookmarkDirectoryDialogFragment.kt +++ b/app/src/main/java/me/zhanghai/android/files/navigation/EditBookmarkDirectoryDialogFragment.kt @@ -30,9 +30,8 @@ import me.zhanghai.android.files.util.putState import me.zhanghai.android.files.util.setTextWithSelection class EditBookmarkDirectoryDialogFragment : AppCompatDialogFragment() { - private val pickPathLauncher = registerForActivityResult( - FileListActivity.PickDirectoryContract(), this::onPickPathResult - ) + private val openPathLauncher = + registerForActivityResult(FileListActivity.OpenDirectoryContract(), ::onOpenPathResult) private val args by args() @@ -75,10 +74,10 @@ class EditBookmarkDirectoryDialogFragment : AppCompatDialogFragment() { } private fun onEditPath() { - pickPathLauncher.launchSafe(path, this) + openPathLauncher.launchSafe(path, this) } - private fun onPickPathResult(result: Path?) { + private fun onOpenPathResult(result: Path?) { result ?: return path = result updatePathText() diff --git a/app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListFragment.kt b/app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListFragment.kt index b91f88df5..6852680f8 100644 --- a/app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListFragment.kt +++ b/app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListFragment.kt @@ -32,9 +32,8 @@ import me.zhanghai.android.files.util.putArgs import me.zhanghai.android.files.util.startActivitySafe class BookmarkDirectoryListFragment : Fragment(), BookmarkDirectoryListAdapter.Listener { - private val pickPathLauncher = registerForActivityResult( - FileListActivity.PickDirectoryContract(), this::onPickPathResult - ) + private val openPathLauncher = + registerForActivityResult(FileListActivity.OpenDirectoryContract(), ::onOpenPathResult) private lateinit var binding: BookmarkDirectoryListFragmentBinding @@ -98,10 +97,10 @@ class BookmarkDirectoryListFragment : Fragment(), BookmarkDirectoryListAdapter.L } private fun onAddBookmarkDirectory() { - pickPathLauncher.launchSafe(null, this) + openPathLauncher.launchSafe(null, this) } - private fun onPickPathResult(result: Path?) { + private fun onOpenPathResult(result: Path?) { result ?: return BookmarkDirectories.add(BookmarkDirectory(null, result)) } diff --git a/app/src/main/java/me/zhanghai/android/files/settings/PathPreference.kt b/app/src/main/java/me/zhanghai/android/files/settings/PathPreference.kt index b4d310fb4..50d841a0f 100644 --- a/app/src/main/java/me/zhanghai/android/files/settings/PathPreference.kt +++ b/app/src/main/java/me/zhanghai/android/files/settings/PathPreference.kt @@ -24,7 +24,7 @@ import me.zhanghai.android.files.util.startActivityForResultSafe import me.zhanghai.android.files.util.valueCompat abstract class PathPreference : Preference, PreferenceActivityResultListener { - private val pickDirectoryContract = FileListActivity.PickDirectoryContract() + private val openPathContract = FileListActivity.OpenDirectoryContract() var path: Path = persistedPath set(value) { @@ -76,13 +76,13 @@ abstract class PathPreference : Preference, PreferenceActivityResultListener { override fun onPreferenceClick(fragment: PreferenceFragmentCompat, preference: Preference) { fragment.startActivityForResultSafe( - pickDirectoryContract.createIntent(fragment.requireContext(), path), requestCode + openPathContract.createIntent(fragment.requireContext(), path), requestCode ) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == this.requestCode) { - val result = pickDirectoryContract.parseResult(resultCode, data) + val result = openPathContract.parseResult(resultCode, data) if (result != null) { path = result } diff --git a/app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerFragment.kt b/app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerFragment.kt index 122fefec4..3626fc48e 100644 --- a/app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerFragment.kt +++ b/app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerFragment.kt @@ -43,8 +43,8 @@ import me.zhanghai.android.files.util.viewModels import java.net.URI class EditSftpServerFragment : Fragment() { - private val pickPrivateKeyFileLauncher = registerForActivityResult( - FileListActivity.PickFileContract(), this::onPickPrivateKeyFileResult + private val openPrivateKeyFileLauncher = registerForActivityResult( + FileListActivity.OpenFileContract(), this::onOpenPrivateKeyFileResult ) private val args by args() @@ -204,10 +204,10 @@ class EditSftpServerFragment : Fragment() { if (!viewModel.readPrivateKeyFileState.value.isReady) { return } - pickPrivateKeyFileLauncher.launchSafe(listOf(MimeType.ANY), this) + openPrivateKeyFileLauncher.launchSafe(listOf(MimeType.ANY), this) } - private fun onPickPrivateKeyFileResult(result: Path?) { + private fun onOpenPrivateKeyFileResult(result: Path?) { result ?: return viewModel.readPrivateKeyFile(result) } diff --git a/app/src/main/java/me/zhanghai/android/files/ui/ToolbarActionMode.kt b/app/src/main/java/me/zhanghai/android/files/ui/ToolbarActionMode.kt index db94b2daa..b80709577 100644 --- a/app/src/main/java/me/zhanghai/android/files/ui/ToolbarActionMode.kt +++ b/app/src/main/java/me/zhanghai/android/files/ui/ToolbarActionMode.kt @@ -24,9 +24,9 @@ abstract class ToolbarActionMode( private var callback: Callback? = null init { - toolbar.setNavigationOnClickListener { finish() } + toolbar.setNavigationOnClickListener { callback?.onToolbarNavigationIconClicked(this) } toolbar.setOnMenuItemClickListener { - callback?.onToolbarActionModeItemClicked(this, it) ?: false + callback?.onToolbarActionModeMenuItemClicked(this, it) ?: false } } @@ -42,8 +42,15 @@ abstract class ToolbarActionMode( toolbar.navigationIcon = value } - fun setNavigationIcon(@DrawableRes iconRes: Int) { + var navigationContentDescription: CharSequence? + get() = toolbar.navigationContentDescription + set(value) { + toolbar.navigationContentDescription = value + } + + fun setNavigationIcon(@DrawableRes iconRes: Int, @StringRes contentDescriptionRes: Int) { toolbar.setNavigationIcon(iconRes) + toolbar.setNavigationContentDescription(contentDescriptionRes) } var title: CharSequence? @@ -104,9 +111,13 @@ abstract class ToolbarActionMode( protected abstract fun hide(bar: ViewGroup, animate: Boolean) interface Callback { - fun onToolbarActionModeStarted(toolbarActionMode: ToolbarActionMode) + fun onToolbarActionModeStarted(toolbarActionMode: ToolbarActionMode) {} + + fun onToolbarNavigationIconClicked(toolbarActionMode: ToolbarActionMode) { + toolbarActionMode.finish() + } - fun onToolbarActionModeItemClicked( + fun onToolbarActionModeMenuItemClicked( toolbarActionMode: ToolbarActionMode, item: MenuItem ): Boolean diff --git a/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt b/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt index 2ad3cfd9a..2b50e40fd 100644 --- a/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt +++ b/app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt @@ -163,11 +163,13 @@ val Fragment.mediumAnimTime val Fragment.longAnimTime get() = requireContext().longAnimTime -fun Fragment.showToast(textRes: Int, duration: Int = Toast.LENGTH_SHORT) = +fun Fragment.showToast(textRes: Int, duration: Int = Toast.LENGTH_SHORT) { requireContext().showToast(textRes, duration) +} -fun Fragment.showToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) = +fun Fragment.showToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) { requireContext().showToast(text, duration) +} fun Fragment.startActivitySafe(intent: Intent, options: Bundle? = null) { try { diff --git a/app/src/main/java/me/zhanghai/android/files/util/IntentPathExtensions.kt b/app/src/main/java/me/zhanghai/android/files/util/IntentPathExtensions.kt index 60a567205..62074bfca 100644 --- a/app/src/main/java/me/zhanghai/android/files/util/IntentPathExtensions.kt +++ b/app/src/main/java/me/zhanghai/android/files/util/IntentPathExtensions.kt @@ -37,6 +37,17 @@ var Intent.extraPath: Path? putExtra(EXTRA_PATH_URI, value?.toUri()?.toString()) } +val Intent.saveAsPath: Path? + get() { + val uri = + when (action) { + Intent.ACTION_VIEW -> data + Intent.ACTION_SEND -> getParcelableExtraSafe(Intent.EXTRA_STREAM) as? Uri + else -> null + } + return uri?.toPathOrNull() + } + private fun Uri.toPathOrNull(): Path? = when (scheme) { ContentResolver.SCHEME_FILE, null -> path?.takeIfNotEmpty()?.let { Paths.get(it) } diff --git a/app/src/main/java/me/zhanghai/android/files/viewer/saveas/SaveAsActivity.kt b/app/src/main/java/me/zhanghai/android/files/viewer/saveas/SaveAsActivity.kt new file mode 100644 index 000000000..a550f34c4 --- /dev/null +++ b/app/src/main/java/me/zhanghai/android/files/viewer/saveas/SaveAsActivity.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Hai Zhang + * All Rights Reserved. + */ + +package me.zhanghai.android.files.viewer.saveas + +import android.os.Bundle +import android.os.Environment +import java8.nio.file.Path +import java8.nio.file.Paths +import me.zhanghai.android.files.R +import me.zhanghai.android.files.app.AppActivity +import me.zhanghai.android.files.file.MimeType +import me.zhanghai.android.files.file.asMimeTypeOrNull +import me.zhanghai.android.files.filejob.FileJobService +import me.zhanghai.android.files.filelist.FileListActivity +import me.zhanghai.android.files.util.saveAsPath +import me.zhanghai.android.files.util.showToast + +class SaveAsActivity : AppActivity() { + private val createFileLauncher = + registerForActivityResult(FileListActivity.CreateFileContract(), ::onCreateFileResult) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val mimeType = intent.type?.asMimeTypeOrNull() ?: MimeType.ANY + val path = intent.saveAsPath + if (path == null) { + showToast(R.string.save_as_error) + finish() + return + } + val title = path.fileName.toString() + val initialPath = + Paths.get( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path + ) + createFileLauncher.launch(Triple(mimeType, title, initialPath)) + } + + private fun onCreateFileResult(result: Path?) { + if (result == null) { + finish() + return + } + FileJobService.save(intent.saveAsPath!!, result, this) + finish() + } +} diff --git a/app/src/main/res/layout-sw600dp-land/file_list_fragment_include.xml b/app/src/main/res/layout-sw600dp-land/file_list_fragment_include.xml index d63ce4d11..c24dabea8 100644 --- a/app/src/main/res/layout-sw600dp-land/file_list_fragment_include.xml +++ b/app/src/main/res/layout-sw600dp-land/file_list_fragment_include.xml @@ -38,7 +38,7 @@ - + diff --git a/app/src/main/res/layout/file_list_fragment_bottom_bar_include.xml b/app/src/main/res/layout/file_list_fragment_bottom_bar_include.xml index 44f531e33..512cefb80 100644 --- a/app/src/main/res/layout/file_list_fragment_bottom_bar_include.xml +++ b/app/src/main/res/layout/file_list_fragment_bottom_bar_include.xml @@ -26,6 +26,17 @@ android:paddingStart="@dimen/file_list_toolbar_padding_start" android:paddingEnd="@dimen/file_list_toolbar_padding_end_no_overflow" app:navigationIcon="@drawable/close_icon_control_normal_24dp" - app:popupTheme="?actionBarPopupTheme" /> + app:popupTheme="?actionBarPopupTheme"> + + + diff --git a/app/src/main/res/layout/file_list_fragment_include.xml b/app/src/main/res/layout/file_list_fragment_include.xml index a4837e422..a873bffd2 100644 --- a/app/src/main/res/layout/file_list_fragment_include.xml +++ b/app/src/main/res/layout/file_list_fragment_include.xml @@ -35,7 +35,7 @@ - + diff --git a/app/src/main/res/menu/file_list_pick_bottom.xml b/app/src/main/res/menu/file_list_pick_bottom.xml new file mode 100644 index 000000000..a69615f70 --- /dev/null +++ b/app/src/main/res/menu/file_list_pick_bottom.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index ac2eaad82..37f3d40b3 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -349,7 +349,7 @@ مجلد جديد ملفات - + تحديد الملف تحديد الملف تحديد الملفان @@ -357,7 +357,7 @@ تحديد الملفات تحديد الملفات - + تحديد المجلد تحديد المجلد تحديد المجلدان @@ -404,7 +404,7 @@ إنشاء اختصار افتح في نافذة جديدة %1$,d - تحديد ”%1$s“ + تحديد ”%1$s“ نقل %1$,d نسخ %1$,d استخراج %1$,d diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index b62997cc2..d2539b3aa 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -262,11 +262,11 @@ Нова папка Файлове - + Избиране на файл Избиране на файлове - + Избиране на папка Избиране на папки @@ -301,7 +301,7 @@ Създаване на пряк път Отваряне в нов прозорез %1$,d - Избиране на „%1$s” + Избиране на „%1$s” Преместване на %1$,d Копиране на %1$,d Извличане на %1$,d diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index af79b836d..0304ecd11 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -262,11 +262,11 @@ Carpeta nova Fitxers - + Seleccioneu un fitxer Seleccioneu els fitxers - + Seleccioneu una carpeta Seleccioneu les carpetes @@ -301,7 +301,7 @@ Crea una drecera Obre en una finestra nova %1$,d - Selecciona «%1$s» + Selecciona «%1$s» S\'està movent %1$,d S\'està copiant %1$,d S\'està extraient %1$,d diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index c586be458..4a172df2a 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -309,13 +309,13 @@ Nová složka Soubory - + Vyberte soubor Vyberte soubory Vyberte soubory Vyberte soubory - + Vyberte složku Vyberte složky Vyberte složky @@ -355,7 +355,7 @@ Přidat záložku Vytvořit zástupce Otevřít v novém okně - Vyberte \"%1$s\" + Vyberte \"%1$s\" Přesouvání %1$,d Kopírování %1$,d Rozbalování %1$,d diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5111eea69..d60dbd02e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -262,11 +262,11 @@ Neuer Ordner Dateien - + Wähle eine Datei aus Wähle Dateien aus - + Wähle einen Ordner aus Wähle Ordner aus @@ -301,7 +301,7 @@ Verknüpfung erstellen In neuem Fenster öffnen %1$,d - “%1$s” auswählen + “%1$s” auswählen Verschiebe %1$,d Kopiere %1$,d Extrahiere %1$,d diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 5b60262ed..71da4e161 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -253,11 +253,11 @@ Νέος φάκελος Αρχεία - + Επιλογή αρχείου Επιλογή αρχείων - + Επιλογή φακέλου Επιλογή φακέλων @@ -291,7 +291,7 @@ Προσθήκη σελιδοδείκτη Δημιουργία συντόμευσης Άνοιγμα σε νέο παράθυρο - Επιλογή “%1$s” + Επιλογή “%1$s” Μετακίνηση %1$,d Αντιγραφή %1$,d Εξαγωγή %1$,d diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 8927634c5..2b45b4377 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -286,12 +286,12 @@ Nueva carpeta Archivos - + Seleccionar un archivo Seleccionar archivos Seleccionar archivos - + Seleccionar una carpeta Seleccionar carpetas Seleccionar carpetas @@ -329,7 +329,7 @@ Crear atajo Abrir en una nueva ventana %1$,d - Seleccionar \"%1$s\" + Seleccionar \"%1$s\" Moviendo %1$,d Copiando %1$,d Extrayendo %1$,d diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index e364f9f10..91521a631 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -233,11 +233,11 @@ Karpeta berria Fitxategiak - + Hautatu fitxategia Hautatu fitxategiak - + Hautatu karpeta Hautatu karpetak @@ -264,7 +264,7 @@ Gehitu laster-marka Sortu laster-marka Ireki leiho berrian - Hautatu “%1$s” + Hautatu “%1$s” %1$,d lekuz aldatzen %1$,d kopiatzen %1$,d erauzten diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index d52942a66..6fe3514a9 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -254,11 +254,11 @@ شاخهٔ جدید پرونده‌ها - + گزینش یک پرونده گزینش پرونده‌ها - + گزینش یک شاخه گزینش شاخه‌ها @@ -288,7 +288,7 @@ افزودن نشانک ایجاد میان‌بر گشودن در پنجرهٔ جدید - گزینش «⁨%1$s⁩» + گزینش «⁨%1$s⁩» در حال جابه‌جایی %1$,d در حال رونوشت %1$,d در حال استخراج %1$,d diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 47dd1ffe5..719186a83 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -241,11 +241,11 @@ Uusi kansio Tiedostot - + Valitse tiedosto Valitse tiedostot - + Valitse kansio Valitse kansiot @@ -280,7 +280,7 @@ Luo pikakuvake Avaa uudessa ikkunassa %1$,d - Valitse “%1$s” + Valitse “%1$s” Siirretään %1$,d Kopioidaan %1$,d Puretaan %1$,d diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index b7e0b4ef0..779b2eb98 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -288,12 +288,12 @@ Nouveau dossier Fichiers - + Sélectionner un fichier Sélectionner les fichiers Sélectionner les fichiers - + Sélectionner un dossier Sélectionner les dossiers Sélectionner les dossiers @@ -331,7 +331,7 @@ Créer un raccourci Ouvrir dans une nouvelle fenêtre %1$,d - Selectionner “%1$s” + Selectionner “%1$s” Déplacer %1$,d Copier %1$,d Extraire %1$,d diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 2b2708a84..29201a1bf 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -253,11 +253,11 @@ Új mappa Fájlok - + Fájl kiválasztása Fájlok kiválasztása - + Mappa kiválasztása Mappák kiválasztása @@ -292,7 +292,7 @@ Parancsikon létrehozása Megnyitás új ablakban %1$,d - „%1$s” kiválasztása + „%1$s” kiválasztása %1$,d áthelyezése %1$,d másolása %1$,d fájl kibontása diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 9f4e53a08..44cd7470b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -238,10 +238,10 @@ Folder baru File - + Pilih berkas - + Pilih folder @@ -273,7 +273,7 @@ Buat pintasan Buka di jendela baru %1$,d - Pilih “%1$s” + Pilih “%1$s” Memindahkan %1$,d Menyalin %1$,d Mengekstrak %1$,d diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 5825e5f9d..3f8717bd2 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -264,11 +264,11 @@ Ný mappa Skrár - + Veldu skrá Veldu skrár - + Veldu möppu Veldu möppur @@ -303,7 +303,7 @@ Útbúa flýtileið Opna í nýjum glugga %1$,d - Velja “%1$s” + Velja “%1$s” Færi %1$,d Afrita %1$,d Afþjappa %1$,d diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index e966cf5e6..29b70d274 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -286,12 +286,12 @@ Nuova cartella File - + Seleziona un file Seleziona più file Seleziona più file - + Seleziona una cartella Seleziona cartelle Seleziona cartelle @@ -329,7 +329,7 @@ Crea segnalibro Apri in una nuova finestra %1$,d - Seleziona “%1$s” + Seleziona “%1$s” Spostamento di %1$,d Copia di %1$,d Estrazione di %1$,d diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 2907b4980..8ec0d8d24 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -309,13 +309,13 @@ תיקיה חדשה קבצים - + בחירת קובץ בחירת קבצים בחירת קבצים בחירת קבצים - + בחירת תיקייה בחירת תיקיות בחירת תיקיות @@ -356,7 +356,7 @@ יצירת קיצור דרך פתיחה בחלון חדש %1$,d - בחירת “%1$s” + בחירת “%1$s” מעביר את %1$,d מעתיק את %1$,d מחלץ את %1$,d diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 009773876..2c8e9f206 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -238,10 +238,10 @@ 新しいフォルダ ファイル - + ファイルを選択 - + フォルダを選択 @@ -273,7 +273,7 @@ ショートカットを作成 新しいウィンドウで開く %1$,d - 「%1$s」を選択 + 「%1$s」を選択 %1$,d を移動中 %1$,d をコピー中 %1$,d を展開中 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index c970b98d4..856fd3d6f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -238,10 +238,10 @@ 새 폴더 파일 - + 파일 선택 - + 폴더 선택 @@ -273,7 +273,7 @@ 바로가기 만들기 새 창에서 열기 %1$,d - “%1$s” 선택 + “%1$s” 선택 %1$,d개 항목 이동 %1$,d개 항목 복사 %1$,d개 항목 압축 해제 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 7853f45e0..422101127 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -303,13 +303,13 @@ Naujas aplankas Failai - + Pasirinkti failą Pasirinkti failus Pasirinkti failus Pasirinkti failus - + Pasirinkti aplanką Pasirinkti aplankus Pasirinkti aplankus @@ -350,7 +350,7 @@ Sukurti spartųjį kelią Atidaryti naujame lange %1$,d - Pasirinkti \"%1$s\" + Pasirinkti \"%1$s\" Perkeliama %1$,d Kopijuojama %1$,d Išskleidžiama %1$,d diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index ac887cabd..8a63534e5 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -262,11 +262,11 @@ Ny mappe Filer - + Velg en fil Velg filer - + Velg en mappe Velg mapper @@ -301,7 +301,7 @@ Opprett snarvei Åpne i et nytt vindu %1$,d - Velg «%1$s» + Velg «%1$s» Flytter %1$,d Kopierer %1$,d Pakker ut %1$,d diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 5472e408d..c407552da 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -262,11 +262,11 @@ Nieuwe map Bestanden - + Bestand selecteren Bestanden selecteren - + Map selecteren Mappen selecteren @@ -301,7 +301,7 @@ Snelkoppeling maken Openen in nieuw venster %1$,d - ‘%1$s’ selecteren + ‘%1$s’ selecteren Bezig met verplaatsen van %1$,d Bezig met kopiëren van %1$,d Bezig met uitpakken van %1$,d diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 6858a5e7c..72f3def3f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -310,13 +310,13 @@ Nowy katalog Pliki - + Wybierz plik Wybierz pliki Wybierz pliki Wybierz pliki - + Wybierz katalog Wybierz katalogi Wybierz katalogi @@ -357,7 +357,7 @@ Utwórz skrót Otwórz w nowym oknie %1$,d - Wybierz „%1$s” + Wybierz „%1$s” Przenoszenie %1$,d Kopiowanie %1$,d Rozpakowywanie %1$,d diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index bd7bb0ece..0baa9a706 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -286,12 +286,12 @@ Nova pasta Arquivos - + Selecione um arquivo Selecionar arquivos Selecionar arquivos - + Selecione uma pasta Selecionar pastas Selecionar pastas @@ -329,7 +329,7 @@ Criar atalho Abrir em uma nova janela %1$,d - Selecionar “%1$s” + Selecionar “%1$s” Movendo %1$,d Copiando %1$,d Extraindo %1$,d diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index c48d20b8f..3eeb17b83 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -288,12 +288,12 @@ Nova pasta Ficheiros - + Selecionar ficheiro Selecionar ficheiros Selecionar ficheiros - + Selecionar pasta Selecionar pastas Selecionar pastas @@ -331,7 +331,7 @@ Criar atalho Abrir em nova janela %1$,d - “%1$s” selecionado + “%1$s” selecionado A mover %1$,d A copiar %1$,d A extrair %1$,d diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 6210c9757..b32939594 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -286,12 +286,12 @@ Dosar nou Fișiere - + Selectează fișierul Selectează fișierele Selectează fișierele - + Selectează dosarul Selectează dosarele Selectează dosarele @@ -329,7 +329,7 @@ Creează scurtătură Deschide într-o fereastră nouă %1$,d - Selectat “%1$s” + Selectat “%1$s” Se mută %1$,d Se copiază %1$,d Se extrag %1$,d diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6643ca4b6..e94969492 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -312,13 +312,13 @@ Новая папка Файлы - + Выбрать файл Выбрать файлы Выбрать файлы Выбрать файлы - + Выбрать папку Выбрать папки Выбрать папки @@ -359,7 +359,7 @@ Создать ярлык Открыть в новом окне %1$,d - Выбрать “%1$s” + Выбрать “%1$s” Перемещение %1$,d Копирование %1$,d Извлечение %1$,d diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 16eec2d1f..18398b6f0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -239,11 +239,11 @@ Yeni klasör Dosyalar - + Bir dosya seç Dosyaları seç - + Bir klasör seç Klasörleri seç @@ -270,7 +270,7 @@ Yer işareti ekle Kısayol oluştur Yeni pencerede aç - “%1$s”`i seç + “%1$s”`i seç %1$,d taşınıyor %1$,d kopyalanıyor %1$,d çıkartılıyor diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3d49c4135..e41e7c7c3 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -312,13 +312,13 @@ Нова тека Файли - + Вибрати файл Вибрати файли Вибрати файли Вибрати файли - + Вибарти теку Вибрати теки Вибрати теки @@ -359,7 +359,7 @@ Створити скорочення Відкрити в новому вікні %1$,d - Вибрати “%1$s” + Вибрати “%1$s” Переміщення %1$,d Копіювання %1$,d Видобування %1$,d diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 17d02a4af..78af23044 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -228,10 +228,10 @@ Thư mục mới Tệp - + Chọn tệp - + Chọn thư mục @@ -258,7 +258,7 @@ Thêm đánh dấu Tạo lối tắt Mở trong cửa sổ mới - Chọn “%1$s” + Chọn “%1$s” Đang di chuyển %1$,d Đang sao chép %1$,d Đang giải nén %1$,d diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index d1a604a26..2721c003f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -215,6 +215,7 @@ 文件名不能为空 无效文件名 已存在同名的文件 + 替换“%1$s”? 删除“%1$s”? 删除文件夹“%1$s”和它的内容? @@ -236,10 +237,11 @@ 新建文件夹 文件 - + 选择文件 - + 保存文件 + 选择文件夹 @@ -271,7 +273,8 @@ 创建快捷方式 在新窗口中打开 %1$,d - 选择“%1$s” + 文件名 + 选择“%1$s” 移动 %1$,d 复制 %1$,d 提取 %1$,d @@ -541,6 +544,9 @@ 归档文件查看器 图片查看器 %1$,d/%2$,d + 另存为 + 保存时发生错误 + 已将“%1$s”保存到“%2$s” 文本编辑器 %1$s *%1$s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 10ccbca41..2313926ca 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -215,6 +215,7 @@ 檔案名稱不能為空 無效的檔案名稱 已存在同名的檔案 + 取代「%1$s」? 刪除「%1$s」? 刪除資料夾「%1$s」和它的內容? @@ -236,10 +237,11 @@ 新資料夾 檔案 - + 選取檔案 - + 儲存檔案 + 選取資料夾 @@ -271,7 +273,8 @@ 建立捷徑 在新視窗中開啟 %1$,d - 選取「%1$s」 + 檔案名稱 + 選取「%1$s」 移動 %1$,d 複製 %1$,d 解開 %1$,d @@ -541,6 +544,9 @@ 封存檔檢視器 圖片檢視器 %1$,d/%2$,d + 另存為 + 儲存時發生錯誤 + 已將「%1$s」儲存到「%2$s」 文字編輯器 %1$s *%1$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6ac6e9353..b0c9d31df 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -241,6 +241,7 @@ File name cannot be empty Invalid file name A file with this name already exists + Replace “%1$s”? Delete “%1$s”? Delete folder “%1$s” and its contents? @@ -265,11 +266,12 @@ New folder Files - + Select a file Select files - + Save file + Select a folder Select folders @@ -308,7 +310,10 @@ %1$,d @string/file_item_action_extract @string/file_item_action_archive - Select “%1$s” + File name + @string/file_name_error_empty + @string/file_name_error_invalid + Select “%1$s” Moving %1$,d Copying %1$,d Extracting %1$,d @@ -681,6 +686,9 @@ Image viewer %1$,d/%2$,d @string/file_delete_message_file_format + Save as + Error while saving file + “%1$s” has been saved to “%2$s” Text editor %1$s *%1$s