Fix issue with installing being called multiple times when a completed download is queue again, fix issues with UpdateService.kt stiopping when it might be needed (extended stopSelf timeout (5 secs) & made all calls to the service stack in a HashSet in case the service was not running when it was needed - AppDetailsActivity.kt & UpdatesFragment.kt), remove useless ThreadPoolExecutor in ServiceInstaller.kt, made the installing list in UpdateService.kt a set and made it concurrency-proof with a lock and dynamic thread creation when needed

This commit is contained in:
Konstantin Tuev
2021-06-15 14:11:29 +03:00
parent ccf14dfa51
commit 014d3478f2
4 changed files with 340 additions and 194 deletions

View File

@@ -42,11 +42,12 @@ import java.io.File
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
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
@@ -96,190 +97,169 @@ class ServiceInstaller(context: Context) : InstallerBase(context) {
}
override fun uninstall(packageName: String) {
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
val attachedToServiceTimeout = AtomicBoolean(false)
AuroraApplication.enqueuedInstalls.add(packageName)
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
attachedToServiceTimeout.set(true)
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) {
}
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) {
}
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 {
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
removeFromInstallQueue(packageName)
readyWithAction = true
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
handleCallbackUninstall(packageName, returnCode, extra)
}
}
override fun onServiceDisconnected(name: ComponentName) {
try {
service.deletePackageX(
packageName,
2,
BuildConfig.APPLICATION_ID,
callback
)
} catch (e: RemoteException) {
Log.e("Failed to connect Aurora Services")
removeFromInstallQueue(packageName)
Log.e("Disconnected from Aurora Services")
readyWithAction = true
}
} else {
removeFromInstallQueue(packageName)
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
}
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)
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
)
Handler(Looper.getMainLooper()).postDelayed({
if (!attachedToServiceTimeout.get()) {
removeFromInstallQueue(packageName)
}
}, 25 * 1000)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun xInstall(packageName: String, uriList: List<Uri>, fileList: List<String>) {
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
val attachedToServiceTimeout = AtomicBoolean(false)
AuroraApplication.enqueuedInstalls.add(packageName)
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
attachedToServiceTimeout.set(true)
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) {
}
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) {
}
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
removeFromInstallQueue(packageName)
readyWithAction = true
handleCallback(packageName, returnCode, extra)
}
}
override fun handleResultX(
packageName: String,
returnCode: Int,
extra: String?
) {
removeFromInstallQueue(packageName)
handleCallback(packageName, returnCode, extra)
}
}
try {
if (service.isMoreMethodImplemented) {
try {
if (service.isMoreMethodImplemented) {
try {
service.installSplitPackageMore(
packageName,
uriList,
ACTION_INSTALL_REPLACE_EXISTING,
BuildConfig.APPLICATION_ID,
callback,
fileList
)
} catch (e: RemoteException) {
removeFromInstallQueue(packageName)
readyWithAction = true
postError(packageName, e.localizedMessage, e.stackTraceToString())
}
} else {
throw Exception("New method not implemented")
}
} catch (th: Throwable) {
th.printStackTrace()
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())
}
service.installSplitPackageMore(
packageName,
uriList,
ACTION_INSTALL_REPLACE_EXISTING,
BuildConfig.APPLICATION_ID,
callback,
fileList
)
} catch (e: RemoteException) {
removeFromInstallQueue(packageName)
postError(packageName, e.localizedMessage, e.stackTraceToString())
}
} else {
removeFromInstallQueue(packageName)
readyWithAction = true
postError(
throw Exception("New method not implemented")
}
} catch (th: Throwable) {
th.printStackTrace()
try {
service.installSplitPackageX(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
uriList,
ACTION_INSTALL_REPLACE_EXISTING,
BuildConfig.APPLICATION_ID,
callback
)
} catch (e: RemoteException) {
removeFromInstallQueue(packageName)
postError(packageName, e.localizedMessage, e.stackTraceToString())
}
}
override fun onServiceDisconnected(name: ComponentName) {
removeFromInstallQueue(packageName)
readyWithAction = true
Log.e("Disconnected from Aurora Services")
}
} else {
removeFromInstallQueue(packageName)
postError(
packageName,
context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_service_misconfigured)
)
}
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)
override fun onServiceDisconnected(name: ComponentName) {
removeFromInstallQueue(packageName)
Log.e("Disconnected from Aurora Services")
}
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
)
Handler(Looper.getMainLooper()).postDelayed({
if (!attachedToServiceTimeout.get()) {
removeFromInstallQueue(packageName)
}
}, 25 * 1000)
}
private fun handleCallbackUninstall(packageName: String, returnCode: Int, extra: String?) {

View File

@@ -36,7 +36,7 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.timerTask
class UpdateService: LifecycleService() {
@@ -46,9 +46,9 @@ class UpdateService: LifecycleService() {
private lateinit var fetchListener: FetchGroupListener
private var fetchActiveDownloadObserver = object : FetchObserver<Boolean> {
override fun onChanged(data: Boolean, reason: Reason) {
if (!data && installing.isEmpty() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
if (!data && isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
Handler(Looper.getMainLooper()).postDelayed ({
if (installing.isEmpty() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
if (isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
stopSelf()
}
}, 5 * 1000)
@@ -73,7 +73,97 @@ class UpdateService: LifecycleService() {
}
}
private var installing = CopyOnWriteArrayList<String>()
// HashMap<packageName, HashSet<downloadFilePath>>
private var downloadsInCompletedGroup = HashMap<String, HashSet<String>>()
private var installing = HashSet<String>()
private var lock = ReentrantLock()
fun putInInstalling(packageName: String?) {
if (packageName == null) {
return
}
if (lock.tryLock()) {
installing.add(packageName)
try {
lock.unlock()
} catch (th: Throwable) {
th.printStackTrace()
}
} else {
Thread {
while (!lock.tryLock()) {
Thread.sleep(50)
}
installing.add(packageName)
try {
lock.unlock()
} catch (th: Throwable) {
th.printStackTrace()
}
}.start()
}
}
fun removeFromInstalling(packageName: String?, runFromCurrentThread: Boolean = false) {
if (packageName == null) {
return
}
if (lock.tryLock()) {
installing.remove(packageName)
try {
lock.unlock()
} catch (th: Throwable) {
th.printStackTrace()
}
} else {
val toRun = Runnable {
while (!lock.tryLock()) {
Thread.sleep(50)
}
installing.remove(packageName)
try {
lock.unlock()
} catch (th: Throwable) {
th.printStackTrace()
}
}
if (runFromCurrentThread) {
toRun.run()
} else {
Thread(toRun).start()
}
}
}
fun isEmptyInstalling(): Boolean {
while (!lock.tryLock()) {
Thread.sleep(50)
}
val out: Boolean = installing.isEmpty()
try {
lock.unlock()
} catch (th: Throwable) {
th.printStackTrace()
}
return out
}
fun containsInInstalling(packageName: String?): Boolean {
if (packageName == null) {
return false
}
while (!lock.tryLock()) {
Thread.sleep(50)
}
val out = installing.contains(packageName)
try {
lock.unlock()
} catch (th: Throwable) {
th.printStackTrace()
}
return out
}
private lateinit var purchaseHelper: PurchaseHelper
@@ -130,7 +220,7 @@ class UpdateService: LifecycleService() {
it.onAdded(groupId, download, fetchGroup)
}
if (download.tag != null) {
installing.remove(download.tag)
removeFromInstalling(download.tag, runFromCurrentThread = true)
}
if (!hasActiveDownloadObserver) {
hasActiveDownloadObserver = true
@@ -243,7 +333,18 @@ class UpdateService: LifecycleService() {
if (fetchListeners.isEmpty()) {
fetchPendingEvents[groupId] = AppDownloadStatus(download, fetchGroup, isComplete = true)
}
if (fetchGroup.groupDownloadProgress == 100) {
var packageDownloadFilesWhichCompleted = downloadsInCompletedGroup[download.tag!!]
if (packageDownloadFilesWhichCompleted == null) {
packageDownloadFilesWhichCompleted = HashSet()
downloadsInCompletedGroup[download.tag!!] = packageDownloadFilesWhichCompleted
}
packageDownloadFilesWhichCompleted.add(download.file)
if (fetchGroup.groupDownloadProgress == 100 && fetchGroup.downloads.all { packageDownloadFilesWhichCompleted.contains(it.file) }) {
downloadsInCompletedGroup.remove(download.tag!!)
if (download.tag != null) {
removeFromInstalling(download.tag, runFromCurrentThread = true)
}
Log.d("Group (${download.tag!!}) downloaded and verified all downloaded!")
Handler(Looper.getMainLooper()).post {
try {
install(download.tag!!, fetchGroup.downloads)
@@ -251,6 +352,9 @@ class UpdateService: LifecycleService() {
Log.e(e.stackTraceToString())
}
}
}
if (fetchGroup.groupDownloadProgress == 100) {
Log.d("Group (${download.tag!!}) downloaded but NOT verified all downloaded!")
} /* else if (fetchGroup.groupDownloadProgress == -1) {
fetch.deleteGroup(fetchGroup.id)
}*/
@@ -369,7 +473,7 @@ class UpdateService: LifecycleService() {
}
fun updateApp(app: App, removeExisiting: Boolean = false) {
installing.add(app.packageName)
putInInstalling(app.packageName)
task {
val files = purchaseHelper.purchase(
app.packageName,
@@ -390,7 +494,7 @@ class UpdateService: LifecycleService() {
Log.i("Updating ${app.displayName}")
}
} else {
installing.remove(app.packageName)
removeFromInstalling(app.packageName)
Log.e("Failed to download : ${app.displayName}")
appMetadataListeners.forEach {
it.onAppMetadataStatusError(getString(R.string.purchase_session_expired), app)
@@ -401,7 +505,7 @@ class UpdateService: LifecycleService() {
}
}
} failUi { failException ->
installing.remove(app.packageName)
removeFromInstalling(app.packageName)
var reason = "Unknown"
when (failException) {
@@ -438,9 +542,9 @@ class UpdateService: LifecycleService() {
var timer: Timer? = null
val timerTaskRun: Runnable = Runnable {
Handler(Looper.getMainLooper()).post {
if (installing.isEmpty() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
if (isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
fetch.hasActiveDownloads(true) { hasActiveDownloads ->
if (!hasActiveDownloads && installing.isEmpty() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
if (!hasActiveDownloads && isEmptyInstalling() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
Handler(Looper.getMainLooper()).post {
stopSelf()
}
@@ -452,7 +556,11 @@ class UpdateService: LifecycleService() {
@Synchronized
private fun install(packageName: String, files: List<Download>?) {
installing.add(packageName)
if (containsInInstalling(packageName)) {
println("Already installing $packageName!")
return
}
putInInstalling(packageName)
files?.let { downloads ->
var filesExist = true
@@ -472,11 +580,15 @@ class UpdateService: LifecycleService() {
.map { it.file }.toList()
)
} catch (th: Throwable) {
removeFromInstalling(packageName)
th.printStackTrace()
}
}.fail {
removeFromInstalling(packageName)
Log.e(it.stackTraceToString())
}
} else {
removeFromInstalling(packageName)
}
}
}
@@ -495,7 +607,7 @@ class UpdateService: LifecycleService() {
is InstallerEvent.Failed -> event.packageName
else -> null
}?.run {
installing.remove(this)
removeFromInstalling(this)
}
synchronized(timerLock) {
if (timer != null) {
@@ -556,14 +668,15 @@ class UpdateService: LifecycleService() {
override fun onUnbind(intent: Intent?): Boolean {
fetchListeners.clear()
appMetadataListeners.clear()
if (installing.isEmpty()) {
fetch.hasActiveDownloads(true) { hasActiveDownloads ->
if (!hasActiveDownloads && installing.isEmpty() && fetchListeners.isEmpty() && appMetadataListeners.isEmpty()) {
Handler(Looper.getMainLooper()).post {
stopSelf()
}
}
synchronized(timerLock) {
if (timer != null) {
timer!!.cancel()
timer = null
}
if (timer == null) {
timer = Timer()
}
timer!!.schedule(timerTask { timerTaskRun.run() }, 5 * 1000)
}
return true
}

View File

@@ -77,21 +77,32 @@ class AppDetailsActivity : BaseDetailsActivity() {
private var fetch: Fetch? = null
private var downloadManager: DownloadManager? = null
private var attachToServiceCalled = false
private var updateService: UpdateService? = null
private var pendingAddListener = true
private var pendingAddListeners = true
private var serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
updateService = (binder as UpdateService.UpdateServiceBinder).getUpdateService()
if (::fetchGroupListener.isInitialized && ::appMetadataListener.isInitialized) {
if (::fetchGroupListener.isInitialized && ::appMetadataListener.isInitialized && pendingAddListeners) {
updateService!!.registerFetchListener(fetchGroupListener)
// appMetadataListener needs to be initialized after the fetchGroupListener
updateService!!.registerAppMetadataListener(appMetadataListener)
pendingAddListener = false
pendingAddListeners = false
}
if (listOfActionsWhenServiceAttaches.isNotEmpty()) {
val iterator = listOfActionsWhenServiceAttaches.iterator()
while (iterator.hasNext()) {
val next = iterator.next()
next.run()
iterator.remove()
}
}
}
override fun onServiceDisconnected(name: ComponentName) {
updateService = null
attachToServiceCalled = false
pendingAddListeners = true
}
}
private lateinit var fetchGroupListener: FetchGroupListener
@@ -103,6 +114,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
private var isNone = false
private var status = Status.NONE
private var isInstalled: Boolean = false
private var isUpdatable: Boolean = false
private var autoDownload: Boolean = false
private var downloadOnly: Boolean = false
@@ -466,6 +478,8 @@ class AppDetailsActivity : BaseDetailsActivity() {
}
}
val listOfActionsWhenServiceAttaches = ArrayList<Runnable>()
private fun purchase() {
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
@@ -475,7 +489,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) {
updateService?.updateApp(app, removeExisiting = true)
if (updateService == null) {
listOfActionsWhenServiceAttaches.add({
updateService?.updateApp(app, removeExisiting = true)
})
getUpdateServiceInstance()
} else {
updateService?.updateApp(app, removeExisiting = true)
}
}
}
@@ -485,9 +506,6 @@ class AppDetailsActivity : BaseDetailsActivity() {
downloadedBytesPerSecond: Long
) {
runOnUiThread {
if (isInstalled) {
return@runOnUiThread
}
val progress = if (fetchGroup.groupDownloadProgress > 0)
fetchGroup.groupDownloadProgress
else
@@ -538,7 +556,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
B.layoutDetailsInstall.btnDownload.let { btn ->
if (isInstalled) {
val isUpdatable = PackageUtil.isUpdatable(
isUpdatable = PackageUtil.isUpdatable(
this,
app.packageName,
app.versionCode.toLong()
@@ -729,16 +747,19 @@ class AppDetailsActivity : BaseDetailsActivity() {
app.getGroupId(this@AppDetailsActivity)
)
}
if (pendingAddListener && updateService != null) {
pendingAddListener = false
if (updateService != null) {
pendingAddListeners = false
updateService!!.registerFetchListener(fetchGroupListener)
// appMetadataListener needs to be initialized after the fetchGroupListener
updateService!!.registerAppMetadataListener(appMetadataListener)
} else {
pendingAddListeners = true
}
}
fun getUpdateServiceInstance() {
if (updateService == null) {
if (updateService == null && !attachToServiceCalled) {
attachToServiceCalled = true
val intent = Intent(this, UpdateService::class.java)
startService(intent)
bindService(
@@ -752,6 +773,8 @@ class AppDetailsActivity : BaseDetailsActivity() {
override fun onPause() {
if (updateService != null) {
updateService = null
attachToServiceCalled = false
pendingAddListeners = true
unbindService(serviceConnection)
}
super.onPause()
@@ -761,6 +784,8 @@ class AppDetailsActivity : BaseDetailsActivity() {
super.onDestroy()
if (updateService != null) {
updateService = null
attachToServiceCalled = false
pendingAddListeners = true
unbindService(serviceConnection)
}
}

View File

@@ -57,15 +57,26 @@ class UpdatesFragment : BaseFragment() {
private lateinit var B: FragmentUpdatesBinding
private lateinit var VM: UpdatesViewModel
val listOfActionsWhenServiceAttaches = ArrayList<Runnable>()
private lateinit var fetchListener: AbstractFetchGroupListener
private var attachToServiceCalled = false
private var serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
updateService = (binder as UpdateService.UpdateServiceBinder).getUpdateService()
updateService!!.registerFetchListener(fetchListener)
if (listOfActionsWhenServiceAttaches.isNotEmpty()) {
val iterator = listOfActionsWhenServiceAttaches.iterator()
while (iterator.hasNext()) {
val next = iterator.next()
next.run()
iterator.remove()
}
}
}
override fun onServiceDisconnected(name: ComponentName) {
updateService = null
attachToServiceCalled = false
}
}
@@ -131,6 +142,7 @@ class UpdatesFragment : BaseFragment() {
override fun onPause() {
if (updateService != null) {
updateService = null
attachToServiceCalled = false
requireContext().unbindService(serviceConnection)
}
super.onPause()
@@ -140,6 +152,7 @@ class UpdatesFragment : BaseFragment() {
super.onDestroy()
if (updateService != null) {
updateService = null
attachToServiceCalled = false
requireContext().unbindService(serviceConnection)
}
}
@@ -228,9 +241,17 @@ class UpdatesFragment : BaseFragment() {
private var updateService: UpdateService? = null
private fun updateSingle(app: App) {
fun runInService(runnable: Runnable) {
if (updateService == null) {
listOfActionsWhenServiceAttaches.add(runnable)
getUpdateServiceInstance()
} else {
runnable.run()
}
}
if (updateService != null) {
private fun updateSingle(app: App) {
runInService {
VM.updateState(app.getGroupId(requireContext()), State.QUEUED)
updateService?.updateApp(app)
@@ -238,21 +259,28 @@ class UpdatesFragment : BaseFragment() {
}
private fun cancelSingle(app: App) {
updateService?.fetch?.cancelGroup(app.getGroupId(requireContext()))
runInService {
updateService?.fetch?.cancelGroup(app.getGroupId(requireContext()))
}
}
private fun updateAll() {
updateService?.updateAll(updateFileMap)
VM.updateAllEnqueued = true
runInService {
updateService?.updateAll(updateFileMap)
VM.updateAllEnqueued = true
}
}
private fun cancelAll() {
VM.updateAllEnqueued = false
updateFileMap.values.forEach { updateService?.fetch?.cancelGroup(it.app.getGroupId(requireContext())) }
runInService {
VM.updateAllEnqueued = false
updateFileMap.values.forEach { updateService?.fetch?.cancelGroup(it.app.getGroupId(requireContext())) }
}
}
fun getUpdateServiceInstance() {
if (updateService == null) {
if (updateService == null && !attachToServiceCalled) {
attachToServiceCalled = true
val intent = Intent(requireContext(), UpdateService::class.java)
requireContext().startService(intent)
requireContext().bindService(