Skip to content

Commit 9a640c8

Browse files
committed
Add support for creating password protected zips
1 parent 2ac5648 commit 9a640c8

File tree

4 files changed

+69
-17
lines changed

4 files changed

+69
-17
lines changed

app/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@ android {
6464
}
6565

6666
dependencies {
67-
implementation 'com.github.SimpleMobileTools:Simple-Commons:84c71fdcc1'
67+
implementation 'com.github.SimpleMobileTools:Simple-Commons:db25f91be3'
6868
implementation 'com.github.tibbi:AndroidPdfViewer:e6a533125b'
6969
implementation 'com.github.Stericson:RootTools:df729dcb13'
7070
implementation 'com.github.Stericson:RootShell:1.6'
7171
implementation 'com.alexvasilkov:gesture-views:2.5.2'
7272
implementation 'androidx.documentfile:documentfile:1.0.1'
7373
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
7474
implementation 'me.grantland:autofittextview:0.2.1'
75+
implementation 'net.lingala.zip4j:zip4j:2.11.5'
7576
}

app/src/main/kotlin/com/simplemobiletools/filemanager/pro/adapters/ItemsAdapter.kt

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ import kotlinx.android.synthetic.main.item_file_dir_list.view.item_icon
4444
import kotlinx.android.synthetic.main.item_file_dir_list.view.item_name
4545
import kotlinx.android.synthetic.main.item_file_grid.view.*
4646
import kotlinx.android.synthetic.main.item_section.view.*
47+
import net.lingala.zip4j.io.outputstream.ZipOutputStream
48+
import net.lingala.zip4j.model.ZipParameters
4749
import java.io.BufferedInputStream
4850
import java.io.Closeable
4951
import java.io.File
5052
import java.util.*
5153
import java.util.zip.ZipEntry
5254
import java.util.zip.ZipInputStream
53-
import java.util.zip.ZipOutputStream
5455

