Skip to content

Commit 8ee16de

Browse files
committed
wip: ui
1 parent d1e75ef commit 8ee16de

File tree

8 files changed

+178
-94
lines changed

8 files changed

+178
-94
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import java.io.FileInputStream
21
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
32
import org.jetbrains.kotlin.konan.properties.Properties
3+
import java.io.FileInputStream
44

55
plugins {
66
alias(libs.plugins.android)
@@ -93,6 +93,7 @@ dependencies {
9393
implementation(libs.androidx.documentfile)
9494
implementation(libs.androidx.swiperefreshlayout)
9595
implementation(libs.androidpdfviewer)
96+
implementation(libs.apache.commons.compress)
9697
implementation(libs.roottools)
9798
implementation(libs.rootshell)
9899
implementation(libs.gestureviews)

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

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,8 @@ import com.simplemobiletools.filemanager.pro.models.ListItem
4646
import com.stericson.RootTools.RootTools
4747
import net.lingala.zip4j.exception.ZipException
4848
import net.lingala.zip4j.io.inputstream.ZipInputStream
49-
import net.lingala.zip4j.io.outputstream.ZipOutputStream
5049
import net.lingala.zip4j.model.LocalFileHeader
51-
import net.lingala.zip4j.model.ZipParameters
52-
import net.lingala.zip4j.model.enums.EncryptionMethod
5350
import java.io.BufferedInputStream
54-
import java.io.Closeable
5551
import java.io.File
5652
import java.util.*
5753

@@ -476,7 +472,7 @@ class ItemsAdapter(
476472
return
477473
}
478474

479-
CompressAsDialog(activity, firstPath) { destination, password ->
475+
CompressAsDialog(activity, firstPath) { destination, compressionFormat, password ->
480476
activity.handleAndroidSAFDialog(firstPath) { granted ->
481477
if (!granted) {
482478
return@handleAndroidSAFDialog
@@ -489,7 +485,7 @@ class ItemsAdapter(
489485
activity.toast(R.string.compressing)
490486
val paths = getSelectedFileDirItems().map { it.path }
491487
ensureBackgroundThread {
492-
if (compressPaths(paths, destination, password)) {
488+
if (CompressionHelper.compressPaths(activity, paths, destination, compressionFormat, password)) {
493489
activity.runOnUiThread {
494490
activity.toast(R.string.compression_successful)
495491
listener?.refreshFragment()
@@ -638,89 +634,6 @@ class ItemsAdapter(
638634
}
639635
}
640636

641-
@SuppressLint("NewApi")
642-
private fun compressPaths(sourcePaths: List<String>, targetPath: String, password: String? = null): Boolean {
643-
val queue = LinkedList<String>()
644-
val fos = activity.getFileOutputStreamSync(targetPath, "application/zip") ?: return false
645-
646-
val zout = password?.let { ZipOutputStream(fos, password.toCharArray()) } ?: ZipOutputStream(fos)
647-
var res: Closeable = fos
648-
649-
fun zipEntry(name: String) = ZipParameters().also {
650-
it.fileNameInZip = name
651-
if (password != null) {
652-
it.isEncryptFiles = true
653-
it.encryptionMethod = EncryptionMethod.AES
654-
}
655-
}
656-
657-
try {
658-
sourcePaths.forEach { currentPath ->
659-
var name: String
660-
var mainFilePath = currentPath
661-
val base = "${mainFilePath.getParentPath()}/"
662-
res = zout
663-
queue.push(mainFilePath)
664-
if (activity.getIsPathDirectory(mainFilePath)) {
665-
name = "${mainFilePath.getFilenameFromPath()}/"
666-
zout.putNextEntry(
667-
ZipParameters().also {
668-
it.fileNameInZip = name
669-
}
670-
)
671-
}
672-
673-
while (!queue.isEmpty()) {
674-
mainFilePath = queue.pop()
675-
if (activity.getIsPathDirectory(mainFilePath)) {
676-
if (activity.isRestrictedSAFOnlyRoot(mainFilePath)) {
677-
activity.getAndroidSAFFileItems(mainFilePath, true) { files ->
678-
for (file in files) {
679-
name = file.path.relativizeWith(base)
680-
if (activity.getIsPathDirectory(file.path)) {
681-
queue.push(file.path)
682-
name = "${name.trimEnd('/')}/"
683-
zout.putNextEntry(zipEntry(name))
684-
} else {
685-
zout.putNextEntry(zipEntry(name))
686-
activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
687-
zout.closeEntry()
688-
}
689-
}
690-
}
691-
} else {
692-
val mainFile = File(mainFilePath)
693-
for (file in mainFile.listFiles()) {
694-
name = file.path.relativizeWith(base)
695-
if (activity.getIsPathDirectory(file.absolutePath)) {
696-
queue.push(file.absolutePath)
697-
name = "${name.trimEnd('/')}/"
698-
zout.putNextEntry(zipEntry(name))
699-
} else {
700-
zout.putNextEntry(zipEntry(name))
701-
activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
702-
zout.closeEntry()
703-
}
704-
}
705-
}
706-
707-
} else {
708-
name = if (base == currentPath) currentPath.getFilenameFromPath() else mainFilePath.relativizeWith(base)
709-
zout.putNextEntry(zipEntry(name))
710-
activity.getFileInputStreamSync(mainFilePath)!!.copyTo(zout)
711-
zout.closeEntry()
712-
}
713-
}
714-
}
715-
} catch (exception: Exception) {
716-
activity.showErrorToast(exception)
717-
return false
718-
} finally {
719-
res.close()
720-
}
721-
return true
722-
}
723-
724637
private fun askConfirmDelete() {
725638
activity.handleDeletePasswordProtection {
726639
val itemsCnt = selectedKeys.size

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package com.simplemobiletools.filemanager.pro.dialogs
22

33
import android.view.View
4+
import android.widget.ArrayAdapter
45
import androidx.appcompat.app.AlertDialog
56
import com.simplemobiletools.commons.activities.BaseSimpleActivity
67
import com.simplemobiletools.commons.dialogs.FilePickerDialog
78
import com.simplemobiletools.commons.extensions.*
89
import com.simplemobiletools.filemanager.pro.R
910
import com.simplemobiletools.filemanager.pro.databinding.DialogCompressAsBinding
1011
import com.simplemobiletools.filemanager.pro.extensions.config
12+
import com.simplemobiletools.filemanager.pro.helpers.CompressionFormat
1113

12-
class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val callback: (destination: String, password: String?) -> Unit) {
14+
class CompressAsDialog(
15+
val activity: BaseSimpleActivity,
16+
val path: String,
17+
val callback: (destination: String, compressionFormat: CompressionFormat, password: String?) -> Unit
18+
) {
1319
private val binding = DialogCompressAsBinding.inflate(activity.layoutInflater)
1420

1521
init {
@@ -29,6 +35,19 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
2935
}
3036
}
3137

38+
compressionFormatValue.apply {
39+
setOnClickListener {
40+
activity.hideKeyboard(filenameValue)
41+
}
42+
val adapter = ArrayAdapter(activity, android.R.layout.simple_dropdown_item_1line, CompressionFormat.values().map { it.extension })
43+
setAdapter(adapter)
44+
setText(adapter.getItem(0), false)
45+
46+
setOnItemClickListener { _, _, i, _ ->
47+
val extension = CompressionFormat.values()[i].extension
48+
filenameHint.hint = String.format(activity.getString(R.string.filename_without_extension), extension)
49+
}
50+
}
3251
passwordProtect.setOnCheckedChangeListener { _, _ ->
3352
enterPasswordHint.beVisibleIf(passwordProtect.isChecked)
3453
}
@@ -53,14 +72,14 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
5372
when {
5473
name.isEmpty() -> activity.toast(R.string.empty_name)
5574
name.isAValidFilename() -> {
56-
val newPath = "$realPath/$name.zip"
75+
val newPath = "$realPath/$name${getSelectedCompressionFormat().extension}"
5776
if (activity.getDoesFilePathExist(newPath)) {
5877
activity.toast(R.string.name_taken)
5978
return@OnClickListener
6079
}
6180

6281
alertDialog.dismiss()
63-
callback(newPath, password)
82+
callback(newPath, getSelectedCompressionFormat(), password)
6483
}
6584

6685
else -> activity.toast(R.string.invalid_name)
@@ -69,4 +88,6 @@ class CompressAsDialog(val activity: BaseSimpleActivity, val path: String, val c
6988
}
7089
}
7190
}
91+
92+
private fun getSelectedCompressionFormat() = CompressionFormat.fromExtension(binding.compressionFormatValue.text.toString())
7293
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.simplemobiletools.filemanager.pro.helpers
2+
3+
enum class CompressionFormat(val extension: String) {
4+
ZIP(".zip"),
5+
SEVEN_ZIP(".7z"),
6+
TAR_GZ(".tar.gz"),
7+
TAR_XZ(".tar.xz");
8+
9+
companion object {
10+
fun fromExtension(extension: String): CompressionFormat {
11+
return when (extension.lowercase()) {
12+
ZIP.extension -> ZIP
13+
SEVEN_ZIP.extension -> SEVEN_ZIP
14+
TAR_GZ.extension -> TAR_GZ
15+
TAR_XZ.extension -> TAR_XZ
16+
else -> ZIP
17+
}
18+
}
19+
}
20+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package com.simplemobiletools.filemanager.pro.helpers
2+
3+
import android.annotation.SuppressLint
4+
import com.simplemobiletools.commons.activities.BaseSimpleActivity
5+
import com.simplemobiletools.commons.extensions.*
6+
import net.lingala.zip4j.io.outputstream.ZipOutputStream
7+
import net.lingala.zip4j.model.ZipParameters
8+
import net.lingala.zip4j.model.enums.EncryptionMethod
9+
import java.io.Closeable
10+
import java.io.File
11+
import java.util.LinkedList
12+
13+
class CompressionHelper {
14+
15+
companion object {
16+
@SuppressLint("NewApi")
17+
fun compressPaths(
18+
activity: BaseSimpleActivity,
19+
sourcePaths: List<String>,
20+
targetPath: String,
21+
compressionFormat: CompressionFormat,
22+
password: String? = null
23+
): Boolean {
24+
val queue = LinkedList<String>()
25+
val fos = activity.getFileOutputStreamSync(targetPath, "application/zip") ?: return false
26+
27+
val zout = password?.let { ZipOutputStream(fos, password.toCharArray()) } ?: ZipOutputStream(fos)
28+
var res: Closeable = fos
29+
30+
fun zipEntry(name: String) = ZipParameters().also {
31+
it.fileNameInZip = name
32+
if (password != null) {
33+
it.isEncryptFiles = true
34+
it.encryptionMethod = EncryptionMethod.AES
35+
}
36+
}
37+
38+
try {
39+
sourcePaths.forEach { currentPath ->
40+
var name: String
41+
var mainFilePath = currentPath
42+
val base = "${mainFilePath.getParentPath()}/"
43+
res = zout
44+
queue.push(mainFilePath)
45+
if (activity.getIsPathDirectory(mainFilePath)) {
46+
name = "${mainFilePath.getFilenameFromPath()}/"
47+
zout.putNextEntry(
48+
ZipParameters().also {
49+
it.fileNameInZip = name
50+
}
51+
)
52+
}
53+
54+
while (!queue.isEmpty()) {
55+
mainFilePath = queue.pop()
56+
if (activity.getIsPathDirectory(mainFilePath)) {
57+
if (activity.isRestrictedSAFOnlyRoot(mainFilePath)) {
58+
activity.getAndroidSAFFileItems(mainFilePath, true) { files ->
59+
for (file in files) {
60+
name = file.path.relativizeWith(base)
61+
if (activity.getIsPathDirectory(file.path)) {
62+
queue.push(file.path)
63+
name = "${name.trimEnd('/')}/"
64+
zout.putNextEntry(zipEntry(name))
65+
} else {
66+
zout.putNextEntry(zipEntry(name))
67+
activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
68+
zout.closeEntry()
69+
}
70+
}
71+
}
72+
} else {
73+
val mainFile = File(mainFilePath)
74+
for (file in mainFile.listFiles()) {
75+
name = file.path.relativizeWith(base)
76+
if (activity.getIsPathDirectory(file.absolutePath)) {
77+
queue.push(file.absolutePath)
78+
name = "${name.trimEnd('/')}/"
79+
zout.putNextEntry(zipEntry(name))
80+
} else {
81+
zout.putNextEntry(zipEntry(name))
82+
activity.getFileInputStreamSync(file.path)!!.copyTo(zout)
83+
zout.closeEntry()
84+
}
85+
}
86+
}
87+
88+
} else {
89+
name = if (base == currentPath) currentPath.getFilenameFromPath() else mainFilePath.relativizeWith(base)
90+
zout.putNextEntry(zipEntry(name))
91+
activity.getFileInputStreamSync(mainFilePath)!!.copyTo(zout)
92+
zout.closeEntry()
93+
}
94+
}
95+
}
96+
} catch (exception: Exception) {
97+
activity.showErrorToast(exception)
98+
return false
99+
} finally {
100+
res.close()
101+
}
102+
return true
103+
}
104+
}
105+
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,28 @@
2424
</com.simplemobiletools.commons.views.MyTextInputLayout>
2525

2626
<com.simplemobiletools.commons.views.MyTextInputLayout
27-
android:id="@+id/filename_hint"
27+
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
28+
android:id="@+id/compression_format_hint"
2829
android:layout_width="match_parent"
2930
android:layout_height="wrap_content"
3031
android:layout_below="@+id/folder_hint"
32+
android:layout_marginBottom="@dimen/activity_margin"
33+
android:hint="@string/compression_format">
34+
35+
<AutoCompleteTextView
36+
android:id="@+id/compression_format_value"
37+
android:layout_width="match_parent"
38+
android:layout_height="wrap_content"
39+
android:inputType="none"
40+
android:textSize="@dimen/bigger_text_size" />
41+
42+
</com.simplemobiletools.commons.views.MyTextInputLayout>
43+
44+
<com.simplemobiletools.commons.views.MyTextInputLayout
45+
android:id="@+id/filename_hint"
46+
android:layout_width="match_parent"
47+
android:layout_height="wrap_content"
48+
android:layout_below="@+id/compression_format_hint"
3149
android:hint="@string/filename_without_zip">
3250

3351
<com.google.android.material.textfield.TextInputEditText

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
<string name="decompression_successful">Decompression successful</string>
3636
<string name="compressing_failed">Compressing failed</string>
3737
<string name="decompressing_failed">Decompressing failed</string>
38+
<!-- TODO: add missing translations-->
39+
<string name="compression_format">Compression format</string>
40+
<!-- TODO: move to commons-->
41+
<string name="filename_without_extension">Filename (without %s)</string>
3842

3943
<!-- Favorites -->
4044
<string name="manage_favorites">Manage favorites</string>

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ androidpdfviewer = "e6a533125b"
1313
rootshell = "1.6"
1414
roottools = "df729dcb13"
1515
zip4j = "2.11.5"
16+
apachecommonscompress = "1.22"
1617
#Gradle
1718
gradlePlugins-agp = "8.1.0"
1819
#build
@@ -38,6 +39,7 @@ gestureviews = { module = "com.alexvasilkov:gesture-views", version.ref = "gestu
3839
rootshell = { module = "com.github.Stericson:RootShell", version.ref = "rootshell" }
3940
roottools = { module = "com.github.Stericson:RootTools", version.ref = "roottools" }
4041
zip4j = { module = "net.lingala.zip4j:zip4j", version.ref = "zip4j" }
42+
apache-commons-compress = { module = "org.apache.commons:commons-compress", version.ref = "apachecommonscompress" }
4143
[plugins]
4244
android = { id = "com.android.application", version.ref = "gradlePlugins-agp" }
4345
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

0 commit comments

Comments
 (0)