Loading build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ buildscript { propMinSdkVersion = 16 propTargetSdkVersion = propCompileSdkVersion propVersionCode = 1 propVersionName = '3.8.12' propVersionName = '3.9.9' kotlin_version = '1.2.21' support_libs = '27.0.2' } Loading commons/src/main/kotlin/com/simplemobiletools/commons/activities/BaseSimpleActivity.kt +31 −11 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.view.WindowManager import com.simplemobiletools.commons.R import com.simplemobiletools.commons.asynctasks.CopyMoveTask import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.FileConflictDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.APP_LICENSES import com.simplemobiletools.commons.helpers.APP_NAME Loading Loading @@ -174,22 +175,13 @@ open class BaseSimpleActivity : AppCompatActivity() { return } if (files.size == 1) { if (File(destination, files[0].name).exists()) { toast(R.string.name_taken) return } } handleSAFDialog(destinationFolder) { copyMoveCallback = callback if (isCopyOperation) { toast(R.string.copying) startCopyMove(files, destinationFolder, isCopyOperation, copyPhotoVideoOnly) } else { if (isPathOnSD(source) || isPathOnSD(destination) || files.first().isDirectory || isNougatPlus()) { handleSAFDialog(File(source)) { toast(R.string.moving) startCopyMove(files, destinationFolder, isCopyOperation, copyPhotoVideoOnly) } } else { Loading Loading @@ -222,8 +214,36 @@ open class BaseSimpleActivity : AppCompatActivity() { } private fun startCopyMove(files: ArrayList<File>, destinationFolder: File, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean) { checkConflict(files, destinationFolder, 0, LinkedHashMap()) { toast(if (isCopyOperation) R.string.copying else R.string.moving) val pair = Pair(files, destinationFolder) CopyMoveTask(this, isCopyOperation, copyPhotoVideoOnly, copyMoveListener).execute(pair) CopyMoveTask(this, isCopyOperation, copyPhotoVideoOnly, it, copyMoveListener).execute(pair) } } private fun checkConflict(files: ArrayList<File>, destinationFolder: File, index: Int, conflictResolutions: LinkedHashMap<String, Int>, callback: (resolutions: LinkedHashMap<String, Int>) -> Unit) { if (index == files.size) { callback(conflictResolutions) return } val file = files[index] val newFile = File(destinationFolder, file.name) if (newFile.exists()) { FileConflictDialog(this, newFile) { resolution, applyForAll -> if (applyForAll) { conflictResolutions.clear() conflictResolutions[""] = resolution checkConflict(files, destinationFolder, files.size, conflictResolutions, callback) } else { conflictResolutions[newFile.absolutePath] = resolution checkConflict(files, destinationFolder, index + 1, conflictResolutions, callback) } } } else { checkConflict(files, destinationFolder, index + 1, conflictResolutions, callback) } } fun handlePermission(permissionId: Int, callback: (granted: Boolean) -> Unit) { Loading commons/src/main/kotlin/com/simplemobiletools/commons/adapters/MyRecyclerViewAdapter.kt +14 −3 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.TextView import com.bignerdranch.android.multiselector.ModalMultiSelectorCallback import com.bignerdranch.android.multiselector.MultiSelector import com.bignerdranch.android.multiselector.SwappingHolder Loading @@ -31,6 +32,7 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc private val multiSelector = MultiSelector() private var actMode: ActionMode? = null private var actBarTextView: TextView? = null abstract fun getActionMenuId(): Int Loading Loading @@ -69,10 +71,10 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc private fun updateTitle(cnt: Int) { val selectableItemCount = getSelectableItemCount() val selectedCount = Math.min(cnt, selectableItemCount) val oldTitle = actMode?.title val oldTitle = actBarTextView?.text val newTitle = "$selectedCount / $selectableItemCount" if (oldTitle != newTitle) { actMode?.title = newTitle actBarTextView?.text = newTitle actMode?.invalidate() } } Loading Loading @@ -190,6 +192,15 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc override fun onCreateActionMode(actionMode: ActionMode?, menu: Menu?): Boolean { super.onCreateActionMode(actionMode, menu) actMode = actionMode actBarTextView = layoutInflater.inflate(R.layout.actionbar_title, null) as TextView actMode!!.customView = actBarTextView actBarTextView!!.setOnClickListener { if (getSelectableItemCount() == selectedPositions.size) { finishActMode() } else { selectAll() } } activity.menuInflater.inflate(getActionMenuId(), menu) return true } Loading @@ -205,7 +216,7 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc markItemSelection(false, itemViews[it]) } selectedPositions.clear() actMode?.title = "" actBarTextView?.text = "" actMode = null } } Loading commons/src/main/kotlin/com/simplemobiletools/commons/asynctasks/CopyMoveTask.kt +25 −6 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ import android.support.v4.util.Pair import com.simplemobiletools.commons.R import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.CONFLICT_OVERWRITE import com.simplemobiletools.commons.helpers.CONFLICT_SKIP import com.simplemobiletools.commons.interfaces.CopyMoveListener import java.io.File import java.io.FileInputStream Loading @@ -16,7 +18,7 @@ import java.io.OutputStream import java.lang.ref.WeakReference import java.util.* class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = false, val copyMediaOnly: Boolean, class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = false, val copyMediaOnly: Boolean, val conflictResolutions: LinkedHashMap<String, Int>, listener: CopyMoveListener) : AsyncTask<Pair<ArrayList<File>, File>, Void, Boolean>() { private var mListener: WeakReference<CopyMoveListener>? = null private var mMovedFiles: ArrayList<File> = ArrayList() Loading @@ -28,19 +30,26 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal } override fun doInBackground(vararg params: Pair<ArrayList<File>, File>): Boolean? { if (params.isEmpty()) if (params.isEmpty()) { return false } val pair = params[0] mFiles = pair.first!! for (file in mFiles) { try { val curFile = File(pair.second, file.name) if (curFile.exists()) val newFile = File(pair.second, file.name) if (newFile.exists()) { val resolution = getConflictResolution(newFile) if (resolution == CONFLICT_SKIP) { continue } else if (resolution == CONFLICT_OVERWRITE) { activity.deleteFilesBg(arrayListOf(newFile), true) } } copy(file, curFile) copy(file, newFile) } catch (e: Exception) { activity.toast(e.toString()) return false Loading @@ -55,6 +64,16 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal return true } private fun getConflictResolution(file: File): Int { return if (conflictResolutions.size == 1 && conflictResolutions.containsKey("")) { conflictResolutions[""]!! } else if (conflictResolutions.containsKey(file.absolutePath)) { conflictResolutions[file.absolutePath]!! } else { CONFLICT_SKIP } } private fun copy(source: File, destination: File) { if (source.isDirectory) { copyDirectory(source, destination) Loading commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FileConflictDialog.kt 0 → 100644 +58 −0 Original line number Diff line number Diff line package com.simplemobiletools.commons.dialogs import android.app.Activity import android.support.v7.app.AlertDialog import com.simplemobiletools.commons.R import com.simplemobiletools.commons.R.id.conflict_dialog_radio_merge import com.simplemobiletools.commons.R.id.conflict_dialog_radio_skip import com.simplemobiletools.commons.extensions.baseConfig import com.simplemobiletools.commons.extensions.beVisibleIf import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.helpers.CONFLICT_MERGE import com.simplemobiletools.commons.helpers.CONFLICT_OVERWRITE import com.simplemobiletools.commons.helpers.CONFLICT_SKIP import kotlinx.android.synthetic.main.dialog_file_conflict.view.* import java.io.File class FileConflictDialog(val activity: Activity, val file: File, val callback: (resolution: Int, applyForAll: Boolean) -> Unit) { val view = activity.layoutInflater.inflate(R.layout.dialog_file_conflict, null)!! init { view.apply { val stringBase = if (file.isDirectory) R.string.folder_already_exists else R.string.file_already_exists conflict_dialog_title.text = String.format(activity.getString(stringBase), file.name) conflict_dialog_apply_to_all.isChecked = activity.baseConfig.lastConflictApplyToAll conflict_dialog_radio_merge.beVisibleIf(file.isDirectory) val resolutionButton = when (activity.baseConfig.lastConflictResolution) { CONFLICT_OVERWRITE -> conflict_dialog_radio_overwrite CONFLICT_MERGE -> conflict_dialog_radio_merge else -> conflict_dialog_radio_skip } resolutionButton.isChecked = true } AlertDialog.Builder(activity) .setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() }) .setNegativeButton(R.string.cancel, null) .create().apply { activity.setupDialogStuff(view, this) } } private fun dialogConfirmed() { val resolution = when (view.conflict_dialog_radio_group.checkedRadioButtonId) { conflict_dialog_radio_skip -> CONFLICT_SKIP conflict_dialog_radio_merge -> CONFLICT_MERGE else -> CONFLICT_OVERWRITE } val applyToAll = view.conflict_dialog_apply_to_all.isChecked activity.baseConfig.apply { lastConflictApplyToAll = applyToAll lastConflictResolution = resolution } callback(resolution, applyToAll) } } Loading
build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ buildscript { propMinSdkVersion = 16 propTargetSdkVersion = propCompileSdkVersion propVersionCode = 1 propVersionName = '3.8.12' propVersionName = '3.9.9' kotlin_version = '1.2.21' support_libs = '27.0.2' } Loading
commons/src/main/kotlin/com/simplemobiletools/commons/activities/BaseSimpleActivity.kt +31 −11 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.view.WindowManager import com.simplemobiletools.commons.R import com.simplemobiletools.commons.asynctasks.CopyMoveTask import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.FileConflictDialog import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.APP_LICENSES import com.simplemobiletools.commons.helpers.APP_NAME Loading Loading @@ -174,22 +175,13 @@ open class BaseSimpleActivity : AppCompatActivity() { return } if (files.size == 1) { if (File(destination, files[0].name).exists()) { toast(R.string.name_taken) return } } handleSAFDialog(destinationFolder) { copyMoveCallback = callback if (isCopyOperation) { toast(R.string.copying) startCopyMove(files, destinationFolder, isCopyOperation, copyPhotoVideoOnly) } else { if (isPathOnSD(source) || isPathOnSD(destination) || files.first().isDirectory || isNougatPlus()) { handleSAFDialog(File(source)) { toast(R.string.moving) startCopyMove(files, destinationFolder, isCopyOperation, copyPhotoVideoOnly) } } else { Loading Loading @@ -222,8 +214,36 @@ open class BaseSimpleActivity : AppCompatActivity() { } private fun startCopyMove(files: ArrayList<File>, destinationFolder: File, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean) { checkConflict(files, destinationFolder, 0, LinkedHashMap()) { toast(if (isCopyOperation) R.string.copying else R.string.moving) val pair = Pair(files, destinationFolder) CopyMoveTask(this, isCopyOperation, copyPhotoVideoOnly, copyMoveListener).execute(pair) CopyMoveTask(this, isCopyOperation, copyPhotoVideoOnly, it, copyMoveListener).execute(pair) } } private fun checkConflict(files: ArrayList<File>, destinationFolder: File, index: Int, conflictResolutions: LinkedHashMap<String, Int>, callback: (resolutions: LinkedHashMap<String, Int>) -> Unit) { if (index == files.size) { callback(conflictResolutions) return } val file = files[index] val newFile = File(destinationFolder, file.name) if (newFile.exists()) { FileConflictDialog(this, newFile) { resolution, applyForAll -> if (applyForAll) { conflictResolutions.clear() conflictResolutions[""] = resolution checkConflict(files, destinationFolder, files.size, conflictResolutions, callback) } else { conflictResolutions[newFile.absolutePath] = resolution checkConflict(files, destinationFolder, index + 1, conflictResolutions, callback) } } } else { checkConflict(files, destinationFolder, index + 1, conflictResolutions, callback) } } fun handlePermission(permissionId: Int, callback: (granted: Boolean) -> Unit) { Loading
commons/src/main/kotlin/com/simplemobiletools/commons/adapters/MyRecyclerViewAdapter.kt +14 −3 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.TextView import com.bignerdranch.android.multiselector.ModalMultiSelectorCallback import com.bignerdranch.android.multiselector.MultiSelector import com.bignerdranch.android.multiselector.SwappingHolder Loading @@ -31,6 +32,7 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc private val multiSelector = MultiSelector() private var actMode: ActionMode? = null private var actBarTextView: TextView? = null abstract fun getActionMenuId(): Int Loading Loading @@ -69,10 +71,10 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc private fun updateTitle(cnt: Int) { val selectableItemCount = getSelectableItemCount() val selectedCount = Math.min(cnt, selectableItemCount) val oldTitle = actMode?.title val oldTitle = actBarTextView?.text val newTitle = "$selectedCount / $selectableItemCount" if (oldTitle != newTitle) { actMode?.title = newTitle actBarTextView?.text = newTitle actMode?.invalidate() } } Loading Loading @@ -190,6 +192,15 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc override fun onCreateActionMode(actionMode: ActionMode?, menu: Menu?): Boolean { super.onCreateActionMode(actionMode, menu) actMode = actionMode actBarTextView = layoutInflater.inflate(R.layout.actionbar_title, null) as TextView actMode!!.customView = actBarTextView actBarTextView!!.setOnClickListener { if (getSelectableItemCount() == selectedPositions.size) { finishActMode() } else { selectAll() } } activity.menuInflater.inflate(getActionMenuId(), menu) return true } Loading @@ -205,7 +216,7 @@ abstract class MyRecyclerViewAdapter(val activity: BaseSimpleActivity, val recyc markItemSelection(false, itemViews[it]) } selectedPositions.clear() actMode?.title = "" actBarTextView?.text = "" actMode = null } } Loading
commons/src/main/kotlin/com/simplemobiletools/commons/asynctasks/CopyMoveTask.kt +25 −6 Original line number Diff line number Diff line Loading @@ -8,6 +8,8 @@ import android.support.v4.util.Pair import com.simplemobiletools.commons.R import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.helpers.CONFLICT_OVERWRITE import com.simplemobiletools.commons.helpers.CONFLICT_SKIP import com.simplemobiletools.commons.interfaces.CopyMoveListener import java.io.File import java.io.FileInputStream Loading @@ -16,7 +18,7 @@ import java.io.OutputStream import java.lang.ref.WeakReference import java.util.* class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = false, val copyMediaOnly: Boolean, class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = false, val copyMediaOnly: Boolean, val conflictResolutions: LinkedHashMap<String, Int>, listener: CopyMoveListener) : AsyncTask<Pair<ArrayList<File>, File>, Void, Boolean>() { private var mListener: WeakReference<CopyMoveListener>? = null private var mMovedFiles: ArrayList<File> = ArrayList() Loading @@ -28,19 +30,26 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal } override fun doInBackground(vararg params: Pair<ArrayList<File>, File>): Boolean? { if (params.isEmpty()) if (params.isEmpty()) { return false } val pair = params[0] mFiles = pair.first!! for (file in mFiles) { try { val curFile = File(pair.second, file.name) if (curFile.exists()) val newFile = File(pair.second, file.name) if (newFile.exists()) { val resolution = getConflictResolution(newFile) if (resolution == CONFLICT_SKIP) { continue } else if (resolution == CONFLICT_OVERWRITE) { activity.deleteFilesBg(arrayListOf(newFile), true) } } copy(file, curFile) copy(file, newFile) } catch (e: Exception) { activity.toast(e.toString()) return false Loading @@ -55,6 +64,16 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal return true } private fun getConflictResolution(file: File): Int { return if (conflictResolutions.size == 1 && conflictResolutions.containsKey("")) { conflictResolutions[""]!! } else if (conflictResolutions.containsKey(file.absolutePath)) { conflictResolutions[file.absolutePath]!! } else { CONFLICT_SKIP } } private fun copy(source: File, destination: File) { if (source.isDirectory) { copyDirectory(source, destination) Loading
commons/src/main/kotlin/com/simplemobiletools/commons/dialogs/FileConflictDialog.kt 0 → 100644 +58 −0 Original line number Diff line number Diff line package com.simplemobiletools.commons.dialogs import android.app.Activity import android.support.v7.app.AlertDialog import com.simplemobiletools.commons.R import com.simplemobiletools.commons.R.id.conflict_dialog_radio_merge import com.simplemobiletools.commons.R.id.conflict_dialog_radio_skip import com.simplemobiletools.commons.extensions.baseConfig import com.simplemobiletools.commons.extensions.beVisibleIf import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.helpers.CONFLICT_MERGE import com.simplemobiletools.commons.helpers.CONFLICT_OVERWRITE import com.simplemobiletools.commons.helpers.CONFLICT_SKIP import kotlinx.android.synthetic.main.dialog_file_conflict.view.* import java.io.File class FileConflictDialog(val activity: Activity, val file: File, val callback: (resolution: Int, applyForAll: Boolean) -> Unit) { val view = activity.layoutInflater.inflate(R.layout.dialog_file_conflict, null)!! init { view.apply { val stringBase = if (file.isDirectory) R.string.folder_already_exists else R.string.file_already_exists conflict_dialog_title.text = String.format(activity.getString(stringBase), file.name) conflict_dialog_apply_to_all.isChecked = activity.baseConfig.lastConflictApplyToAll conflict_dialog_radio_merge.beVisibleIf(file.isDirectory) val resolutionButton = when (activity.baseConfig.lastConflictResolution) { CONFLICT_OVERWRITE -> conflict_dialog_radio_overwrite CONFLICT_MERGE -> conflict_dialog_radio_merge else -> conflict_dialog_radio_skip } resolutionButton.isChecked = true } AlertDialog.Builder(activity) .setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() }) .setNegativeButton(R.string.cancel, null) .create().apply { activity.setupDialogStuff(view, this) } } private fun dialogConfirmed() { val resolution = when (view.conflict_dialog_radio_group.checkedRadioButtonId) { conflict_dialog_radio_skip -> CONFLICT_SKIP conflict_dialog_radio_merge -> CONFLICT_MERGE else -> CONFLICT_OVERWRITE } val applyToAll = view.conflict_dialog_apply_to_all.isChecked activity.baseConfig.apply { lastConflictApplyToAll = applyToAll lastConflictResolution = resolution } callback(resolution, applyToAll) } }