AppDetailsFragment: Download apps using DownloadWorker
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -129,4 +129,8 @@ class ActionButton : RelativeLayout {
|
|||||||
fun addOnClickListener(onClickListener: OnClickListener?) {
|
fun addOnClickListener(onClickListener: OnClickListener?) {
|
||||||
B.btn.setOnClickListener(onClickListener)
|
B.btn.setOnClickListener(onClickListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addOnLongClickListener(onLongClickListener: OnLongClickListener?) {
|
||||||
|
B.btn.setOnLongClickListener(onLongClickListener)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,12 @@ package com.aurora.store.view.ui.details
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.os.IBinder
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
@@ -44,6 +41,8 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import androidx.work.WorkInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
import coil.load
|
import coil.load
|
||||||
import coil.transform.RoundedCornersTransformation
|
import coil.transform.RoundedCornersTransformation
|
||||||
import com.airbnb.epoxy.EpoxyRecyclerView
|
import com.airbnb.epoxy.EpoxyRecyclerView
|
||||||
@@ -65,15 +64,13 @@ import com.aurora.gplayapi.data.models.StreamCluster
|
|||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.State
|
import com.aurora.store.State
|
||||||
import com.aurora.store.data.ViewState
|
import com.aurora.store.data.ViewState
|
||||||
import com.aurora.store.data.downloader.DownloadManager
|
|
||||||
import com.aurora.store.data.downloader.getGroupId
|
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.BusEvent
|
||||||
import com.aurora.store.data.event.InstallerEvent
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
import com.aurora.store.data.installer.AppInstaller
|
||||||
import com.aurora.store.data.installer.RootInstaller
|
import com.aurora.store.data.installer.RootInstaller
|
||||||
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
import com.aurora.store.data.service.AppMetadataStatusListener
|
import com.aurora.store.data.work.DownloadWorker
|
||||||
import com.aurora.store.data.service.UpdateService
|
|
||||||
import com.aurora.store.databinding.FragmentDetailsBinding
|
import com.aurora.store.databinding.FragmentDetailsBinding
|
||||||
import com.aurora.store.databinding.LayoutDetailsBetaBinding
|
import com.aurora.store.databinding.LayoutDetailsBetaBinding
|
||||||
import com.aurora.store.databinding.LayoutDetailsDescriptionBinding
|
import com.aurora.store.databinding.LayoutDetailsDescriptionBinding
|
||||||
@@ -81,7 +78,6 @@ import com.aurora.store.databinding.LayoutDetailsDevBinding
|
|||||||
import com.aurora.store.databinding.LayoutDetailsPermissionsBinding
|
import com.aurora.store.databinding.LayoutDetailsPermissionsBinding
|
||||||
import com.aurora.store.databinding.LayoutDetailsReviewBinding
|
import com.aurora.store.databinding.LayoutDetailsReviewBinding
|
||||||
import com.aurora.store.util.CommonUtil
|
import com.aurora.store.util.CommonUtil
|
||||||
import com.aurora.store.util.Log
|
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.util.PackageUtil
|
||||||
import com.aurora.store.util.PathUtil
|
import com.aurora.store.util.PathUtil
|
||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
@@ -98,14 +94,6 @@ import com.aurora.store.viewmodel.details.DetailsClusterViewModel
|
|||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.tonyodev.fetch2.AbstractFetchGroupListener
|
|
||||||
import com.tonyodev.fetch2.Download
|
|
||||||
import com.tonyodev.fetch2.Error
|
|
||||||
import com.tonyodev.fetch2.Fetch
|
|
||||||
import com.tonyodev.fetch2.FetchGroup
|
|
||||||
import com.tonyodev.fetch2.FetchGroupListener
|
|
||||||
import com.tonyodev.fetch2.Status
|
|
||||||
import com.tonyodev.fetch2core.DownloadBlock
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
@@ -113,6 +101,7 @@ import org.greenrobot.eventbus.Subscribe
|
|||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.io.path.pathString
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
||||||
@@ -130,64 +119,29 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
private val startForStorageManagerResult =
|
private val startForStorageManagerResult =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
||||||
updateApp(app)
|
DownloadWorker.enqueueApp(requireContext(), app)
|
||||||
} else {
|
} else {
|
||||||
toast(R.string.permissions_denied)
|
toast(R.string.permissions_denied)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private val startForPermissions =
|
private val startForPermissions =
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||||
if (it) updateApp(app) else toast(R.string.permissions_denied)
|
if (it) {
|
||||||
|
DownloadWorker.enqueueApp(requireContext(), app)
|
||||||
|
} else {
|
||||||
|
toast(R.string.permissions_denied)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var authData: AuthData
|
private lateinit var authData: AuthData
|
||||||
private lateinit var app: App
|
private lateinit var app: App
|
||||||
private var fetch: Fetch? = null
|
|
||||||
private var downloadManager: DownloadManager? = null
|
|
||||||
|
|
||||||
private var attachToServiceCalled = false
|
|
||||||
private var updateService: UpdateService? = null
|
|
||||||
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 && pendingAddListeners) {
|
|
||||||
updateService!!.registerFetchListener(fetchGroupListener)
|
|
||||||
// appMetadataListener needs to be initialized after the fetchGroupListener
|
|
||||||
updateService!!.registerAppMetadataListener(appMetadataListener)
|
|
||||||
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
|
|
||||||
private lateinit var appMetadataListener: AppMetadataStatusListener
|
|
||||||
private lateinit var completionMarker: File
|
|
||||||
private lateinit var inProgressMarker: File
|
|
||||||
|
|
||||||
private var isExternal = false
|
private var isExternal = false
|
||||||
private var isNone = false
|
private var downloadStatus = DownloadStatus.UNAVAILABLE
|
||||||
private var status = Status.NONE
|
|
||||||
private var isUpdatable: Boolean = false
|
private var isUpdatable: Boolean = false
|
||||||
private var autoDownload: Boolean = false
|
private var autoDownload: Boolean = false
|
||||||
private var downloadOnly: Boolean = false
|
|
||||||
private var uninstallActionEnabled = false
|
private var uninstallActionEnabled = false
|
||||||
|
|
||||||
val listOfActionsWhenServiceAttaches = ArrayList<Runnable>()
|
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
EventBus.getDefault().register(this)
|
EventBus.getDefault().register(this)
|
||||||
@@ -283,6 +237,38 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Downloads
|
||||||
|
binding.layoutDetailsInstall.imgCancel.setOnClickListener {
|
||||||
|
DownloadWorker.cancelDownload(it.context, app)
|
||||||
|
if (downloadStatus != DownloadStatus.DOWNLOADING) flip(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
val uniqueWorkName = "${DownloadWorker.DOWNLOAD_WORKER}/${app.packageName}"
|
||||||
|
WorkManager.getInstance(view.context)
|
||||||
|
.getWorkInfosForUniqueWorkLiveData(uniqueWorkName)
|
||||||
|
.observe(viewLifecycleOwner) { workList ->
|
||||||
|
workList.getOrNull(0)?.let {
|
||||||
|
if (it.state.isFinished) flip(0) else flip(1)
|
||||||
|
when (it.state) {
|
||||||
|
WorkInfo.State.ENQUEUED,
|
||||||
|
WorkInfo.State.RUNNING -> {
|
||||||
|
downloadStatus = DownloadStatus.DOWNLOADING
|
||||||
|
updateProgress(
|
||||||
|
it.progress.getInt(DownloadWorker.DOWNLOAD_PROGRESS, 0),
|
||||||
|
it.progress.getLong(DownloadWorker.DOWNLOAD_SPEED, -1),
|
||||||
|
it.progress.getLong(DownloadWorker.DOWNLOAD_TIME, -1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkInfo.State.FAILED -> downloadStatus = DownloadStatus.FAILED
|
||||||
|
WorkInfo.State.CANCELLED -> downloadStatus = DownloadStatus.CANCELLED
|
||||||
|
WorkInfo.State.SUCCEEDED -> downloadStatus = DownloadStatus.COMPLETED
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reviews
|
// Reviews
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewModel.reviews.collect {
|
viewModel.reviews.collect {
|
||||||
@@ -434,7 +420,6 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
getUpdateServiceInstance()
|
|
||||||
checkAndSetupInstall()
|
checkAndSetupInstall()
|
||||||
super.onResume()
|
super.onResume()
|
||||||
}
|
}
|
||||||
@@ -459,34 +444,24 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyAndInstall(files: List<Download>) {
|
|
||||||
if (downloadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
var filesExist = true
|
|
||||||
|
|
||||||
files.forEach { download ->
|
|
||||||
filesExist = filesExist && File(download.file).exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filesExist)
|
|
||||||
install(files)
|
|
||||||
else
|
|
||||||
purchase()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun install(files: List<Download>) {
|
private fun install() {
|
||||||
updateActionState(State.IDLE)
|
val files = File(
|
||||||
|
PathUtil.getAppDownloadDir(
|
||||||
|
requireContext(),
|
||||||
|
app.packageName,
|
||||||
|
app.versionCode
|
||||||
|
).pathString
|
||||||
|
).listFiles() ?: return
|
||||||
|
|
||||||
val apkFiles = files.filter { it.file.endsWith(".apk") }
|
val apkFiles = files.filter { it.path.endsWith(".apk") }
|
||||||
val preferredInstaller =
|
val preferredInstaller =
|
||||||
Preferences.getInteger(requireContext(), Preferences.PREFERENCE_INSTALLER_ID)
|
Preferences.getInteger(requireContext(), Preferences.PREFERENCE_INSTALLER_ID)
|
||||||
|
|
||||||
if (apkFiles.size > 1 && preferredInstaller == 1) {
|
if (apkFiles.size > 1 && preferredInstaller == 1) {
|
||||||
showDialog(R.string.title_installer, R.string.dialog_desc_native_split)
|
showDialog(R.string.title_installer, R.string.dialog_desc_native_split)
|
||||||
} else {
|
} else {
|
||||||
viewModel.install(requireContext(), app.packageName, apkFiles.map { it.file })
|
viewModel.install(requireContext(), app.packageName, apkFiles)
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
binding.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
|
binding.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
|
||||||
@@ -515,16 +490,10 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachWhiteListStatus() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inflatePartialApp() {
|
private fun inflatePartialApp() {
|
||||||
if (::app.isInitialized) {
|
if (::app.isInitialized) {
|
||||||
attachWhiteListStatus()
|
|
||||||
attachHeader()
|
attachHeader()
|
||||||
attachBottomSheet()
|
attachBottomSheet()
|
||||||
attachFetch()
|
|
||||||
attachActions()
|
attachActions()
|
||||||
|
|
||||||
if (autoDownload) {
|
if (autoDownload) {
|
||||||
@@ -592,21 +561,29 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun startDownload() {
|
private fun startDownload(forceDownload: Boolean = false) {
|
||||||
when (status) {
|
when (downloadStatus) {
|
||||||
Status.PAUSED -> {
|
DownloadStatus.DOWNLOADING -> {
|
||||||
fetch?.resumeGroup(app.getGroupId(requireContext()))
|
|
||||||
}
|
|
||||||
|
|
||||||
Status.DOWNLOADING -> {
|
|
||||||
flip(1)
|
flip(1)
|
||||||
toast("Already downloading")
|
toast("Already downloading")
|
||||||
}
|
}
|
||||||
|
|
||||||
Status.COMPLETED -> {
|
DownloadStatus.COMPLETED -> {
|
||||||
fetch?.getFetchGroup(app.getGroupId(requireContext())) {
|
if (forceDownload) {
|
||||||
verifyAndInstall(it.downloads)
|
purchase()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if files are still present, else proceed to re-download the app
|
||||||
|
val files = File(
|
||||||
|
PathUtil.getAppDownloadDir(
|
||||||
|
requireContext(),
|
||||||
|
app.packageName,
|
||||||
|
app.versionCode
|
||||||
|
).pathString
|
||||||
|
).listFiles()
|
||||||
|
|
||||||
|
if (files?.isNotEmpty() == true) install() else purchase()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
@@ -627,7 +604,7 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
updateApp(app)
|
DownloadWorker.enqueueApp(requireContext(), app)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ContextCompat.checkSelfPermission(
|
if (ContextCompat.checkSelfPermission(
|
||||||
@@ -635,42 +612,23 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
) {
|
) {
|
||||||
updateApp(app)
|
DownloadWorker.enqueueApp(requireContext(), app)
|
||||||
} else {
|
} else {
|
||||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateApp(app)
|
DownloadWorker.enqueueApp(requireContext(), app)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateApp(app: App) {
|
private fun updateProgress(progress: Int, speed: Long = -1, timeRemaining: Long = -1) {
|
||||||
if (updateService == null) {
|
|
||||||
listOfActionsWhenServiceAttaches.add {
|
|
||||||
updateService?.updateApp(app, true)
|
|
||||||
}
|
|
||||||
getUpdateServiceInstance()
|
|
||||||
} else {
|
|
||||||
updateService?.updateApp(app, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateProgress(
|
|
||||||
fetchGroup: FetchGroup,
|
|
||||||
etaInMilliSeconds: Long,
|
|
||||||
downloadedBytesPerSecond: Long
|
|
||||||
) {
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
val progress = if (fetchGroup.groupDownloadProgress > 0)
|
|
||||||
fetchGroup.groupDownloadProgress
|
|
||||||
else
|
|
||||||
0
|
|
||||||
|
|
||||||
if (progress == 100) {
|
if (progress == 100) {
|
||||||
binding.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
|
binding.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
|
||||||
return@runOnUiThread
|
return@runOnUiThread
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.layoutDetailsInstall.apply {
|
binding.layoutDetailsInstall.apply {
|
||||||
txtProgressPercent.text = ("${progress}%")
|
txtProgressPercent.text = ("${progress}%")
|
||||||
|
|
||||||
@@ -680,31 +638,8 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
progressDownload.progress = progress
|
progressDownload.progress = progress
|
||||||
}
|
}
|
||||||
|
|
||||||
txtEta.text = CommonUtil.getETAString(
|
txtEta.text = CommonUtil.getETAString(requireContext(), timeRemaining)
|
||||||
requireContext(),
|
txtSpeed.text = CommonUtil.getDownloadSpeedString(requireContext(), speed)
|
||||||
etaInMilliSeconds
|
|
||||||
)
|
|
||||||
txtSpeed.text =
|
|
||||||
CommonUtil.getDownloadSpeedString(
|
|
||||||
requireContext(),
|
|
||||||
downloadedBytesPerSecond
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun expandBottomSheet(message: String?) {
|
|
||||||
runOnUiThread {
|
|
||||||
bottomSheetBehavior.isHideable = false
|
|
||||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
|
|
||||||
with(binding.layoutDetailsInstall) {
|
|
||||||
txtPurchaseError.text = message
|
|
||||||
btnDownload.updateState(State.IDLE)
|
|
||||||
if (app.isFree)
|
|
||||||
btnDownload.setText(R.string.action_install)
|
|
||||||
else
|
|
||||||
btnDownload.setText(app.price)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -713,6 +648,8 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
|
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
||||||
|
|
||||||
binding.layoutDetailsInstall.btnDownload.let { btn ->
|
binding.layoutDetailsInstall.btnDownload.let { btn ->
|
||||||
if (app.isInstalled) {
|
if (app.isInstalled) {
|
||||||
isUpdatable = PackageUtil.isUpdatable(
|
isUpdatable = PackageUtil.isUpdatable(
|
||||||
@@ -729,6 +666,10 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
("$installedVersion ➔ ${app.versionName} (${app.versionCode})")
|
("$installedVersion ➔ ${app.versionName} (${app.versionCode})")
|
||||||
btn.setText(R.string.action_update)
|
btn.setText(R.string.action_update)
|
||||||
btn.addOnClickListener { startDownload() }
|
btn.addOnClickListener { startDownload() }
|
||||||
|
btn.addOnLongClickListener {
|
||||||
|
startDownload(true)
|
||||||
|
true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.layoutDetailsApp.txtLine3.text = installedVersion
|
binding.layoutDetailsApp.txtLine3.text = installedVersion
|
||||||
btn.setText(R.string.action_open)
|
btn.setText(R.string.action_open)
|
||||||
@@ -738,7 +679,10 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
binding.layoutDetailsToolbar.toolbar.invalidateMenu()
|
binding.layoutDetailsToolbar.toolbar.invalidateMenu()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (app.isFree) {
|
val downloading = downloadStatus == DownloadStatus.DOWNLOADING
|
||||||
|
if (DownloadWorker.isEnqueued(app.packageName) && !downloading) {
|
||||||
|
flip(1)
|
||||||
|
} else if (app.isFree) {
|
||||||
btn.setText(R.string.action_install)
|
btn.setText(R.string.action_install)
|
||||||
} else {
|
} else {
|
||||||
btn.setText(app.price)
|
btn.setText(app.price)
|
||||||
@@ -752,6 +696,15 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
startDownload()
|
startDownload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
btn.addOnLongClickListener {
|
||||||
|
if (authData.isAnonymous && !app.isFree) {
|
||||||
|
toast(R.string.toast_purchase_blocked)
|
||||||
|
} else {
|
||||||
|
btn.setText(R.string.download_metadata)
|
||||||
|
startDownload(true)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
if (uninstallActionEnabled) {
|
if (uninstallActionEnabled) {
|
||||||
binding.layoutDetailsToolbar.toolbar.invalidateMenu()
|
binding.layoutDetailsToolbar.toolbar.invalidateMenu()
|
||||||
}
|
}
|
||||||
@@ -771,186 +724,6 @@ class AppDetailsFragment : BaseFragment(R.layout.fragment_details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachFetch() {
|
|
||||||
if (fetch == null) {
|
|
||||||
downloadManager = DownloadManager.with(requireContext())
|
|
||||||
fetch = downloadManager!!.fetch
|
|
||||||
}
|
|
||||||
fetch?.getFetchGroup(app.getGroupId(requireContext())) { fetchGroup: FetchGroup ->
|
|
||||||
if (fetchGroup.groupDownloadProgress == 100 && fetchGroup.completedDownloads.isNotEmpty()) {
|
|
||||||
status = Status.COMPLETED
|
|
||||||
} else if (downloadManager?.isDownloading(fetchGroup) == true) {
|
|
||||||
status = Status.DOWNLOADING
|
|
||||||
flip(1)
|
|
||||||
} else if (downloadManager?.isCanceled(fetchGroup) == true) {
|
|
||||||
status = Status.CANCELLED
|
|
||||||
} else if (fetchGroup.pausedDownloads.isNotEmpty()) {
|
|
||||||
status = Status.PAUSED
|
|
||||||
} else {
|
|
||||||
status = Status.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchGroupListener = object : AbstractFetchGroupListener() {
|
|
||||||
|
|
||||||
override fun onAdded(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
status = download.status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStarted(
|
|
||||||
groupId: Int,
|
|
||||||
download: Download,
|
|
||||||
downloadBlocks: List<DownloadBlock>,
|
|
||||||
totalBlocks: Int,
|
|
||||||
fetchGroup: FetchGroup
|
|
||||||
) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
status = download.status
|
|
||||||
flip(1)
|
|
||||||
|
|
||||||
val pkgDir = PathUtil.getPackageDirectory(requireContext(), app.packageName)
|
|
||||||
completionMarker =
|
|
||||||
File("$pkgDir/.${app.versionCode}.download-complete")
|
|
||||||
inProgressMarker =
|
|
||||||
File("$pkgDir/.${app.versionCode}.download-in-progress")
|
|
||||||
|
|
||||||
if (completionMarker.exists())
|
|
||||||
completionMarker.delete()
|
|
||||||
|
|
||||||
inProgressMarker.createNewFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResumed(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
status = download.status
|
|
||||||
flip(1)
|
|
||||||
inProgressMarker.parentFile?.mkdirs()
|
|
||||||
inProgressMarker.createNewFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPaused(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onProgress(
|
|
||||||
groupId: Int,
|
|
||||||
download: Download,
|
|
||||||
etaInMilliSeconds: Long,
|
|
||||||
downloadedBytesPerSecond: Long,
|
|
||||||
fetchGroup: FetchGroup
|
|
||||||
) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
updateProgress(fetchGroup, etaInMilliSeconds, downloadedBytesPerSecond)
|
|
||||||
Log.i(
|
|
||||||
"${app.displayName} : ${download.file} -> Progress : %d",
|
|
||||||
fetchGroup.groupDownloadProgress
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(requireContext()) && fetchGroup.groupDownloadProgress == 100) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
updateProgress(fetchGroup, -1, -1)
|
|
||||||
try {
|
|
||||||
inProgressMarker.delete()
|
|
||||||
completionMarker.createNewFile()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
inProgressMarker.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(
|
|
||||||
groupId: Int,
|
|
||||||
download: Download,
|
|
||||||
error: Error,
|
|
||||||
throwable: Throwable?,
|
|
||||||
fetchGroup: FetchGroup
|
|
||||||
) {
|
|
||||||
if (groupId == app.getGroupId(requireContext())) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
inProgressMarker.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appMetadataListener = object : AppMetadataStatusListener {
|
|
||||||
override fun onAppMetadataStatusError(reason: String, app: App) {
|
|
||||||
if (app.packageName == this@AppDetailsFragment.app.packageName) {
|
|
||||||
updateActionState(State.IDLE)
|
|
||||||
expandBottomSheet(reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getUpdateServiceInstance()
|
|
||||||
|
|
||||||
binding.layoutDetailsInstall.imgCancel.setOnClickListener {
|
|
||||||
fetch?.cancelGroup(
|
|
||||||
app.getGroupId(requireContext())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (updateService != null) {
|
|
||||||
pendingAddListeners = false
|
|
||||||
updateService!!.registerFetchListener(fetchGroupListener)
|
|
||||||
// appMetadataListener needs to be initialized after the fetchGroupListener
|
|
||||||
updateService!!.registerAppMetadataListener(appMetadataListener)
|
|
||||||
} else {
|
|
||||||
pendingAddListeners = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getUpdateServiceInstance() {
|
|
||||||
if (updateService == null && !attachToServiceCalled) {
|
|
||||||
attachToServiceCalled = true
|
|
||||||
val intent = Intent(requireContext(), UpdateService::class.java)
|
|
||||||
activity?.startService(intent)
|
|
||||||
activity?.bindService(
|
|
||||||
intent,
|
|
||||||
serviceConnection,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
if (updateService != null) {
|
|
||||||
updateService = null
|
|
||||||
attachToServiceCalled = false
|
|
||||||
pendingAddListeners = true
|
|
||||||
activity?.unbindService(serviceConnection)
|
|
||||||
}
|
|
||||||
super.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
if (updateService != null) {
|
|
||||||
updateService = null
|
|
||||||
attachToServiceCalled = false
|
|
||||||
pendingAddListeners = true
|
|
||||||
activity?.unbindService(serviceConnection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|||||||
Reference in New Issue
Block a user