AppMenuSheet: Use ACTION_CREATE_DOCUMENT to export APK

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-02-01 11:58:57 +01:00
parent aa95542b0d
commit 4297bac4fe
5 changed files with 29 additions and 72 deletions

View File

@@ -22,40 +22,29 @@ package com.aurora.store.util
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.util.Log
import com.aurora.store.util.PackageUtil.getPackageInfo
import java.io.File
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
class ApkCopier(private val context: Context, private val packageName: String) {
object ApkCopier {
fun copy() {
val destination = File(PathUtil.getBaseCopyDirectory(context))
destination.let {
if (it.exists()) {
Log.i("Base copy directory is available")
} else {
it.mkdirs()
Log.e("Base copy directory is created : ${it.path}")
}
}
private const val TAG = "ApkCopier"
fun copy(context: Context, packageName: String, uri: Uri) {
val packageInfo = getPackageInfo(context, packageName, PackageManager.GET_META_DATA)
val baseApk = getBaseApk(packageInfo)
val fileList: MutableList<File?> = mutableListOf()
/*Add base APK*/
fileList.add(baseApk)
val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs
if (splitSourceDirs != null && splitSourceDirs.isNotEmpty()) {
/*Add Split APKs*/
if (!packageInfo.applicationInfo.splitSourceDirs.isNullOrEmpty()) {
fileList.addAll(getSplitAPKs(packageInfo))
}
bundleAllAPKs(context, fileList)
bundleAllAPKs(context, fileList.filterNotNull(), uri)
}
private fun getBaseApk(packageInfo: PackageInfo?): File? {
@@ -75,25 +64,22 @@ class ApkCopier(private val context: Context, private val packageName: String) {
return fileList
}
private fun bundleAllAPKs(context: Context, fileList: List<File?>) {
private fun bundleAllAPKs(context: Context, fileList: List<File>, uri: Uri) {
try {
val fileOutputStream =
FileOutputStream(PathUtil.getBaseCopyDirectory(context) + "$packageName.zip")
val zipOutputStream = ZipOutputStream(fileOutputStream)
val zipOutputStream = ZipOutputStream(context.contentResolver.openOutputStream(uri))
for (file in fileList) {
file?.inputStream()?.use {
fileList.forEach { file ->
file.inputStream().use { output ->
val zipEntry = ZipEntry(file.name)
zipOutputStream.putNextEntry(zipEntry)
it.copyTo(zipOutputStream)
output.copyTo(zipOutputStream)
zipOutputStream.closeEntry()
}
}
zipOutputStream.close()
} catch (e: Exception) {
e.printStackTrace()
Log.e("ApkCopier : %s", e.message)
} catch (exception: Exception) {
Log.e(TAG, "Failed to copy app bundles", exception)
}
}
}

View File

@@ -90,10 +90,6 @@ object PathUtil {
) + "/${file.name}"
}
fun getApkDownloadFile(context: Context, packageName: String, versionCode: Int): String {
return getVersionDirectory(context, packageName, versionCode)
}
fun getExternalPath(context: Context): String {
val defaultDir =
File("${Environment.getExternalStorageDirectory().absolutePath}/Aurora/Store")
@@ -108,10 +104,6 @@ object PathUtil {
)
}
fun getBaseCopyDirectory(context: Context): String {
return "${getExternalPath(context)}/Exports/"
}
private fun getObbDownloadPath(packageName: String): String {
return Environment.getExternalStorageDirectory()
.toString() + "/Android/obb/" + packageName

View File

@@ -20,21 +20,14 @@
package com.aurora.store.view.ui.sheets
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.navArgs
import com.aurora.extensions.isRAndAbove
import com.aurora.extensions.openInfo
import com.aurora.extensions.toast
import com.aurora.store.R
@@ -54,14 +47,16 @@ class AppMenuSheet : BaseBottomSheet() {
private val viewModel: SheetsViewModel by viewModels()
private val args: AppMenuSheetArgs by navArgs()
private val exportMimeType = "application/zip"
private val startForPermissions =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
viewModel.copyApk(requireContext(), args.app.packageName)
private val requestDocumentCreation =
registerForActivityResult(ActivityResultContracts.CreateDocument(exportMimeType)) {
if (it != null) {
viewModel.copyApk(requireContext(), args.app.packageName, it)
} else {
toast(R.string.permissions_denied)
toast(R.string.failed_apk_export)
}
dismissAllowingStateLoss()
}
override fun onCreateContentView(
@@ -106,48 +101,30 @@ class AppMenuSheet : BaseBottomSheet() {
requireContext().toast(R.string.toast_apk_blacklisted)
}
dismissAllowingStateLoss()
EventBus.getDefault()
.post(BusEvent.Blacklisted(args.app.packageName, ""))
}
R.id.action_local -> {
export()
requestDocumentCreation.launch("${args.app.packageName}.zip")
}
R.id.action_uninstall -> {
AppInstaller.uninstall(requireContext(), args.app.packageName)
dismissAllowingStateLoss()
}
R.id.action_info -> {
requireContext().openInfo(args.app.packageName)
dismissAllowingStateLoss()
}
}
dismissAllowingStateLoss()
false
}
}
}
private fun export() {
if (isRAndAbove()) {
if (!Environment.isExternalStorageManager()) {
startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))
} else {
viewModel.copyApk(requireContext(), args.app.packageName)
}
} else {
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
) {
viewModel.copyApk(requireContext(), args.app.packageName)
} else {
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}
}
companion object {
const val TAG = "APP_MENU_SHEET"
}

View File

@@ -1,6 +1,7 @@
package com.aurora.store.viewmodel.sheets
import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -39,9 +40,9 @@ class SheetsViewModel : ViewModel() {
}
}
fun copyApk(context: Context, packageName: String) {
fun copyApk(context: Context, packageName: String, uri: Uri) {
viewModelScope.launch(Dispatchers.IO) {
ApkCopier(context, packageName).copy()
ApkCopier.copy(context, packageName, uri)
}
}
}

View File

@@ -405,4 +405,5 @@
<string name="device_doze_extra">Optionally you can choose to keep the optimizations enabled, but then you won\'t be able to take benefit of background downloads and automated updates, so choice is yours.</string>
<string name="pref_self_update_title">Self-updates</string>
<string name="pref_self_update_summary">Check and notify if a new stable version of Aurora Store is available</string>
<string name="failed_apk_export">Failed to export APKs</string>
</resources>