Blog Infos
Author
Published
Topics
Author
Published
class FileDownloadWorker(
private val context:Context,
workerParameters: WorkerParameters
): CoroutineWorker(context, workerParameters) {
override suspend fun doWork(): Result {
}
}

Now define two constants objects one for input and output data and other for notifications.

object FileParams{
const val KEY_FILE_URL = "key_file_url"
const val KEY_FILE_TYPE = "key_file_type"
const val KEY_FILE_NAME = "key_file_name"
const val KEY_FILE_URI = "key_file_uri"
}
object NotificationConstants{
const val CHANNEL_NAME = "download_file_worker_demo_channel"
const val CHANNEL_DESCRIPTION = "download_file_worker_demo_description"
const val CHANNEL_ID = "download_file_worker_demo_channel_123456"
const val NOTIFICATION_ID = 1
}
private fun getSavedFileUri(
fileName:String,
fileType:String,
fileUrl:String,
context: Context): Uri?{
val mimeType = when(fileType){
"PDF" -> "application/pdf"
"PNG" -> "image/png"
"MP4" -> "video/mp4"
else -> ""
} // different types of files will have different mime type
if (mimeType.isEmpty()) return null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, "Download/DownloaderDemo")
}
val resolver = context.contentResolver
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
return if (uri!=null){
URL(fileUrl).openStream().use { input->
resolver.openOutputStream(uri).use { output->
input.copyTo(output!!, DEFAULT_BUFFER_SIZE)
}
}
uri
}else{
null
}
}else{
val target = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
fileName
)
URL(fileUrl).openStream().use { input->
FileOutputStream(target).use { output ->
input.copyTo(output)
}
}
return target.toUri()
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val name = NotificationConstants.CHANNEL_NAME
val description = NotificationConstants.CHANNEL_DESCRIPTION
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(NotificationConstants.CHANNEL_ID,name,importance)
channel.description = description
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
notificationManager?.createNotificationChannel(channel)
}
val builder = NotificationCompat.Builder(context,NotificationConstants.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Downloading your file...")
.setOngoing(true)
.setProgress(0,0,true)
NotificationManagerCompat.from(context).notify(NotificationConstants.NOTIFICATION_ID,builder.build())

We only need to show notification while saving of file is in progress. After that we have to cancel it as well.
Now the doWork() method will something look like this.

override suspend fun doWork(): Result {
val fileUrl = inputData.getString(FileParams.KEY_FILE_URL) ?: ""
val fileName = inputData.getString(FileParams.KEY_FILE_NAME) ?: ""
val fileType = inputData.getString(FileParams.KEY_FILE_TYPE) ?: ""
Log.d("TAG", "doWork: $fileUrl | $fileName | $fileType")
if (fileName.isEmpty()
|| fileType.isEmpty()
|| fileUrl.isEmpty()
){
Result.failure()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val name = NotificationConstants.CHANNEL_NAME
val description = NotificationConstants.CHANNEL_DESCRIPTION
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(NotificationConstants.CHANNEL_ID,name,importance)
channel.description = description
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
notificationManager?.createNotificationChannel(channel)
}
val builder = NotificationCompat.Builder(context,NotificationConstants.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Downloading your file...")
.setOngoing(true)
.setProgress(0,0,true)
NotificationManagerCompat.from(context).notify(NotificationConstants.NOTIFICATION_ID,builder.build())
val uri = getSavedFileUri(
fileName = fileName,
fileType = fileType,
fileUrl = fileUrl,
context = context
)
NotificationManagerCompat.from(context).cancel(NotificationConstants.NOTIFICATION_ID)
return if (uri != null){
Result.success(workDataOf(FileParams.KEY_FILE_URI to uri.toString()))
}else{
Result.failure()
}
}

Step 3: Define File to be downloaded Model

data class File(
val id:String,
val name:String,
val type:String,
val url:String,
var downloadedUri:String?=null,
var isDownloading:Boolean = false,
)
view raw File.kt hosted with ❤ by GitHub
@Composable
fun ItemFile(
file: File,
startDownload:(File) -> Unit,
openFile:(File) -> Unit
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.background(color = Color.White)
.border(width = 2.dp, color = Color.Blue, shape = RoundedCornerShape(16.dp))
.clickable {
if (!file.isDownloading){
if (file.downloadedUri.isNullOrEmpty()){
startDownload(file)
}else{
openFile(file)
}
}
}
.padding(16.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.fillMaxWidth(0.8f)
) {
Text(
text = file.name,
style = Typography.body1,
color = Color.Black
)
Row {
val description = if (file.isDownloading){
"Downloading..."
}else{
if (file.downloadedUri.isNullOrEmpty()) "Tap to download the file" else "Tap to open file"
}
Text(
text = description,
style = Typography.body2,
color = Color.DarkGray
)
}
}
if (file.isDownloading){
CircularProgressIndicator(
color = Color.Blue,
modifier = Modifier
.size(32.dp)
.align(Alignment.CenterVertically)
)
}
}
}
}
view raw ItemFile.kt hosted with ❤ by GitHub

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Migrating to Jetpack Compose – an interop love story

Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you…
Watch Video

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer for Jetpack Compose
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engin ...
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer f ...
Google

