Fix AppInstaller spawning useless instances by preserving the original instance of every instantiated installer type (fixes wrongly started or concurrent installs/uninstalls), implement service based install for new downloads (integrated in the UpdateService.kt and the AppDetailsActivity.kt), prevent multiple calls for install by using ThreadPoolExecutor (applied only to ServiceInstaller.kt for now), fix timer issues in the UpdateService.kt

This commit is contained in:
Konstantin Tuev
2021-06-09 23:09:27 +03:00
parent ca4f8d939e
commit 4e111fa9f4
8 changed files with 459 additions and 173 deletions

View File

@@ -26,9 +26,10 @@ import com.aurora.store.R
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
open class AppInstaller constructor(var context: Context) {
open class AppInstaller private constructor(var context: Context) {
companion object {
private var instance: AppInstaller? = null
fun getErrorString(context: Context, status: Int): String {
return when (status) {
PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(R.string.installer_status_user_action)
@@ -40,22 +41,50 @@ open class AppInstaller constructor(var context: Context) {
else -> context.getString(R.string.installer_status_failure)
}
}
fun getInstance(context: Context): AppInstaller {
if (instance == null) {
instance = AppInstaller(context.applicationContext)
}
return instance!!
}
}
val choiceAndInstaller = HashMap<Int, IInstaller>()
fun getPreferredInstaller(): IInstaller {
val prefValue = Preferences.getInteger(
context,
PREFERENCE_INSTALLER_ID
)
if (choiceAndInstaller.containsKey(prefValue)) {
return choiceAndInstaller[prefValue]!!
}
return when (prefValue) {
1 -> NativeInstaller(context)
2 -> RootInstaller(context)
3 -> ServiceInstaller(context)
1 -> {
val installer = NativeInstaller(context)
choiceAndInstaller[prefValue] = installer
installer
}
2 -> {
val installer = RootInstaller(context)
choiceAndInstaller[prefValue] = installer
installer
}
3 -> {
val installer = ServiceInstaller(context)
choiceAndInstaller[prefValue] = installer
installer
}
else -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
SessionInstaller(context)
val installer = SessionInstaller(context)
choiceAndInstaller[prefValue] = installer
installer
} else {
NativeInstaller(context)
val installer = NativeInstaller(context)
choiceAndInstaller[prefValue] = installer
installer
}
}
}

View File

@@ -25,9 +25,7 @@ import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageInstaller
import android.net.Uri
import android.os.Build
import android.os.IBinder
import android.os.RemoteException
import android.os.*
import androidx.annotation.RequiresApi
import androidx.core.content.FileProvider
import com.aurora.services.IPrivilegedCallback
@@ -41,10 +39,14 @@ import com.aurora.store.util.Log
import com.aurora.store.util.PackageUtil
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
class ServiceInstaller(context: Context) : InstallerBase(context) {
private lateinit var serviceConnection: ServiceConnection
private val executor = ThreadPoolExecutor(0, 1, 30L, TimeUnit.SECONDS, LinkedBlockingQueue())
companion object {
const val ACTION_INSTALL_REPLACE_EXISTING = 2
@@ -85,134 +87,168 @@ class ServiceInstaller(context: Context) : InstallerBase(context) {
}
override fun uninstall(packageName: String) {
val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
AuroraApplication.enqueuedInstalls.add(packageName)
val service = IPrivilegedService.Stub.asInterface(binder)
if (service.hasPrivilegedPermissions()) {
Log.i(context.getString(R.string.installer_service_available))
val callback = object : IPrivilegedCallback.Stub() {
override fun handleResult(packageName: String, returnCode: Int) {
executor.execute {
var readyWithAction = false
Handler(Looper.getMainLooper()).post {
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
if (isAlreadyQueued(packageName)) {
if (::serviceConnection.isInitialized) {
context.unbindService(serviceConnection)
}
readyWithAction = true
return
}
AuroraApplication.enqueuedInstalls.add(packageName)
val service = IPrivilegedService.Stub.asInterface(binder)
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
if (service.hasPrivilegedPermissions()) {
Log.i(context.getString(R.string.installer_service_available))
val callback = object : IPrivilegedCallback.Stub() {
override fun handleResult(packageName: String, returnCode: Int) {
}
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
removeFromInstallQueue(packageName)
readyWithAction = true
handleCallbackUninstall(packageName, returnCode, extra)
}
}
try {
service.deletePackageX(
packageName,
2,
BuildConfig.APPLICATION_ID,
callback
)
} catch (e: RemoteException) {
Log.e("Failed to connect Aurora Services")
removeFromInstallQueue(packageName)
readyWithAction = true
}
} else {
removeFromInstallQueue(packageName)
handleCallbackUninstall(packageName, returnCode, extra)
readyWithAction = true
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
}
}
try {
service.deletePackageX(
packageName,
2,
BuildConfig.APPLICATION_ID,
callback
)
} catch (e: RemoteException) {
Log.e("Failed to connect Aurora Services")
override fun onServiceDisconnected(name: ComponentName) {
removeFromInstallQueue(packageName)
Log.e("Disconnected from Aurora Services")
readyWithAction = true
}
} else {
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
removeFromInstallQueue(packageName)
}
}
override fun onServiceDisconnected(name: ComponentName) {
removeFromInstallQueue(packageName)
Log.e("Disconnected from Aurora Services")
val intent = Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT)
intent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME)
context.bindService(
intent,
serviceConnection,
Context.BIND_AUTO_CREATE
)
}
while (!readyWithAction) {
Thread.sleep(1000)
}
}
val intent = Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT)
intent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME)
context.bindService(
intent,
serviceConnection,
Context.BIND_AUTO_CREATE
)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun xInstall(packageName: String, uriList: List<Uri>) {
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
if (isAlreadyQueued(packageName)) {
if (::serviceConnection.isInitialized) {
context.unbindService(serviceConnection)
}
return
}
AuroraApplication.enqueuedInstalls.add(packageName)
val service = IPrivilegedService.Stub.asInterface(binder)
if (service.hasPrivilegedPermissions()) {
Log.i(context.getString(R.string.installer_service_available))
val callback = object : IPrivilegedCallback.Stub() {
override fun handleResult(packageName: String, returnCode: Int) {
executor.execute {
var readyWithAction = false
Handler(Looper.getMainLooper()).post {
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
if (isAlreadyQueued(packageName)) {
if (::serviceConnection.isInitialized) {
context.unbindService(serviceConnection)
}
readyWithAction = true
return
}
AuroraApplication.enqueuedInstalls.add(packageName)
val service = IPrivilegedService.Stub.asInterface(binder)
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
if (service.hasPrivilegedPermissions()) {
Log.i(context.getString(R.string.installer_service_available))
val callback = object : IPrivilegedCallback.Stub() {
override fun handleResult(packageName: String, returnCode: Int) {
}
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
removeFromInstallQueue(packageName)
readyWithAction = true
handleCallback(packageName, returnCode, extra)
}
}
try {
service.installSplitPackageX(
packageName,
uriList,
ACTION_INSTALL_REPLACE_EXISTING,
BuildConfig.APPLICATION_ID,
callback
)
} catch (e: RemoteException) {
removeFromInstallQueue(packageName)
readyWithAction = true
postError(packageName, e.localizedMessage, e.stackTraceToString())
}
} else {
removeFromInstallQueue(packageName)
handleCallback(packageName, returnCode, extra)
readyWithAction = true
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
}
}
try {
service.installSplitPackageX(
packageName,
uriList,
ACTION_INSTALL_REPLACE_EXISTING,
BuildConfig.APPLICATION_ID,
callback
)
} catch (e: RemoteException) {
override fun onServiceDisconnected(name: ComponentName) {
removeFromInstallQueue(packageName)
postError(packageName, e.localizedMessage, e.stackTraceToString())
readyWithAction = true
Log.e("Disconnected from Aurora Services")
}
} else {
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
removeFromInstallQueue(packageName)
}
}
override fun onServiceDisconnected(name: ComponentName) {
removeFromInstallQueue(packageName)
Log.e("Disconnected from Aurora Services")
val intent = Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT)
intent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME)
context.bindService(
intent,
serviceConnection,
Context.BIND_AUTO_CREATE
)
}
while (!readyWithAction) {
Thread.sleep(1000)
}
Log.i("Services Callback : install wait done")
}
val intent = Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT)
intent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME)
context.bindService(
intent,
serviceConnection,
Context.BIND_AUTO_CREATE
)
}
private fun handleCallbackUninstall(packageName: String, returnCode: Int, extra: String?) {

View File

@@ -50,7 +50,7 @@ open class PackageManagerReceiver : BroadcastReceiver() {
}
//Clear installation queue
AppInstaller(context)
AppInstaller.getInstance(context)
.getPreferredInstaller()
.removeFromInstallQueue(packageName)

View File

@@ -401,7 +401,7 @@ class NotificationService : Service() {
@Synchronized
private fun install(packageName: String, files: List<Download>) {
AppInstaller(this)
AppInstaller.getInstance(this)
.getPreferredInstaller()
.install(
packageName,

View File

@@ -17,32 +17,31 @@ import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager
import com.aurora.store.data.downloader.RequestBuilder
import com.aurora.store.data.downloader.RequestGroupIdBuilder
import com.aurora.store.data.downloader.getGroupId
import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.model.UpdateFile
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.util.Log
import com.tonyodev.fetch2.*
import com.tonyodev.fetch2core.DownloadBlock
import com.tonyodev.fetch2core.FetchObserver
import com.tonyodev.fetch2core.Reason
import nl.komponents.kovenant.task
import nl.komponents.kovenant.then
import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.apache.commons.io.FileUtils
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.collections.ArrayList
import kotlin.concurrent.timerTask
class UpdateService: LifecycleService() {
lateinit var fetch: Fetch
private lateinit var fetchListener: AbstractFetchGroupListener
lateinit var downloadManager: DownloadManager
private lateinit var fetchListener: FetchGroupListener
private var fetchActiveDownloadObserver = object : FetchObserver<Boolean> {
override fun onChanged(data: Boolean, reason: Reason) {
if (!data && !installing.get() && listeners.isEmpty()) {
@@ -56,7 +55,7 @@ class UpdateService: LifecycleService() {
}
private var hasActiveDownloadObserver = false
private val listeners: ArrayList<AbstractFetchGroupListener> = ArrayList()
private val listeners: ArrayList<FetchGroupListener> = ArrayList()
private val pendingEvents: MutableMap</*groupId: */Int, AppDownloadStatus> = mutableMapOf()
@@ -114,8 +113,9 @@ class UpdateService: LifecycleService() {
EventBus.getDefault().register(this)
authData = AuthProvider.with(this).getAuthData()
purchaseHelper = PurchaseHelper(authData)
fetch = DownloadManager.with(this).fetch
fetchListener = object : AbstractFetchGroupListener() {
downloadManager = DownloadManager.with(this)
fetch = downloadManager.fetch
fetchListener = object : FetchGroupListener {
override fun onAdded(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
@@ -127,6 +127,12 @@ class UpdateService: LifecycleService() {
}
}
override fun onAdded(download: Download) {
listeners.forEach {
it.onAdded(download)
}
}
override fun onProgress(
groupId: Int,
download: Download,
@@ -139,6 +145,86 @@ class UpdateService: LifecycleService() {
}
}
override fun onProgress(download: Download, etaInMilliSeconds: Long, downloadedBytesPerSecond: Long) {
listeners.forEach {
it.onProgress(download, etaInMilliSeconds, downloadedBytesPerSecond)
}
}
override fun onQueued(groupId: Int, download: Download, waitingNetwork: Boolean, fetchGroup: FetchGroup) {
listeners.forEach {
it.onQueued(groupId, download, waitingNetwork, fetchGroup)
}
}
override fun onQueued(download: Download, waitingOnNetwork: Boolean) {
listeners.forEach {
it.onQueued(download, waitingOnNetwork)
}
}
override fun onRemoved(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onRemoved(groupId, download, fetchGroup)
}
}
override fun onRemoved(download: Download) {
listeners.forEach {
it.onRemoved(download)
}
}
override fun onResumed(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onResumed(groupId, download, fetchGroup)
}
}
override fun onResumed(download: Download) {
listeners.forEach {
it.onResumed(download)
}
}
override fun onStarted(
groupId: Int,
download: Download,
downloadBlocks: List<DownloadBlock>,
totalBlocks: Int,
fetchGroup: FetchGroup
) {
listeners.forEach {
it.onStarted(
groupId,
download,
downloadBlocks,
totalBlocks,
fetchGroup)
}
}
override fun onStarted(download: Download, downloadBlocks: List<DownloadBlock>, totalBlocks: Int) {
listeners.forEach {
it.onStarted(
download,
downloadBlocks,
totalBlocks)
}
}
override fun onWaitingNetwork(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onWaitingNetwork(groupId, download, fetchGroup)
}
}
override fun onWaitingNetwork(download: Download) {
listeners.forEach {
it.onWaitingNetwork(download)
}
}
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onCompleted(groupId, download, fetchGroup)
@@ -146,7 +232,7 @@ class UpdateService: LifecycleService() {
if (listeners.isEmpty()) {
pendingEvents[groupId] = AppDownloadStatus(download, fetchGroup, isComplete = true)
}
if (fetchGroup.groupDownloadProgress == 100 || fetchGroup.groupDownloadProgress == -1) {
if (fetchGroup.groupDownloadProgress == 100) {
Handler(Looper.getMainLooper()).post {
try {
install(download.tag!!, fetchGroup.downloads)
@@ -159,6 +245,12 @@ class UpdateService: LifecycleService() {
}*/
}
override fun onCompleted(download: Download) {
listeners.forEach {
it.onCompleted(download)
}
}
override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onCancelled(groupId, download, fetchGroup)
@@ -168,6 +260,10 @@ class UpdateService: LifecycleService() {
}
}
override fun onCancelled(download: Download) {
TODO("Not yet implemented")
}
override fun onDeleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onDeleted(groupId, download, fetchGroup)
@@ -176,6 +272,80 @@ class UpdateService: LifecycleService() {
pendingEvents[groupId] = AppDownloadStatus(download, fetchGroup, isCancelled = true)
}
}
override fun onDeleted(download: Download) {
listeners.forEach {
it.onDeleted(download)
}
}
override fun onDownloadBlockUpdated(
groupId: Int,
download: Download,
downloadBlock: DownloadBlock,
totalBlocks: Int,
fetchGroup: FetchGroup
) {
listeners.forEach {
it.onDownloadBlockUpdated(
groupId,
download,
downloadBlock,
totalBlocks,
fetchGroup
)
}
}
override fun onDownloadBlockUpdated(download: Download, downloadBlock: DownloadBlock, totalBlocks: Int) {
listeners.forEach {
it.onDownloadBlockUpdated(
download,
downloadBlock,
totalBlocks
)
}
}
override fun onError(
groupId: Int,
download: Download,
error: Error,
throwable: Throwable?,
fetchGroup: FetchGroup
) {
listeners.forEach {
it.onError(
groupId,
download,
error,
throwable,
fetchGroup
)
}
}
override fun onError(download: Download, error: Error, throwable: Throwable?) {
listeners.forEach {
it.onError(
download,
error,
throwable
)
}
}
override fun onPaused(groupId: Int, download: Download, fetchGroup: FetchGroup) {
listeners.forEach {
it.onPaused(groupId, download, fetchGroup)
}
}
override fun onPaused(download: Download) {
listeners.forEach {
it.onPaused(download)
}
}
}
/*liveUpdateData.observe(this) { updateData ->
@@ -210,16 +380,16 @@ class UpdateService: LifecycleService() {
}
var timer: Timer? = null
val timerTask: TimerTask = timerTask {
val timerTaskRun: Runnable = Runnable {
Handler(Looper.getMainLooper()).post {
if (!installing.get() && listeners.isEmpty()) {
fetch.hasActiveDownloads(true, { hasActiveDownloads ->
fetch.hasActiveDownloads(true) { hasActiveDownloads ->
if (!hasActiveDownloads && !installing.get() && listeners.isEmpty()) {
Handler(Looper.getMainLooper()).post {
stopSelf()
}
}
})
}
}
}
}
@@ -237,7 +407,7 @@ class UpdateService: LifecycleService() {
if (filesExist) {
task {
try {
val installer = AppInstaller(this)
val installer = AppInstaller.getInstance(this)
.getPreferredInstaller()
installer.install(
packageName,
@@ -255,30 +425,24 @@ class UpdateService: LifecycleService() {
}
}
@Subscribe()
fun onEventMainThreadExec(event: Any) {
var timerLock = Object()
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onEventBackgroundThreadExec(event: Any) {
when (event) {
is InstallerEvent.Success -> {
if (timer != null) {
timer!!.cancel()
timer = null
}
if (timer == null) {
timer = Timer()
}
installing.set(false)
timer!!.schedule(timerTask, 10 * 1000)
}
is InstallerEvent.Success,
is InstallerEvent.Failed -> {
if (timer != null) {
timer!!.cancel()
timer = null
synchronized(timerLock) {
if (timer != null) {
timer!!.cancel()
timer = null
}
if (timer == null) {
timer = Timer()
}
installing.set(false)
timer!!.schedule(timerTask { timerTaskRun.run() }, 10 * 1000)
}
if (timer == null) {
timer = Timer()
}
installing.set(false)
timer!!.schedule(timerTask, 10 * 1000)
}
else -> {
@@ -286,7 +450,7 @@ class UpdateService: LifecycleService() {
}
}
fun registerListener(listener: AbstractFetchGroupListener) {
fun registerListener(listener: FetchGroupListener) {
listeners.add(listener)
val iterator = pendingEvents.iterator()
while (iterator.hasNext()) {

View File

@@ -21,9 +21,12 @@ package com.aurora.store.view.ui.details
import android.Manifest
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -47,6 +50,7 @@ import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.data.service.UpdateService
import com.aurora.store.databinding.ActivityDetailsBinding
import com.aurora.store.util.*
import com.aurora.store.view.ui.downloads.DownloadActivity
@@ -74,8 +78,22 @@ class AppDetailsActivity : BaseDetailsActivity() {
private lateinit var authData: AuthData
private lateinit var app: App
private lateinit var downloadManager: DownloadManager
private lateinit var fetch: Fetch
private var updateService: UpdateService? = null
private var pendingAddListener = true
private var serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
updateService = (binder as UpdateService.UpdateServiceBinder).getUpdateService()
if (::fetchGroupListener.isInitialized) {
updateService!!.registerListener(fetchGroupListener)
pendingAddListener = false
}
}
override fun onServiceDisconnected(name: ComponentName) {
updateService = null
}
}
private lateinit var fetchGroupListener: FetchGroupListener
private lateinit var completionMarker: java.io.File
private lateinit var inProgressMarker: java.io.File
@@ -166,9 +184,8 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
override fun onResume() {
if (!isLAndAbove()) {
checkAndSetupInstall()
}
getUpdateServiceInstance()
checkAndSetupInstall()
super.onResume()
}
@@ -207,11 +224,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
}
private var uninstallActionEnabled = false
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_details, menu)
if (::app.isInitialized) {
val installed = PackageUtil.isInstalled(this, app.packageName)
menu?.findItem(R.id.action_uninstall)?.isVisible = installed
uninstallActionEnabled = installed
}
return true
}
@@ -306,7 +326,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
showDialog(R.string.title_installer, R.string.dialog_desc_native_split)
} else {
task {
AppInstaller(this)
AppInstaller.getInstance(this)
.getPreferredInstaller()
.install(
app.packageName,
@@ -325,7 +345,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
@Synchronized
private fun uninstallApp() {
task {
AppInstaller(this)
AppInstaller.getInstance(this)
.getPreferredInstaller()
.uninstall(app.packageName)
}
@@ -428,14 +448,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
private fun startDownload() {
when (status) {
Status.PAUSED -> {
fetch.resumeGroup(app.getGroupId(this@AppDetailsActivity))
updateService?.fetch?.resumeGroup(app.getGroupId(this@AppDetailsActivity))
}
Status.DOWNLOADING -> {
flip(1)
toast("Already downloading")
}
Status.COMPLETED -> {
fetch.getFetchGroup(app.getGroupId(this@AppDetailsActivity)) {
updateService?.fetch?.getFetchGroup(app.getGroupId(this@AppDetailsActivity)) {
verifyAndInstall(it.downloads)
}
}
@@ -519,10 +539,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
if (requestList.isNotEmpty()) {
/*Remove old fetch group if downloaded earlier, mostly in case of updates*/
fetch.deleteGroup(app.getGroupId(this@AppDetailsActivity))
updateService?.fetch?.deleteGroup(app.getGroupId(this@AppDetailsActivity))
/*Enqueue new fetch group*/
fetch.enqueue(
updateService?.fetch?.enqueue(
requestList
) {
status = Status.ADDED
@@ -604,6 +624,9 @@ class AppDetailsActivity : BaseDetailsActivity() {
btn.setText(R.string.action_open)
btn.addOnClickListener { openApp() }
}
if (!uninstallActionEnabled) {
invalidateOptionsMenu()
}
} else {
if (app.isFree) {
btn.setText(R.string.action_install)
@@ -619,6 +642,9 @@ class AppDetailsActivity : BaseDetailsActivity() {
startDownload()
}
}
if (uninstallActionEnabled) {
invalidateOptionsMenu()
}
}
}
}
@@ -636,16 +662,13 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
private fun attachFetch() {
downloadManager = DownloadManager.with(this)
fetch = downloadManager.fetch
fetch.getFetchGroup(app.getGroupId(this@AppDetailsActivity)) { fetchGroup: FetchGroup ->
updateService?.fetch?.getFetchGroup(app.getGroupId(this@AppDetailsActivity)) { fetchGroup: FetchGroup ->
if (fetchGroup.groupDownloadProgress == 100 && fetchGroup.completedDownloads.isNotEmpty()) {
status = Status.COMPLETED
} else if (downloadManager.isDownloading(fetchGroup)) {
} else if (updateService?.downloadManager?.isDownloading(fetchGroup) == true) {
status = Status.DOWNLOADING
flip(1)
} else if (downloadManager.isCanceled(fetchGroup)) {
} else if (updateService?.downloadManager?.isCanceled(fetchGroup) == true) {
status = Status.CANCELLED
} else if (fetchGroup.pausedDownloads.isNotEmpty()) {
status = Status.PAUSED
@@ -716,12 +739,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
status = download.status
flip(0)
updateProgress(fetchGroup, -1, -1)
inProgressMarker.delete()
completionMarker.createNewFile()
try {
verifyAndInstall(fetchGroup.downloads)
} catch (e: Exception) {
Log.e(e.stackTraceToString())
inProgressMarker.delete()
completionMarker.createNewFile()
} catch (ex: Exception) {
ex.printStackTrace()
}
runOnUiThread {
B.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
}
}
}
@@ -749,13 +774,45 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
}
fetch.addListener(fetchGroupListener)
getUpdateServiceInstance()
B.layoutDetailsInstall.imgCancel.setOnClickListener {
fetch.cancelGroup(
updateService?.fetch?.cancelGroup(
app.getGroupId(this@AppDetailsActivity)
)
}
if (pendingAddListener && updateService != null) {
pendingAddListener = false
updateService!!.registerListener(fetchGroupListener)
}
}
fun getUpdateServiceInstance() {
if (updateService == null) {
val intent = Intent(this, UpdateService::class.java)
startService(intent)
bindService(
intent,
serviceConnection,
0
)
}
}
override fun onPause() {
if (updateService != null) {
updateService = null
unbindService(serviceConnection)
}
super.onPause()
}
override fun onDestroy() {
super.onDestroy()
if (updateService != null) {
updateService = null
unbindService(serviceConnection)
}
}
private fun attachBottomSheet() {

View File

@@ -108,7 +108,7 @@ class AppMenuSheet : BaseBottomSheet() {
R.id.action_uninstall -> {
task {
AppInstaller(requireContext())
AppInstaller.getInstance(requireContext())
.getPreferredInstaller().uninstall(app.packageName)
}
}

View File

@@ -285,7 +285,7 @@ class UpdatesFragment : BaseFragment() {
if (filesExist) {
task {
AppInstaller(requireContext())
AppInstaller.getInstance(requireContext())
.getPreferredInstaller()
.install(
packageName,