5556
class ItemsAdapter(
5657
activity: SimpleActivity, var listItems: MutableList<ListItem>, val listener: ItemOperationsListener?, recyclerView: MyRecyclerView,
@@ -482,8 +483,7 @@ class ItemsAdapter(
482483
return
483484
}
484485

485-
CompressAsDialog(activity, firstPath) {
486-
val destination = it
486+
CompressAsDialog(activity, firstPath) { destination, password ->
487487
activity.handleAndroidSAFDialog(firstPath) { granted ->
488488
if (!granted) {
489489
return@handleAndroidSAFDialog
@@ -496,7 +496,7 @@ class ItemsAdapter(
496496
activity.toast(R.string.compressing)
497497
val paths = getSelectedFileDirItems().map { it.path }
498498
ensureBackgroundThread {
499-
if (compressPaths(paths, destination)) {
499+
if (compressPaths(paths, destination, password)) {
500500
activity.runOnUiThread {
501501
activity.toast(R.string.compression_successful)
502502
listener?.refreshFragment()
@@ -643,13 +643,17 @@ class ItemsAdapter(
643643
}
644644

645645
@SuppressLint("NewApi")
646-
private fun compressPaths(sourcePaths: List<String>, targetPath: String): Boolean {
646+
private fun compressPaths(sourcePaths: List<String>, targetPath: String, password: String? = null): Boolean {
647647
val queue = LinkedList<String>()
648648
val fos = activity.getFileOutputStreamSync(targetPath, "application/zip") ?: return false
649649

650-
val zout = ZipOutputStream(fos)
650+
val zout = password?.let { ZipOutputStream(fos, password.toCharArray()) } ?: ZipOutputStream(fos)
651651
var res: Closeable = fos
652652

653+
fun zipEntry(name: String) = ZipParameters().also {
654+
it.fileNameInZip = name
655+
}
656+
653657
try {
654658
sourcePaths.forEach { currentPath ->
655659
var name: String
@@ -659,7 +663,11 @@ class ItemsAdapter(
659663
queue.push(mainFilePath)
660664
if (activity.getIsPathDirectory(mainFilePath)) {
661665
name = "${mainFilePath.getFilenameFromPath()}/"
662-
zout.putNextEntry(ZipEntry(name))
666+
zout.putNextEntry(
667+
ZipParameters().also {
668+
it.fileNameInZip = name
669+
}
670+
)
663671
}
664672

665673
while (!queue.isEmpty()) {
@@ -672,9 +680,9 @@ class ItemsAdapter(
672680
if (activity.getIsPathDirectory(file.path)) {
673681
queue.push(file.path)
674682
name = "${name.trimEnd('/')}/"
675-
zout.putNextEntry(ZipEntry(name))
683+
zout.putNextEntry(zipEntry(name))
676684
} else {
677-
zout.putNextEntry(ZipEntry(name))
685+
zout.putNextEntry(zipEntry(name))
678686
activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
679687
zout.closeEntry()
680688
}
@@ -687,9 +695,9 @@ class ItemsAdapter(
687695
if (activity.getIsPathDirectory(file.absolutePath)) {
688696
queue.push(file.absolutePath)
689697
name = "${name.trimEnd('/')}/"
690-
zout.putNextEntry(ZipEntry(name))
698+
zout.putNextEntry(zipEntry(name))
691699
} else {
692-
zout.putNextEntry(ZipEntry(name))
700+
zout.putNextEntry(zipEntry(name))
693701
activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
694702
zout.closeEntry()
695703
}
@@ -698,7 +706,7 @@ class ItemsAdapter(
698706

699707
} else {
700708
name = if (base == currentPath) currentPath.getFilenameFromPath() else mainFilePath.relativizeWith(base)
701-
zout.putNextEntry(ZipEntry(name))
709+
zout.putNextEntry(zipEntry(name))
702710
activity.getFileInputStreamSync(mainFilePath)!!.copyTo(zout)
703711
zout.closeEntry()
704712
}

app/src/main/kotlin/com/simplemobiletools/filemanager/pro/dialogs/CompressAsDialog.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ import com.simplemobiletools.commons.dialogs.FilePickerDialog
77
import com.simplemobiletools.commons.extensions.*
88
import com.simplemobiletools.filemanager.pro.R
99
import com.simplemobiletools.filemanager.pro.extensions.config
10-
import kotlinx.android.synthetic.main.dialog_compress_as.view.filename_value
11-
import kotlinx.android.synthetic.main.dialog_compress_as.view.folder
10+
import kotlinx.android.synthetic.main.dialog_compress_as.view.*
1211

13-
class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val callback: (destination: String) -> Unit) {
12+
class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val callback: (destination: String, password: String?) -> Unit) {
1413
private val view = activity.layoutInflater.inflate(R.layout.dialog_compress_as, null)
1514

1615
init {
@@ -29,6 +28,10 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
2928
realPath = it
3029
}
3130
}
31+
32+
password_protect.setOnCheckedChangeListener { _, _ ->
33+
enter_password_hint.beVisibleIf(password_protect.isChecked)
34+
}
3235
}
3336

3437
activity.getAlertDialogBuilder()
@@ -39,6 +42,14 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
3942
alertDialog.showKeyboard(view.filename_value)
4043
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener {
4144
val name = view.filename_value.value
45+
var password: String? = null
46+
if (view.password_protect.isChecked) {
47+
password = view.password.value
48+
if (password.isEmpty()) {
49+
activity.toast(R.string.empty_password)
50+
return@OnClickListener
51+
}
52+
}
4253
when {
4354
name.isEmpty() -> activity.toast(R.string.empty_name)
4455
name.isAValidFilename() -> {
@@ -49,8 +60,9 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
4960
}
5061

5162
alertDialog.dismiss()
52-
callback(newPath)
63+
callback(newPath, password)
5364
}
65+
5466
else -> activity.toast(R.string.invalid_name)
5567
}
5668
})

app/src/main/res/layout/dialog_compress_as.xml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
android:layout_width="match_parent"
2828
android:layout_height="wrap_content"
2929
android:layout_below="@+id/folder_hint"
30+
android:layout_marginBottom="@dimen/activity_margin"
3031
android:hint="@string/filename_without_zip">
3132

3233
<com.google.android.material.textfield.TextInputEditText
@@ -39,4 +40,34 @@
3940
android:textSize="@dimen/bigger_text_size" />
4041

4142
</com.simplemobiletools.commons.views.MyTextInputLayout>
43+
44+
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
45+
android:id="@+id/password_protect"
46+
android:layout_width="match_parent"
47+
android:layout_height="wrap_content"
48+
android:layout_below="@id/filename_hint"
49+
android:layout_marginTop="@dimen/small_margin"
50+
android:paddingTop="@dimen/normal_margin"
51+
android:paddingBottom="@dimen/normal_margin"
52+
android:text="@string/password" />
53+
54+
<com.simplemobiletools.commons.views.MyTextInputLayout
55+
android:id="@+id/enter_password_hint"
56+
android:layout_width="match_parent"
57+
android:layout_height="wrap_content"
58+
android:layout_below="@id/password_protect"
59+
android:hint="@string/password"
60+
android:visibility="gone">
61+
62+
<com.google.android.material.textfield.TextInputEditText
63+
android:id="@+id/password"
64+
android:layout_width="match_parent"
65+
android:layout_height="wrap_content"
66+
android:layout_marginBottom="@dimen/activity_margin"
67+
android:inputType="textPassword"
68+
android:singleLine="true"
69+
android:textCursorDrawable="@null"
70+
android:textSize="@dimen/normal_text_size" />
71+
72+
</com.simplemobiletools.commons.views.MyTextInputLayout>
4273
</RelativeLayout>

0 commit comments

Comments
 (0)