Jobs

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
class MainActivity : ComponentActivity() {
private lateinit var requestMultiplePermission: ActivityResultLauncher<Array<String>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestMultiplePermission = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
){
var isGranted = false
it.forEach { s, b ->
isGranted = b
}
if (!isGranted){
Toast.makeText(this, "Permission Not Granted", Toast.LENGTH_SHORT).show()
}
}
setContent {
DownloadFileWorkManagerDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
requestMultiplePermission.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
)
Home()
}
}
}
}
view raw MainActivity.kt hosted with ❤ by GitHub
private fun startDownloadingFile(
file: File,
success:(String) -> Unit,
failed:(String) -> Unit,
running:() -> Unit
) {
val data = Data.Builder()
data.apply {
putString(FileDownloadWorker.FileParams.KEY_FILE_NAME, file.name)
putString(FileDownloadWorker.FileParams.KEY_FILE_URL, file.url)
putString(FileDownloadWorker.FileParams.KEY_FILE_TYPE, file.type)
}
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresStorageNotLow(true)
.setRequiresBatteryNotLow(true)
.build()
val fileDownloadWorker = OneTimeWorkRequestBuilder<FileDownloadWorker>()
.setConstraints(constraints)
.setInputData(data.build())
.build()
workManager.enqueueUniqueWork(
"oneFileDownloadWork_${System.currentTimeMillis()}",
ExistingWorkPolicy.KEEP,
fileDownloadWorker
)
workManager.getWorkInfoByIdLiveData(fileDownloadWorker.id)
.observe(this){ info->
info?.let {
when (it.state) {
WorkInfo.State.SUCCEEDED -> {
success(it.outputData.getString(FileDownloadWorker.FileParams.KEY_FILE_URI) ?: "")
}
WorkInfo.State.FAILED -> {
failed("Downloading failed!")
}
WorkInfo.State.RUNNING -> {
running()
}
else -> {
failed("Something went wrong")
}
}
}
}
}
view raw MainActivity.kt hosted with ❤ by GitHub
@Composable
fun Home() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(32.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
val data = remember {
mutableStateOf(
File(
id = "10",
name = "Pdf File 10 MB",
type = "PDF",
url = "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-download-10-mb.pdf",
downloadedUri = null
)
)
}
ItemFile(
file = data.value,
startDownload = {
startDownloadingFile(
file = data.value,
success = {
data.value = data.value.copy().apply {
isDownloading = false
downloadedUri = it
}
},
failed = {
data.value = data.value.copy().apply {
isDownloading = false
downloadedUri = null
}
},
running = {
data.value = data.value.copy().apply {
isDownloading = true
}
}
)
},
openFile = {
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(it.downloadedUri?.toUri(),"application/pdf")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
try {
startActivity(intent)
}catch (e:ActivityNotFoundException){
Toast.makeText(
this@MainActivity,
"Can't open Pdf",
Toast.LENGTH_SHORT
).show()
}
}
)
}
}
view raw MainActivity.kt hosted with ❤ by GitHub

That’s it for this example. See you in the next one.
You can connect with me on LinkedIn and Twitter.

This article was originally published on proandroiddev.com on March 04, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Between platform changes, new rules, and Android customizations by OEMs, scheduling long-running background jobs…
READ MORE
blog
In this article, we will learn about WorkManager best practices including performant component initialization…
READ MORE
blog
Uploading images to a remote server is an intensive operation. We would usually use…
READ MORE
blog
I paired Glance Widget with Work Manager API to create a feature for my…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu