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:
@@ -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?) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user