11package com.fingerprint.app
22
3+ import android.content.ContentValues
4+ import android.content.Context
5+ import android.graphics.Bitmap
36import android.os.Bundle
7+ import android.os.Environment
8+ import android.provider.MediaStore
49import android.widget.Toast
510import androidx.activity.ComponentActivity
611import androidx.activity.compose.setContent
@@ -13,49 +18,59 @@ import androidx.compose.foundation.layout.Arrangement
1318import androidx.compose.foundation.layout.Box
1419import androidx.compose.foundation.layout.Column
1520import androidx.compose.foundation.layout.ColumnScope
21+ import androidx.compose.foundation.layout.Row
1622import androidx.compose.foundation.layout.fillMaxSize
1723import androidx.compose.foundation.layout.fillMaxWidth
24+ import androidx.compose.foundation.layout.height
1825import androidx.compose.foundation.layout.padding
19- import androidx.compose.foundation.layout.size
2026import androidx.compose.foundation.lazy.grid.GridCells
2127import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
2228import androidx.compose.foundation.lazy.grid.itemsIndexed
2329import androidx.compose.foundation.shape.CircleShape
2430import androidx.compose.foundation.text.KeyboardOptions
2531import androidx.compose.material.icons.Icons
32+ import androidx.compose.material.icons.rounded.Save
2633import androidx.compose.material.icons.rounded.Star
2734import androidx.compose.material3.AlertDialog
2835import androidx.compose.material3.Button
36+ import androidx.compose.material3.Checkbox
2937import androidx.compose.material3.HorizontalDivider
3038import androidx.compose.material3.Icon
39+ import androidx.compose.material3.IconButton
3140import androidx.compose.material3.Scaffold
3241import androidx.compose.material3.Text
3342import androidx.compose.material3.TextButton
3443import androidx.compose.material3.TextField
3544import androidx.compose.material3.TextFieldDefaults
45+ import androidx.compose.material3.VerticalDivider
3646import androidx.compose.runtime.Composable
3747import androidx.compose.runtime.LaunchedEffect
38- import androidx.compose.runtime.collectAsState
3948import androidx.compose.runtime.getValue
4049import androidx.compose.runtime.mutableStateOf
4150import androidx.compose.runtime.remember
4251import androidx.compose.runtime.setValue
4352import androidx.compose.ui.Alignment
4453import androidx.compose.ui.Modifier
4554import androidx.compose.ui.graphics.Color
55+ import androidx.compose.ui.graphics.ImageBitmap
56+ import androidx.compose.ui.graphics.asAndroidBitmap
4657import androidx.compose.ui.input.pointer.pointerInput
4758import androidx.compose.ui.platform.LocalClipboardManager
4859import androidx.compose.ui.platform.LocalContext
4960import androidx.compose.ui.platform.LocalFocusManager
5061import androidx.compose.ui.text.AnnotatedString
5162import androidx.compose.ui.text.input.KeyboardType
5263import androidx.compose.ui.unit.dp
64+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
5365import com.fingerprint.FingerprintInitializer
54- import com.fingerprint.app.ui.theme.FingerprintHF4000Theme
66+ import com.fingerprint.app.ui.theme.FingerprintTheme
5567import com.fingerprint.manager.FingerprintEvent
5668import com.fingerprint.manager.FingerprintManager
5769import kotlinx.coroutines.CoroutineScope
5870import kotlinx.coroutines.Dispatchers
71+ import kotlinx.coroutines.launch
72+ import java.io.OutputStream
73+ import java.util.UUID
5974
6075
6176class MainActivity : ComponentActivity () {
@@ -68,7 +83,7 @@ class MainActivity : ComponentActivity() {
6883 initializeFingerprintManager()
6984
7085 setContent {
71- FingerprintHF4000Theme {
86+ FingerprintTheme {
7287 App (fingerprintManager)
7388 }
7489 }
@@ -86,18 +101,23 @@ class MainActivity : ComponentActivity() {
86101@Composable
87102fun App (fingerprintManager : FingerprintManager ) {
88103 val clipboardManager = LocalClipboardManager .current
104+ val scope = remember { CoroutineScope (Dispatchers .IO ) }
89105 var status by remember { mutableStateOf(" " ) }
90106 var deviceInfo by remember { mutableStateOf(" " ) }
91107 var scanCount by remember { mutableStateOf(" 5" ) }
92108 var finished by remember { mutableStateOf(true ) }
93109 var showInfo by remember { mutableStateOf(false ) }
94- val events by fingerprintManager.eventsFlow.collectAsState()
110+ var isBlue by remember { mutableStateOf(false ) }
111+ var isFilter by remember { mutableStateOf(false ) }
112+ val events by fingerprintManager.eventsFlow.collectAsStateWithLifecycle()
95113 val focusManager = LocalFocusManager .current
96114
97115 LaunchedEffect (key1 = events) {
98116 println (" DEBUGGING -> Progress: ${fingerprintManager.progress} " )
99117 when (events) {
118+ is FingerprintEvent .Connected ,
100119 is FingerprintEvent .CapturedSuccessfully ,
120+ is FingerprintEvent .Disconnected ,
101121 is FingerprintEvent .ProcessCanceledTheFingerLifted -> {
102122 finished = true
103123 status = events.message
@@ -147,6 +167,40 @@ fun App(fingerprintManager: FingerprintManager) {
147167 text = { Text (text = deviceInfo) }
148168 )
149169
170+ Row (
171+ verticalAlignment = Alignment .CenterVertically
172+ ) {
173+ Checkbox (checked = isFilter, onCheckedChange = {
174+ isFilter = it
175+ scope.launch {
176+ fingerprintManager.improveTheBestCapture(
177+ isApplyFilters = isFilter,
178+ isBlue = isBlue
179+ )
180+ }
181+ })
182+ Text (text = " Apply Filter" )
183+ VerticalDivider (
184+ modifier = Modifier
185+ .height(20 .dp)
186+ .padding(start = 14 .dp)
187+ )
188+ Checkbox (
189+ checked = isBlue,
190+ onCheckedChange = {
191+ isBlue = it
192+ scope.launch {
193+ fingerprintManager.improveTheBestCapture(
194+ isApplyFilters = isFilter,
195+ isBlue = isBlue
196+ )
197+ }
198+ },
199+ enabled = isFilter
200+ )
201+ Text (text = " Blue Pixels" )
202+ }
203+
150204 Button (onClick = {
151205 deviceInfo = fingerprintManager.deviceInfo.toString()
152206 showInfo = ! showInfo
@@ -184,16 +238,37 @@ fun ColumnScope.CaptureGrid(fingerprintManager: FingerprintManager) {
184238 horizontalArrangement = Arrangement .SpaceBetween
185239 ) {
186240 itemsIndexed(items = fingerprintManager.captures) { index, bitmap ->
187- Box (contentAlignment = Alignment .TopStart ) {
188- Image (bitmap = bitmap, contentDescription = null )
189- if (index == fingerprintManager.bestCaptureIndex) {
190- Icon (
191- imageVector = Icons .Rounded .Star ,
192- contentDescription = null ,
193- tint = Color .Yellow .copy(green = 0.75f )
194- )
195- }
196- }
241+ FingerprintImage (
242+ bitmap = bitmap,
243+ index = index,
244+ bestCaptureIndex = fingerprintManager.bestCaptureIndex
245+ )
246+ }
247+ }
248+ }
249+
250+ @Composable
251+ fun FingerprintImage (bitmap : ImageBitmap , index : Int = -1, bestCaptureIndex : Int = -2) {
252+ val context = LocalContext .current
253+ Box (contentAlignment = Alignment .TopStart ) {
254+ Image (bitmap = bitmap, contentDescription = null )
255+ if (index == bestCaptureIndex) {
256+ Icon (
257+ imageVector = Icons .Rounded .Star ,
258+ contentDescription = null ,
259+ tint = Color .Yellow .copy(green = 0.75f )
260+ )
261+ }
262+
263+ IconButton (
264+ modifier = Modifier .align(Alignment .BottomEnd ),
265+ onClick = { bitmap.saveBitmapToGallery(context) }
266+ ) {
267+ Icon (
268+ imageVector = Icons .Rounded .Save ,
269+ contentDescription = null ,
270+ tint = Color .Yellow .copy(green = 0.75f )
271+ )
197272 }
198273 }
199274}
@@ -206,14 +281,10 @@ fun BestCaptureSection(fingerprintManager: FingerprintManager, scanCount: String
206281 verticalArrangement = Arrangement .spacedBy(16 .dp)
207282 ) {
208283 HorizontalDivider ()
209- Text (text = " Best Capture" )
284+ Text (text = " Improved Best Capture" )
210285 HorizontalDivider ()
211- fingerprintManager.bestCapture?.let {
212- Image (
213- bitmap = it,
214- contentDescription = null ,
215- modifier = Modifier .size(172 .dp)
216- )
286+ fingerprintManager.bestCapture?.let { bitmap ->
287+ FingerprintImage (bitmap = bitmap)
217288 }
218289 HorizontalDivider ()
219290 }
@@ -240,3 +311,23 @@ fun ScanButton(
240311 }
241312 ) { Text (text = " Scan" ) }
242313}
314+
315+ fun ImageBitmap.saveBitmapToGallery (context : Context , title : String? = null) = runCatching {
316+ val filename = " ${title ? : UUID .randomUUID().leastSignificantBits.toString()} .png"
317+ val resolver = context.contentResolver
318+ val contentValues = ContentValues ().apply {
319+ put(MediaStore .MediaColumns .DISPLAY_NAME , filename)
320+ put(MediaStore .MediaColumns .MIME_TYPE , " image/png" )
321+ put(MediaStore .MediaColumns .RELATIVE_PATH , Environment .DIRECTORY_PICTURES )
322+ }
323+
324+ val imageUri = resolver.insert(MediaStore .Images .Media .EXTERNAL_CONTENT_URI , contentValues)
325+ val fos: OutputStream ? = imageUri?.let { resolver.openOutputStream(it) }
326+
327+ fos?.use {
328+ asAndroidBitmap().compress(Bitmap .CompressFormat .PNG , 100 , it)
329+ Toast .makeText(context, " Image saved to gallery" , Toast .LENGTH_SHORT ).show()
330+ } ? : run {
331+ Toast .makeText(context, " Failed to save image" , Toast .LENGTH_SHORT ).show()
332+ }
333+ }
0 commit comments