mirror of
https://github.com/whyorean/AuroraStore.git
synced 2026-06-11 17:26:53 -04:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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?) {
|
||||
|
||||
@@ -50,7 +50,7 @@ open class PackageManagerReceiver : BroadcastReceiver() {
|
||||
}
|
||||
|
||||
//Clear installation queue
|
||||
AppInstaller(context)
|
||||
AppInstaller.getInstance(context)
|
||||
.getPreferredInstaller()
|
||||
.removeFromInstallQueue(packageName)
|
||||
|
||||
|
||||
@@ -401,7 +401,7 @@ class NotificationService : Service() {
|
||||
|
||||
@Synchronized
|
||||
private fun install(packageName: String, files: List<Download>) {
|
||||
AppInstaller(this)
|
||||
AppInstaller.getInstance(this)
|
||||
.getPreferredInstaller()
|
||||
.install(
|
||||
packageName,
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -108,7 +108,7 @@ class AppMenuSheet : BaseBottomSheet() {
|
||||
|
||||
R.id.action_uninstall -> {
|
||||
task {
|
||||
AppInstaller(requireContext())
|
||||
AppInstaller.getInstance(requireContext())
|
||||
.getPreferredInstaller().uninstall(app.packageName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ class UpdatesFragment : BaseFragment() {
|
||||
|
||||
if (filesExist) {
|
||||
task {
|
||||
AppInstaller(requireContext())
|
||||
AppInstaller.getInstance(requireContext())
|
||||
.getPreferredInstaller()
|
||||
.install(
|
||||
packageName,
|
||||
|
||||
Reference in New Issue
Block a user