Android INSTALL_FAILED_UPDATE_INCOMPATIBLE error on Package installer - android

I can install the application without the play store with the apk I downloaded from the server, but I want to do this without asking for read/write permission.
In Firebase app tester we can install apk without getting read/write permissions. I want to do the same in my own application and I decided to use Package installer for this, but after the APK is downloaded, the update dialog appears on the screen and then I get the error below.
INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package com.xxx.xxx signatures do not match previously installed version; ignoring!
Soloution reference : https://commonsware.com/Q/pages/chap-pkg-001
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun installPackage(destination: String) {
val onComplete = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent1: Intent
) {
try {
context.registerReceiver(mInstallReceiver, IntentFilter(ACTION_INSTALL_COMPLETE))
factory.dismiss()
val installer = context.packageManager.packageInstaller
val resolver = context.contentResolver
val contentUri = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + PROVIDER_PATH,
File(destination)
)
resolver.openInputStream(contentUri)?.use { apkStream ->
val length = DocumentFile.fromSingleUri(context, contentUri)?.length() ?: -1
val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
params.setAppPackageName(context.packageName)
val sessionId = installer.createSession(params)
val session = installer.openSession(sessionId)
session.openWrite(NAME, 0, length).use { sessionStream ->
apkStream.copyTo(sessionStream)
session.fsync(sessionStream)
}
val intent = Intent(ACTION_INSTALL_COMPLETE)
val pendingIntent = PendingIntent.getBroadcast(context, PI_INSTALL, intent, PendingIntent.FLAG_UPDATE_CURRENT)
session.commit(pendingIntent.intentSender)
session.close()
}
}
catch (e :Exception){
e.printStackTrace()
context.unregisterReceiver(mInstallReceiver)
}
}
}
context.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
private val mInstallReceiver = object : BroadcastReceiver() {
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onReceive(context: Context, intent: Intent) {
if (ACTION_INSTALL_COMPLETE != intent.action) {
return
}
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val install = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
if (install?.resolveActivity(context.packageManager) != null) {
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(install)
}
}
PackageInstaller.STATUS_SUCCESS -> ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100)
.startTone(ToneGenerator.TONE_PROP_ACK)
else -> {
val msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
context.showToast("$msg")
Log.e("Installer app", "received $status and $msg")
}
}
}
}

Related

Flutter : Get value of pressed numbers in Home widget with number buttons

I am using this package with customisation home_widget and trying to build a home widget with number buttons like this image.
I am trying to achieve it by the following code
My WidgetProvider.kt
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
appWidgetIds.forEach { widgetId ->
val views = RemoteViews(context.packageName, R.layout.widget_layout).apply {
// Open App on Widget Click
val pendingIntent = HomeWidgetLaunchIntent.getActivity(context,
MainActivity::class.java)
setOnClickPendingIntent(R.id.widget_root, pendingIntent)
val counter = widgetData.getString("_number", "")
Log.i("onMethodCall", "onMethodCall: $counter")
print("onMethodCall: $counter")
var counterText = "$counter"
setTextViewText(R.id.tv_counter, counterText)
val pendingIntent2 = HomeWidgetLaunchIntent.getActivity(context,
MainActivity::class.java)
setOnClickPendingIntent(R.id.bt_update, pendingIntent2)
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(context, Uri.parse("myAppWidget://onNumberBtnClick"))
setOnClickPendingIntent(R.id.bt_one, backgroundIntent)
}
appWidgetManager.updateAppWidget(widgetId, views)
}
}
I am passing intent as "onNumberBtnClick" and this is my broadcast function
fun getBroadcast(context: Context, uri: Uri? = null): PendingIntent {
val intent = Intent(context, HomeWidgetBackgroundReceiver::class.java)
intent.data = uri
intent.action = HOME_WIDGET_BACKGROUND_ACTION
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= 23) {
flags = flags or PendingIntent.FLAG_IMMUTABLE
}
return PendingIntent.getBroadcast(context, 0, intent, flags)
}
in my "HomeWidgetPlugin" class I have set that value in shared preference.
"onNumberBtnClick" -> {
if (call.hasArgument("id") && call.hasArgument("data")) {
Log.i("onMethodCallHere", "onMethodCallHere: ")
print("onMethodCallHere")
val id = call.argument<String>("id")
val data = call.argument<Any>("data")
val prefs = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).edit()
if(data != null) {
when (data) {
is Boolean -> prefs.putBoolean(id, data)
is Float -> prefs.putFloat(id, data)
is String -> prefs.putString(id, data)
is Double -> prefs.putLong(id, java.lang.Double.doubleToRawLongBits(data))
is Int -> prefs.putInt(id, data)
else -> result.error("-10", "Invalid Type ${data!!::class.java.simpleName}. Supported types are Boolean, Float, String, Double, Long", IllegalArgumentException())
}
} else {
prefs.remove(id);
}
result.success(prefs.commit())
}else {
result.error("-1", "InvalidArguments saveWidgetData must be called with id and data", IllegalArgumentException())
}
}
In main.dart I handled callback with workmanager plugin as in the example of the home widget plugin
Future<void> backgroundCallback(Uri? uri) async {
if (uri?.host == 'onNumberBtnClick') {
await HomeWidget.getWidgetData<String>('_number', defaultValue: "1").then((value) {
print("num:$value");
});
await HomeWidget.saveWidgetData<String>('_number', "13");
await HomeWidget.updateWidget(name: 'AppWidgetProvider', iOSName: 'AppWidgetProvider');
}
}
Can someone please help me with what I am missing or how can I achieve this?

