DownloadFragment: Refactor to work with new downloads logic

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2023-12-13 18:04:24 +05:30
parent 711c26af42
commit bd55b157e6
9 changed files with 116 additions and 249 deletions

View File

@@ -1,10 +1,13 @@
package com.aurora.store.data.room.download package com.aurora.store.data.room.download
import android.os.Parcelable
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.data.model.DownloadStatus import com.aurora.store.data.model.DownloadStatus
import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(tableName = "download") @Entity(tableName = "download")
data class Download( data class Download(
@PrimaryKey val packageName: String, @PrimaryKey val packageName: String,
@@ -21,7 +24,7 @@ data class Download(
var timeRemaining: Long, var timeRemaining: Long,
var totalFiles: Int, var totalFiles: Int,
var downloadedFiles: Int var downloadedFiles: Int
) { ) : Parcelable {
val isFinished get() = status in DownloadStatus.finished val isFinished get() = status in DownloadStatus.finished
companion object { companion object {

View File

@@ -21,6 +21,8 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.deleteRecursively
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
class DownloadWorkerUtil @Inject constructor( class DownloadWorkerUtil @Inject constructor(
@@ -42,6 +44,8 @@ class DownloadWorkerUtil @Inject constructor(
val downloadsList = downloadDao.downloads() val downloadsList = downloadDao.downloads()
.stateIn(GlobalScope, SharingStarted.WhileSubscribed(), emptyList()) .stateIn(GlobalScope, SharingStarted.WhileSubscribed(), emptyList())
private val TAG = DownloadWorkerUtil::class.java.simpleName
fun init() { fun init() {
// Run cleanup for last finished download and drop it database // Run cleanup for last finished download and drop it database
GlobalScope.launch { GlobalScope.launch {
@@ -68,13 +72,35 @@ class DownloadWorkerUtil @Inject constructor(
downloadDao.insert(Download.fromApp(app)) downloadDao.insert(Download.fromApp(app))
} }
fun cancelDownload(packageName: String) { suspend fun cancelDownload(packageName: String) {
Log.i(TAG, "Cancelling download for $packageName")
WorkManager.getInstance(context).cancelAllWorkByTag("$PACKAGE_NAME:$packageName") WorkManager.getInstance(context).cancelAllWorkByTag("$PACKAGE_NAME:$packageName")
downloadsList.value
.find { it.packageName == packageName && it.status == DownloadStatus.QUEUED }
?.let { downloadDao.update(it.copy(status = DownloadStatus.CANCELLED)) }
} }
fun cancelAll(context: Context, downloads: Boolean = true, updates: Boolean = true) { @OptIn(ExperimentalPathApi::class)
val workManager = WorkManager.getInstance(context) suspend fun clearDownload(packageName: String, versionCode: Int) {
Log.i(TAG, "Clearing downloads for $packageName ($versionCode)")
downloadDao.delete(packageName)
PathUtil.getAppDownloadDir(context, packageName, versionCode)
.deleteRecursively()
}
suspend fun clearFinishedDownloads() {
downloadsList.value.filter { it.isFinished }.forEach {
clearDownload(it.packageName, it.versionCode)
}
}
suspend fun cancelAll(downloads: Boolean = true, updates: Boolean = true) {
// Cancel all enqueued downloads first to avoid triggering re-download
downloadsList.value.filter { it.status == DownloadStatus.QUEUED }.forEach {
downloadDao.update(it.copy(status = DownloadStatus.CANCELLED))
}
val workManager = WorkManager.getInstance(context)
if (downloads) workManager.cancelAllWorkByTag(DOWNLOAD_APP) if (downloads) workManager.cancelAllWorkByTag(DOWNLOAD_APP)
if (updates) workManager.cancelAllWorkByTag(DOWNLOAD_UPDATE) if (updates) workManager.cancelAllWorkByTag(DOWNLOAD_UPDATE)
} }

View File

@@ -27,17 +27,14 @@ import coil.transform.RoundedCornersTransformation
import com.airbnb.epoxy.CallbackProp import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView import com.airbnb.epoxy.ModelView
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.model.DownloadFile import com.aurora.store.data.model.DownloadStatus
import com.aurora.store.data.room.download.Download
import com.aurora.store.databinding.ViewDownloadBinding import com.aurora.store.databinding.ViewDownloadBinding
import com.aurora.store.util.CommonUtil.getDownloadSpeedString import com.aurora.store.util.CommonUtil.getDownloadSpeedString
import com.aurora.store.util.CommonUtil.getETAString import com.aurora.store.util.CommonUtil.getETAString
import com.aurora.store.util.CommonUtil.humanReadableByteValue
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.tonyodev.fetch2.Status
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.util.Locale import java.util.Locale
@@ -49,10 +46,6 @@ class DownloadView : RelativeLayout {
private lateinit var B: ViewDownloadBinding private lateinit var B: ViewDownloadBinding
private val gson: Gson = GsonBuilder()
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
.create()
constructor(context: Context?) : super(context) { constructor(context: Context?) : super(context) {
init(context) init(context)
} }
@@ -75,18 +68,12 @@ class DownloadView : RelativeLayout {
} }
@ModelProp @ModelProp
fun download(downloadFile: DownloadFile) { fun download(download: Download) {
val download = downloadFile.download B.imgDownload.load(download.iconURL) {
val extras = download.extras.getString(Constants.STRING_EXTRA, "{}") placeholder(R.drawable.bg_placeholder)
val app = gson.fromJson(extras, App::class.java) transformations(RoundedCornersTransformation(32F))
app?.let {
B.imgDownload.load(app.iconArtwork.url) {
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(32F))
}
B.txtTitle.text = app.displayName
} }
B.txtTitle.text = download.displayName
B.txtStatus.text = download.status.name B.txtStatus.text = download.status.name
.lowercase(Locale.getDefault()) .lowercase(Locale.getDefault())
@@ -98,33 +85,24 @@ class DownloadView : RelativeLayout {
} }
} }
B.txtSize.text = StringBuilder() B.progressDownload.apply {
.append(humanReadableByteValue(download.downloaded, true)) progress = download.progress
.append("/") isIndeterminate = download.progress <= 0 && !download.isFinished
.append(humanReadableByteValue(download.total, true))
val file = download.file
B.txtFile.text = file.substring(file.lastIndexOf("/") + 1)
var progress = download.progress
if (progress == -1) {
progress = 0
} }
B.txtProgress.text = ("${download.progress}%")
B.progressDownload.progress = progress B.txtEta.text = getETAString(context, download.timeRemaining)
B.txtProgress.text = ("$progress%")
B.txtEta.text = getETAString(context, download.etaInMilliSeconds)
B.txtSpeed.text = getDownloadSpeedString( B.txtSpeed.text = getDownloadSpeedString(
context, context,
download.downloadedBytesPerSecond download.speed
) )
when (download.status) { when (download.status) {
Status.DOWNLOADING, Status.QUEUED, Status.ADDED -> { DownloadStatus.DOWNLOADING, DownloadStatus.QUEUED -> {
B.txtSpeed.visibility = VISIBLE B.txtSpeed.visibility = VISIBLE
B.txtEta.visibility = VISIBLE B.txtEta.visibility = VISIBLE
} }
else -> { else -> {
B.txtSpeed.visibility = INVISIBLE B.txtSpeed.visibility = INVISIBLE
B.txtEta.visibility = INVISIBLE B.txtEta.visibility = INVISIBLE

View File

@@ -22,24 +22,21 @@ package com.aurora.store.view.ui.downloads
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.aurora.store.MobileNavigationDirections import com.aurora.store.MobileNavigationDirections
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager import com.aurora.store.data.room.download.Download
import com.aurora.store.data.model.DownloadFile
import com.aurora.store.databinding.FragmentDownloadBinding import com.aurora.store.databinding.FragmentDownloadBinding
import com.aurora.store.util.Preferences import com.aurora.store.util.DownloadWorkerUtil
import com.aurora.store.view.epoxy.views.DownloadViewModel_ import com.aurora.store.view.epoxy.views.DownloadViewModel_
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_ import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.view.ui.commons.BaseFragment
import com.tonyodev.fetch2.AbstractFetchListener
import com.tonyodev.fetch2.BuildConfig
import com.tonyodev.fetch2.Download
import com.tonyodev.fetch2.Error
import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.FetchListener
import com.tonyodev.fetch2.Status
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
class DownloadFragment : BaseFragment(R.layout.fragment_download) { class DownloadFragment : BaseFragment(R.layout.fragment_download) {
@@ -48,53 +45,8 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
private val binding: FragmentDownloadBinding private val binding: FragmentDownloadBinding
get() = _binding!! get() = _binding!!
private lateinit var fetch: Fetch @Inject
lateinit var downloadWorkerUtil: DownloadWorkerUtil
private var fetchListener: FetchListener = object : AbstractFetchListener() {
override fun onAdded(download: Download) {
updateDownloadsList()
}
override fun onQueued(download: Download, waitingOnNetwork: Boolean) {
updateDownloadsList()
}
override fun onCompleted(download: Download) {
updateDownloadsList()
}
override fun onError(download: Download, error: Error, throwable: Throwable?) {
updateDownloadsList()
}
override fun onProgress(
download: Download,
etaInMilliSeconds: Long,
downloadedBytesPerSecond: Long
) {
updateDownloadsList()
}
override fun onPaused(download: Download) {
updateDownloadsList()
}
override fun onResumed(download: Download) {
updateDownloadsList()
}
override fun onCancelled(download: Download) {
updateDownloadsList()
}
override fun onRemoved(download: Download) {
updateDownloadsList()
}
override fun onDeleted(download: Download) {
updateDownloadsList()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@@ -110,73 +62,32 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
setNavigationOnClickListener { findNavController().navigateUp() } setNavigationOnClickListener { findNavController().navigateUp() }
setOnMenuItemClickListener { setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_pause_all -> {
fetch.pauseAll()
}
R.id.action_resume_all -> {
fetch.resumeAll()
}
R.id.action_cancel_all -> { R.id.action_cancel_all -> {
fetch.cancelAll() viewLifecycleOwner.lifecycleScope.launch(NonCancellable) {
downloadWorkerUtil.cancelAll()
}
} }
R.id.action_clear_completed -> { R.id.action_clear_completed -> {
fetch.removeAllWithStatus(Status.COMPLETED) viewLifecycleOwner.lifecycleScope.launch(NonCancellable) {
Preferences.getPrefs(view.context).edit() downloadWorkerUtil.clearFinishedDownloads()
.remove(Preferences.PREFERENCE_UNIQUE_GROUP_IDS).apply() }
}
R.id.action_force_clear_all -> {
fetch.deleteAll()
Preferences.getPrefs(view.context).edit()
.remove(Preferences.PREFERENCE_UNIQUE_GROUP_IDS).apply()
} }
} }
true true
} }
} }
fetch = DownloadManager.with(view.context).fetch viewLifecycleOwner.lifecycleScope.launch {
updateDownloadsList() downloadWorkerUtil.downloadsList.collectLatest { updateController(it) }
binding.swipeRefreshLayout.setOnRefreshListener {
updateDownloadsList()
} }
} }
override fun onResume() {
super.onResume()
if (::fetch.isInitialized)
fetch.addListener(fetchListener)
}
override fun onPause() {
if (::fetch.isInitialized)
fetch.removeListener(fetchListener)
super.onPause()
}
override fun onDestroyView() { override fun onDestroyView() {
if (::fetch.isInitialized) fetch.removeListener(fetchListener)
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
private fun updateDownloadsList() { private fun updateController(downloads: List<Download>) {
if (::fetch.isInitialized)
fetch.getDownloads { downloads ->
updateController(
downloads
.filter { it.id != BuildConfig.APPLICATION_ID.hashCode() }
.sortedWith { o1, o2 -> o2.created.compareTo(o1.created) }
.map { DownloadFile(it) }
)
}
}
private fun updateController(downloads: List<DownloadFile>) {
binding.recycler.withModels { binding.recycler.withModels {
if (downloads.isEmpty()) { if (downloads.isEmpty()) {
add( add(
@@ -188,12 +99,12 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
downloads.forEach { downloads.forEach {
add( add(
DownloadViewModel_() DownloadViewModel_()
.id(it.download.id, it.download.progress, it.download.status.value) .id(it.packageName)
.download(it) .download(it)
.click { _ -> openDetailsActivity(it) } .click { _ -> openDetailsFragment(it.packageName) }
.longClick { _ -> .longClick { _ ->
openDownloadMenuSheet(it) openDownloadMenuSheet(it)
false true
} }
) )
} }
@@ -202,15 +113,9 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
binding.swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
} }
private fun openDetailsActivity(downloadFile: DownloadFile) { private fun openDownloadMenuSheet(download: Download) {
findNavController().navigate( findNavController().navigate(
MobileNavigationDirections.actionGlobalAppDetailsFragment(downloadFile.download.tag!!) DownloadFragmentDirections.actionDownloadFragmentToDownloadMenuSheet(download)
)
}
private fun openDownloadMenuSheet(downloadFile: DownloadFile) {
findNavController().navigate(
DownloadFragmentDirections.actionDownloadFragmentToDownloadMenuSheet(downloadFile)
) )
} }
} }

View File

@@ -23,79 +23,72 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import com.aurora.extensions.copyToClipBoard import com.aurora.extensions.copyToClipBoard
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.downloader.DownloadManager import com.aurora.store.data.downloader.DownloadManager
import com.aurora.store.data.model.DownloadStatus
import com.aurora.store.databinding.SheetDownloadMenuBinding import com.aurora.store.databinding.SheetDownloadMenuBinding
import com.aurora.store.util.DownloadWorkerUtil
import com.tonyodev.fetch2.Fetch import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.Status import com.tonyodev.fetch2.Status
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlin.properties.Delegates import kotlin.properties.Delegates
@AndroidEntryPoint @AndroidEntryPoint
class DownloadMenuSheet : BaseBottomSheet() { class DownloadMenuSheet : BaseBottomSheet() {
private lateinit var B: SheetDownloadMenuBinding private var _binding: SheetDownloadMenuBinding? = null
private lateinit var fetch: Fetch private val binding get() = _binding!!
private val args: DownloadMenuSheetArgs by navArgs() private val args: DownloadMenuSheetArgs by navArgs()
private var status by Delegates.notNull<Int>() private val playStoreURL = "https://play.google.com/store/apps/details?id="
@Inject
lateinit var downloadWorkerUtil: DownloadWorkerUtil
override fun onCreateContentView( override fun onCreateContentView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup, container: ViewGroup,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
B = SheetDownloadMenuBinding.inflate(layoutInflater) _binding = SheetDownloadMenuBinding.inflate(layoutInflater)
return B.root return binding.root
} }
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) { override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
fetch = DownloadManager with(binding.navigationView) {
.with(requireContext()) menu.findItem(R.id.action_cancel).isVisible = !args.download.isFinished
.getFetchInstance() menu.findItem(R.id.action_clear).isVisible = args.download.isFinished
status = args.downloadFile.download.status.value
attachNavigation()
}
private fun attachNavigation() {
with(B.navigationView) {
if (status == Status.PAUSED.value || status == Status.COMPLETED.value || status == Status.CANCELLED.value) {
menu.findItem(R.id.action_pause).isVisible = false
}
if (status == Status.DOWNLOADING.value || status == Status.COMPLETED.value || status == Status.QUEUED.value) {
menu.findItem(R.id.action_resume).isVisible = false
}
if (status == Status.COMPLETED.value || status == Status.CANCELLED.value) {
menu.findItem(R.id.action_cancel).isVisible = false
}
setNavigationItemSelectedListener { item -> setNavigationItemSelectedListener { item ->
when (item.itemId) { when (item.itemId) {
R.id.action_copy -> { R.id.action_copy -> {
requireContext().copyToClipBoard(args.downloadFile.download.url) requireContext().copyToClipBoard(
"${playStoreURL}${args.download.packageName}"
)
requireContext().toast(requireContext().getString(R.string.toast_clipboard_copied)) requireContext().toast(requireContext().getString(R.string.toast_clipboard_copied))
} }
R.id.action_pause -> {
fetch.pause(args.downloadFile.download.id)
}
R.id.action_resume -> if (status == Status.FAILED.value || status == Status.CANCELLED.value) {
fetch.retry(args.downloadFile.download.id)
} else {
fetch.resume(args.downloadFile.download.id)
}
R.id.action_cancel -> { R.id.action_cancel -> {
fetch.cancel(args.downloadFile.download.id) findViewTreeLifecycleOwner()?.lifecycleScope?.launch(NonCancellable) {
downloadWorkerUtil.cancelDownload(args.download.packageName)
}
} }
R.id.action_clear -> { R.id.action_clear -> {
fetch.delete(args.downloadFile.download.id) findViewTreeLifecycleOwner()?.lifecycleScope?.launch(NonCancellable) {
downloadWorkerUtil.clearDownload(
args.download.packageName,
args.download.versionCode
)
}
} }
} }
dismissAllowingStateLoss() dismissAllowingStateLoss()
@@ -103,4 +96,9 @@ class DownloadMenuSheet : BaseBottomSheet() {
} }
} }
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
} }

View File

@@ -51,43 +51,15 @@
android:textAlignment="viewStart" android:textAlignment="viewStart"
tools:text="App Name" /> tools:text="App Name" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="2">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_status"
style="@style/AuroraTextStyle.Line2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="Status" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_size"
style="@style/AuroraTextStyle.Line2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewEnd"
tools:text="33 MB" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/txt_file" android:id="@+id/txt_status"
style="@style/AuroraTextStyle.Line3" style="@style/AuroraTextStyle.Line2"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="17dp" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:singleLine="true" android:singleLine="true"
tools:text="File path" /> android:textAlignment="viewStart"
tools:text="Status" />
<ProgressBar <ProgressBar
android:id="@+id/progress_download" android:id="@+id/progress_download"

View File

@@ -18,19 +18,10 @@
--> -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_pause_all"
android:title="@string/download_pause_all" />
<item
android:id="@+id/action_resume_all"
android:title="@string/download_resume_all" />
<item <item
android:id="@+id/action_cancel_all" android:id="@+id/action_cancel_all"
android:title="@string/download_cancel_all" /> android:title="@string/download_cancel_all" />
<item <item
android:id="@+id/action_clear_completed" android:id="@+id/action_clear_completed"
android:title="@string/download_clear_completed" /> android:title="@string/download_clear_completed" />
<item
android:id="@+id/action_force_clear_all"
android:title="@string/download_force_clear_all" />
</menu> </menu>

View File

@@ -21,12 +21,6 @@
<item <item
android:id="@+id/action_copy" android:id="@+id/action_copy"
android:title="@string/action_copy_link" /> android:title="@string/action_copy_link" />
<item
android:id="@+id/action_pause"
android:title="@string/action_pause" />
<item
android:id="@+id/action_resume"
android:title="@string/action_resume" />
<item <item
android:id="@+id/action_cancel" android:id="@+id/action_cancel"
android:title="@string/action_cancel" /> android:title="@string/action_cancel" />

View File

@@ -432,8 +432,8 @@
android:label="DownloadMenuSheet" android:label="DownloadMenuSheet"
tools:layout="@layout/sheet_download_menu"> tools:layout="@layout/sheet_download_menu">
<argument <argument
android:name="downloadFile" android:name="download"
app:argType="com.aurora.store.data.model.DownloadFile" /> app:argType="com.aurora.store.data.room.download.Download" />
</dialog> </dialog>
<dialog <dialog
android:id="@+id/deviceMiuiSheet" android:id="@+id/deviceMiuiSheet"