diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineActivity.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineActivity.kt index 656e40d..27c4f4a 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineActivity.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineActivity.kt @@ -1,9 +1,16 @@ package pt.ulisboa.ist.pharmacist.ui.screens.medicine +import android.content.ContentValues import android.content.Context import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri import android.os.Bundle +import android.os.Environment +import android.provider.MediaStore +import android.util.Log import androidx.activity.compose.setContent +import androidx.compose.ui.graphics.asAndroidBitmap import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import pt.ulisboa.ist.pharmacist.ui.screens.PharmacistActivity @@ -76,10 +83,82 @@ class MedicineActivity : PharmacistActivity() { }, toggleMedicineNotification = { viewModel.toggleMedicineNotification() + }, + onShareClick = { + viewModel.medicine?.let { + lifecycleScope.launch { + val imageUri = downloadAndStoreImage() + if (imageUri != null) { + val sendIntent = Intent().apply { + action = Intent.ACTION_SEND + putExtra( + Intent.EXTRA_TEXT, + "Check out this medicine!" + + "\n\nName: ${it.medicine.name}" + + "\n\nDownload the Pharmacist app to see more details!" + ) + putExtra(Intent.EXTRA_TITLE, "Check out this medicine!") + putExtra(Intent.EXTRA_SUBJECT, "Check out this medicine!") + putExtra(Intent.EXTRA_STREAM, imageUri) + type = "image/*" + } + val shareIntent = Intent.createChooser( + sendIntent, + "Share this pharmacy" + ) + startActivity(shareIntent) + } + } + } } ) } } + /** + * Downloads and stores the image of the medicine. + * Used to share the medicine image. + */ + private suspend fun downloadAndStoreImage(): Uri? { + viewModel.downloadImage() + val contentValues = ContentValues().apply { + put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") + put(MediaStore.Images.Media.DISPLAY_NAME, "pharmacy") + put( + MediaStore.MediaColumns.RELATIVE_PATH, + Environment.DIRECTORY_PICTURES + ) + put(MediaStore.Images.Media.IS_PENDING, 1) + } + + val imageUri = contentResolver.insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + contentValues + ) + if (imageUri == null) { + Log.e("PharmacyActivity", "Failed to create image uri") + return null + } + + contentResolver.openOutputStream(imageUri).use { outputStream -> + if (outputStream == null) { + Log.e("PharmacyActivity", "Failed to open output stream") + return null + } + + val imageBitmap = viewModel.medicineImage?.asAndroidBitmap() + if (imageBitmap == null) { + Log.e("PharmacyActivity", "Failed to get image bitmap") + return null + } + imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) + } + + contentValues.clear() + contentValues.put(MediaStore.Images.Media.IS_PENDING, 0) + contentResolver.update(imageUri, contentValues, null, null) + + return imageUri + } } diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineScreen.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineScreen.kt index 6f0b30e..66219ac 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineScreen.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineScreen.kt @@ -44,7 +44,8 @@ fun MedicineScreen( loadingState: MedicineViewModel.MedicineLoadingState, pharmaciesState: Flow>, onPharmacyClick: (Pharmacy) -> Unit, - toggleMedicineNotification: () -> Unit + toggleMedicineNotification: () -> Unit, + onShareClick: () -> Unit ) { if (loadingState == MedicineViewModel.MedicineLoadingState.LOADED && medicineModel != null) { val (medicine, notificationsActive) = medicineModel @@ -76,7 +77,7 @@ fun MedicineScreen( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxSize() ) { - MedicineHeader(medicine, toggleMedicineNotification, notificationsActive) + MedicineHeader(medicine, toggleMedicineNotification, notificationsActive, onShareClick) MedicinePharmacyList(pharmacies, onPharmacyClick) } else @@ -84,7 +85,7 @@ fun MedicineScreen( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxSize() ) { - MedicineHeader(medicine, toggleMedicineNotification, notificationsActive) + MedicineHeader(medicine, toggleMedicineNotification, notificationsActive, onShareClick) MedicinePharmacyList(pharmacies, onPharmacyClick) } } diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineViewModel.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineViewModel.kt index 623f2ca..fcb5465 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineViewModel.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/MedicineViewModel.kt @@ -1,19 +1,23 @@ package pt.ulisboa.ist.pharmacist.ui.screens.medicine import android.content.Context +import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.ImageBitmap import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import pt.ulisboa.ist.pharmacist.domain.medicines.GetMedicineOutputModel import pt.ulisboa.ist.pharmacist.domain.pharmacies.Location import pt.ulisboa.ist.pharmacist.service.LocationService @@ -23,6 +27,7 @@ import pt.ulisboa.ist.pharmacist.session.SessionManager import pt.ulisboa.ist.pharmacist.ui.screens.PharmacistViewModel import pt.ulisboa.ist.pharmacist.ui.screens.medicine.MedicineViewModel.MedicineLoadingState.LOADED import pt.ulisboa.ist.pharmacist.ui.screens.medicine.MedicineViewModel.MedicineLoadingState.NOT_LOADED +import pt.ulisboa.ist.pharmacist.ui.screens.shared.ImageHandlingUtils import pt.ulisboa.ist.pharmacist.ui.screens.shared.hasLocationPermission /** @@ -46,6 +51,9 @@ class MedicineViewModel( private set private val locationFlow = MutableStateFlow(null) + var medicineImage: ImageBitmap? by mutableStateOf(null) + private set + @OptIn(ExperimentalCoroutinesApi::class) private val _pharmaciesState = locationFlow.flatMapLatest { location -> Pager( @@ -109,6 +117,22 @@ class MedicineViewModel( .collect() } + /** + * Downloads the pharmacy image. + */ + suspend fun downloadImage() { + medicine?.let { + withContext(Dispatchers.IO) { + val img: ImageBitmap? = ImageHandlingUtils.downloadImage(it.medicine.boxPhotoUrl) + if (img == null) { + Log.e("PharmacyActivity", "Failed to download image") + return@withContext + } + medicineImage = img + } + } + } + enum class MedicineLoadingState { NOT_LOADED, LOADING, diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/components/MedicineHeader.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/components/MedicineHeader.kt index 2ff0424..0668222 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/components/MedicineHeader.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/medicine/components/MedicineHeader.kt @@ -2,11 +2,13 @@ package pt.ulisboa.ist.pharmacist.ui.screens.medicine.components import android.content.res.Configuration import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.NotificationsActive import androidx.compose.material.icons.rounded.NotificationsOff +import androidx.compose.material.icons.rounded.Share import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -25,7 +27,8 @@ import pt.ulisboa.ist.pharmacist.ui.screens.shared.components.MeteredAsyncImage fun MedicineHeader( medicine: Medicine, toggleMedicineNotification: () -> Unit, - notificationsActive: Boolean + notificationsActive: Boolean, + onShareClick: () -> Unit ) { val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE @@ -53,15 +56,24 @@ fun MedicineHeader( style = MaterialTheme.typography.bodyLarge ) - IconButton( - modifier = Modifier, - onClick = toggleMedicineNotification, - ) { - Icon( - if (notificationsActive) Icons.Rounded.NotificationsActive else Icons.Rounded.NotificationsOff, - contentDescription = stringResource(R.string.medicine_addToNotifications_button_description), - tint = MaterialTheme.colorScheme.primary - ) + Row { + IconButton( + modifier = Modifier, + onClick = toggleMedicineNotification, + ) { + Icon( + if (notificationsActive) Icons.Rounded.NotificationsActive else Icons.Rounded.NotificationsOff, + contentDescription = stringResource(R.string.medicine_addToNotifications_button_description), + tint = MaterialTheme.colorScheme.primary + ) + } + IconButton(onClick = onShareClick) { + Icon( + Icons.Rounded.Share, + contentDescription = stringResource(R.string.share), + tint = MaterialTheme.colorScheme.primary + ) + } } } } \ No newline at end of file diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyScreen.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyScreen.kt index cbe6bc7..e1328c5 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyScreen.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyScreen.kt @@ -51,7 +51,7 @@ fun PharmacyScreen( val pagerState = rememberPagerState(initialPage = 0) PharmacistScreen { - if (loadingState == PharmacyViewModel.PharmacyLoadingState.LOADED && pharmacy != null) + if (loadingState.isLoaded() && pharmacy != null) if (isLandscape) Row( verticalAlignment = Alignment.CenterVertically, diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyViewModel.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyViewModel.kt index 6c91367..73ef497 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyViewModel.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/PharmacyViewModel.kt @@ -302,11 +302,12 @@ class PharmacyViewModel( } } - enum class PharmacyLoadingState { NOT_LOADED, LOADING, - LOADED + LOADED; + + fun isLoaded() = this == LOADED } companion object { diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/components/PharmacyHeader.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/components/PharmacyHeader.kt index 007980a..f024e27 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/components/PharmacyHeader.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/pharmacy/components/PharmacyHeader.kt @@ -35,6 +35,7 @@ import com.google.maps.android.compose.Marker import com.google.maps.android.compose.MarkerState import pt.ulisboa.ist.pharmacist.R import pt.ulisboa.ist.pharmacist.service.http.services.pharmacies.models.getPharmacyById.PharmacyWithUserDataModel +import pt.ulisboa.ist.pharmacist.ui.screens.pharmacy.PharmacyViewModel import pt.ulisboa.ist.pharmacist.ui.screens.shared.components.MeteredAsyncImage import pt.ulisboa.ist.pharmacist.ui.theme.Favorite diff --git a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/shared/ImageHandlingUtils.kt b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/shared/ImageHandlingUtils.kt index 4d56d52..1543437 100644 --- a/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/shared/ImageHandlingUtils.kt +++ b/src/frontend/app/src/main/kotlin/pt/ulisboa/ist/pharmacist/ui/screens/shared/ImageHandlingUtils.kt @@ -142,6 +142,13 @@ object ImageHandlingUtils { return chooser } + /** + * Downloads an image. + * + * @param url the URL of the image + * + * @return the image bitmap + */ fun downloadImage(url: String): ImageBitmap? { val inputStream = URL(url).openStream() return BitmapFactory.decodeStream(inputStream)?.asImageBitmap()