Android 12 breaks installation of APK via the Package Manager?

I was working on an update for my app, a section of which deals with downloading and installing an APK file.
As long as the previous version were targeting SDK 30 everything worked pretty smoothly. But as soon as I incremented the target and compile SDK to 32 it just started behaving queerly.
​
Here is the code that deals which the package manager and the installation:
private fun installOnClickListener() {
binding.termuxInstallCard.showProgress()
var session: PackageInstaller.Session? = null
try {
val packageInstaller: PackageInstaller =
requireContext().packageManager.packageInstaller
val params = PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL
)
val sessionId = packageInstaller.createSession(params)
session = packageInstaller.openSession(sessionId)
viewModel.addApkToSession(session)
var installBroadcast: PendingIntent? = null
val intent =
Intent(PACKAGE_INSTALLED_ACTION).putExtra(
"packageName",
"com.termux"
)
installBroadcast = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(
context,
0,
intent,
FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(context, 0, intent, FLAG_UPDATE_CURRENT)
}
session.commit(installBroadcast.intentSender)
session.close()
} catch (e: IOException) {
throw RuntimeException("Couldn't install package", e)
} catch (e: RuntimeException) {
session?.abandon()
throw e
} finally {
session?.close()
}
}
Here is what is happening:
​
As I am targeting SDK 32, I am required to specify the Mutability of PendingIntent
Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent
When I use:
FLAG_MUTABLE- The installation just fails stating the error code- STATUS_FAILURE_INVALID with no extra message for debugging in EXTRA_STATUS_MESSAGE. The thing is that when I try to install the same downloaded APK via the adb shell, it just installs normally without any issues.
FLAG_IMMUTABLE- The installation succeeds without prompting user with the installation dialog but nothing is actually installed.
​
More code in case you need it-
fun addApkToInstallSession(
path: String,
session: PackageInstaller.Session
) {
val file = File("${context.filesDir.path}/$path")
val packageInSession: OutputStream = session.openWrite("com.termux", 0, -1)
val inputStream = FileInputStream(file)
val byteStream = inputStream.read()
try {
var c: Int
val buffer = ByteArray(16384)
while (inputStream.read(buffer).also { c = it } >= 0) {
packageInSession.write(buffer, 0, c)
}
} catch (e: IOException) {
println("IOEX")
} finally {
try {
packageInSession.close()
inputStream.close()
} catch (e: IOException) {
println("IOEX in closing the stream")
}
}
}
​
private val broadcastReceiverForInstallEvents = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
lifecycleScope.launch(Dispatchers.IO) {
val extras = intent.extras
val status = extras!!.getInt(PackageInstaller.EXTRA_STATUS)
val packageName = extras.getString("packageName")!!
if (PACKAGE_INSTALLED_ACTION == intent.action) {
println("STATUS $status")
when (status) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
try {
val confirmIntent = extras[Intent.EXTRA_INTENT] as Intent
confirmIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(confirmIntent)
} catch (e: Exception) {
lifecycleScope.launch(Dispatchers.Main) {
Toast.makeText(
requireContext(),
"We could not find an application to handle the installation of apps. Please download a package installer.",
Toast.LENGTH_SHORT
).show()
}
}
}
PackageInstaller.STATUS_SUCCESS -> {
lifecycleScope.launch(Dispatchers.Main) {
println("$packageName Install succeeded!")
// todo all done animation
binding.termuxInstallCard.markAsComplete()
Toast.makeText(requireContext(), "All Done!", Toast.LENGTH_SHORT)
.show()
lifecycleScope.launch {
// viewModel.setTermuxSetupDone()
}
/* redirecting... */
Handler(Looper.getMainLooper()).postDelayed({
redirect()
}, 2000)
}
}
PackageInstaller.STATUS_FAILURE, PackageInstaller.STATUS_FAILURE_ABORTED, PackageInstaller.STATUS_FAILURE_BLOCKED, PackageInstaller.STATUS_FAILURE_CONFLICT, PackageInstaller.STATUS_FAILURE_INCOMPATIBLE, PackageInstaller.STATUS_FAILURE_INVALID, PackageInstaller.STATUS_FAILURE_STORAGE -> {
lifecycleScope.launch(Dispatchers.Main) {
println("Extra Status Message${extras.getString("EXTRA_STATUS_MESSAGE")}")
"There was an error installing Termux. Please retry.".showSnackbar(
binding.root,
true
)
binding.termuxInstallCard.hideProgress()
}
}
else -> {
lifecycleScope.launch(Dispatchers.Main) {
println("$packageName Install failed else!")
// exitActivity("Package failed to install -> Unknown Error!")
binding.termuxInstallCard.hideProgress()
}
}
}
}
}
}
}
I would really appreciate some help!
​
In the past when I tried to install APKs I used either Knox on Samsung devices or I'd refer the user to install the package via package manager, this is the code I've used:
public static void cleanInstall(String datum, Context context) {
File file = new File(datum);
if (file.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri downloadedApk = FileProvider.getUriForFile(context, ".MyPackage.myFileProvider", file);
intent.setDataAndType(downloadedApk, type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.fromFile(file), type);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
} else {
Toast.makeText(context, "ّFile not found!", Toast.LENGTH_SHORT).show();
}
}
I've not used this code in a while so I don't know if it works anymore but it's something you might want to check, to see if it does. Make sure the path you are referring to does not reside on SD as you'll probably face permission denied issues.
On calculating the hash of the file I was committing to the session of the Package Manager, I found out that it didn't write the APK file correctly.
val inputStream: InputStream = session.openRead("com.termux")
val file = File("${requireContext().filesDir.path}/test2.apk")
if (!file.exists()) {
file.createNewFile()
}
try {
inputStream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
} catch (e: Exception) {
println("Exception occured $e")
}
if (file.length() > 0) {
val hash = file.getMD5Hash()
println("HASH of session - $hash")
}
Fixed that and with the combination of the Mutable pending intent. The package is now installing perfectly.

