Merge branch 'master' into feature/password-protected-zips-compress

This commit is contained in:
Ensar Sarajčić 2023-07-07 19:10:53 +02:00
commit 173efd1896
4 changed files with 76 additions and 19 deletions

View File

@ -64,7 +64,7 @@ android {
} }
dependencies { dependencies {
implementation 'com.github.SimpleMobileTools:Simple-Commons:db25f91be3' implementation 'com.github.SimpleMobileTools:Simple-Commons:f54d4f7606'
implementation 'com.github.tibbi:AndroidPdfViewer:e6a533125b' implementation 'com.github.tibbi:AndroidPdfViewer:e6a533125b'
implementation 'com.github.Stericson:RootTools:df729dcb13' implementation 'com.github.Stericson:RootTools:df729dcb13'
implementation 'com.github.Stericson:RootShell:1.6' implementation 'com.github.Stericson:RootShell:1.6'

View File

@ -3,6 +3,7 @@ package com.simplemobiletools.filemanager.pro.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import com.simplemobiletools.commons.dialogs.EnterPasswordDialog
import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.NavigationIcon import com.simplemobiletools.commons.helpers.NavigationIcon
@ -13,14 +14,22 @@ import com.simplemobiletools.filemanager.pro.adapters.DecompressItemsAdapter
import com.simplemobiletools.filemanager.pro.extensions.config import com.simplemobiletools.filemanager.pro.extensions.config
import com.simplemobiletools.filemanager.pro.models.ListItem import com.simplemobiletools.filemanager.pro.models.ListItem
import kotlinx.android.synthetic.main.activity_decompress.* import kotlinx.android.synthetic.main.activity_decompress.*
import net.lingala.zip4j.exception.ZipException
import net.lingala.zip4j.exception.ZipException.Type
import net.lingala.zip4j.io.inputstream.ZipInputStream
import net.lingala.zip4j.model.LocalFileHeader
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
class DecompressActivity : SimpleActivity() { class DecompressActivity : SimpleActivity() {
companion object {
private const val PASSWORD = "password"
}
private val allFiles = ArrayList<ListItem>() private val allFiles = ArrayList<ListItem>()
private var currentPath = "" private var currentPath = ""
private var uri: Uri? = null private var uri: Uri? = null
private var password: String? = null
private var passwordDialog: EnterPasswordDialog? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true isMaterialActivity = true
@ -36,10 +45,11 @@ class DecompressActivity : SimpleActivity() {
return return
} }
password = savedInstanceState?.getString(PASSWORD, null)
val realPath = getRealPathFromURI(uri!!) val realPath = getRealPathFromURI(uri!!)
decompress_toolbar.title = realPath?.getFilenameFromPath() ?: Uri.decode(uri.toString().getFilenameFromPath()) decompress_toolbar.title = realPath?.getFilenameFromPath() ?: Uri.decode(uri.toString().getFilenameFromPath())
fillAllListItems(uri!!) setupFilesList()
updateCurrentPath("")
} }
override fun onResume() { override fun onResume() {
@ -47,6 +57,11 @@ class DecompressActivity : SimpleActivity() {
setupToolbar(decompress_toolbar, NavigationIcon.Arrow) setupToolbar(decompress_toolbar, NavigationIcon.Arrow)
} }
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(PASSWORD, password)
}
private fun setupOptionsMenu() { private fun setupOptionsMenu() {
decompress_toolbar.setOnMenuItemClickListener { menuItem -> decompress_toolbar.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) { when (menuItem.itemId) {
@ -57,6 +72,11 @@ class DecompressActivity : SimpleActivity() {
} }
} }
private fun setupFilesList() {
fillAllListItems(uri!!)
updateCurrentPath("")
}
override fun onBackPressed() { override fun onBackPressed() {
if (currentPath.isEmpty()) { if (currentPath.isEmpty()) {
super.onBackPressed() super.onBackPressed()
@ -99,6 +119,9 @@ class DecompressActivity : SimpleActivity() {
try { try {
val inputStream = contentResolver.openInputStream(uri!!) val inputStream = contentResolver.openInputStream(uri!!)
val zipInputStream = ZipInputStream(BufferedInputStream(inputStream!!)) val zipInputStream = ZipInputStream(BufferedInputStream(inputStream!!))
if (password != null) {
zipInputStream.setPassword(password?.toCharArray())
}
val buffer = ByteArray(1024) val buffer = ByteArray(1024)
zipInputStream.use { zipInputStream.use {
@ -106,7 +129,7 @@ class DecompressActivity : SimpleActivity() {
val entry = zipInputStream.nextEntry ?: break val entry = zipInputStream.nextEntry ?: break
val filename = title.toString().substringBeforeLast(".") val filename = title.toString().substringBeforeLast(".")
val parent = "$destination/$filename" val parent = "$destination/$filename"
val newPath = "$parent/${entry.name.trimEnd('/')}" val newPath = "$parent/${entry.fileName.trimEnd('/')}"
if (!getDoesFilePathExist(parent)) { if (!getDoesFilePathExist(parent)) {
if (!createDirectorySync(parent)) { if (!createDirectorySync(parent)) {
@ -161,10 +184,25 @@ class DecompressActivity : SimpleActivity() {
} }
val zipInputStream = ZipInputStream(BufferedInputStream(inputStream)) val zipInputStream = ZipInputStream(BufferedInputStream(inputStream))
var zipEntry: ZipEntry? if (password != null) {
zipInputStream.setPassword(password?.toCharArray())
}
var zipEntry: LocalFileHeader?
while (true) { while (true) {
try { try {
zipEntry = zipInputStream.nextEntry zipEntry = zipInputStream.nextEntry
} catch (passwordException: ZipException) {
if (passwordException.type == Type.WRONG_PASSWORD) {
if (password != null) {
toast(getString(R.string.invalid_password))
passwordDialog?.clearPassword()
} else {
askForPassword()
}
return
} else {
break
}
} catch (ignored: Exception) { } catch (ignored: Exception) {
break break
} }
@ -173,10 +211,24 @@ class DecompressActivity : SimpleActivity() {
break break
} }
val lastModified = if (isOreoPlus()) zipEntry.lastModifiedTime.toMillis() else 0 val lastModified = if (isOreoPlus()) zipEntry.lastModifiedTime else 0
val filename = zipEntry.name.removeSuffix("/") val filename = zipEntry.fileName.removeSuffix("/")
val listItem = ListItem(filename, filename.getFilenameFromPath(), zipEntry.isDirectory, 0, 0L, lastModified, false, false) val listItem = ListItem(filename, filename.getFilenameFromPath(), zipEntry.isDirectory, 0, 0L, lastModified, false, false)
allFiles.add(listItem) allFiles.add(listItem)
} }
passwordDialog?.dismiss(notify = false)
}
private fun askForPassword() {
passwordDialog = EnterPasswordDialog(
this,
callback = { newPassword ->
password = newPassword
setupFilesList()
},
cancelCallback = {
finish()
}
)
} }
} }

View File

@ -632,7 +632,8 @@ class MainActivity : SimpleActivity() {
} }
private fun launchAbout() { private fun launchAbout() {
val licenses = LICENSE_GLIDE or LICENSE_PATTERN or LICENSE_REPRINT or LICENSE_GESTURE_VIEWS or LICENSE_PDF_VIEWER or LICENSE_AUTOFITTEXTVIEW val licenses =
LICENSE_GLIDE or LICENSE_PATTERN or LICENSE_REPRINT or LICENSE_GESTURE_VIEWS or LICENSE_PDF_VIEWER or LICENSE_AUTOFITTEXTVIEW or LICENSE_ZIP4J
val faqItems = arrayListOf( val faqItems = arrayListOf(
FAQItem(R.string.faq_3_title_commons, R.string.faq_3_text_commons), FAQItem(R.string.faq_3_title_commons, R.string.faq_3_text_commons),

View File

@ -44,15 +44,16 @@ 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_dir_list.view.item_name
import kotlinx.android.synthetic.main.item_file_grid.view.* import kotlinx.android.synthetic.main.item_file_grid.view.*
import kotlinx.android.synthetic.main.item_section.view.* import kotlinx.android.synthetic.main.item_section.view.*
import net.lingala.zip4j.exception.ZipException
import net.lingala.zip4j.io.inputstream.ZipInputStream
import net.lingala.zip4j.io.outputstream.ZipOutputStream import net.lingala.zip4j.io.outputstream.ZipOutputStream
import net.lingala.zip4j.model.LocalFileHeader
import net.lingala.zip4j.model.ZipParameters import net.lingala.zip4j.model.ZipParameters
import net.lingala.zip4j.model.enums.EncryptionMethod import net.lingala.zip4j.model.enums.EncryptionMethod
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.Closeable import java.io.Closeable
import java.io.File import java.io.File
import java.util.* import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
class ItemsAdapter( class ItemsAdapter(
activity: SimpleActivity, var listItems: MutableList<ListItem>, val listener: ItemOperationsListener?, recyclerView: MyRecyclerView, activity: SimpleActivity, var listItems: MutableList<ListItem>, val listener: ItemOperationsListener?, recyclerView: MyRecyclerView,
@ -548,13 +549,11 @@ class ItemsAdapter(
val fileDirItems = ArrayList<FileDirItem>() val fileDirItems = ArrayList<FileDirItem>()
var entry = zipInputStream.nextEntry var entry = zipInputStream.nextEntry
while (entry != null) { while (entry != null) {
val currPath = if (entry.isDirectory) path else "${path.getParentPath().trimEnd('/')}/${entry.name}" val currPath = if (entry.isDirectory) path else "${path.getParentPath().trimEnd('/')}/${entry.fileName}"
val fileDirItem = FileDirItem(currPath, entry.name, entry.isDirectory, 0, entry.size) val fileDirItem = FileDirItem(currPath, entry.fileName, entry.isDirectory, 0, entry.uncompressedSize)
fileDirItems.add(fileDirItem) fileDirItems.add(fileDirItem)
zipInputStream.closeEntry()
entry = zipInputStream.nextEntry entry = zipInputStream.nextEntry
} }
zipInputStream.closeEntry()
val destinationPath = fileDirItems.first().getParentPath().trimEnd('/') val destinationPath = fileDirItems.first().getParentPath().trimEnd('/')
activity.runOnUiThread { activity.runOnUiThread {
activity.checkConflicts(fileDirItems, destinationPath, 0, LinkedHashMap()) { activity.checkConflicts(fileDirItems, destinationPath, 0, LinkedHashMap()) {
@ -563,6 +562,12 @@ class ItemsAdapter(
} }
} }
} }
} catch (zipException: ZipException) {
if (zipException.type == ZipException.Type.WRONG_PASSWORD) {
activity.showErrorToast(activity.getString(R.string.invalid_password))
} else {
activity.showErrorToast(zipException)
}
} catch (exception: Exception) { } catch (exception: Exception) {
activity.showErrorToast(exception) activity.showErrorToast(exception)
} }
@ -580,7 +585,7 @@ class ItemsAdapter(
val newFolderName = zipFileName.subSequence(0, zipFileName.length - 4) val newFolderName = zipFileName.subSequence(0, zipFileName.length - 4)
while (entry != null) { while (entry != null) {
val parentPath = path.getParentPath() val parentPath = path.getParentPath()
val newPath = "$parentPath/$newFolderName/${entry.name.trimEnd('/')}" val newPath = "$parentPath/$newFolderName/${entry.fileName.trimEnd('/')}"
val resolution = getConflictResolution(conflictResolutions, newPath) val resolution = getConflictResolution(conflictResolutions, newPath)
val doesPathExist = activity.getDoesFilePathExist(newPath) val doesPathExist = activity.getDoesFilePathExist(newPath)
@ -607,7 +612,6 @@ class ItemsAdapter(
extractEntry(newPath, entry, zipInputStream) extractEntry(newPath, entry, zipInputStream)
} }
zipInputStream.closeEntry()
entry = zipInputStream.nextEntry entry = zipInputStream.nextEntry
} }
callback(true) callback(true)
@ -619,7 +623,7 @@ class ItemsAdapter(
} }
} }
private fun extractEntry(newPath: String, entry: ZipEntry, zipInputStream: ZipInputStream) { private fun extractEntry(newPath: String, entry: LocalFileHeader, zipInputStream: ZipInputStream) {
if (entry.isDirectory) { if (entry.isDirectory) {
if (!activity.createDirectorySync(newPath) && !activity.getDoesFilePathExist(newPath)) { if (!activity.createDirectorySync(newPath) && !activity.getDoesFilePathExist(newPath)) {
val error = String.format(activity.getString(R.string.could_not_create_file), newPath) val error = String.format(activity.getString(R.string.could_not_create_file), newPath)