Skip to content

Commit

Permalink
(android) Save photo uri for contact (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpad85 committed Jun 28, 2024
1 parent 4087dc6 commit 544438b
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fun ContactCompactView(

SplashClickableContent(onClick = { showSheet = true }) {
Row(verticalAlignment = Alignment.CenterVertically) {
ContactPhotoView(image = contact.photo?.toByteArray(), name = contact.name, onChange = null, imageSize = 28.dp, borderSize = 2.dp)
ContactPhotoView(photoUri = contact.photoUri, name = contact.name, onChange = null, imageSize = 28.dp, borderSize = 2.dp)
Spacer(modifier = Modifier.width(8.dp))
Text(text = contact.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fun ContactDetailsView(
val scope = rememberCoroutineScope()

var name by remember(contact) { mutableStateOf(contact.name) }
var photo by remember(contact) { mutableStateOf(contact.photo?.toByteArray()) }
var photoUri by remember(contact) { mutableStateOf(contact.photoUri) }
var showAllDetails by remember { mutableStateOf(false) }

ModalBottomSheet(
Expand All @@ -104,9 +104,9 @@ fun ContactDetailsView(
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (onContactChange != null) {
ContactPhotoView(image = photo, name = contact.name, onChange = { photo = it })
ContactPhotoView(photoUri = photoUri, name = contact.name, onChange = { photoUri = it })
} else {
ContactPhotoView(image = photo, name = contact.name, onChange = null)
ContactPhotoView(photoUri = photoUri, name = contact.name, onChange = null)
}
Spacer(modifier = Modifier.height(24.dp))
TextInput(
Expand Down Expand Up @@ -139,10 +139,10 @@ fun ContactDetailsView(
Button(
text = stringResource(id = R.string.contact_save_button),
icon = R.drawable.ic_check,
enabled = contact.name != name || contact.photo != photo?.byteVector(),
enabled = contact.name != name || contact.photoUri != photoUri,
onClick = {
scope.launch {
val newContact = contactsManager.updateContact(contact.id, name, photo, contact.offers)
val newContact = contactsManager.updateContact(contact.id, name, photoUri, contact.offers)
onContactChange(newContact)
onDismiss()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
Expand All @@ -53,26 +54,30 @@ import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import fr.acinq.phoenix.android.R
import fr.acinq.phoenix.android.utils.BitmapHelper
import fr.acinq.phoenix.android.utils.createContactPictureUri
import fr.acinq.phoenix.android.utils.mutedBgColor
import java.io.ByteArrayOutputStream

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun ContactPhotoView(
image: ByteArray?,
photoUri: String?,
name: String?,
onChange: ((ByteArray?) -> Unit)?,
onChange: ((String?) -> Unit)?,
imageSize: Dp = 96.dp,
borderSize: Dp = 4.dp
) {
val context = LocalContext.current

val bitmap = remember(image) {
image?.let {
try {
BitmapFactory.decodeByteArray(image, 0, it.size).asImageBitmap()
} catch (e: Exception) {
null
val tempPhotoUri by remember { mutableStateOf(photoUri?.let { Uri.parse(it) } ?: context.createContactPictureUri()) }
var realUri by remember { mutableStateOf<Uri?>(null) }
val bitmap: ImageBitmap? = remember(tempPhotoUri) {
realUri?.let { uri ->
// TODO: load image from URI
val contentResolver = context.contentResolver
contentResolver.openInputStream(uri)?.use {
BitmapFactory.decodeStream(it).asImageBitmap()
}
}
}
Expand All @@ -81,14 +86,9 @@ fun ContactPhotoView(
val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA)

val cameraLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.TakePicturePreview(),
onResult = {
val bos = ByteArrayOutputStream()
it?.compress(Bitmap.CompressFormat.JPEG, 80, bos)
onChange?.let { it(bos.toByteArray()) }
}
contract = ActivityResultContracts.TakePicture(),
onResult = { realUri = tempPhotoUri }
)

Surface(
shape = CircleShape,
border = BorderStroke(width = borderSize, color = MaterialTheme.colors.surface),
Expand All @@ -98,7 +98,7 @@ fun ContactPhotoView(
role = Role.Button,
onClick = {
if (cameraPermissionState.status.isGranted) {
cameraLauncher.launch()
cameraLauncher.launch(tempPhotoUri)
} else {
cameraAccessDenied = cameraPermissionState.status.shouldShowRationale
if (cameraAccessDenied) {
Expand All @@ -113,6 +113,7 @@ fun ContactPhotoView(
)
} else Modifier
) {

if (bitmap == null) {
Image(
painter = painterResource(id = R.drawable.ic_contact_placeholder),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private fun ContactRow(contact: ContactInfo, canEditContact: Boolean, onEditCont
verticalAlignment = Alignment.CenterVertically,
) {
Spacer(modifier = Modifier.width(12.dp))
ContactPhotoView(image = contact.photo?.toByteArray(), name = contact.name, onChange = null, imageSize = 32.dp, borderSize = 0.dp)
ContactPhotoView(photoUri = contact.photoUri, name = contact.name, onChange = null, imageSize = 32.dp, borderSize = 0.dp)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = contact.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ fun SaveNewContactDialog(
var offer by remember { mutableStateOf(initialOffer?.encode() ?: "") }
var offerErrorMessage by remember { mutableStateOf("") }
var name by remember { mutableStateOf("") }
var photo by remember { mutableStateOf<ByteArray?>(null) }
var photoUri by remember { mutableStateOf<String?>(null) }

val contactsManager = business.contactsManager

Expand Down Expand Up @@ -113,7 +113,7 @@ fun SaveNewContactDialog(
) {
Text(text = stringResource(id = R.string.contact_add_title), style = MaterialTheme.typography.h4, textAlign = TextAlign.Center)
Spacer(modifier = Modifier.height(24.dp))
ContactPhotoView(image = photo, name = name, onChange = { photo = it })
ContactPhotoView(photoUri = photoUri, name = name, onChange = { photoUri = it })
Spacer(modifier = Modifier.height(24.dp))
TextInput(
text = name,
Expand Down Expand Up @@ -153,7 +153,7 @@ fun SaveNewContactDialog(
if (existingContact != null) {
offerErrorMessage = context.getString(R.string.contact_error_offer_known, existingContact.name)
} else {
val contact = contactsManager.saveNewContact(name, photo, res.result)
val contact = contactsManager.saveNewContact(name, photoUri, res.result)
onSaved(contact)
}
}
Expand All @@ -164,7 +164,7 @@ fun SaveNewContactDialog(
if (existingContact != null) {
offerErrorMessage = context.getString(R.string.contact_error_offer_known, existingContact.name)
} else {
val contact = contactsManager.saveNewContact(name, photo, initialOffer)
val contact = contactsManager.saveNewContact(name, photoUri, initialOffer)
onSaved(contact)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@
package fr.acinq.phoenix.android.utils

import android.content.*
import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.content.FileProvider
import fr.acinq.lightning.db.*
import fr.acinq.lightning.utils.Connection
import fr.acinq.lightning.utils.currentTimestampMillis
import fr.acinq.phoenix.android.*
import fr.acinq.phoenix.android.R
import fr.acinq.phoenix.android.utils.Converter.toPrettyString
import fr.acinq.phoenix.data.BitcoinUnit
import fr.acinq.phoenix.data.FiatCurrency
import fr.acinq.phoenix.utils.extensions.desc
import java.io.File
import java.security.cert.CertificateException
import java.util.*
import kotlin.contracts.ExperimentalContracts
Expand Down Expand Up @@ -76,6 +80,16 @@ fun Context.findActivity(): MainActivity {
throw IllegalStateException("not in the context of the main Phoenix activity")
}

fun Context.createContactPictureUri(
provider: String = "${BuildConfig.APPLICATION_ID}.provider",
): Uri {
val contactDir = File(cacheDir, "contacts")
if (!contactDir.exists()) contactDir.mkdir()
val photoFile = File(contactDir, "contact_${currentTimestampMillis()}.jpg")
if (!photoFile.exists() || !photoFile.canWrite()) photoFile.createNewFile()
return FileProvider.getUriForFile(applicationContext, provider, photoFile)
}

@Composable
fun BitcoinUnit.label(): String = when (this) {
BitcoinUnit.Sat -> stringResource(id = R.string.prefs_display_coin_sat_label)
Expand Down
1 change: 1 addition & 0 deletions phoenix-android/src/main/res/xml/provider_paths.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="logs_dir" path="logs" />
<cache-path name="csv_export" path="payments/" />
<cache-path name="contacts" path="contacts/" />
</paths>

0 comments on commit 544438b

Please sign in to comment.