Android Package Manager install not showing

I'm trying to build an auto updater via github. The app so far detects new available versions and downloads the apk file. I however cannot get it to install the apk file.
There are no crashes or Log messages indicating an error. The install dialog just does not show up.
This is my code:
fun downloadAndInstall(link: Uri, fileName: String){
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadUri = link
val request = DownloadManager.Request(downloadUri)
request.setMimeType(MIME_TYPE)
val destination = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/" + fileName
val destinationUri = Uri.parse("file://$destination")
request.setDestinationUri(destinationUri)
fun showInstallOption(destination: String) {
val onComplete = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
val contentUri = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".provider",
File(destination)
)
val installer = context.packageManager.packageInstaller
val resolver = context.contentResolver
resolver.openInputStream(contentUri)?.use { apkStream ->
val length =
DocumentFile.fromSingleUri(context, contentUri)?.length() ?: -1
val params =
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = installer.createSession(params)
val session = installer.openSession(sessionId)
session.openWrite("INSTALL", 0, length).use { sessionStream ->
apkStream.copyTo(sessionStream)
session.fsync(sessionStream)
}
val intent = Intent(context, InstallReceiver::class.java)
intent.action = "com.blazecode.tsviewer.util.updater.SESSION_API_PACKAGE_INSTALLED"
val pi = PendingIntent.getBroadcast(
context,
3,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
session.commit(pi.intentSender)
session.close()
}
Toast.makeText(context, "done", Toast.LENGTH_LONG).show()
}
}
context.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
showInstallOption(destination)
downloadManager.enqueue(request)
Toast.makeText(context, context.getString(R.string.downloading), Toast.LENGTH_LONG).show()
}
I tested this on Android 13 and 8, both with the same result. Android 13 is a physical device. Android 8 is an emulator.

How to Open APK File in Android 10

