Merge pull request #74 from SimpleMobileTools/master

ups
This commit is contained in:
solokot 2019-11-10 00:16:31 +03:00 committed by GitHub
commit 25b360427d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 287 additions and 268 deletions

View file

@ -7,7 +7,7 @@ buildscript {
propMinSdkVersion = 21
propTargetSdkVersion = propCompileSdkVersion
propVersionCode = 1
propVersionName = '5.18.12'
propVersionName = '5.19.2'
kotlin_version = '1.3.50'
}
@ -17,7 +17,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

View file

@ -75,7 +75,8 @@ class AboutActivity : BaseSimpleActivity() {
baseConfig.wasBeforeAskingShown = true
about_email.movementMethod = LinkMovementMethod.getInstance()
about_email.setOnClickListener(null)
ConfirmationDialog(this, "", R.string.before_asking_question_read_faq, R.string.read_it, R.string.skip) {
val msg = "${getString(R.string.before_asking_question_read_faq)}\n\n${getString(R.string.make_sure_latest)}"
ConfirmationDialog(this, msg, 0, R.string.read_it, R.string.skip) {
about_faq_label.performClick()
}
}

View file

@ -21,6 +21,7 @@ import com.simplemobiletools.commons.asynctasks.CopyMoveTask
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.ExportSettingsDialog
import com.simplemobiletools.commons.dialogs.FileConflictDialog
import com.simplemobiletools.commons.dialogs.WritePermissionDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.interfaces.CopyMoveListener
@ -285,6 +286,28 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
}
}
fun handleOTGPermission(callback: (success: Boolean) -> Unit) {
if (baseConfig.OTGTreeUri.isNotEmpty()) {
callback(true)
return
}
funAfterSAFPermission = callback
WritePermissionDialog(this, true) {
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
if (resolveActivity(packageManager) == null) {
type = "*/*"
}
if (resolveActivity(packageManager) != null) {
startActivityForResult(this, OPEN_DOCUMENT_TREE_OTG)
} else {
toast(R.string.unknown_error_occurred)
}
}
}
}
fun copyMoveFilesTo(fileDirItems: ArrayList<FileDirItem>, source: String, destination: String, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean,
copyHidden: Boolean, callback: (destinationPath: String) -> Unit) {
if (source == destination) {
@ -292,7 +315,7 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
return
}
if (!File(destination).exists()) {
if (!getDoesFilePathExist(destination)) {
toast(R.string.invalid_destination)
return
}
@ -359,14 +382,14 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
val newName = String.format("%s(%d).%s", file.nameWithoutExtension, fileIndex, file.extension)
newFile = File(file.parent, newName)
fileIndex++
} while (File(newFile!!.absolutePath).exists())
} while (getDoesFilePathExist(newFile!!.absolutePath))
return newFile
}
private fun startCopyMove(files: ArrayList<FileDirItem>, destinationPath: String, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean, copyHidden: Boolean) {
val availableSpace = destinationPath.getAvailableStorageB()
val sumToCopy = files.sumByLong { it.getProperSize(copyHidden) }
if (sumToCopy < availableSpace) {
val sumToCopy = files.sumByLong { it.getProperSize(applicationContext, copyHidden) }
if (availableSpace == -1L || sumToCopy < availableSpace) {
checkConflicts(files, destinationPath, 0, LinkedHashMap()) {
toast(if (isCopyOperation) R.string.copying else R.string.moving)
val pair = Pair(files, destinationPath)
@ -387,7 +410,7 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
val file = files[index]
val newFileDirItem = FileDirItem("$destinationPath/${file.name}", file.name, file.isDirectory)
if (File(newFileDirItem.path).exists()) {
if (getDoesFilePathExist(newFileDirItem.path)) {
FileConflictDialog(this, newFileDirItem, files.size > 1) { resolution, applyForAll ->
if (applyForAll) {
conflictResolutions.clear()

View file

@ -9,9 +9,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withC
import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.formatSize
import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor
import com.simplemobiletools.commons.extensions.isGif
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.models.FileDirItem
import com.simplemobiletools.commons.views.MyRecyclerView
import kotlinx.android.synthetic.main.filepicker_list_item.view.*
@ -21,6 +19,7 @@ class FilepickerItemsAdapter(activity: BaseSimpleActivity, val fileDirItems: Lis
private val folderDrawable = activity.resources.getColoredDrawableWithColor(R.drawable.ic_folder_vector, textColor)
private val fileDrawable = activity.resources.getColoredDrawableWithColor(R.drawable.ic_file_vector, textColor)
private val hasOTGConnected = activity.hasOTGConnected()
init {
folderDrawable.alpha = 180
@ -76,7 +75,7 @@ class FilepickerItemsAdapter(activity: BaseSimpleActivity, val fileDirItems: Lis
.centerCrop()
.error(fileDrawable)
val itemToLoad = if (fileDirItem.name.endsWith(".apk", true)) {
var itemToLoad = if (fileDirItem.name.endsWith(".apk", true)) {
val packageInfo = context.packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES)
if (packageInfo != null) {
val appInfo = packageInfo.applicationInfo
@ -94,6 +93,10 @@ class FilepickerItemsAdapter(activity: BaseSimpleActivity, val fileDirItems: Lis
if (itemToLoad.toString().isGif()) {
Glide.with(activity).asBitmap().load(itemToLoad).apply(options).into(list_item_icon)
} else {
if (hasOTGConnected && itemToLoad is String && activity.isPathOnOTG(itemToLoad)) {
itemToLoad = itemToLoad.getOTGPublicPath(activity)
}
Glide.with(activity).load(itemToLoad).transition(withCrossFade()).apply(options).into(list_item_icon)
}
}

View file

@ -63,10 +63,10 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
mMaxSize = 0
for (file in mFiles) {
if (file.size == 0L) {
file.size = file.getProperSize(copyHidden)
file.size = file.getProperSize(activity, copyHidden)
}
val newPath = "$mDestinationPath/${file.name}"
val fileExists = File(newPath).exists()
val fileExists = activity.getDoesFilePathExist(newPath)
if (getConflictResolution(conflictResolutions, newPath) != CONFLICT_SKIP || !fileExists) {
mMaxSize += (file.size / 1000).toInt()
}
@ -81,13 +81,13 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
try {
val newPath = "$mDestinationPath/${file.name}"
var newFileDirItem = FileDirItem(newPath, newPath.getFilenameFromPath(), file.isDirectory)
if (File(newPath).exists()) {
if (activity.getDoesFilePathExist(newPath)) {
val resolution = getConflictResolution(conflictResolutions, newPath)
if (resolution == CONFLICT_SKIP) {
mFileCountToCopy--
continue
} else if (resolution == CONFLICT_OVERWRITE) {
newFileDirItem.isDirectory = if (File(newPath).exists()) File(newPath).isDirectory else activity.getSomeDocumentFile(newPath)!!.isDirectory
newFileDirItem.isDirectory = if (activity.getDoesFilePathExist(newPath)) File(newPath).isDirectory else activity.getSomeDocumentFile(newPath)!!.isDirectory
activity.deleteFileBg(newFileDirItem, true)
} else if (resolution == CONFLICT_KEEP_BOTH) {
val newFile = activity.getAlternativeFile(File(newFileDirItem.path))
@ -180,19 +180,35 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
return
}
val children = File(source.path).list()
for (child in children) {
val newPath = "$destinationPath/$child"
if (File(newPath).exists()) {
continue
}
if (activity.isPathOnOTG(source.path)) {
val children = activity.getDocumentFile(source.path)?.listFiles() ?: return
for (child in children) {
val newPath = "$destinationPath/${child.name}"
if (File(newPath).exists()) {
continue
}
val oldFile = File(source.path, child)
val oldFileDirItem = oldFile.toFileDirItem()
val newFileDirItem = FileDirItem(newPath, newPath.getFilenameFromPath(), oldFile.isDirectory)
copy(oldFileDirItem, newFileDirItem)
val oldPath = "${source.path}/${child.name}"
val oldFileDirItem = FileDirItem(oldPath, child.name!!, child.isDirectory, 0, child.length())
val newFileDirItem = FileDirItem(newPath, child.name!!, child.isDirectory)
copy(oldFileDirItem, newFileDirItem)
}
mTransferredFiles.add(source)
} else {
val children = File(source.path).list()
for (child in children) {
val newPath = "$destinationPath/$child"
if (activity.getDoesFilePathExist(newPath)) {
continue
}
val oldFile = File(source.path, child)
val oldFileDirItem = oldFile.toFileDirItem(activity)
val newFileDirItem = FileDirItem(newPath, newPath.getFilenameFromPath(), oldFile.isDirectory)
copy(oldFileDirItem, newFileDirItem)
}
mTransferredFiles.add(source)
}
mTransferredFiles.add(source)
}
private fun copyFile(source: FileDirItem, destination: FileDirItem) {
@ -217,7 +233,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
mDocuments[directory] = activity.getDocumentFile(directory)
}
out = activity.getFileOutputStreamSync(destination.path, source.path.getMimeType(), mDocuments[directory])
inputStream = activity.getFileInputStreamSync(source.path)
inputStream = activity.getFileInputStreamSync(source.path)!!
var copiedSize = 0L
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
@ -231,7 +247,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
out?.flush()
if (source.size == copiedSize && File(destination.path).exists()) {
if (source.size == copiedSize && activity.getDoesFilePathExist(destination.path)) {
mTransferredFiles.add(source)
if (activity.baseConfig.keepLastModified) {
copyOldLastModified(source.path, destination.path)

View file

@ -5,7 +5,6 @@ import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import kotlinx.android.synthetic.main.dialog_export_settings.view.*
import java.io.File
class ExportSettingsDialog(val activity: BaseSimpleActivity, val defaultFilename: String, callback: (path: String) -> Unit) {
init {
@ -39,7 +38,7 @@ class ExportSettingsDialog(val activity: BaseSimpleActivity, val defaultFilename
return@setOnClickListener
}
if (File(newPath).exists()) {
if (activity.getDoesFilePathExist(newPath)) {
val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newPath.getFilenameFromPath())
ConfirmationDialog(activity, title) {
callback(newPath)

View file

@ -44,11 +44,11 @@ class FilePickerDialog(val activity: BaseSimpleActivity,
private var mDialogView = activity.layoutInflater.inflate(R.layout.dialog_filepicker, null)
init {
if (!File(currPath).exists()) {
if (!activity.getDoesFilePathExist(currPath)) {
currPath = activity.internalStoragePath
}
if (!File(currPath).isDirectory) {
if (!activity.getIsPathDirectory(currPath)) {
currPath = currPath.getParentPath()
}
@ -174,9 +174,16 @@ class FilePickerDialog(val activity: BaseSimpleActivity,
}
private fun verifyPath() {
val file = File(currPath)
if ((pickFile && file.isFile) || (!pickFile && file.isDirectory)) {
sendSuccess()
if (activity.isPathOnOTG(currPath)) {
val fileDocument = activity.getSomeDocumentFile(currPath) ?: return
if ((pickFile && fileDocument.isFile) || (!pickFile && fileDocument.isDirectory)) {
sendSuccess()
}
} else {
val file = File(currPath)
if ((pickFile && file.isFile) || (!pickFile && file.isDirectory)) {
sendSuccess()
}
}
}
@ -191,6 +198,14 @@ class FilePickerDialog(val activity: BaseSimpleActivity,
}
private fun getItems(path: String, getProperFileSize: Boolean, callback: (List<FileDirItem>) -> Unit) {
if (activity.isPathOnOTG(path)) {
activity.getOTGItems(path, showHidden, getProperFileSize, callback)
} else {
getRegularItems(path, getProperFileSize, callback)
}
}
private fun getRegularItems(path: String, getProperFileSize: Boolean, callback: (List<FileDirItem>) -> Unit) {
val items = ArrayList<FileDirItem>()
val base = File(path)
val files = base.listFiles()

View file

@ -10,15 +10,15 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.commons.helpers.sumByInt
import com.simplemobiletools.commons.helpers.sumByLong
import com.simplemobiletools.commons.models.FileDirItem
import kotlinx.android.synthetic.main.dialog_properties.view.*
import kotlinx.android.synthetic.main.property_item.view.*
import java.io.File
import java.io.FileNotFoundException
import java.util.*
class PropertiesDialog() {
@ -35,7 +35,7 @@ class PropertiesDialog() {
* @param countHiddenItems toggle determining if we will count hidden files themselves and their sizes (reasonable only at directory properties)
*/
constructor(activity: Activity, path: String, countHiddenItems: Boolean = false) : this() {
if (!File(path).exists()) {
if (!activity.getDoesFilePathExist(path)) {
activity.toast(String.format(activity.getString(R.string.source_file_doesnt_exist), path))
return
}
@ -44,16 +44,16 @@ class PropertiesDialog() {
mInflater = LayoutInflater.from(activity)
mResources = activity.resources
val view = mInflater.inflate(R.layout.dialog_properties, null)
mPropertyView = view.properties_holder
mPropertyView = view.properties_holder!!
val fileDirItem = FileDirItem(path, path.getFilenameFromPath(), File(path).isDirectory, 0, 0, File(path).lastModified())
val fileDirItem = FileDirItem(path, path.getFilenameFromPath(), activity.getIsPathDirectory(path))
addProperty(R.string.name, fileDirItem.name)
addProperty(R.string.path, fileDirItem.getParentPath())
addProperty(R.string.size, "", R.id.properties_size)
ensureBackgroundThread {
val fileCount = fileDirItem.getProperFileCount(countHiddenItems)
val size = fileDirItem.getProperSize(countHiddenItems).formatSize()
val fileCount = fileDirItem.getProperFileCount(activity, countHiddenItems)
val size = fileDirItem.getProperSize(activity, countHiddenItems).formatSize()
activity.runOnUiThread {
view.findViewById<TextView>(R.id.properties_size).property_value.text = size
@ -73,11 +73,16 @@ class PropertiesDialog() {
val dateModified = cursor.getLongValue(MediaStore.Images.Media.DATE_MODIFIED) * 1000L
updateLastModified(activity, view, dateModified)
} else {
updateLastModified(activity, view, fileDirItem.modified)
updateLastModified(activity, view, fileDirItem.getLastModified(activity))
}
}
val exif = ExifInterface(fileDirItem.path)
val exif = if (isNougatPlus() && activity.isPathOnOTG(fileDirItem.path)) {
ExifInterface((activity as BaseSimpleActivity).getFileInputStreamSync(fileDirItem.path))
} else {
ExifInterface(fileDirItem.path)
}
val latLon = FloatArray(2)
if (exif.getLatLong(latLon)) {
activity.runOnUiThread {
@ -96,7 +101,7 @@ class PropertiesDialog() {
when {
fileDirItem.isDirectory -> {
addProperty(R.string.direct_children_count, fileDirItem.getDirectChildrenCount(countHiddenItems).toString())
addProperty(R.string.direct_children_count, fileDirItem.getDirectChildrenCount(activity, countHiddenItems).toString())
addProperty(R.string.files_count, "", R.id.properties_file_count)
}
fileDirItem.path.isImageSlow() -> {
@ -117,13 +122,13 @@ class PropertiesDialog() {
}
if (fileDirItem.isDirectory) {
addProperty(R.string.last_modified, fileDirItem.modified.formatDate(activity))
addProperty(R.string.last_modified, fileDirItem.getLastModified(activity).formatDate(activity))
} else {
addProperty(R.string.last_modified, "", R.id.properties_last_modified)
try {
addExifProperties(path, activity)
} catch (e: FileNotFoundException) {
activity.toast(R.string.unknown_error_occurred)
} catch (e: Exception) {
activity.showErrorToast(e)
return
}
}
@ -157,7 +162,7 @@ class PropertiesDialog() {
val fileDirItems = ArrayList<FileDirItem>(paths.size)
paths.forEach {
val fileDirItem = FileDirItem(it, it.getFilenameFromPath(), File(it).isDirectory, 0, 0, File(it).lastModified())
val fileDirItem = FileDirItem(it, it.getFilenameFromPath(), activity.getIsPathDirectory(it))
fileDirItems.add(fileDirItem)
}
@ -172,8 +177,8 @@ class PropertiesDialog() {
addProperty(R.string.files_count, "", R.id.properties_file_count)
ensureBackgroundThread {
val fileCount = fileDirItems.sumByInt { it.getProperFileCount(countHiddenItems) }
val size = fileDirItems.sumByLong { it.getProperSize(countHiddenItems) }.formatSize()
val fileCount = fileDirItems.sumByInt { it.getProperFileCount(activity, countHiddenItems) }
val size = fileDirItems.sumByLong { it.getProperSize(activity, countHiddenItems) }.formatSize()
activity.runOnUiThread {
view.findViewById<TextView>(R.id.properties_size).property_value.text = size
view.findViewById<TextView>(R.id.properties_file_count).property_value.text = fileCount.toString()
@ -188,7 +193,12 @@ class PropertiesDialog() {
}
private fun addExifProperties(path: String, activity: Activity) {
val exif = ExifInterface(path)
val exif = if (isNougatPlus() && activity.isPathOnOTG(path)) {
ExifInterface((activity as BaseSimpleActivity).getFileInputStreamSync(path))
} else {
ExifInterface(path)
}
val dateTaken = exif.getExifDateTaken(activity)
if (dateTaken.isNotEmpty()) {
addProperty(R.string.date_taken, dateTaken)

View file

@ -5,7 +5,6 @@ import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import kotlinx.android.synthetic.main.dialog_rename_item.view.*
import java.io.File
import java.util.*
class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val callback: (newPath: String) -> Unit) {
@ -16,7 +15,7 @@ class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val c
var name = fullName
val view = activity.layoutInflater.inflate(R.layout.dialog_rename_item, null).apply {
if (dotAt > 0 && !File(path).isDirectory) {
if (dotAt > 0 && !activity.getIsPathDirectory(path)) {
name = fullName.substring(0, dotAt)
val extension = fullName.substring(dotAt + 1)
rename_item_extension.setText(extension)
@ -59,13 +58,13 @@ class RenameItemDialog(val activity: BaseSimpleActivity, val path: String, val c
newName += ".$newExtension"
}
if (!File(path).exists()) {
if (!activity.getDoesFilePathExist(path)) {
activity.toast(String.format(activity.getString(R.string.source_file_doesnt_exist), path))
return@setOnClickListener
}
val newPath = "${path.getParentPath()}/$newName"
if (File(newPath).exists()) {
if (activity.getDoesFilePathExist(newPath)) {
activity.toast(R.string.name_taken)
return@setOnClickListener
}

View file

@ -6,7 +6,6 @@ import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import kotlinx.android.synthetic.main.dialog_rename_items.*
import kotlinx.android.synthetic.main.dialog_rename_items.view.*
import java.io.File
import java.util.*
class RenameItemsDialog(val activity: BaseSimpleActivity, val paths: ArrayList<String>, val callback: () -> Unit) {
@ -39,7 +38,7 @@ class RenameItemsDialog(val activity: BaseSimpleActivity, val paths: ArrayList<S
return@setOnClickListener
}
val validPaths = paths.filter { File(it).exists() }
val validPaths = paths.filter { activity.getDoesFilePathExist(it) }
val sdFilePath = validPaths.firstOrNull { activity.isPathOnSD(it) } ?: validPaths.firstOrNull()
if (sdFilePath == null) {
activity.toast(R.string.unknown_error_occurred)
@ -68,7 +67,7 @@ class RenameItemsDialog(val activity: BaseSimpleActivity, val paths: ArrayList<S
val newPath = "${path.getParentPath()}/$newName"
if (File(newPath).exists()) {
if (activity.getDoesFilePathExist(newPath)) {
continue
}

View file

@ -1,121 +0,0 @@
package com.simplemobiletools.commons.dialogs
import android.media.ExifInterface
import android.text.format.DateFormat
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isNougatPlus
import kotlinx.android.synthetic.main.dialog_rename_items_pattern.view.*
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
class RenameItemsPatternDialog(val activity: BaseSimpleActivity, val paths: ArrayList<String>, val callback: () -> Unit) {
init {
var ignoreClicks = false
val view = activity.layoutInflater.inflate(R.layout.dialog_rename_items_pattern, null)
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this, R.string.rename) {
showKeyboard(view.rename_items_value)
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (ignoreClicks) {
return@setOnClickListener
}
val validPaths = paths.filter { File(it).exists() }
val sdFilePath = validPaths.firstOrNull { activity.isPathOnSD(it) } ?: validPaths.firstOrNull()
if (sdFilePath == null) {
activity.toast(R.string.unknown_error_occurred)
dismiss()
return@setOnClickListener
}
activity.handleSAFDialog(sdFilePath) {
ignoreClicks = true
var pathsCnt = validPaths.size
for (path in validPaths) {
val exif = ExifInterface(path)
var dateTime = if (isNougatPlus()) {
exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)
?: exif.getAttribute(ExifInterface.TAG_DATETIME)
} else {
exif.getAttribute(ExifInterface.TAG_DATETIME)
}
if (dateTime == null) {
val calendar = Calendar.getInstance(Locale.ENGLISH)
calendar.timeInMillis = File(path).lastModified()
dateTime = DateFormat.format("yyyy:MM:dd kk:mm:ss", calendar).toString()
}
var newName = view.rename_items_value.value
val simpleDateFormat = SimpleDateFormat("yyyy:MM:dd kk:mm:ss", Locale.ENGLISH)
val dt = simpleDateFormat.parse(dateTime)
val cal = Calendar.getInstance()
cal.time = dt
val year = cal.get(Calendar.YEAR).toString()
val month = (cal.get(Calendar.MONTH) + 1).ensureTwoDigits()
val day = (cal.get(Calendar.DAY_OF_MONTH)).ensureTwoDigits()
val hours = (cal.get(Calendar.HOUR_OF_DAY)).ensureTwoDigits()
val minutes = (cal.get(Calendar.MINUTE)).ensureTwoDigits()
val seconds = (cal.get(Calendar.SECOND)).ensureTwoDigits()
newName = newName
.replace("%Y", year, false)
.replace("%M", month, false)
.replace("%D", day, false)
.replace("%h", hours, false)
.replace("%m", minutes, false)
.replace("%s", seconds, false)
if (newName.isEmpty()) {
continue
}
if (!newName.contains(".") && path.contains(".")) {
val extension = path.substringAfterLast(".")
newName += ".$extension"
}
var newPath = "${path.getParentPath()}/$newName"
var currentIndex = 0
while (File(newPath).exists()) {
currentIndex++
var extension = ""
val name = if (newName.contains(".")) {
extension = ".${newName.substringAfterLast(".")}"
newName.substringBeforeLast(".")
} else {
newName
}
newPath = "${path.getParentPath()}/$name~$currentIndex$extension"
}
activity.renameFile(path, newPath) {
if (it) {
pathsCnt--
if (pathsCnt == 0) {
callback()
dismiss()
}
} else {
ignoreClicks = false
activity.toast(R.string.unknown_error_occurred)
dismiss()
}
}
}
}
}
}
}
}
}

View file

@ -105,14 +105,14 @@ class StoragePickerDialog(val activity: BaseSimpleActivity, currPath: String, va
}
private fun otgPicked() {
if (activity.baseConfig.OTGPath.isEmpty()) {
activity.getStorageDirectories().firstOrNull { it.trimEnd('/') != activity.internalStoragePath && it.trimEnd('/') != activity.sdCardPath }?.apply {
activity.baseConfig.OTGPath = trimEnd('/')
activity.handleOTGPermission {
if (it) {
callback(activity.otgPath)
mDialog.dismiss()
} else {
radioGroup.check(defaultSelectedId)
}
}
mDialog.dismiss()
callback(activity.otgPath)
}
private fun rootPicked() {

View file

@ -70,12 +70,12 @@ fun Activity.appLaunched(appId: String) {
showDonateOrUpgradeDialog()
}
if (baseConfig.appRunCount > 203 && !baseConfig.wasRateUsPromptShown) {
if (baseConfig.appRunCount > 175 && !baseConfig.wasRateUsPromptShown) {
baseConfig.wasRateUsPromptShown = true
RateUsDialog(this)
}
if (baseConfig.navigationBarColor == INVALID_NAVIGATION_BAR_COLOR) {
if (baseConfig.navigationBarColor == INVALID_NAVIGATION_BAR_COLOR && (window.attributes.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN == 0)) {
baseConfig.defaultNavigationBarColor = window.navigationBarColor
baseConfig.navigationBarColor = window.navigationBarColor
}
@ -426,7 +426,7 @@ fun BaseSimpleActivity.deleteFolderBg(fileDirItem: FileDirItem, deleteMediaOnly:
val files = filesArr.toMutableList().filter { !deleteMediaOnly || it.isMediaFile() }
for (file in files) {
deleteFileBg(file.toFileDirItem(), false) { }
deleteFileBg(file.toFileDirItem(applicationContext), false) { }
}
if (folder.listFiles()?.isEmpty() == true) {
@ -491,7 +491,7 @@ fun BaseSimpleActivity.deleteFileBg(fileDirItem: FileDirItem, allowDeleteFolder:
callback?.invoke(true)
}
} else {
if (file.isDirectory && allowDeleteFolder) {
if (getIsPathDirectory(file.absolutePath) && allowDeleteFolder) {
fileDeleted = deleteRecursively(file)
}
@ -629,7 +629,7 @@ fun BaseSimpleActivity.getFileOutputStream(fileDirItem: FileDirItem, allowCreati
return@handleSAFDialog
}
if (!File(fileDirItem.path).exists()) {
if (!getDoesFilePathExist(fileDirItem.path)) {
document = document.createFile("", fileDirItem.name)
}
@ -671,7 +671,7 @@ fun BaseSimpleActivity.getFileOutputStreamSync(path: String, mimeType: String, p
return if (needsStupidWritePermissions(path)) {
var documentFile = parentDocumentFile
if (documentFile == null) {
if (targetFile.parentFile?.exists() == true) {
if (getDoesFilePathExist(targetFile.parentFile.absolutePath)) {
documentFile = getDocumentFile(targetFile.parent)
} else {
documentFile = getDocumentFile(targetFile.parentFile.parent)
@ -700,7 +700,14 @@ fun BaseSimpleActivity.getFileOutputStreamSync(path: String, mimeType: String, p
}
}
fun BaseSimpleActivity.getFileInputStreamSync(path: String) = FileInputStream(File(path))
fun BaseSimpleActivity.getFileInputStreamSync(path: String): InputStream? {
return if (isPathOnOTG(path)) {
val fileDocument = getSomeDocumentFile(path)
applicationContext.contentResolver.openInputStream(fileDocument?.uri)
} else {
FileInputStream(File(path))
}
}
fun Activity.handleHiddenFolderPasswordProtection(callback: () -> Unit) {
if (baseConfig.isHiddenPasswordProtectionOn) {
@ -747,7 +754,7 @@ fun Activity.handleLockedFolderOpening(path: String, callback: (success: Boolean
}
fun BaseSimpleActivity.createDirectorySync(directory: String): Boolean {
if (File(directory).exists()) {
if (getDoesFilePathExist(directory)) {
return true
}

View file

@ -191,17 +191,18 @@ fun Context.getFastDocumentFile(path: String): DocumentFile? {
return DocumentFile.fromSingleUri(this, Uri.parse(fullUri))
}
fun Context.getOTGFastDocumentFile(path: String): DocumentFile? {
fun Context.getOTGFastDocumentFile(path: String, otgPathToUse: String? = null): DocumentFile? {
if (baseConfig.OTGTreeUri.isEmpty()) {
return null
}
val otgPath = otgPathToUse ?: baseConfig.OTGPath
if (baseConfig.OTGPartition.isEmpty()) {
baseConfig.OTGPartition = baseConfig.OTGTreeUri.removeSuffix("%3A").substringAfterLast('/').trimEnd('/')
updateOTGPathFromPartition()
}
val relativePath = Uri.encode(path.substring(baseConfig.OTGPath.length).trim('/'))
val relativePath = Uri.encode(path.substring(otgPath.length).trim('/'))
val fullUri = "${baseConfig.OTGTreeUri}/document/${baseConfig.OTGPartition}%3A$relativePath"
return DocumentFile.fromSingleUri(this, Uri.parse(fullUri))
}
@ -285,7 +286,7 @@ fun Context.getFileUri(path: String) = when {
// these functions update the mediastore instantly, MediaScannerConnection.scanFileRecursively takes some time to really get applied
fun Context.deleteFromMediaStore(path: String) {
if (File(path).isDirectory) {
if (getDoesFilePathExist(path) || getIsPathDirectory(path)) {
return
}
@ -335,7 +336,12 @@ fun Context.updateLastModified(path: String, lastModified: Long) {
fun Context.getOTGItems(path: String, shouldShowHidden: Boolean, getProperFileSize: Boolean, callback: (ArrayList<FileDirItem>) -> Unit) {
val items = ArrayList<FileDirItem>()
val OTGTreeUri = baseConfig.OTGTreeUri
var rootUri = DocumentFile.fromTreeUri(applicationContext, Uri.parse(OTGTreeUri))
var rootUri = try {
DocumentFile.fromTreeUri(applicationContext, Uri.parse(OTGTreeUri))
} catch (ignored: Exception) {
null
}
if (rootUri == null) {
callback(items)
return
@ -409,13 +415,31 @@ fun Context.trySAFFileDelete(fileDirItem: FileDirItem, allowDeleteFolder: Boolea
}
fun Context.updateOTGPathFromPartition() {
baseConfig.OTGPath = if (File("/storage/${baseConfig.OTGPartition}").exists()) {
val otgPath = "/storage/${baseConfig.OTGPartition}"
baseConfig.OTGPath = if (getOTGFastDocumentFile(otgPath, otgPath)?.exists() == true) {
"/storage/${baseConfig.OTGPartition}"
} else {
"/mnt/media_rw/${baseConfig.OTGPartition}"
}
}
fun Context.getDoesFilePathExist(path: String, otgPathToUse: String? = null): Boolean {
val otgPath = otgPathToUse ?: baseConfig.OTGPath
return if (otgPath.isNotEmpty() && path.startsWith(otgPath)) {
getOTGFastDocumentFile(path)?.exists() ?: false
} else {
File(path).exists()
}
}
fun Context.getIsPathDirectory(path: String): Boolean {
return if (isPathOnOTG(path)) {
getOTGFastDocumentFile(path)?.isDirectory ?: false
} else {
File(path).isDirectory
}
}
// avoid these being set as SD card paths
private val physicalPaths = arrayListOf(
"/storage/sdcard1", // Motorola Xoom

View file

@ -319,11 +319,13 @@ fun Context.getMimeTypeFromUri(uri: Uri): String {
}
fun Context.ensurePublicUri(path: String, applicationId: String): Uri? {
val uri = Uri.parse(path)
return when {
isPathOnOTG(path) && baseConfig.OTGPartition.isNotEmpty() && baseConfig.OTGTreeUri.isNotEmpty() -> getDocumentFile(path)?.uri
uri.scheme == "content" -> uri
else -> {
return if (isPathOnOTG(path)) {
getDocumentFile(path)?.uri
} else {
val uri = Uri.parse(path)
if (uri.scheme == "content") {
uri
} else {
val newPath = if (uri.toString().startsWith("/")) uri.toString() else uri.path
val file = File(newPath)
getFilePublicUri(file, applicationId)

View file

@ -1,5 +1,6 @@
package com.simplemobiletools.commons.extensions
import android.content.Context
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FileDirItem
import java.io.File
@ -74,7 +75,7 @@ private fun getDirectoryFileCount(dir: File, countHiddenItems: Boolean): Int {
fun File.getDirectChildrenCount(countHiddenItems: Boolean) = listFiles()?.filter { if (countHiddenItems) true else !it.isHidden }?.size ?: 0
fun File.toFileDirItem() = FileDirItem(absolutePath, name, File(absolutePath).isDirectory, 0, length(), lastModified())
fun File.toFileDirItem(context: Context) = FileDirItem(absolutePath, name, context.getIsPathDirectory(absolutePath), 0, length(), lastModified())
fun File.containsNoMedia() = isDirectory && File(this, NOMEDIA).exists()

View file

@ -39,6 +39,8 @@ fun String.isAValidFilename(): Boolean {
return true
}
fun String.getOTGPublicPath(context: Context) = "${context.baseConfig.OTGTreeUri}/document/${context.baseConfig.OTGPartition}%3A${substring(context.baseConfig.OTGPath.length).replace("/", "%2F")}"
fun String.isMediaFile() = isImageFast() || isVideoFast() || isGif() || isRawFast() || isSvg() || isPortrait()
fun String.isGif() = endsWith(".gif", true)
@ -204,9 +206,13 @@ fun String.getFileKey(): String {
}
fun String.getAvailableStorageB(): Long {
val stat = StatFs(this)
val bytesAvailable = stat.blockSizeLong * stat.availableBlocksLong
return bytesAvailable
return try {
val stat = StatFs(this)
val bytesAvailable = stat.blockSizeLong * stat.availableBlocksLong
bytesAvailable
} catch (e: Exception) {
-1L
}
}
// remove diacritics, for example č -> c

View file

@ -355,4 +355,8 @@ open class BaseConfig(val context: Context) {
var lastRenameUsed: Int
get() = prefs.getInt(LAST_RENAME_USED, RENAME_SIMPLE)
set(lastRenameUsed) = prefs.edit().putInt(LAST_RENAME_USED, lastRenameUsed).apply()
var lastRenamePatternUsed: String
get() = prefs.getString(LAST_RENAME_PATTERN_USED, "")!!
set(lastRenamePatternUsed) = prefs.edit().putString(LAST_RENAME_PATTERN_USED, lastRenamePatternUsed).apply()
}

View file

@ -119,6 +119,7 @@ const val WAS_RATE_US_PROMPT_SHOWN = "was_rate_us_prompt_shown"
const val WAS_SORTING_BY_NUMERIC_VALUE_ADDED = "was_sorting_by_numeric_value_added"
const val WAS_FOLDER_LOCKING_NOTICE_SHOWN = "was_folder_locking_notice_shown"
const val LAST_RENAME_USED = "last_rename_used"
const val LAST_RENAME_PATTERN_USED = "last_rename_pattern_used"
// licenses
internal const val LICENSE_KOTLIN = 1

View file

@ -55,11 +55,37 @@ open class FileDirItem(val path: String, val name: String = "", var isDirectory:
else -> name
}
fun getProperSize(countHidden: Boolean) = File(path).getProperSize(countHidden)
fun getProperSize(context: Context, countHidden: Boolean): Long {
return if (context.isPathOnOTG(path)) {
context.getDocumentFile(path)?.getItemSize(countHidden) ?: 0
} else {
File(path).getProperSize(countHidden)
}
}
fun getProperFileCount(countHidden: Boolean) = File(path).getFileCount(countHidden)
fun getProperFileCount(context: Context, countHidden: Boolean): Int {
return if (context.isPathOnOTG(path)) {
context.getDocumentFile(path)?.getFileCount(countHidden) ?: 0
} else {
File(path).getFileCount(countHidden)
}
}
fun getDirectChildrenCount(countHiddenItems: Boolean) = File(path).getDirectChildrenCount(countHiddenItems)
fun getDirectChildrenCount(context: Context, countHiddenItems: Boolean): Int {
return if (context.isPathOnOTG(path)) {
context.getDocumentFile(path)?.listFiles()?.filter { if (countHiddenItems) true else !it.name!!.startsWith(".") }?.size ?: 0
} else {
File(path).getDirectChildrenCount(countHiddenItems)
}
}
fun getLastModified(context: Context): Long {
return if (context.isPathOnOTG(path)) {
context.getFastDocumentFile(path)?.lastModified() ?: 0L
} else {
File(path).lastModified()
}
}
fun getParentPath() = path.getParentPath()
@ -78,4 +104,6 @@ open class FileDirItem(val path: String, val name: String = "", var isDirectory:
fun getVideoResolution(context: Context) = context.getVideoResolution(path)
fun getImageResolution() = path.getImageResolution()
fun getPublicUri(context: Context) = context.getDocumentFile(path)?.uri ?: ""
}

View file

@ -29,6 +29,7 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
override fun initTab(activity: BaseSimpleActivity, paths: ArrayList<String>) {
this.activity = activity
this.paths = paths
rename_items_value.setText(activity.baseConfig.lastRenamePatternUsed)
}
override fun dialogConfirmed(callback: (success: Boolean) -> Unit) {
@ -42,13 +43,14 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
return
}
val validPaths = paths.filter { File(it).exists() }
val validPaths = paths.filter { activity?.getDoesFilePathExist(it) == true }
val sdFilePath = validPaths.firstOrNull { activity?.isPathOnSD(it) == true } ?: validPaths.firstOrNull()
if (sdFilePath == null) {
activity?.toast(R.string.unknown_error_occurred)
return
}
activity?.baseConfig?.lastRenamePatternUsed = rename_items_value.value
activity?.handleSAFDialog(sdFilePath) {
ignoreClicks = true
var pathsCnt = validPaths.size
@ -67,7 +69,8 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
dateTime = DateFormat.format("yyyy:MM:dd kk:mm:ss", calendar).toString()
}
val simpleDateFormat = SimpleDateFormat("yyyy:MM:dd kk:mm:ss", Locale.ENGLISH)
val pattern = if (dateTime.substring(4, 5) == "-") "yyyy-MM-dd kk:mm:ss" else "yyyy:MM:dd kk:mm:ss"
val simpleDateFormat = SimpleDateFormat(pattern, Locale.ENGLISH)
val dt = simpleDateFormat.parse(dateTime)
val cal = Calendar.getInstance()
cal.time = dt
@ -98,7 +101,7 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
var newPath = "${path.getParentPath()}/$newName"
var currentIndex = 0
while (File(newPath).exists()) {
while (activity?.getDoesFilePathExist(newPath) == true) {
currentIndex++
var extension = ""
val name = if (newName.contains(".")) {

View file

@ -8,7 +8,6 @@ import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.interfaces.RenameTab
import kotlinx.android.synthetic.main.tab_rename_simple.view.*
import java.io.File
class RenameSimpleTab(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs), RenameTab {
var ignoreClicks = false
@ -39,7 +38,7 @@ class RenameSimpleTab(context: Context, attrs: AttributeSet) : RelativeLayout(co
return
}
val validPaths = paths.filter { File(it).exists() }
val validPaths = paths.filter { activity?.getDoesFilePathExist(it) == true }
val sdFilePath = validPaths.firstOrNull { activity?.isPathOnSD(it) == true } ?: validPaths.firstOrNull()
if (sdFilePath == null) {
activity?.toast(R.string.unknown_error_occurred)
@ -67,7 +66,7 @@ class RenameSimpleTab(context: Context, attrs: AttributeSet) : RelativeLayout(co
val newPath = "${path.getParentPath()}/$newName"
if (File(newPath).exists()) {
if (activity?.getDoesFilePathExist(newPath) == true) {
continue
}

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">نسخ</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopyala</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copy</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Reanomenat simple</string>
<string name="pattern_renaming">Patró</string>
<string name="string_to_add">Cadena per afegir</string>
<string name="rename_date_time_pattern">%Y - any\n%M - mes\n%D - dia\n%h - hora\n%m - minut\n%s - segon</string>
<string name="rename_date_time_pattern">%Y - any\n%M - mes\n%D - dia\n%h - hora\n%m - minut\n%s - segon\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copiar</string>
@ -646,8 +646,8 @@
Un cop llançat, simplement configureu la icona de taronja per defecte # F57C00. És possible que hàgiu de tornar a instal·lar l\'aplicació en el pitjor dels casos.</string>
<string name="faq_7_title_commons">Els diners han estat deduïts del meu compte bancari, però no puc descarregar l\'aplicació. Què puc fer?</string>
<string name="faq_7_text_commons">Google fa que els pagaments siguin completament gestionats i alguns cops això genera problemes de sistema. Només cal que esborris la memòria cau de l\aplicació Google Play i reiniciis el dispositiu. A continuació prova a baixar-la de nou.</string>
<string name="faq_8_title_commons">Per què he d'actualitzar a la versió Pro?</string>
<string name="faq_8_text_commons">Com que la versió de l\'aplicació ja no s'actualitza, els errors que heu vist, no seran corregits. Tampoc s\hi afegiran noves funcions. Podeu comprar la versió Pro a Google Play per una petita quantitat de diners.
<string name="faq_8_title_commons">Per què he d\'actualitzar a la versió Pro?</string>
<string name="faq_8_text_commons">Com que la versió de l\'aplicació ja no s\'actualitza, els errors que heu vist, no seran corregits. Tampoc s\hi afegiran noves funcions. Podeu comprar la versió Pro a Google Play per una petita quantitat de diners.
Es tracta d\un pagament únic, cosa que significa que un cop l\adquireixis, no hauràs de tornar a pagar mai més. Ni tan sols després de canviar de dispositiu. Si no us agrada la versió Pro, podeu desinstal·lar-la en poques hores i automàticament obtindreu els diners.
Si voleu un reemborsament en qualsevol moment més tard, només cal que contacteu amb nosaltres a l\'adreça hello@simplemobiletools.com i ho aconseguireu.</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopírovat</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copïo</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple omdøbning</string>
<string name="pattern_renaming">Mønster</string>
<string name="string_to_add">Tekststreng der skal tilføjes</string>
<string name="rename_date_time_pattern">%Y - år\n%M - måned\n%D - dag\n%h - time\n%m - minut\n%s - sekund</string>
<string name="rename_date_time_pattern">%Y - år\n%M - måned\n%D - dag\n%h - time\n%m - minut\n%s - sekund\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopier</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopieren</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Απλή μετονομασία</string>
<string name="pattern_renaming">Μοτίβο</string>
<string name="string_to_add">Συμβολοσειρά για προσθήκη</string>
<string name="rename_date_time_pattern">%Y - έτος\n%M - μήνας\n%D - ημέρα\n%h - ώρα\n%m - λεπτό\n%s - δευτ/το</string>
<string name="rename_date_time_pattern">%Y - έτος\n%M - μήνας\n%D - ημέρα\n%h - ώρα\n%m - λεπτό\n%s - δευτ/το\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Αντιγραφή</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Renombrado simple</string>
<string name="pattern_renaming">Modelo</string>
<string name="string_to_add">Cadena para agregar</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copiar</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopioi</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple</string>
<string name="pattern_renaming">Modèle</string>
<string name="string_to_add">Caractères à ajouter</string>
<string name="rename_date_time_pattern">%Y - année\n%M - mois\n%D - jour\n%h - heure\n%m - minute\n%s - seconde</string>
<string name="rename_date_time_pattern">%Y - année\n%M - mois\n%D - jour\n%h - heure\n%m - minute\n%s - seconde\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copier</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copiar</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copy</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopiraj</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%É - év\\n%H - hónap\\n%N - nap\\n%ó - óra\\n%p - perc\\n%mp - másodperc</string>
<string name="rename_date_time_pattern">%É - év\\n%H - hónap\\n%N - nap\\n%ó - óra\\n%p - perc\\n%mp - másodperc\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Másolás</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Ubah nama</string>
<string name="pattern_renaming">Pola</string>
<string name="string_to_add">Kata untuk ditambahkan</string>
<string name="rename_date_time_pattern">%Y - tahun\n%M - bulan\n%D - hari\n%h - jam\n%m - menit\n%s - detik</string>
<string name="rename_date_time_pattern">%Y - tahun\n%M - bulan\n%D - hari\n%h - jam\n%m - menit\n%s - detik\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Salin</string>
@ -572,7 +572,7 @@
<string name="purchase">Beli</string>
<string name="update_thank_you">Silakan perbarui Simple Thank You ke versi terbaru</string>
<string name="before_asking_question_read_faq">Sebelum anda bertanya, silakan periksa pengaturan aplikasi dan baca Pertanyaan yang Paling Sering Ditanyakan terlebih dahulu. Mungkin solusi yang anda cari ada di sana.</string>
<string name="make_sure_latest">Also make sure that you are using the latest app version.</string>
<string name="make_sure_latest">Pastikan juga anda menggunakan versi aplikasi terbaru.</string>
<string name="read_it">Baca</string>
<string name="rate_us_prompt">Halo,\n\nsepertinya anda sudah menggunakan aplikasi ini cukup lama, dan kami sangat menghargainya.\n\nJika anda tidak keberatan, silakan nilai kami di Google Play. Itu akan sangat membantu kami.\n\nApapun keputusan anda, anda tidak akan melihat pesan ini lagi.\n\nTerima kasih!</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Ubah nama</string>
<string name="pattern_renaming">Pola</string>
<string name="string_to_add">Kata untuk ditambahkan</string>
<string name="rename_date_time_pattern">%Y - tahun\n%M - bulan\n%D - hari\n%h - jam\n%m - menit\n%s - detik</string>
<string name="rename_date_time_pattern">%Y - tahun\n%M - bulan\n%D - hari\n%h - jam\n%m - menit\n%s - detik\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Salin</string>
@ -572,7 +572,7 @@
<string name="purchase">Beli</string>
<string name="update_thank_you">Silakan perbarui Simple Thank You ke versi terbaru</string>
<string name="before_asking_question_read_faq">Sebelum anda bertanya, silakan periksa pengaturan aplikasi dan baca Pertanyaan yang Paling Sering Ditanyakan terlebih dahulu. Mungkin solusi yang anda cari ada di sana.</string>
<string name="make_sure_latest">Also make sure that you are using the latest app version.</string>
<string name="make_sure_latest">Pastikan juga anda menggunakan versi aplikasi terbaru.</string>
<string name="read_it">Baca</string>
<string name="rate_us_prompt">Halo,\n\nsepertinya anda sudah menggunakan aplikasi ini cukup lama, dan kami sangat menghargainya.\n\nJika anda tidak keberatan, silakan nilai kami di Google Play. Itu akan sangat membantu kami.\n\nApapun keputusan anda, anda tidak akan melihat pesan ini lagi.\n\nTerima kasih!</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Rinominazione semplice</string>
<string name="pattern_renaming">Modello</string>
<string name="string_to_add">Stringa da aggiungere</string>
<string name="rename_date_time_pattern">%Y - anno\n%M - mese\n%D - giorno\n%h - ora\n%m - minuto\n%s - secondo</string>
<string name="rename_date_time_pattern">%Y - anno\n%M - mese\n%D - giorno\n%h - ora\n%m - minuto\n%s - secondo\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copia</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copy</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">コピー</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">심플 이름 바꾸기</string>
<string name="pattern_renaming">패턴</string>
<string name="string_to_add">추가할 문장</string>
<string name="rename_date_time_pattern">%Y - 년\n%M - 월\n%D - 일\n%h - 시\n%m - 분\n%s - 초</string>
<string name="rename_date_time_pattern">%Y - 년\n%M - 월\n%D - 일\n%h - 시\n%m - 분\n%s - 초\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">복사</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopijuoti</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopier</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Eenvoudig hernoemen</string>
<string name="pattern_renaming">Patroon</string>
<string name="string_to_add">Tekens toevoegen</string>
<string name="rename_date_time_pattern">%Y - jaar\n%M - maand\n%D - dag\n%h - uur\n%m - minuut\n%s - seconde</string>
<string name="rename_date_time_pattern">%Y - jaar\n%M - maand\n%D - dag\n%h - uur\n%m - minuut\n%s - seconde\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopiëren</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopier</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopiuj</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - ano\n%M - mês\n%D - dia\n%h - hora\n%m - minuto\n%s - segundo</string>
<string name="rename_date_time_pattern">%Y - ano\n%M - mês\n%D - dia\n%h - hora\n%m - minuto\n%s - segundo\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copiar</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Mudar nome</string>
<string name="pattern_renaming">Padrão</string>
<string name="string_to_add">Texto a adicionar</string>
<string name="rename_date_time_pattern">%Y - ano\n%M - mês\n%D - dia\n%h - horas\n%m - minutos\n%s - segundos</string>
<string name="rename_date_time_pattern">%Y - ano\n%M - mês\n%D - dia\n%h - horas\n%m - minutos\n%s - segundos\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copiar</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Простое переименование</string>
<string name="pattern_renaming">Шаблон</string>
<string name="string_to_add">Строка для добавления</string>
<string name="rename_date_time_pattern">%Y - год\n%M - месяц\n%D - день\n%h - час\n%m - минута\n%s - секунда</string>
<string name="rename_date_time_pattern">%Y - год\n%M - месяц\n%D - день\n%h - час\n%m - минута\n%s - секунда\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Копировать</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Jednoduché premenovanie</string>
<string name="pattern_renaming">Vzor</string>
<string name="string_to_add">Reťazec na pridanie</string>
<string name="rename_date_time_pattern">%Y - rok\n%M - mesiac\n%D - deň\n%h - hodina\n%m - minúta\n%s - sekunda</string>
<string name="rename_date_time_pattern">%Y - rok\n%M - mesiac\n%D - deň\n%h - hodina\n%m - minúta\n%s - sekunda\n%i - číslo narastajúce od 1</string>
<!-- Copy / Move -->
<string name="copy">Kopírovať</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopiraj</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Копирај</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - år\n%M - månad\n%D - dag\n%h - timme\n%m - minut\n%s - sekund</string>
<string name="rename_date_time_pattern">%Y - år\n%M - månad\n%D - dag\n%h - timme\n%m - minut\n%s - sekund\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopiera</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Basit yeniden adlandırma</string>
<string name="pattern_renaming">Desen</string>
<string name="string_to_add">Eklenecek dize</string>
<string name="rename_date_time_pattern">%Y - yıl\n%M - ay\n%D - gün\n%h - saat\n%m - dakika\n%s - saniye</string>
<string name="rename_date_time_pattern">%Y - yıl\n%M - ay\n%D - gün\n%h - saat\n%m - dakika\n%s - saniye\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Kopyala</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - рік\n%M - місяць\n%D - день\n%h - година\n%m - хвилина\n%s - секунда</string>
<string name="rename_date_time_pattern">%Y - рік\n%M - місяць\n%D - день\n%h - година\n%m - хвилина\n%s - секунда\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Копіювати</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">简单重命名</string>
<string name="pattern_renaming">图案</string>
<string name="string_to_add">要添加的字符串</string>
<string name="rename_date_time_pattern">%Y - 年\n%M - 月\n%D - 日\n%h - 时\n%m - 分\n%s - 秒</string>
<string name="rename_date_time_pattern">%Y - 年\n%M - 月\n%D - 日\n%h - 时\n%m - 分\n%s - 秒\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">复制</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">簡易重新命名</string>
<string name="pattern_renaming">格式重新命名</string>
<string name="string_to_add">添加文字</string>
<string name="rename_date_time_pattern">%Y - 年\n%M - 月\n%D - 日\n%h - 小時\n%m - 分鐘\n%s - 秒鐘</string>
<string name="rename_date_time_pattern">%Y - 年\n%M - 月\n%D - 日\n%h - 小時\n%m - 分鐘\n%s - 秒鐘\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">複製</string>
@ -536,8 +536,8 @@
<string name="sunday_short">週日</string>
<!-- Pro version -->
<string name="upgrade_to_pro_long">Your app version will not be updated anymore. Please upgrade to the Pro version to receive new fixes and other improvements.</string>
<string name="upgrade_to_pro_long_click">Your app version will not be updated anymore. Please upgrade to the Pro version to receive new fixes and other improvements by clicking here.</string>
<string name="upgrade_to_pro_long">你的應用程式版本將不再更新。請更新到專業版以獲得新的修復和其他改善。</string>
<string name="upgrade_to_pro_long_click">你的應用程式版本將不再更新。請點擊這裡,更新到專業版以獲得新的修復和其他改善。</string>
<string name="it_is_free">它在此日期前免費:%s。如果您在到期前下載將能夠永久免費使用。</string>
<string name="more_info">更多資訊</string>
<string name="upgrade">升級</string>
@ -572,7 +572,7 @@
<string name="purchase">購買</string>
<string name="update_thank_you">請更新[Simple Thank You]至最新版本</string>
<string name="before_asking_question_read_faq">在您發問前,請先檢查程式設定及閱讀[常見問題]。或許解決方法就在那裡。</string>
<string name="make_sure_latest">Also make sure that you are using the latest app version.</string>
<string name="make_sure_latest">並且確保您正使用最新的應用程式版本。</string>
<string name="read_it">讀它</string>
<string name="rate_us_prompt">哈囉\n\n您似乎已經使用這應用程式一段時間了我們相當感激。\n\n如果我們能請您幫個忙請在Google Play上為我們評分。這對我們很有幫助。\n\n無論您的決定如何都不會再看到這則訊息。\n\n感謝!</string>
@ -613,10 +613,10 @@
一旦啟動,只要設回預設的橘色圖標 #F57C00 即可。最糟的情況,你可能必須重新安裝應用程式。</string>
<string name="faq_7_title_commons">錢已經從我的銀行帳戶扣除,但我無法下載這應用程式。我能怎麼做?</string>
<string name="faq_7_text_commons">支付完全交由Google處理他們的系統偶爾會發生故障。只要清除您的Google Play應用程式快取並重新啟動裝置然後再次嘗試下載。</string>
<string name="faq_8_title_commons">Why should I upgrade to the Pro version?</string>
<string name="faq_8_text_commons">As your app version is no longer updated, the bugs you have maybe spotted will never be fixed. There will also be no new functions added. You can purchase the Pro version on Google Play for a small sum of money.
It is a one time payment, which means that once you purchase it, you will never have to pay again. Not even after getting a new device. If you won\'t like the Pro version, you can just uninstall it within a few hours and you will automatically get your money back.
If you want a refund anytime later, just contact us at hello@simplemobiletools.com and you will get it.</string>
<string name="faq_8_title_commons">為什麼我應該升級到專業版?</string>
<string name="faq_8_text_commons">由於你的程式版本不再更新將永遠不會修復可能遇到的錯誤也不會再添加新功能。你可以在Google Play上用一小筆錢購買專業版。
這是一次性付款。也就是說,一旦你購買了將不必再次付費,即使之後有新裝置也不用。如果你不喜歡專業版,則可以在幾個小時內解除安裝,將自動拿回你的錢。
之後如果何時想退款,只要你以 hello@simplemobiletools.com 聯絡我們然後就能獲得退款了。</string>
<!-- License -->
<string name="notice">這程式使用了下列的第三方函式庫來讓我工作簡化。感謝。</string>

View file

@ -67,7 +67,7 @@
<string name="simple_renaming">Simple renaming</string>
<string name="pattern_renaming">Pattern</string>
<string name="string_to_add">String to add</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second</string>
<string name="rename_date_time_pattern">%Y - year\n%M - month\n%D - day\n%h - hour\n%m - minute\n%s - second\n%i - number increasing from 1</string>
<!-- Copy / Move -->
<string name="copy">Copy</string>