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


    Senior Android Engineer (m/f/d)

    Flink
    Berlin
    • Full Time
    apply now

    Senior Android Engineer – Outfits Program

    Zalando SE
    Berlin
    • Full Time
    apply now

    Reverse Engineer-Andriod

    Sauce Labs
    Anywhere
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

, ,

The Evolution of Android Graphics in Android 12/13

Android 12 and 13 both added significant new capabilities to Android platform graphics, including RenderEffect, RuntimeShader, and more. At the same time, RenderScript has been deprecated and we’ve introduced the RenderScript Intrinsics Replacement Toolkit. This…
Watch Video

The Evolution of Android Graphics in Android 12/13

Daniel Galpin
Android Developer Advocate and Fast Talking YouTuber
Google

The Evolution of Android Graphics in Android 12/13

Daniel Galpin
Android Developer Ad ...
Google

The Evolution of Android Graphics in Android 12/13

Daniel Galpin
Android Developer Advocat ...
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
I paired Glance Widget with Work Manager API to create a feature for my…
READ MORE
blog
Notifications provide short, timely information about events in your app while it’s not in…
READ MORE

Leave a Reply

Your email address will not be published.

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

Menu