i am trying to open an apk file from within my activity the following way:
File.openAttachment(attachmentName: String, context: Context, activity: Activity, mimeType: String) {
if (attachmentName.isNotEmpty() && attachmentName != "-") {
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
intent.setDataAndType(this.getFileURI(), mimeType.toLowerCase(Locale.ENGLISH))
activity.startActivity(Intent.createChooser(intent, context.getString(R.string.choose_an_app)))
}
}
File(Utils.getAttachmentPathDirectory(MyApplication.applicationContext())+"/apkTest.apk")
.openAttachment("apkTest.apk", this, this, "application/vnd.android.package-archive")
i also tried the following way:
val intent_install = Intent(Intent.ACTION_VIEW)
intent_install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent_install.setDataAndType(FileProvider.getUriForFile(mContext, mContext.packageName+".provider",File(Utils.getAttachmentPathDirectory(MyApplication.applicationContext())+"/"+fileName)), "application/vnd.android.package-archive")
mActivity?.startActivity(intent_install)
there are no errors but the file is not opening
any help is appreciated
thanks to #CommonWare and the provided link
https://gitlab.com/commonsguy/cw-android-q/tree/vFINAL/AppInstaller
i have managed to achieve what i want using the following code
val apkUri = FileProvider.getUriForFile(this, packageName+".provider", File(Utils.getAttachmentPathDirectory(MyApplication.applicationContext())+"/testapk.apk"))
MyApplication.applicationContext().contentResolver.openInputStream(apkUri).use { apkStream ->
val length = DocumentFile.fromSingleUri(application, apkUri)?.length() ?: -1
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val sessionId = installer.createSession(params)
val session = installer.openSession(sessionId)
session.openWrite(NAME, 0, length).use { sessionStream ->
apkStream?.copyTo(sessionStream)
session.fsync(sessionStream)
}
val intent = Intent(application, InstallReceiver::class.java)
val pi = PendingIntent.getBroadcast(
application,
PI_INSTALL,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
session.commit(pi.intentSender)
session.close()
}
class InstallReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val activityIntent =
intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
context.startActivity(activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
PackageInstaller.STATUS_SUCCESS ->
ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100)
.startTone(ToneGenerator.TONE_PROP_ACK)
}
}
}

Second WorkRequest of WorkContinuation do not work

