taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

QrCodeUriComposable.kt (7531B)


      1 /*
      2  * This file is part of GNU Taler
      3  * (C) 2022 Taler Systems S.A.
      4  *
      5  * GNU Taler is free software; you can redistribute it and/or modify it under the
      6  * terms of the GNU General Public License as published by the Free Software
      7  * Foundation; either version 3, or (at your option) any later version.
      8  *
      9  * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
     10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License along with
     14  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 package net.taler.wallet.compose
     18 
     19 import android.graphics.Bitmap
     20 import android.graphics.drawable.Drawable
     21 import androidx.compose.foundation.Image
     22 import androidx.compose.foundation.horizontalScroll
     23 import androidx.compose.foundation.layout.Arrangement
     24 import androidx.compose.foundation.layout.Box
     25 import androidx.compose.foundation.layout.ColumnScope
     26 import androidx.compose.foundation.layout.Row
     27 import androidx.compose.foundation.layout.Spacer
     28 import androidx.compose.foundation.layout.aspectRatio
     29 import androidx.compose.foundation.layout.fillMaxSize
     30 import androidx.compose.foundation.layout.fillMaxWidth
     31 import androidx.compose.foundation.layout.padding
     32 import androidx.compose.foundation.layout.size
     33 import androidx.compose.foundation.rememberScrollState
     34 import androidx.compose.material.icons.Icons
     35 import androidx.compose.material.icons.filled.ContentCopy
     36 import androidx.compose.material3.Button
     37 import androidx.compose.material3.ButtonColors
     38 import androidx.compose.material3.ButtonDefaults
     39 import androidx.compose.material3.Card
     40 import androidx.compose.material3.Icon
     41 import androidx.compose.material3.MaterialTheme
     42 import androidx.compose.material3.Text
     43 import androidx.compose.runtime.Composable
     44 import androidx.compose.runtime.getValue
     45 import androidx.compose.runtime.produceState
     46 import androidx.compose.ui.Alignment
     47 import androidx.compose.ui.Modifier
     48 import androidx.compose.ui.graphics.asImageBitmap
     49 import androidx.compose.ui.platform.LocalConfiguration
     50 import androidx.compose.ui.platform.LocalContext
     51 import androidx.compose.ui.res.painterResource
     52 import androidx.compose.ui.res.stringResource
     53 import androidx.compose.ui.text.font.FontFamily
     54 import androidx.compose.ui.unit.Dp
     55 import androidx.compose.ui.unit.dp
     56 import androidx.compose.ui.unit.min
     57 import androidx.core.content.ContextCompat
     58 import net.taler.lib.android.QrCodeManager
     59 import net.taler.lib.android.QrLogoSize
     60 import net.taler.lib.android.copyToClipBoard
     61 import net.taler.lib.android.AnimatedQrCodeComposable
     62 import net.taler.wallet.R
     63 
     64 sealed class QrCodeParams {
     65     data object Taler: QrCodeParams()
     66 
     67     data class Custom(
     68         val centerLogo: Drawable? = null,
     69         val centerLogoSize: QrLogoSize = QrLogoSize.MEDIUM,
     70         val drawCenterLogoBackground: Boolean = false,
     71     ): QrCodeParams()
     72 }
     73 
     74 @Composable
     75 fun ColumnScope.QrCodeUriComposable(
     76     modifier: Modifier = Modifier,
     77     qrData: String,
     78     clipboardLabel: String,
     79     params: QrCodeParams,
     80     buttonText: String = stringResource(R.string.copy),
     81     showContents: Boolean = true,
     82     shareAsQrCode: Boolean = false,
     83     inBetween: (@Composable ColumnScope.() -> Unit)? = null,
     84 ) {
     85     val context = LocalContext.current
     86     val qrCodeSize = getQrCodeSize()
     87     val qrState by produceState<Bitmap?>(null) {
     88         value = QrCodeManager.makeQrCode(
     89             qrData,
     90             qrCodeSize.value.toInt(),
     91             centerLogo = when (params) {
     92                 QrCodeParams.Taler -> ContextCompat.getDrawable(context, net.taler.common.R.drawable.ic_taler_logo_qr)
     93                 is QrCodeParams.Custom -> params.centerLogo
     94             },
     95 
     96             centerLogoSize = when (params) {
     97                 QrCodeParams.Taler -> QrLogoSize.MEDIUM
     98                 is QrCodeParams.Custom -> params.centerLogoSize
     99             },
    100 
    101             drawBackground = false,
    102         )
    103     }
    104 
    105     Box(
    106         modifier
    107             .fillMaxWidth()
    108             .aspectRatio(1f)
    109             .padding(bottom = if (showContents) 8.dp else 0.dp),
    110         contentAlignment = Alignment.Center,
    111     ) {
    112         when (params) {
    113             QrCodeParams.Taler -> {
    114                 AnimatedQrCodeComposable(
    115                     modifier = Modifier.fillMaxSize(),
    116                     link = qrData,
    117                     logoPainter = painterResource(net.taler.common.R.drawable.ic_taler_logo_qr)
    118                 )
    119             }
    120 
    121             is QrCodeParams.Custom -> {
    122                 qrState?.let { qrCode ->
    123                     Image(
    124                         modifier = Modifier.fillMaxSize(),
    125                         bitmap = qrCode.asImageBitmap(),
    126                         contentDescription = null,
    127                     )
    128                 }
    129             }
    130         }
    131     }
    132 
    133     if (inBetween != null) inBetween()
    134     val scrollState = rememberScrollState()
    135     if (showContents) {
    136         if (!shareAsQrCode) {
    137             Card(modifier = Modifier
    138                 .padding(horizontal = 16.dp)
    139                 .padding(bottom = 16.dp, top = 10.dp)) {
    140                 Text(
    141                     modifier = Modifier
    142                         .padding(6.dp)
    143                         .horizontalScroll(scrollState),
    144                     fontFamily = FontFamily.Monospace,
    145                     style = MaterialTheme.typography.bodyMedium,
    146                     text = qrData,
    147                 )
    148             }
    149         }
    150 
    151         Row(
    152             modifier = Modifier
    153                 .padding(horizontal = 16.dp)
    154                 .fillMaxWidth(),
    155             horizontalArrangement = Arrangement.SpaceEvenly,
    156         ) {
    157             if (!shareAsQrCode) {
    158                 CopyToClipboardButton(
    159                     label = clipboardLabel,
    160                     content = qrData,
    161                     buttonText = buttonText,
    162                     colors = ButtonDefaults.buttonColors(
    163                         containerColor = MaterialTheme.colorScheme.primaryContainer,
    164                         contentColor = MaterialTheme.colorScheme.onPrimaryContainer
    165                     )
    166                 )
    167             }
    168 
    169             ShareButton(
    170                 content = qrData,
    171                 shareAsQrCode = shareAsQrCode,
    172                 qrBitmap = qrState,
    173                 colors = ButtonDefaults.buttonColors(
    174                     containerColor = MaterialTheme.colorScheme.primaryContainer,
    175                     contentColor = MaterialTheme.colorScheme.onPrimaryContainer
    176                 ),
    177             )
    178         }
    179     }
    180 }
    181 
    182 @Composable
    183 fun getQrCodeSize(): Dp {
    184     val configuration = LocalConfiguration.current
    185     val screenHeight = configuration.screenHeightDp.dp
    186     val screenWidth = configuration.screenWidthDp.dp
    187     return min(screenHeight, screenWidth)
    188 }
    189 
    190 @Composable
    191 fun CopyToClipboardButton(
    192     label: String,
    193     content: String,
    194     modifier: Modifier = Modifier,
    195     buttonText: String = stringResource(R.string.copy),
    196     colors: ButtonColors = ButtonDefaults.buttonColors(),
    197 ) {
    198     val context = LocalContext.current
    199     Button(
    200         modifier = modifier,
    201         colors = colors,
    202         onClick = { copyToClipBoard(context, label, content) },
    203     ) {
    204         Icon(
    205             Icons.Default.ContentCopy,
    206             buttonText,
    207             modifier = Modifier.size(ButtonDefaults.IconSize),
    208         )
    209         Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    210         Text(buttonText)
    211     }
    212 }