mirror of
				https://github.com/SimpleMobileTools/Simple-File-Manager.git
				synced 2025-06-05 22:09:15 +02:00 
			
		
		
		
	Add support for creating password protected zips
This commit is contained in:
		@@ -64,7 +64,7 @@ android {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation 'com.github.SimpleMobileTools:Simple-Commons:84c71fdcc1'
 | 
			
		||||
    implementation 'com.github.SimpleMobileTools:Simple-Commons:db25f91be3'
 | 
			
		||||
    implementation 'com.github.tibbi:AndroidPdfViewer:e6a533125b'
 | 
			
		||||
    implementation 'com.github.Stericson:RootTools:df729dcb13'
 | 
			
		||||
    implementation 'com.github.Stericson:RootShell:1.6'
 | 
			
		||||
@@ -72,4 +72,5 @@ dependencies {
 | 
			
		||||
    implementation 'androidx.documentfile:documentfile:1.0.1'
 | 
			
		||||
    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
 | 
			
		||||
    implementation 'me.grantland:autofittextview:0.2.1'
 | 
			
		||||
    implementation 'net.lingala.zip4j:zip4j:2.11.5'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -44,13 +44,14 @@ import kotlinx.android.synthetic.main.item_file_dir_list.view.item_icon
 | 
			
		||||
import kotlinx.android.synthetic.main.item_file_dir_list.view.item_name
 | 
			
		||||
import kotlinx.android.synthetic.main.item_file_grid.view.*
 | 
			
		||||
import kotlinx.android.synthetic.main.item_section.view.*
 | 
			
		||||
import net.lingala.zip4j.io.outputstream.ZipOutputStream
 | 
			
		||||
import net.lingala.zip4j.model.ZipParameters
 | 
			
		||||
import java.io.BufferedInputStream
 | 
			
		||||
import java.io.Closeable
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.zip.ZipEntry
 | 
			
		||||
import java.util.zip.ZipInputStream
 | 
			
		||||
import java.util.zip.ZipOutputStream
 | 
			
		||||
 | 
			
		||||
class ItemsAdapter(
 | 
			
		||||
    activity: SimpleActivity, var listItems: MutableList<ListItem>, val listener: ItemOperationsListener?, recyclerView: MyRecyclerView,
 | 
			
		||||
@@ -482,8 +483,7 @@ class ItemsAdapter(
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CompressAsDialog(activity, firstPath) {
 | 
			
		||||
            val destination = it
 | 
			
		||||
        CompressAsDialog(activity, firstPath) { destination, password ->
 | 
			
		||||
            activity.handleAndroidSAFDialog(firstPath) { granted ->
 | 
			
		||||
                if (!granted) {
 | 
			
		||||
                    return@handleAndroidSAFDialog
 | 
			
		||||
@@ -496,7 +496,7 @@ class ItemsAdapter(
 | 
			
		||||
                    activity.toast(R.string.compressing)
 | 
			
		||||
                    val paths = getSelectedFileDirItems().map { it.path }
 | 
			
		||||
                    ensureBackgroundThread {
 | 
			
		||||
                        if (compressPaths(paths, destination)) {
 | 
			
		||||
                        if (compressPaths(paths, destination, password)) {
 | 
			
		||||
                            activity.runOnUiThread {
 | 
			
		||||
                                activity.toast(R.string.compression_successful)
 | 
			
		||||
                                listener?.refreshFragment()
 | 
			
		||||
@@ -643,13 +643,17 @@ class ItemsAdapter(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("NewApi")
 | 
			
		||||
    private fun compressPaths(sourcePaths: List<String>, targetPath: String): Boolean {
 | 
			
		||||
    private fun compressPaths(sourcePaths: List<String>, targetPath: String, password: String? = null): Boolean {
 | 
			
		||||
        val queue = LinkedList<String>()
 | 
			
		||||
        val fos = activity.getFileOutputStreamSync(targetPath, "application/zip") ?: return false
 | 
			
		||||
 | 
			
		||||
        val zout = ZipOutputStream(fos)
 | 
			
		||||
        val zout = password?.let { ZipOutputStream(fos, password.toCharArray()) } ?: ZipOutputStream(fos)
 | 
			
		||||
        var res: Closeable = fos
 | 
			
		||||
 | 
			
		||||
        fun zipEntry(name: String) = ZipParameters().also {
 | 
			
		||||
            it.fileNameInZip = name
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            sourcePaths.forEach { currentPath ->
 | 
			
		||||
                var name: String
 | 
			
		||||
@@ -659,7 +663,11 @@ class ItemsAdapter(
 | 
			
		||||
                queue.push(mainFilePath)
 | 
			
		||||
                if (activity.getIsPathDirectory(mainFilePath)) {
 | 
			
		||||
                    name = "${mainFilePath.getFilenameFromPath()}/"
 | 
			
		||||
                    zout.putNextEntry(ZipEntry(name))
 | 
			
		||||
                    zout.putNextEntry(
 | 
			
		||||
                        ZipParameters().also {
 | 
			
		||||
                            it.fileNameInZip = name
 | 
			
		||||
                        }
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                while (!queue.isEmpty()) {
 | 
			
		||||
@@ -672,9 +680,9 @@ class ItemsAdapter(
 | 
			
		||||
                                    if (activity.getIsPathDirectory(file.path)) {
 | 
			
		||||
                                        queue.push(file.path)
 | 
			
		||||
                                        name = "${name.trimEnd('/')}/"
 | 
			
		||||
                                        zout.putNextEntry(ZipEntry(name))
 | 
			
		||||
                                        zout.putNextEntry(zipEntry(name))
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        zout.putNextEntry(ZipEntry(name))
 | 
			
		||||
                                        zout.putNextEntry(zipEntry(name))
 | 
			
		||||
                                        activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
 | 
			
		||||
                                        zout.closeEntry()
 | 
			
		||||
                                    }
 | 
			
		||||
@@ -687,9 +695,9 @@ class ItemsAdapter(
 | 
			
		||||
                                if (activity.getIsPathDirectory(file.absolutePath)) {
 | 
			
		||||
                                    queue.push(file.absolutePath)
 | 
			
		||||
                                    name = "${name.trimEnd('/')}/"
 | 
			
		||||
                                    zout.putNextEntry(ZipEntry(name))
 | 
			
		||||
                                    zout.putNextEntry(zipEntry(name))
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    zout.putNextEntry(ZipEntry(name))
 | 
			
		||||
                                    zout.putNextEntry(zipEntry(name))
 | 
			
		||||
                                    activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
 | 
			
		||||
                                    zout.closeEntry()
 | 
			
		||||
                                }
 | 
			
		||||
@@ -698,7 +706,7 @@ class ItemsAdapter(
 | 
			
		||||
 | 
			
		||||
                    } else {
 | 
			
		||||
                        name = if (base == currentPath) currentPath.getFilenameFromPath() else mainFilePath.relativizeWith(base)
 | 
			
		||||
                        zout.putNextEntry(ZipEntry(name))
 | 
			
		||||
                        zout.putNextEntry(zipEntry(name))
 | 
			
		||||
                        activity.getFileInputStreamSync(mainFilePath)!!.copyTo(zout)
 | 
			
		||||
                        zout.closeEntry()
 | 
			
		||||
                    }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,10 +7,9 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
 | 
			
		||||
import com.simplemobiletools.commons.extensions.*
 | 
			
		||||
import com.simplemobiletools.filemanager.pro.R
 | 
			
		||||
import com.simplemobiletools.filemanager.pro.extensions.config
 | 
			
		||||
import kotlinx.android.synthetic.main.dialog_compress_as.view.filename_value
 | 
			
		||||
import kotlinx.android.synthetic.main.dialog_compress_as.view.folder
 | 
			
		||||
import kotlinx.android.synthetic.main.dialog_compress_as.view.*
 | 
			
		||||
 | 
			
		||||
class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val callback: (destination: String) -> Unit) {
 | 
			
		||||
class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val callback: (destination: String, password: String?) -> Unit) {
 | 
			
		||||
    private val view = activity.layoutInflater.inflate(R.layout.dialog_compress_as, null)
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
@@ -29,6 +28,10 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
 | 
			
		||||
                    realPath = it
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            password_protect.setOnCheckedChangeListener { _, _ ->
 | 
			
		||||
                enter_password_hint.beVisibleIf(password_protect.isChecked)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        activity.getAlertDialogBuilder()
 | 
			
		||||
@@ -39,6 +42,14 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
 | 
			
		||||
                    alertDialog.showKeyboard(view.filename_value)
 | 
			
		||||
                    alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener {
 | 
			
		||||
                        val name = view.filename_value.value
 | 
			
		||||
                        var password: String? = null
 | 
			
		||||
                        if (view.password_protect.isChecked) {
 | 
			
		||||
                            password = view.password.value
 | 
			
		||||
                            if (password.isEmpty()) {
 | 
			
		||||
                                activity.toast(R.string.empty_password)
 | 
			
		||||
                                return@OnClickListener
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        when {
 | 
			
		||||
                            name.isEmpty() -> activity.toast(R.string.empty_name)
 | 
			
		||||
                            name.isAValidFilename() -> {
 | 
			
		||||
@@ -49,8 +60,9 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                alertDialog.dismiss()
 | 
			
		||||
                                callback(newPath)
 | 
			
		||||
                                callback(newPath, password)
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            else -> activity.toast(R.string.invalid_name)
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_below="@+id/folder_hint"
 | 
			
		||||
        android:layout_marginBottom="@dimen/activity_margin"
 | 
			
		||||
        android:hint="@string/filename_without_zip">
 | 
			
		||||
 | 
			
		||||
        <com.google.android.material.textfield.TextInputEditText
 | 
			
		||||
@@ -39,4 +40,34 @@
 | 
			
		||||
            android:textSize="@dimen/bigger_text_size" />
 | 
			
		||||
 | 
			
		||||
    </com.simplemobiletools.commons.views.MyTextInputLayout>
 | 
			
		||||
 | 
			
		||||
    <com.simplemobiletools.commons.views.MyAppCompatCheckbox
 | 
			
		||||
        android:id="@+id/password_protect"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_below="@id/filename_hint"
 | 
			
		||||
        android:layout_marginTop="@dimen/small_margin"
 | 
			
		||||
        android:paddingTop="@dimen/normal_margin"
 | 
			
		||||
        android:paddingBottom="@dimen/normal_margin"
 | 
			
		||||
        android:text="@string/password" />
 | 
			
		||||
 | 
			
		||||
    <com.simplemobiletools.commons.views.MyTextInputLayout
 | 
			
		||||
        android:id="@+id/enter_password_hint"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="wrap_content"
 | 
			
		||||
        android:layout_below="@id/password_protect"
 | 
			
		||||
        android:hint="@string/password"
 | 
			
		||||
        android:visibility="gone">
 | 
			
		||||
 | 
			
		||||
        <com.google.android.material.textfield.TextInputEditText
 | 
			
		||||
            android:id="@+id/password"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:layout_marginBottom="@dimen/activity_margin"
 | 
			
		||||
            android:inputType="textPassword"
 | 
			
		||||
            android:singleLine="true"
 | 
			
		||||
            android:textCursorDrawable="@null"
 | 
			
		||||
            android:textSize="@dimen/normal_text_size" />
 | 
			
		||||
 | 
			
		||||
    </com.simplemobiletools.commons.views.MyTextInputLayout>
 | 
			
		||||
</RelativeLayout>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user