I am trying to start two work request, one worker sends a request to the server for generating excel file and obtains URL for download. Another work starts after previous and must to download that file. First work starts and returns Result.SUCCESS.
Problem is another WorkRequest just not execute. LoadInvoiceFileWorker do nothing.
What I need to do or what I do wrong?
Here is my code:
InvoiceDetailsViewModel:
class InvoiceDetailsViewModel : ViewModel() {
private val mWorkManager: WorkManager = WorkManager.getInstance()
fun generateAndLoadExcel(invoiceId: Int, invoiceName: String, enterpriseId: Int) {
val genInvoiceWorkerBuilder = OneTimeWorkRequest.Builder(GenerateExcelWorker::class.java)
genInvoiceWorkerBuilder.setInputData(createInputDataForGenerateExcel(invoiceId, invoiceName, enterpriseId))
val constraintBuilder = Constraints.Builder()
//constraintBuilder.setRequiredNetworkType(NetworkType.CONNECTED)
genInvoiceWorkerBuilder.setConstraints(constraintBuilder.build())
val continuation = mWorkManager.beginWith(
genInvoiceWorkerBuilder.build()
)
val loadFileWorkerBuilder = OneTimeWorkRequest.Builder(LoadInvoiceFileWorker::class.java)
//loadFileWorkerBuilder.setConstraints(Constraints.NONE)
continuation.then(loadFileWorkerBuilder.build())
continuation.enqueue()
}
private fun createInputDataForGenerateExcel(invoiceId: Int, invoiceName: String, enterpriseId: Int): Data {
val builder = Data.Builder()
builder.putInt(WorkerConstants.INVOICE_ID, invoiceId)
builder.putString(WorkerConstants.INVOICE_NAME, invoiceName)
builder.putInt(WorkerConstants.ENTERPRISE_ID, enterpriseId)
return builder.build()
}
}
GenerateExcelWorker:
class GenerateExcelWorker : Worker() {
companion object {
private val TAG = GenerateExcelWorker::class.java.simpleName
}
override fun doWork(): Result {
val appCont = applicationContext
val tokenType = PreferenceUtil.getString(TOKEN_TYPE, appCont, R.string.shared_pref_name)
val accessToken = PreferenceUtil.getString(ACCESS_TOKEN, appCont, R.string.shared_pref_name)
val enterpriseId = inputData.getInt(WorkerConstants.ENTERPRISE_ID, 0)
val invoiceId = inputData.getInt(WorkerConstants.INVOICE_ID, 0)
val invoiceName = inputData.getString(WorkerConstants.INVOICE_NAME)
makeStatusNotification(applicationContext, invoiceId, invoiceName
?: ("Invoice ${invoiceId.str()}"))
try {
val rd = RequestData()
rd.putValue("authorization", "$tokenType $accessToken", RequestData.TYPE_HEADER)
rd.putValue(FTUrls.SendingParameters.ENTERPRISE_ID, enterpriseId, RequestData.TYPE_PATH)
rd.putValue(FTUrls.SendingParameters.INVOICE_ID, invoiceId, RequestData.TYPE_PATH)
val excelUrl = InvoiceManager().generateIncomeInvoiceExcel(rd)
outputData = Data.Builder().putString(WorkerConstants.FILE_URL, excelUrl).build()
return Result.SUCCESS
} catch (t: Throwable) {
Log.e(TAG, "Error generating excel file for invoice $invoiceName ($invoiceId)", t)
if (t is UnauthenticatedException) {
outputData = Data.Builder().putBoolean(WorkerConstants.FILE_URL, true).build()
} else {
ExceptionLogger.logException(t)
Toast.makeText(applicationContext, t.message, Toast.LENGTH_SHORT).show()
}
return Result.FAILURE
}
}
private fun makeStatusNotification(context: Context, invoiceId: Int, invoiceTitle: String) {
// Make a channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
val name = WorkerConstants.NOTIFICATION_CHANNEL_NAME
val description = WorkerConstants.NOTIFICATION_CHANNEL_DESCRIPTION
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(WorkerConstants.CHANNEL_ID, name, importance)
channel.description = description
// Add the channel
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
val builder = NotificationCompat.Builder(context, WorkerConstants.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_autorenew_blue)
.setContentTitle(WorkerConstants.NOTIFICATION_TITLE)
.setContentText(String.format(WorkerConstants.NOTIFICATION_TEXT, invoiceTitle))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVibrate(LongArray(0))
NotificationManagerCompat.from(context).notify(invoiceId, builder.build())
}
}
LoadInvoiceFileWorker:
class LoadInvoiceFileWorker : Worker() {
companion object {
private val TAG = LoadInvoiceFileWorker::class.java.simpleName
}
override fun doWork(): Result {
try {
val fileUrl = inputData.getString(WorkerConstants.FILE_URL)
val invoiceId = inputData.getInt(WorkerConstants.INVOICE_ID, 0)
val invoiceName = inputData.getString(WorkerConstants.INVOICE_NAME)
val r = DownloadManager.Request(Uri.parse(fileUrl))
// This put the download in the same Download dir the browser uses
r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, invoiceName
?: ("Invoice ${invoiceId.str()}"))
// Notify user when download is completed
r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
// Start download
val dm = applicationContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager?
if (dm != null) {
dm.enqueue(r)
} else {
Log.w(TAG, "Download manager not exists for load invoice excel file")
ToastError(applicationContext, R.string.download_manager_not_found, Toast.LENGTH_SHORT)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(fileUrl))
try {
applicationContext.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Log.e(TAG, "Error open browser for view invoice excel file", e)
ToastError(applicationContext, R.string.browser_not_found, Toast.LENGTH_SHORT)
}
}
clearGenerateFileNotification(invoiceId)
return Result.SUCCESS
} catch (t: Throwable) {
Log.e(TAG, "Error loading excel generated file", t)
ExceptionLogger.logException(t)
ToastError(applicationContext, R.string.error_during_loading_file, Toast.LENGTH_SHORT)
return Result.FAILURE
}
}
private fun clearGenerateFileNotification(invoiceId: Int) {
val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(invoiceId)
}
}
WorkerConstants:
object WorkerConstants {
const val ENTERPRISE_ID = "enterprise_id"
const val INVOICE_ID = "invoice_id"
const val INVOICE_NAME = "invoice_name"
const val FILE_URL = "file_url"
const val UNIQUE_WORK_NAME_FOR_INVOICE = "generate_and_load_excel_for_invoice"
const val NOTIFICATION_CHANNEL_NAME = "GenerateExcelWorker Notifications"
const val NOTIFICATION_CHANNEL_DESCRIPTION = "Shows notifications whenever work starts"
const val NOTIFICATION_TITLE = "Генерація ексель файла"
const val NOTIFICATION_TEXT = "Генерація ексель файла накладної %s"
const val CHANNEL_ID = "GENERATE_INVOICE_NOTIFICATION"
}
Ok, I found my mistake. Instead of this:
...
continuation.then(loadFileWorkerBuilder.build())
continuation.enqueue()
I need make this:
...
continuation = continuation.then(loadFileWorkerBuilder.build())
continuation.enqueue()
I was apllying enqueue() for first continuation of one request. Method WorkContinuation.then() returns new object which contains old continuation with new added request.

Categories

Resources