diff --git a/app/build.gradle b/app/build.gradle index 27fdbdfa..12820f5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,7 +64,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:db25f91be3' + implementation 'com.github.SimpleMobileTools:Simple-Commons:f54d4f7606' implementation 'com.github.tibbi:AndroidPdfViewer:e6a533125b' implementation 'com.github.Stericson:RootTools:df729dcb13' implementation 'com.github.Stericson:RootShell:1.6' diff --git a/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/DecompressActivity.kt b/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/DecompressActivity.kt index 284d434e..f3a15dc0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/DecompressActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/DecompressActivity.kt @@ -3,6 +3,7 @@ package com.simplemobiletools.filemanager.pro.activities import android.annotation.SuppressLint import android.net.Uri import android.os.Bundle +import com.simplemobiletools.commons.dialogs.EnterPasswordDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.extensions.* 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.models.ListItem 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.util.zip.ZipEntry -import java.util.zip.ZipInputStream class DecompressActivity : SimpleActivity() { + companion object { + private const val PASSWORD = "password" + } + private val allFiles = ArrayList() private var currentPath = "" private var uri: Uri? = null + private var password: String? = null + private var passwordDialog: EnterPasswordDialog? = null override fun onCreate(savedInstanceState: Bundle?) { isMaterialActivity = true @@ -36,10 +45,11 @@ class DecompressActivity : SimpleActivity() { return } + password = savedInstanceState?.getString(PASSWORD, null) + val realPath = getRealPathFromURI(uri!!) decompress_toolbar.title = realPath?.getFilenameFromPath() ?: Uri.decode(uri.toString().getFilenameFromPath()) - fillAllListItems(uri!!) - updateCurrentPath("") + setupFilesList() } override fun onResume() { @@ -47,6 +57,11 @@ class DecompressActivity : SimpleActivity() { setupToolbar(decompress_toolbar, NavigationIcon.Arrow) } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString(PASSWORD, password) + } + private fun setupOptionsMenu() { decompress_toolbar.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { @@ -57,6 +72,11 @@ class DecompressActivity : SimpleActivity() { } } + private fun setupFilesList() { + fillAllListItems(uri!!) + updateCurrentPath("") + } + override fun onBackPressed() { if (currentPath.isEmpty()) { super.onBackPressed() @@ -99,6 +119,9 @@ class DecompressActivity : SimpleActivity() { try { val inputStream = contentResolver.openInputStream(uri!!) val zipInputStream = ZipInputStream(BufferedInputStream(inputStream!!)) + if (password != null) { + zipInputStream.setPassword(password?.toCharArray()) + } val buffer = ByteArray(1024) zipInputStream.use { @@ -106,7 +129,7 @@ class DecompressActivity : SimpleActivity() { val entry = zipInputStream.nextEntry ?: break val filename = title.toString().substringBeforeLast(".") val parent = "$destination/$filename" - val newPath = "$parent/${entry.name.trimEnd('/')}" + val newPath = "$parent/${entry.fileName.trimEnd('/')}" if (!getDoesFilePathExist(parent)) { if (!createDirectorySync(parent)) { @@ -161,10 +184,25 @@ class DecompressActivity : SimpleActivity() { } val zipInputStream = ZipInputStream(BufferedInputStream(inputStream)) - var zipEntry: ZipEntry? + if (password != null) { + zipInputStream.setPassword(password?.toCharArray()) + } + var zipEntry: LocalFileHeader? while (true) { try { 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) { break } @@ -173,10 +211,24 @@ class DecompressActivity : SimpleActivity() { break } - val lastModified = if (isOreoPlus()) zipEntry.lastModifiedTime.toMillis() else 0 - val filename = zipEntry.name.removeSuffix("/") + val lastModified = if (isOreoPlus()) zipEntry.lastModifiedTime else 0 + val filename = zipEntry.fileName.removeSuffix("/") val listItem = ListItem(filename, filename.getFilenameFromPath(), zipEntry.isDirectory, 0, 0L, lastModified, false, false) allFiles.add(listItem) } + passwordDialog?.dismiss(notify = false) + } + + private fun askForPassword() { + passwordDialog = EnterPasswordDialog( + this, + callback = { newPassword -> + password = newPassword + setupFilesList() + }, + cancelCallback = { + finish() + } + ) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/MainActivity.kt index a179ad57..b8ce7e3f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/activities/MainActivity.kt @@ -632,7 +632,8 @@ class MainActivity : SimpleActivity() { } 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( FAQItem(R.string.faq_3_title_commons, R.string.faq_3_text_commons), diff --git a/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/adapters/ItemsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/adapters/ItemsAdapter.kt index 37bb5ff9..440efb08 100644 --- a/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/adapters/ItemsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/filemanager/pro/adapters/ItemsAdapter.kt @@ -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_grid.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.model.LocalFileHeader import net.lingala.zip4j.model.ZipParameters import net.lingala.zip4j.model.enums.EncryptionMethod 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 class ItemsAdapter( activity: SimpleActivity, var listItems: MutableList, val listener: ItemOperationsListener?, recyclerView: MyRecyclerView, @@ -548,13 +549,11 @@ class ItemsAdapter( val fileDirItems = ArrayList() var entry = zipInputStream.nextEntry while (entry != null) { - val currPath = if (entry.isDirectory) path else "${path.getParentPath().trimEnd('/')}/${entry.name}" - val fileDirItem = FileDirItem(currPath, entry.name, entry.isDirectory, 0, entry.size) + val currPath = if (entry.isDirectory) path else "${path.getParentPath().trimEnd('/')}/${entry.fileName}" + val fileDirItem = FileDirItem(currPath, entry.fileName, entry.isDirectory, 0, entry.uncompressedSize) fileDirItems.add(fileDirItem) - zipInputStream.closeEntry() entry = zipInputStream.nextEntry } - zipInputStream.closeEntry() val destinationPath = fileDirItems.first().getParentPath().trimEnd('/') activity.runOnUiThread { 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) { activity.showErrorToast(exception) } @@ -580,7 +585,7 @@ class ItemsAdapter( val newFolderName = zipFileName.subSequence(0, zipFileName.length - 4) while (entry != null) { val parentPath = path.getParentPath() - val newPath = "$parentPath/$newFolderName/${entry.name.trimEnd('/')}" + val newPath = "$parentPath/$newFolderName/${entry.fileName.trimEnd('/')}" val resolution = getConflictResolution(conflictResolutions, newPath) val doesPathExist = activity.getDoesFilePathExist(newPath) @@ -607,7 +612,6 @@ class ItemsAdapter( extractEntry(newPath, entry, zipInputStream) } - zipInputStream.closeEntry() entry = zipInputStream.nextEntry } 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 (!activity.createDirectorySync(newPath) && !activity.getDoesFilePathExist(newPath)) { val error = String.format(activity.getString(R.string.could_not_create_file), newPath)