Merge branch 'master' into feature/password-protected-zips-compress
This commit is contained in:
commit
173efd1896
|
@ -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'
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue