mirror of
				https://github.com/SimpleMobileTools/Simple-File-Manager.git
				synced 2025-06-05 22:09:15 +02:00 
			
		
		
		
	Merge pull request #720 from esensar/feature/password-protected-zips
Add support for decompressing password protected zips
This commit is contained in:
		| @@ -64,7 +64,7 @@ android { | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     implementation 'com.github.SimpleMobileTools:Simple-Commons:a8693482e8' | ||||
|     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' | ||||
| @@ -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' | ||||
| } | ||||
|   | ||||
| @@ -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<ListItem>() | ||||
|     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() | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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), | ||||
|   | ||||
| @@ -44,12 +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.exception.ZipException | ||||
| import net.lingala.zip4j.io.inputstream.ZipInputStream | ||||
| import net.lingala.zip4j.model.LocalFileHeader | ||||
| 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( | ||||
| @@ -547,13 +549,11 @@ class ItemsAdapter( | ||||
|                     val fileDirItems = ArrayList<FileDirItem>() | ||||
|                     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()) { | ||||
| @@ -562,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) | ||||
|                 } | ||||
| @@ -579,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) | ||||
| @@ -606,7 +612,6 @@ class ItemsAdapter( | ||||
|                             extractEntry(newPath, entry, zipInputStream) | ||||
|                         } | ||||
|  | ||||
|                         zipInputStream.closeEntry() | ||||
|                         entry = zipInputStream.nextEntry | ||||
|                     } | ||||
|                     callback(true) | ||||
| @@ -618,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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user