diff --git a/app/src/main/java/com/aurora/store/data/work/ExportWorker.kt b/app/src/main/java/com/aurora/store/data/work/ExportWorker.kt index 00ea2dbde..46d5f53c4 100644 --- a/app/src/main/java/com/aurora/store/data/work/ExportWorker.kt +++ b/app/src/main/java/com/aurora/store/data/work/ExportWorker.kt @@ -1,5 +1,7 @@ package com.aurora.store.data.work +import android.app.NotificationManager +import android.app.Service import android.content.Context import android.content.pm.PackageManager import android.net.Uri @@ -11,6 +13,9 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OutOfQuotaPolicy import androidx.work.WorkManager import androidx.work.WorkerParameters +import com.aurora.gplayapi.data.models.App +import com.aurora.store.data.room.download.Download +import com.aurora.store.util.NotificationUtil import com.aurora.store.util.PackageUtil.getPackageInfo import com.aurora.store.util.PathUtil import dagger.assisted.Assisted @@ -35,17 +40,19 @@ class ExportWorker @AssistedInject constructor( private const val IS_DOWNLOAD = "IS_DOWNLOAD" private const val PACKAGE_NAME = "PACKAGE_NAME" private const val VERSION_CODE = "VERSION_CODE" + private const val DISPLAY_NAME = "DISPLAY_NAME" /** * Exports the installed package to the given URI - * @param packageName packageName of the app to export + * @param app App to export * @see [ExportWorker] */ - fun exportInstalledApp(context: Context, packageName: String, uri: Uri) { + fun exportInstalledApp(context: Context, app: App, uri: Uri) { val inputData = Data.Builder() .putBoolean(IS_DOWNLOAD, false) .putString(URI, uri.toString()) - .putString(PACKAGE_NAME, packageName) + .putString(DISPLAY_NAME, app.displayName) + .putString(PACKAGE_NAME, app.packageName) .build() val oneTimeWorkRequest = OneTimeWorkRequestBuilder() @@ -53,22 +60,22 @@ class ExportWorker @AssistedInject constructor( .setExpedited(OutOfQuotaPolicy.DROP_WORK_REQUEST) .build() - Log.i(TAG, "Exporting $packageName") + Log.i(TAG, "Exporting $app.packageName") WorkManager.getInstance(context).enqueue(oneTimeWorkRequest) } /** * Exports the given download to the URI - * @param packageName Name of the package to export - * @param versionCode version of the package + * @param download Download to export * @see [ExportWorker] */ - fun exportDownloadedApp(context: Context, packageName: String, versionCode: Int, uri: Uri) { + fun exportDownloadedApp(context: Context, download: Download, uri: Uri) { val inputData = Data.Builder() .putBoolean(IS_DOWNLOAD, true) .putString(URI, uri.toString()) - .putString(PACKAGE_NAME, packageName) - .putInt(VERSION_CODE, versionCode) + .putString(DISPLAY_NAME, download.displayName) + .putString(PACKAGE_NAME, download.packageName) + .putInt(VERSION_CODE, download.versionCode) .build() val oneTimeWorkRequest = OneTimeWorkRequestBuilder() @@ -76,19 +83,27 @@ class ExportWorker @AssistedInject constructor( .setExpedited(OutOfQuotaPolicy.DROP_WORK_REQUEST) .build() - Log.i(TAG, "Exporting download for ${packageName}/${versionCode}") + Log.i(TAG, "Exporting download for ${download.packageName}/${download.versionCode}") WorkManager.getInstance(context).enqueue(oneTimeWorkRequest) } } + private lateinit var notificationManager: NotificationManager + private val NOTIFICATION_ID = 500 + override suspend fun doWork(): Result { val isDownload = inputData.getBoolean(IS_DOWNLOAD, false) val uri = Uri.parse(inputData.getString(URI)) val packageName = inputData.getString(PACKAGE_NAME) + val displayName = inputData.getString(DISPLAY_NAME) val versionCode = inputData.getInt(VERSION_CODE, -1) + notificationManager = + appContext.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager + if (packageName.isNullOrEmpty() || isDownload && versionCode == -1) { Log.e(TAG, "Input data is corrupt, bailing out!") + notifyStatus(displayName?: String(), uri, false) return Result.failure() } @@ -98,14 +113,28 @@ class ExportWorker @AssistedInject constructor( } else { copyInstalledApp(packageName, uri) } + notifyStatus(displayName ?: packageName, uri) } catch (exception: Exception) { Log.e(TAG, "Failed to export $packageName", exception) + notifyStatus(displayName?: packageName, uri, false) return Result.failure() } return Result.success() } + private fun notifyStatus(packageName: String, uri: Uri, success: Boolean = true) { + notificationManager.notify( + NOTIFICATION_ID, + NotificationUtil.getExportStatusNotification( + appContext, + packageName, + uri, + success + ) + ) + } + private fun copyInstalledApp(packageName: String, uri: Uri) { val packageInfo = getPackageInfo(appContext, packageName, PackageManager.GET_META_DATA) val fileList: MutableList = mutableListOf() diff --git a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt index 7f71b539f..d9c2845fa 100644 --- a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt +++ b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt @@ -9,6 +9,7 @@ import android.content.Context import android.content.Intent import android.graphics.Bitmap import android.graphics.Color +import android.net.Uri import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.PendingIntentCompat @@ -250,6 +251,64 @@ object NotificationUtil { .build() } + fun getExportStatusNotification( + context: Context, + displayName: String, + uri: Uri, + success: Boolean + ): Notification { + val intent = Intent(Intent.ACTION_SEND).apply { + setDataAndType(uri, "application/zip") + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION + putExtra(Intent.EXTRA_STREAM, uri) + } + val pendingIntent = PendingIntentCompat.getActivity( + context, + UUID.randomUUID().hashCode(), + Intent.createChooser(intent, null), + PendingIntent.FLAG_CANCEL_CURRENT, + false + ) + + val content = if (success) { + context.getString(R.string.export_app_summary_success) + } else { + context.getString(R.string.export_app_summary_fail) + } + + val builder = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ALERT) + .setSmallIcon(R.drawable.ic_file_copy) + .setColor(context.getStyledAttributeColor(R.color.colorAccent)) + .setContentTitle(displayName).setContentText(content) + .setContentIntent(getContentIntentForExport(context, uri)) + .setAutoCancel(true) + + if (success) { + builder.addAction( + NotificationCompat.Action.Builder( + R.drawable.ic_share, + context.getString(R.string.action_share), + pendingIntent + ).build() + ) + } + return builder.build() + } + + private fun getContentIntentForExport(context: Context, uri: Uri): PendingIntent? { + val intent = Intent(Intent.ACTION_VIEW).apply { + setDataAndType(uri, "application/zip") + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION + } + return PendingIntentCompat.getActivity( + context, + UUID.randomUUID().hashCode(), + intent, + PendingIntent.FLAG_CANCEL_CURRENT, + false + ) + } + private fun getContentIntentForDetails(context: Context, packageName: String): PendingIntent { return NavDeepLinkBuilder(context) .setGraph(R.navigation.mobile_navigation) 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 5c3d9d2b8..17345d8d7 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 @@ -57,7 +57,7 @@ class AppMenuSheet : BottomSheetDialogFragment(R.layout.sheet_app_menu) { private val requestDocumentCreation = registerForActivityResult(ActivityResultContracts.CreateDocument(exportMimeType)) { if (it != null) { - viewModel.copyInstalledApp(requireContext(), args.app.packageName, it) + viewModel.copyInstalledApp(requireContext(), args.app, it) } else { toast(R.string.failed_apk_export) } diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt index b1d9f004a..cdc81993e 100644 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt +++ b/app/src/main/java/com/aurora/store/view/ui/sheets/DownloadMenuSheet.kt @@ -59,12 +59,7 @@ class DownloadMenuSheet : BottomSheetDialogFragment(R.layout.sheet_download_menu private val requestDocumentCreation = registerForActivityResult(ActivityResultContracts.CreateDocument(exportMimeType)) { if (it != null) { - viewModel.copyDownloadedApp( - requireContext(), - args.download.packageName, - args.download.versionCode, - it - ) + viewModel.copyDownloadedApp(requireContext(), args.download, it) } else { toast(R.string.failed_apk_export) } 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 a79651267..56fd8be04 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 @@ -9,6 +9,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.helpers.PurchaseHelper import com.aurora.store.data.event.BusEvent import com.aurora.store.data.providers.AuthProvider +import com.aurora.store.data.room.download.Download import com.aurora.store.data.work.ExportWorker import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -45,11 +46,11 @@ class SheetsViewModel @Inject constructor( } } - fun copyInstalledApp(context: Context, packageName: String, uri: Uri) { - ExportWorker.exportInstalledApp(context, packageName, uri) + fun copyInstalledApp(context: Context, app: App, uri: Uri) { + ExportWorker.exportInstalledApp(context, app, uri) } - fun copyDownloadedApp(context: Context, packageName: String, versionCode: Int, uri: Uri) { - ExportWorker.exportDownloadedApp(context, packageName, versionCode, uri) + fun copyDownloadedApp(context: Context, download: Download, uri: Uri) { + ExportWorker.exportDownloadedApp(context, download, uri) } } diff --git a/app/src/main/res/drawable/ic_file_copy.xml b/app/src/main/res/drawable/ic_file_copy.xml new file mode 100644 index 000000000..dc91a7666 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_copy.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fc24e3c8..fda562aa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -462,4 +462,8 @@ Failed to export favourites! Favourites imported! Favourites exported! + + + Successfully exported app bundle + Failed to exported app bundle