From 4297bac4fe8f027612c440f46d5824c59e3e4522 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Thu, 1 Feb 2024 11:58:57 +0100 Subject: [PATCH] AppMenuSheet: Use ACTION_CREATE_DOCUMENT to export APK Signed-off-by: Aayush Gupta --- .../java/com/aurora/store/util/ApkCopier.kt | 42 ++++++----------- .../java/com/aurora/store/util/PathUtil.kt | 8 ---- .../store/view/ui/sheets/AppMenuSheet.kt | 45 +++++-------------- .../store/viewmodel/sheets/SheetsViewModel.kt | 5 ++- app/src/main/res/values/strings.xml | 1 + 5 files changed, 29 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/com/aurora/store/util/ApkCopier.kt b/app/src/main/java/com/aurora/store/util/ApkCopier.kt index d6ab20e69..119b63da2 100644 --- a/app/src/main/java/com/aurora/store/util/ApkCopier.kt +++ b/app/src/main/java/com/aurora/store/util/ApkCopier.kt @@ -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 = 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) { + private fun bundleAllAPKs(context: Context, fileList: List, 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) } } } diff --git a/app/src/main/java/com/aurora/store/util/PathUtil.kt b/app/src/main/java/com/aurora/store/util/PathUtil.kt index d32b43721..8502c7196 100644 --- a/app/src/main/java/com/aurora/store/util/PathUtil.kt +++ b/app/src/main/java/com/aurora/store/util/PathUtil.kt @@ -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 diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt index 39b68cb89..b6e4099c4 100644 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt +++ b/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt @@ -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" } diff --git a/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt index fc9890d29..a1a751b1d 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt @@ -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) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 734bc7003..7c371d052 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -405,4 +405,5 @@ 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. Self-updates Check and notify if a new stable version of Aurora Store is available + Failed to export APKs