Skip to content

Commit

Permalink
[ANDROAPP-5857] image download implementation (#3498)
Browse files Browse the repository at this point in the history
* download image files to external directory

Signed-off-by: Pablo <[email protected]>

* download qr/barcode files to external directory

Signed-off-by: Pablo <[email protected]>

* show message when file is downloaded

Signed-off-by: Pablo <[email protected]>

---------

Signed-off-by: Pablo <[email protected]>
  • Loading branch information
Balcan authored Feb 15, 2024
1 parent 4168308 commit 5fc8fb9
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 33 deletions.
11 changes: 11 additions & 0 deletions commons/src/main/java/org/dhis2/commons/bindings/Permissions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.dhis2.commons.bindings

import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat

fun Context.hasPermissions(permissions: Array<String>): Boolean {
return permissions.all { permission ->
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
}
}
52 changes: 52 additions & 0 deletions commons/src/main/java/org/dhis2/commons/data/FileHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.dhis2.commons.data

import android.graphics.Bitmap
import android.os.Environment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import java.io.File
import java.io.FileOutputStream

class FileHandler {

private val destinationResult = MutableLiveData<File>()

fun saveBitmapAndOpen(
bitmap: Bitmap,
outputFileName: String,
fileCallback: (LiveData<File>) -> Unit,
) {
fileCallback(destinationResult)

val imagesFolder = getDownloadDirectory(outputFileName)
destinationResult.value = saveBitmapAndOpen(bitmap, imagesFolder)
}

fun copyAndOpen(
sourceFile: File,
fileCallback: (LiveData<File>) -> Unit,
) {
fileCallback(destinationResult)

val imagesFolder = getDownloadDirectory(sourceFile.name)
destinationResult.value = copyFile(sourceFile, imagesFolder)
}

private fun saveBitmapAndOpen(bitmap: Bitmap, destinationFolder: File): File {
val os = FileOutputStream(destinationFolder)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os)
os.close()
return destinationFolder
}

private fun copyFile(sourceFile: File, destinationDirectory: File): File {
return sourceFile.copyTo(destinationDirectory, true)
}

private fun getDownloadDirectory(outputFileName: String) = File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS,
),
"dhis2" + File.separator + outputFileName,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package org.dhis2.commons.dialogs.imagedetail
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.core.content.FileProvider
import org.dhis2.commons.R
import org.dhis2.commons.data.FileHandler
import org.dhis2.commons.data.FormFileProvider
import org.dhis2.commons.extensions.getBitmap
import org.hisp.dhis.mobile.ui.designsystem.component.FullScreenImage
Expand All @@ -32,6 +34,8 @@ class ImageDetailActivity : AppCompatActivity() {
}
}

private val fileHandler = FileHandler()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val title = intent.getStringExtra(ARG_IMAGE_TITLE)
Expand All @@ -46,14 +50,26 @@ class ImageDetailActivity : AppCompatActivity() {
painter = painter!!,
title = title.orEmpty(),
onDismiss = { finish() },
onDownloadButtonClick = { },
onDownloadButtonClick = {
fileHandler.copyAndOpen(
File(imagePath),
) { file ->
file.observe(this) {
Toast.makeText(
this,
getString(R.string.file_downladed),
Toast.LENGTH_SHORT,
).show()
}
}
},
onShareButtonClick = { shareImage(imagePath) },
)
}
}

