Skip to content

Commit

Permalink
Bug Fix: OutOfMemoryError when unpacking large 7z archives #2
Browse files Browse the repository at this point in the history
  • Loading branch information
WirelessAlien committed Sep 7, 2023
1 parent 473e6f0 commit db66326
Show file tree
Hide file tree
Showing 41 changed files with 221 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId "com.wirelessalien.zipxtract"
minSdk 23
targetSdk 33
versionCode 2
versionName "2.0"
versionCode 3
versionName "2.1"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand All @@ -28,25 +28,28 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '11'
}
}

dependencies {

implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
//Kotlin Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'

implementation 'org.apache.commons:commons-compress:1.21'
implementation 'org.apache.commons:commons-compress:1.23.0'
implementation 'org.tukaani:xz:1.9'


Expand Down
6 changes: 3 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/app_icon"
android:icon="@mipmap/ic_launcher"
android:name=".App"
android:label="@string/app_name"
android:roundIcon="@drawable/app_icon"
android:supportsRtl="true"
android:theme="@style/Theme.UnZip"
android:theme="@style/AppTheme"
tools:targetApi="31" >
<activity
android:name=".MainActivity"
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/wirelessalien/zipxtract/App.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.wirelessalien.zipxtract

import android.app.Application
import com.google.android.material.color.DynamicColors

class App : Application() {
override fun onCreate() {
super.onCreate()
// Apply dynamic color
DynamicColors.applyToActivitiesIfAvailable(this)

}
}
77 changes: 56 additions & 21 deletions app/src/main/java/com/wirelessalien/zipxtract/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.provider.DocumentsContract
import android.provider.MediaStore
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
Expand All @@ -21,14 +22,19 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.documentfile.provider.DocumentFile
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry
import org.apache.commons.compress.archivers.sevenz.SevenZFile
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipInputStream

class MainActivity : AppCompatActivity() {
Expand All @@ -38,6 +44,7 @@ class MainActivity : AppCompatActivity() {
private var archiveFileUri: Uri? = null
private var outputDirectory: DocumentFile? = null
private lateinit var sharedPreferences: SharedPreferences
private lateinit var progressBar: ProgressBar

private val requestPermissionCode = 1

Expand Down Expand Up @@ -74,8 +81,10 @@ class MainActivity : AppCompatActivity() {

val pickFileButton = findViewById<Button>(R.id.pickFileButton)
extractButton = findViewById(R.id.extractButton)
progressBar = findViewById(R.id.progressBar)
directoryTextView = findViewById(R.id.directoryTextView) // Assign the TextView from the layout
sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE)
progressBar.visibility = View.GONE

pickFileButton.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
Expand All @@ -86,6 +95,7 @@ class MainActivity : AppCompatActivity() {

extractButton.setOnClickListener {
if (archiveFileUri != null) {
progressBar.visibility = View.VISIBLE
extractArchiveFile(archiveFileUri!!)
} else {
Toast.makeText(this, "Please pick a file to extract", Toast.LENGTH_SHORT).show()
Expand Down Expand Up @@ -306,35 +316,59 @@ class MainActivity : AppCompatActivity() {
gzipInputStream.close()
showExtractionCompletedSnackbar(outputDirectory)
}
private suspend fun createTemp7zFileInBackground(bufferedInputStream: BufferedInputStream): File = withContext(Dispatchers.IO) {
return@withContext File.createTempFile("temp_", ".7z", cacheDir).apply {
FileOutputStream(this).use { outputStream ->
val buffer = ByteArray(4096)
var count: Int
while (bufferedInputStream.read(buffer).also { count = it } != -1) {
outputStream.write(buffer, 0, count)
}
}
}
}

private fun extract7z(bufferedInputStream: BufferedInputStream, outputDirectory: DocumentFile?) {
val byteChannel = SeekableInMemoryByteChannel(bufferedInputStream.readBytes())
SevenZFile(byteChannel).use { sevenZFile ->
var entry: SevenZArchiveEntry? = sevenZFile.nextEntry
while (entry != null) {
val outputFile = outputDirectory?.createFile("application/octet-stream", entry.name)
if (entry.isDirectory) {
outputFile?.createDirectory("Un7z")
} else {
outputFile?.uri?.let { uri ->
contentResolver.openOutputStream(uri)?.use { outputStream ->
val buffer = ByteArray(8192)
try {
var count: Int
while (sevenZFile.read(buffer).also { count = it } != -1) {
outputStream.write(buffer, 0, count)
val tempFileJob = CoroutineScope(Dispatchers.Default).launch {
val tempFile = createTemp7zFileInBackground(bufferedInputStream)

// Continue with the rest of your extraction logic using the tempFile
SevenZFile(tempFile).use { sevenZFile ->
var entry: SevenZArchiveEntry? = sevenZFile.nextEntry
while (entry != null) {
val outputFile = outputDirectory?.createFile("application/octet-stream", entry.name)
if (entry.isDirectory) {
outputFile?.createDirectory("Un7z")
} else {
outputFile?.uri?.let { uri ->
contentResolver.openOutputStream(uri)?.use { outputStream ->
val buffer = ByteArray(4096)
try {
var count: Int
while (sevenZFile.read(buffer).also { count = it } != -1) {
outputStream.write(buffer, 0, count)
}
} catch (e: Exception) {
showToast("Extraction failed: ${e.message}")
sevenZFile.close()
}
} catch (e: Exception) {
showToast("Extraction failed: ${e.message}")
return
}
}
}
entry = sevenZFile.nextEntry
}
entry = sevenZFile.nextEntry
}
// Show a completion message after extraction is done
withContext(Dispatchers.Main) {
showExtractionCompletedSnackbar(outputDirectory)
}
}

showExtractionCompletedSnackbar(outputDirectory)
tempFileJob.invokeOnCompletion { throwable ->
if (throwable != null) {
showToast("Temp file creation failed: ${throwable.message}")
}
}
}

private fun extractXz(bufferedInputStream: BufferedInputStream, outputDirectory: DocumentFile?) {
Expand Down Expand Up @@ -364,6 +398,7 @@ class MainActivity : AppCompatActivity() {


private fun showExtractionCompletedSnackbar(outputDirectory: DocumentFile?) {
progressBar.visibility = View.GONE
val rootView = findViewById<View>(android.R.id.content)
val snackbar = Snackbar.make(rootView, "Extraction completed successfully", Snackbar.LENGTH_LONG)

Expand Down
16 changes: 12 additions & 4 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:theme="@style/Theme.Material3.DayNight.NoActionBar"
tools:context=".MainActivity">

<TextView
Expand All @@ -19,14 +18,14 @@
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp" />

<Button
<com.google.android.material.button.MaterialButton
android:id="@+id/pickFileButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/pick_file" />

<Button
<com.google.android.material.button.MaterialButton
android:id="@+id/changeDirectoryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Expand All @@ -46,12 +45,21 @@
android:layout_below="@id/changeDirectoryButton"
android:layout_marginTop="16dp" />

<Button
<com.google.android.material.button.MaterialButton
android:id="@+id/extractButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/extract"
android:layout_below="@id/directoryTextView"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"/>

<com.google.android.material.progressindicator.LinearProgressIndicator
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_alignBottom="@id/extractButton"
android:layout_centerHorizontal="true"
android:layout_marginBottom="-15dp"
android:indeterminate="true" />
</RelativeLayout>
5 changes: 3 additions & 2 deletions app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<background android:drawable="@mipmap/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
</adaptive-icon>
5 changes: 0 additions & 5 deletions app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

This file was deleted.

Binary file added app/src/main/res/mipmap-hdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary file not shown.
Binary file added app/src/main/res/mipmap-mdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary file not shown.
Binary file added app/src/main/res/mipmap-xhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary file not shown.
Binary file added app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file added app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
45 changes: 30 additions & 15 deletions app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.UnZip" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->

<resources>
<style name="AppTheme" parent="Theme.Material3.Dark.NoActionBar">
<item name="colorPrimary">@color/md_theme_dark_primary</item>
<item name="colorOnPrimary">@color/md_theme_dark_onPrimary</item>
<item name="colorPrimaryContainer">@color/md_theme_dark_primaryContainer</item>
<item name="colorOnPrimaryContainer">@color/md_theme_dark_onPrimaryContainer</item>
<item name="colorSecondary">@color/md_theme_dark_secondary</item>
<item name="colorOnSecondary">@color/md_theme_dark_onSecondary</item>
<item name="colorSecondaryContainer">@color/md_theme_dark_secondaryContainer</item>
<item name="colorOnSecondaryContainer">@color/md_theme_dark_onSecondaryContainer</item>
<item name="colorTertiary">@color/md_theme_dark_tertiary</item>
<item name="colorOnTertiary">@color/md_theme_dark_onTertiary</item>
<item name="colorTertiaryContainer">@color/md_theme_dark_tertiaryContainer</item>
<item name="colorOnTertiaryContainer">@color/md_theme_dark_onTertiaryContainer</item>
<item name="colorError">@color/md_theme_dark_error</item>
<item name="colorErrorContainer">@color/md_theme_dark_errorContainer</item>
<item name="colorOnError">@color/md_theme_dark_onError</item>
<item name="colorOnErrorContainer">@color/md_theme_dark_onErrorContainer</item>
<item name="android:colorBackground">@color/md_theme_dark_background</item>
<item name="colorOnBackground">@color/md_theme_dark_onBackground</item>
<item name="colorSurface">@color/md_theme_dark_surface</item>
<item name="colorOnSurface">@color/md_theme_dark_onSurface</item>
<item name="colorSurfaceVariant">@color/md_theme_dark_surfaceVariant</item>
<item name="colorOnSurfaceVariant">@color/md_theme_dark_onSurfaceVariant</item>
<item name="colorOutline">@color/md_theme_dark_outline</item>
<item name="colorOnSurfaceInverse">@color/md_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/md_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/md_theme_dark_inversePrimary</item>
</style>
</resources>
</resources>
Loading

0 comments on commit db66326

Please sign in to comment.