AppMenuSheet: Use ACTION_CREATE_DOCUMENT to export APK
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user