private fun shareImage(image: String) {
val intent = Intent(Intent.ACTION_SEND).apply {
with(Intent(Intent.ACTION_SEND)) {
val contentUri = FileProvider.getUriForFile(
this@ImageDetailActivity,
FormFileProvider.fileProviderAuthority,
Expand All @@ -62,14 +78,14 @@ class ImageDetailActivity : AppCompatActivity() {
setDataAndType(contentUri, contentResolver.getType(contentUri))
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
putExtra(Intent.EXTRA_STREAM, contentUri)
}

val title = resources.getString(R.string.open_with)
val chooser = Intent.createChooser(intent, title)
try {
startActivity(chooser)
} catch (e: IOException) {
Timber.e(e)
val title = resources.getString(R.string.open_with)
val chooser = Intent.createChooser(intent, title)
try {
startActivity(chooser)
} catch (e: IOException) {
Timber.e(e)
}
}
}
}
2 changes: 2 additions & 0 deletions commons/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@
</plurals>
<string name="open_with">Open with</string>

<string name="file_downladed">File downladed successfully</string>


<!--Enrollment labels-->
<plurals name="enrollment">
Expand Down
27 changes: 15 additions & 12 deletions form/src/main/java/org/dhis2/form/ui/FormView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import org.dhis2.commons.Constants
import org.dhis2.commons.bindings.getFileFrom
import org.dhis2.commons.bindings.getFileFromGallery
import org.dhis2.commons.bindings.rotateImage
import org.dhis2.commons.data.FileHandler
import org.dhis2.commons.data.FormFileProvider
import org.dhis2.commons.dialogs.AlertBottomDialog
import org.dhis2.commons.dialogs.CustomDialog
Expand Down Expand Up @@ -324,6 +325,8 @@ class FormView : Fragment() {
Manifest.permission.READ_MEDIA_VIDEO,
)

private val fileHandler = FileHandler()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand Down Expand Up @@ -1004,18 +1007,18 @@ class FormView : Fragment() {
}

private fun openFile(event: RecyclerViewUiEvents.OpenFile) {
event.field.displayName?.let { filePath ->
val file = File(filePath)
val fileUri = FileProvider.getUriForFile(
requireContext(),
FormFileProvider.fileProviderAuthority,
file,
)
startActivity(
Intent(Intent.ACTION_VIEW)
.setDataAndType(fileUri, "*/*")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION),
)
activity?.activityResultRegistry?.let {
event.field.displayName?.let { filePath ->
fileHandler.copyAndOpen(File(filePath)) { file ->
file.observe(viewLifecycleOwner) {
Toast.makeText(
requireContext(),
getString(R.string.file_downladed),
Toast.LENGTH_SHORT,
).show()
}
}
}
}
}

Expand Down
38 changes: 26 additions & 12 deletions form/src/main/java/org/dhis2/form/ui/dialog/QRDetailBottomDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.material.Icon
Expand All @@ -27,6 +28,7 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.core.content.FileProvider
import androidx.fragment.app.viewModels
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.dhis2.commons.data.FileHandler
import org.dhis2.commons.data.FormFileProvider
import org.dhis2.commons.resources.ColorType
import org.dhis2.commons.resources.ColorUtils
Expand Down Expand Up @@ -67,6 +69,8 @@ QRDetailBottomDialog(
}

private var showBottomSheet: Boolean = true
private val fileHandler = FileHandler()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme)
Expand Down Expand Up @@ -127,10 +131,12 @@ QRDetailBottomDialog(
Row(horizontalArrangement = Arrangement.Center) {
when (renderingType) {
UiRenderType.QR_CODE, UiRenderType.GS1_DATAMATRIX -> {
val isGS1Matrix = value.startsWith(GS1Elements.GS1_d2_IDENTIFIER.element)
val isGS1Matrix =
value.startsWith(GS1Elements.GS1_d2_IDENTIFIER.element)
val content = formattedContent(value)
QrCodeBlock(data = content, isDataMatrix = isGS1Matrix)
}

else -> {
BarcodeBlock(data = value)
}
Expand Down Expand Up @@ -185,7 +191,12 @@ QRDetailBottomDialog(
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
setDataAndType(uri, context?.contentResolver?.getType(uri))
putExtra(Intent.EXTRA_STREAM, uri)
startActivity(Intent.createChooser(this, context?.getString(R.string.share)))
startActivity(
Intent.createChooser(
this,
context?.getString(R.string.share),
),
)
}
}
},
Expand All @@ -202,16 +213,19 @@ QRDetailBottomDialog(
enabled = true,
text = resources.getString(R.string.download),
onClick = {
qrContentUri?.let { uri ->
startActivity(
Intent().apply {
action = Intent.ACTION_VIEW
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
setDataAndType(uri, context?.contentResolver?.getType(uri))
putExtra(Intent.EXTRA_STREAM, uri)
},
)
// implement download action here
viewModel.qrBitmap.value?.onSuccess { bitmap ->
fileHandler.saveBitmapAndOpen(
bitmap,
"$label.png",
) { file ->
file.observe(viewLifecycleOwner) {
Toast.makeText(
requireContext(),
getString(R.string.file_downladed),
Toast.LENGTH_SHORT,
).show()
}
}
}
},
),
Expand Down

0 comments on commit 5fc8fb9

Please sign in to comment.