ExportWorker: Show notification on export status when finished

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-07-10 18:29:13 +07:00
parent 294df7aa41
commit d75f70590a
7 changed files with 120 additions and 21 deletions

View File

@@ -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<ExportWorker>()
@@ -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<ExportWorker>()
@@ -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<File?> = mutableListOf()

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}
}