Когда вы commit()
свой Session
, вам нужно указать IntentSender
, который указывает на какой-то компонент, например BroadcastReceiver
. Здесь я делаю это в installCoroutine()
:
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Q
https://commonsware.com/AndroidQ
*/
package com.commonsware.q.appinstaller
import android.app.Application
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageInstaller
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
private const val NAME = "mostly-unused"
private const val PI_INSTALL = 3439
class MainMotor(app: Application) : AndroidViewModel(app) {
private val installer = app.packageManager.packageInstaller
private val resolver = app.contentResolver
fun install(apkUri: Uri) {
viewModelScope.launch(Dispatchers.Main) {
installCoroutine(apkUri)
}
}
private suspend fun installCoroutine(apkUri: Uri) =
withContext(Dispatchers.IO) {
resolver.openInputStream(apkUri)?.use { apkStream ->
val length =
DocumentFile.fromSingleUri(getApplication(), 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(getApplication(), InstallReceiver::class.java)
val pi = PendingIntent.getBroadcast(
getApplication(),
PI_INSTALL,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
session.commit(pi.intentSender)
session.close()
}
}
}
Затем ваш компонент может посмотреть на EXTRA_STATUS
, чтобы получить статус:
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Q
https://commonsware.com/AndroidQ
*/
package com.commonsware.q.appinstaller
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.media.AudioManager
import android.media.ToneGenerator
import android.util.Log
private const val TAG = "AppInstaller"
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)
else -> {
val msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
Log.e(TAG, "received $status and $msg")
}
}
}
}
Похоже, STATUS_FAILURE_INVALID
будет означать недействительную подпись.