This commit is contained in:
sathwikcs
2024-10-09 11:28:36 +05:30
parent ccee7128ba
commit 0eb4b3433b
47 changed files with 1879 additions and 30 deletions

View File

@@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-09-26T11:32:56.616421400Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=R9ZW605ZHAY" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

1
.idea/gradle.xml generated
View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">

11
.idea/other.xml generated
View File

@@ -25,6 +25,17 @@
<option name="screenX" value="1080" />
<option name="screenY" value="2160" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="Lenovo" />
<option name="codename" value="TB370FU" />
<option name="id" value="TB370FU" />
<option name="manufacturer" value="Lenovo" />
<option name="name" value="Tab P12" />
<option name="screenDensity" value="340" />
<option name="screenX" value="1840" />
<option name="screenY" value="2944" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />

View File

@@ -1,4 +1,7 @@
plugins {
id("kotlin-kapt")
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
}
@@ -29,6 +32,11 @@ android {
)
}
}
sourceSets {
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
@@ -42,6 +50,9 @@ android {
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
kapt {
correctErrorTypes = true
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
@@ -59,6 +70,7 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.litert)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -68,8 +80,42 @@ dependencies {
debugImplementation(libs.androidx.ui.test.manifest)
//firebase dependency
//firebase
implementation(platform("com.google.firebase:firebase-bom:33.2.0"))
implementation("com.google.firebase:firebase-analytics")
implementation ("com.google.firebase:firebase-firestore-ktx:23.0.3")
// navigation
val nav_version = "2.8.1"
implementation("androidx.navigation:navigation-compose:$nav_version")
//room
val room_version = "2.6.1"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
// To use Kotlin annotation processing tool (kapt) for room
kapt("androidx.room:room-compiler:$room_version")
//datastore
implementation("androidx.datastore:datastore-preferences:1.1.1")
//viewModel
val lifecycle_version = "2.8.6"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
//camera x
val camerax_version = "1.5.0-alpha01"
// camera-camera2
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
// CameraX Lifecycle library
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
// CameraX View class
implementation("androidx.camera:camera-view:${camerax_version}")
//tensorflow
implementation("org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0")
implementation ("org.tensorflow:tensorflow-lite-select-tf-ops:2.10.0")
}

View File

@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

Binary file not shown.

View File

@@ -0,0 +1,15 @@
package com.sminnovations.prodtrack
import android.app.Application
class MyApplication : Application() {
companion object {
lateinit var instance: MyApplication
private set
}
override fun onCreate() {
super.onCreate()
instance = this
}
}

View File

@@ -1,47 +1,64 @@
package com.sminnovations.prodtrack
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import com.sminnovations.prodtrack.screen.navigation.BottomNavigationBar
import com.sminnovations.prodtrack.screen.navigation.Navigation
import com.sminnovations.prodtrack.screen.navigation.data.navItems
import com.sminnovations.prodtrack.tens.MainScreen24
import com.sminnovations.prodtrack.tens.MainViewModel
import com.sminnovations.prodtrack.ui.theme.ProductionTrackerTheme
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.O_MR1)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
val viewModel: MainViewModel = viewModel()
ProductionTrackerTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
MainScreen24(viewModel)
}
}
}
}
@RequiresApi(Build.VERSION_CODES.O_MR1)
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
fun MainScreen(
) {
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigationBar(navController, navItems)
},
content = { padding ->
Box(modifier = Modifier.padding(padding)) {
Navigation(navController = navController, navItems)
}
},
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ProductionTrackerTheme {
Greeting("Android")
}
}
//
//@Composable
//fun MainScreen2(context: Context) {
// val model = remember { TFLiteModel(context) }
//
// Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
// SevenSegmentDetector(model)
// }
//}

View File

@@ -0,0 +1,83 @@
package com.sminnovations.prodtrack.common.data.dataStore
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class SettingsData(private val context: Context) {
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("Settings")
val DYNAMIC_THEME_KEY = booleanPreferencesKey("dynamic_theme")
val BLACK_THEME_KEY = booleanPreferencesKey("black_theme")
val THEME_MODE_KEY = stringPreferencesKey("app_theme")
val DEV_MODE_TOGGLE_KEY = booleanPreferencesKey("dev_mode_toggle")
}
val getDevModeToggle: Flow<Boolean?> = context.dataStore.data
.map { preferences ->
preferences[DEV_MODE_TOGGLE_KEY] ?: false
}
suspend fun setDevMode(mode: Boolean) {
context.dataStore.edit { preferences ->
preferences[DEV_MODE_TOGGLE_KEY] = mode
}
}
val getDynamicColorThemeValue: Flow<Boolean?> = context.dataStore.data
.map { preferences ->
preferences[DYNAMIC_THEME_KEY] ?: true
}
suspend fun setDynamicColorThemeValue(value: Boolean) {
context.dataStore.edit { preferences ->
preferences[DYNAMIC_THEME_KEY] = value
}
}
val getBlackThemeValue: Flow<Boolean?> = context.dataStore.data
.map { preferences ->
preferences[BLACK_THEME_KEY] ?: false
}
suspend fun setBlackThemeValue(value: Boolean) {
context.dataStore.edit { preferences ->
preferences[BLACK_THEME_KEY] = value
}
}
val getThemeMode: Flow<String> = context.dataStore.data
.map { preferences ->
preferences[THEME_MODE_KEY] ?: "System default"
}
suspend fun saveThemeMode(theme: String) {
context.dataStore.edit { preferences ->
preferences[THEME_MODE_KEY] = theme
}
}
}

View File

@@ -0,0 +1,3 @@
package com.sminnovations.prodtrack.common.data.dataStore

View File

@@ -0,0 +1,8 @@
package com.sminnovations.prodtrack.screen.history
import androidx.compose.runtime.Composable
@Composable
fun HistoryScreen(){
}

View File

@@ -0,0 +1 @@
package com.sminnovations.prodtrack.screen.home

View File

@@ -0,0 +1,100 @@
package com.sminnovations.prodtrack.screen.home.ui
//
//import android.util.Log
//import androidx.annotation.OptIn
//import androidx.camera.core.CameraSelector
//import androidx.camera.core.ExperimentalGetImage
//import androidx.camera.core.ImageAnalysis
//import androidx.camera.core.ImageProxy
//import androidx.camera.core.Preview
//import androidx.camera.lifecycle.ProcessCameraProvider
//import androidx.camera.view.PreviewView
//import androidx.compose.foundation.layout.fillMaxSize
//import androidx.compose.foundation.layout.fillMaxWidth
//import androidx.compose.foundation.layout.height
//import androidx.compose.runtime.Composable
//import androidx.compose.runtime.remember
//import androidx.compose.ui.Modifier
//import androidx.compose.ui.platform.LocalContext
//import androidx.compose.ui.unit.dp
//import androidx.compose.ui.viewinterop.AndroidView
//import androidx.core.content.ContextCompat
//import androidx.lifecycle.compose.LocalLifecycleOwner
//import com.google.mlkit.vision.common.InputImage
//import com.google.mlkit.vision.text.TextRecognition
//import com.google.mlkit.vision.text.TextRecognizer
//import com.google.mlkit.vision.text.latin.TextRecognizerOptions
//
//@Composable
//fun CameraPreview(
// onTextRecognized: (String) -> Unit
//) {
// val context = LocalContext.current
// val lifecycleOwner = LocalLifecycleOwner.current
//
// val previewView = remember { PreviewView(context) }
// val textRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
//
// AndroidView(
// factory = { previewView },
// modifier = Modifier.height(100.dp)
// .fillMaxWidth( )
// ) { view ->
// val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
// cameraProviderFuture.addListener({
// val cameraProvider = cameraProviderFuture.get()
//
// val preview = Preview.Builder().build().also {
// it.setSurfaceProvider(previewView.surfaceProvider)
// }
//
// val imageAnalyzer = ImageAnalysis.Builder().build().also {
// it.setAnalyzer(
// ContextCompat.getMainExecutor(context),
// { imageProxy ->
// processImageProxy(imageProxy, textRecognizer, onTextRecognized)
// }
// )
// }
//
// val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
//
// try {
// cameraProvider.unbindAll()
// cameraProvider.bindToLifecycle(
// lifecycleOwner,
// cameraSelector,
// preview,
// imageAnalyzer
// )
// } catch (e: Exception) {
// Log.e("CameraPreview", "Camera binding failed", e)
// }
// }, ContextCompat.getMainExecutor(context))
// }
//}
//
//
//
//@OptIn(ExperimentalGetImage::class)
//fun processImageProxy(
// imageProxy: ImageProxy,
// recognizer: TextRecognizer,
// onTextRecognized: (String) -> Unit
//) {
// val mediaImage = imageProxy.image
// if (mediaImage != null) {
// val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
//
// recognizer.process(inputImage)
// .addOnSuccessListener { visionText ->
// onTextRecognized(visionText.text) // pass recognized text
// }
// .addOnFailureListener { e ->
// Log.e("TextRecognition", "Text recognition failed", e)
// }
// .addOnCompleteListener {
// imageProxy.close() // Close the image to avoid memory leaks
// }
// }
//}

View File

@@ -0,0 +1,6 @@
package com.sminnovations.prodtrack.screen.home.ui
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect

View File

@@ -0,0 +1,182 @@
//package com.sminnovations.prodtrack.screen.home.viewmodel
//
//
//import android.app.Application
//import android.content.res.AssetManager
//import android.graphics.Bitmap
//import android.graphics.BitmapFactory
//import android.graphics.Color
//import android.net.Uri
//import android.util.Log
//import androidx.activity.compose.rememberLauncherForActivityResult
//import androidx.activity.result.contract.ActivityResultContracts
//import androidx.compose.foundation.Image
//import androidx.compose.foundation.layout.*
//import androidx.compose.material3.*
//import androidx.compose.runtime.*
//import androidx.compose.ui.Alignment
//import androidx.compose.ui.Modifier
//import androidx.compose.ui.graphics.asImageBitmap
//import androidx.compose.ui.platform.LocalContext
//import androidx.compose.ui.unit.dp
//import androidx.lifecycle.AndroidViewModel
//import androidx.lifecycle.viewModelScope
//import kotlinx.coroutines.Dispatchers
//import kotlinx.coroutines.launch
//import org.tensorflow.lite.Delegate
//import org.tensorflow.lite.Interpreter
//import java.io.FileInputStream
//import java.nio.ByteBuffer
//import java.nio.ByteOrder
//import java.nio.channels.FileChannel
//
//class MainViewModel(application: Application) : AndroidViewModel(application) {
//
// private lateinit var tflite: Interpreter
// private val inputImageWidth = 200
// private val inputImageHeight = 31
// private val alphabet = "0123456789abcdefghijklmnopqrstuvwxyz."
// private val blankIndex = alphabet.length
//
// init {
// loadModel()
// }
//
// // Load TFLite model in background
// private fun loadModel() {
// viewModelScope.launch(Dispatchers.IO) {
// val assetManager = getApplication<Application>().assets
// val model = loadModelFile(assetManager, "7seg_classifier.tflite")
// val options = Interpreter.Options()
//
// // Add Flex delegate if needed for the model
// try {
// options.addDelegate(createFlexDelegate())
// } catch (e: Exception) {
// Log.e("FlexDelegate", "Failed to load Flex Delegate", e)
// }
//
// tflite = Interpreter(model, options)
// }
// }
//
// private fun createFlexDelegate(): Delegate {
// return Class.forName("org.tensorflow.lite.flex.FlexDelegate")
// .getDeclaredConstructor()
// .newInstance() as Delegate
// }
//
// // Function to recognize text from image URI
// fun recognizeText(imageUri: Uri): String {
// // Load and preprocess image
// val bitmap = loadAndResizeBitmap(imageUri, inputImageWidth, inputImageHeight)
// val inputBuffer = convertBitmapToByteBuffer(bitmap)
//
// // Prepare output buffer
// val outputBuffer = ByteBuffer.allocateDirect(1 * inputImageWidth * alphabet.length * 4)
// .order(ByteOrder.nativeOrder())
//
// // Run inference
// tflite.run(inputBuffer, outputBuffer)
//
// // Convert output buffer to readable indices
// val results = IntArray(inputImageWidth)
// outputBuffer.asIntBuffer().get(results)
//
// // Debugging output
// results.forEachIndexed { index, value ->
// Log.d("RecognizedIndex", "Index $index: $value")
// }
//
// // Map results to corresponding characters in alphabet
// return results.filter { it != blankIndex && it >= 0 && it < alphabet.length }
// .map { alphabet[it] }
// .joinToString("")
// }
//
// // Function to load and resize bitmap from image URI
// private fun loadAndResizeBitmap(uri: Uri, width: Int, height: Int): Bitmap {
// val inputStream = getApplication<Application>().contentResolver.openInputStream(uri)
// val bitmap = BitmapFactory.decodeStream(inputStream)
//
// // Resize the bitmap to the required dimensions
// val resizedBitmap = Bitmap.createScaledBitmap(bitmap!!, width, height, false)
// inputStream?.close()
//
// // Convert to grayscale
// return convertToGrayscale(resizedBitmap)
// }
//
// // Convert bitmap to grayscale
// private fun convertToGrayscale(bitmap: Bitmap): Bitmap {
// val width = bitmap.width
// val height = bitmap.height
// val grayscaleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
//
// for (x in 0 until width) {
// for (y in 0 until height) {
// val pixel = bitmap.getPixel(x, y)
// val grayScale = (Color.red(pixel) * 0.299 +
// Color.green(pixel) * 0.587 +
// Color.blue(pixel) * 0.114).toInt()
// grayscaleBitmap.setPixel(x, y, Color.rgb(grayScale, grayScale, grayScale))
// }
// }
// return grayscaleBitmap
// }
//
// // Convert bitmap to ByteBuffer for model input
// private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
// val byteBuffer = ByteBuffer.allocateDirect(4 * inputImageWidth * inputImageHeight)
// byteBuffer.order(ByteOrder.nativeOrder())
//
// val pixels = IntArray(inputImageWidth * inputImageHeight)
// bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
//
// for (pixel in pixels) {
// val grayscale = Color.red(pixel) // Since it's grayscale, we only use the red channel
// byteBuffer.putFloat(grayscale / 255.0f)
// }
//
// return byteBuffer
// }
//
// // Load model file from assets
// private fun loadModelFile(assetManager: AssetManager, modelPath: String): ByteBuffer {
// val fileDescriptor = assetManager.openFd(modelPath)
// val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
// val fileChannel = inputStream.channel
// val startOffset = fileDescriptor.startOffset
// val declaredLength = fileDescriptor.declaredLength
// return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
// }
//
//
//}
//
//@Composable
//fun ImagePickerScreen(viewModel: MainViewModel) {
// var selectedImageUri by remember { mutableStateOf<Uri?>(null) }
// val context = LocalContext.current
//
// val imagePickerLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// uri?.let {
// selectedImageUri = it
// viewModel.recognizeText(it)
// }
// }
//
// Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
// selectedImageUri?.let { uri ->
// val bitmap = BitmapFactory.decodeStream(context.contentResolver.openInputStream(uri))
// bitmap?.let {
// Image(bitmap = it.asImageBitmap(), contentDescription = null, modifier = Modifier.size(200.dp))
// }
// }
// Spacer(modifier = Modifier.height(16.dp))
// Button(onClick = { imagePickerLauncher.launch("image/*") }) {
// Text(text = "Pick Image")
// }
// }
//}
//

View File

@@ -0,0 +1,49 @@
package com.sminnovations.prodtrack.screen.navigation
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.sminnovations.prodtrack.screen.navigation.data.BottomNavItem
@Composable
fun BottomNavigationBar(
navController: NavController,
items: List<BottomNavItem>,
) {
NavigationBar {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
NavigationBarItem(
icon = {
BottomNavAnimatedIcon(
isSelected = currentRoute == item.route,
icon = item.icon,
selectedIcon = item.selectedIcon,
contentDescription = item.label
)
},
label = { Text(text = item.label) },
alwaysShowLabel = true,
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
})
}
}
}

View File

@@ -0,0 +1,33 @@
package com.sminnovations.prodtrack.screen.navigation
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
@Composable
fun BottomNavAnimatedIcon(
isSelected: Boolean,
icon: ImageVector,
selectedIcon: ImageVector,
contentDescription: String?
) {
AnimatedContent(
targetState = isSelected,
transitionSpec = {
fadeIn(animationSpec = tween(300)) togetherWith fadeOut(animationSpec = tween(300))
}, label = "animated icon"
) { targetState ->
Icon(
imageVector = if (targetState) selectedIcon else icon,
contentDescription = contentDescription,
tint = if (targetState) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
)
}
}

View File

@@ -0,0 +1,41 @@
package com.sminnovations.prodtrack.screen.navigation
import android.net.wifi.hotspot2.pps.HomeSp
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navDeepLink
import com.sminnovations.prodtrack.screen.history.HistoryScreen
import com.sminnovations.prodtrack.screen.navigation.data.BottomNavItem
import com.sminnovations.prodtrack.screen.settings.SettingsScreen
@RequiresApi(Build.VERSION_CODES.O_MR1)
@Composable
fun Navigation(
navController: NavHostController,
items: List<BottomNavItem>,
// homeViewModel: HomeViewModel,
// historyViewModel: HistoryViewModel,
) {
NavHost(navController, startDestination = items[0].route) {
composable(items[0].route, deepLinks = listOf(navDeepLink { uriPattern= "https://www.smi.com/home" })){
HomeScreen()
}
composable(items[1].route) {
HistoryScreen()
}
composable(items[2].route) {
SettingsScreen()
}
}
}
@Composable
fun HomeScreen(){
}

View File

@@ -0,0 +1,23 @@
package com.sminnovations.prodtrack.screen.navigation.data
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.AddCircle
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.ui.graphics.vector.ImageVector
data class BottomNavItem(
val icon: ImageVector,
val selectedIcon: ImageVector,
val label: String,
val route: String
)
val navItems = listOf(
BottomNavItem(Icons.Outlined.Search, Icons.Filled.Search, "Scan", "scan"),
BottomNavItem(Icons.Outlined.AddCircle, Icons.Filled.AddCircle, "Records", "records"),
BottomNavItem(Icons.Outlined.Settings, Icons.Filled.Settings, "Settings", "settings")
)

View File

@@ -0,0 +1,44 @@
package com.sminnovations.prodtrack.screen.settings
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.OutlinedCard
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.sminnovations.prodtrack.common.data.dataStore.SettingsData
import com.sminnovations.prodtrack.screen.settings.ui.subSetting.AboutSettings
import com.sminnovations.prodtrack.screen.settings.ui.subSetting.Appearance
import com.sminnovations.prodtrack.screen.settings.ui.subSetting.Developer_settings
@Composable
fun SettingsScreen(){
val context = LocalContext.current
val dataStore = SettingsData(context)
val scrollState = rememberScrollState()
val devMode = dataStore.getDevModeToggle.collectAsState(initial = false)
OutlinedCard (modifier = Modifier
.padding(10.dp)
.wrapContentSize()
){
Column(
modifier = Modifier
.verticalScroll(scrollState)
.fillMaxSize(),
) {
Appearance(context = context)
AboutSettings(context =context )
AnimatedVisibility(visible = devMode.value!!) {
Developer_settings(context)
}
}
}
}

View File

@@ -0,0 +1,125 @@
package com.sminnovations.prodtrack.screen.settings.ui.settingsComponents
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable
fun ClickableCard(
title:String,
subtitle:String,
onClick: (Boolean) -> Unit,
icon: ImageVector
) {
Row(
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceContainer)
.clickable{ onClick(true)}
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column(
modifier = Modifier
.weight(1f)
) {
Text(
text = title,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
@OptIn( ExperimentalFoundationApi::class)
@Composable
fun CombinedClickableCard(
title:String,
subtitle:String,
onClick: (Boolean) -> Unit,
onDoubleClick: ((Boolean) -> Unit?)? =null,
onLongClick: ((Boolean) -> Unit?)? =null,
icon: ImageVector
) {
val interactionSource = remember { MutableInteractionSource() }
Row(
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceContainer)
.combinedClickable (
onClick = { onClick(true) },
onDoubleClick = {
if (onDoubleClick != null) {
onDoubleClick(true)
}
},
onLongClick = {
if (onLongClick != null) {
onLongClick(true)
}
}
)
.fillMaxWidth()
.padding(16.dp)
.indication(interactionSource,null)
,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column(
modifier = Modifier
.weight(1f)
) {
Text(
text = title,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodyMedium,
)
}
}
}

View File

@@ -0,0 +1,126 @@
package com.sminnovations.prodtrack.screen.settings.ui.settingsComponents
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.sminnovations.prodtrack.R
@Composable
fun RadioDialogue(
showDialog:Boolean,
listOptions:List<String>,
headText:String,
selectedOption:String,
onClose: () -> Unit,
gotOption:(String)->Unit
) {
if (showDialog) {
Dialog(
onDismissRequest = { onClose() },
DialogProperties(
dismissOnBackPress = true,
dismissOnClickOutside = true,
usePlatformDefaultWidth = true
)
) {
OutlinedCard(
modifier = Modifier
.wrapContentSize()
.clickable { onClose() },
) {
BackHandler(onBack = onClose)
Box(
modifier = Modifier
.background(MaterialTheme.colorScheme.surfaceContainer)
) {
Column {
Text(
text = headText,
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(20.dp)
)
LazyColumn(
modifier =
if (listOptions.size <=5) {
Modifier
.padding(20.dp, 20.dp, 20.dp, 0.dp)
.wrapContentSize()
}else {
Modifier
.padding(20.dp, 20.dp, 20.dp, 0.dp)
.height(400.dp)
},
)
{
items(listOptions) { option ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
gotOption(option)
onClose()
},
verticalAlignment = Alignment.CenterVertically
) {
RadioButtonComponent(value = selectedOption, onValueChange = {
gotOption(option)
onClose()
}, placeholder = option)
}
Spacer(modifier = Modifier.height(10.dp))
}
}
Row {
Spacer(Modifier.weight(1f))
Text(
text = stringResource(R.string.cancel),
modifier = Modifier
.padding(0.dp, 0.dp, 40.dp, 20.dp)
.clickable { onClose() },
color = MaterialTheme.colorScheme.primary
)
}
}
}
}
}
}
}
@Composable
fun RadioButtonComponent(value: String, onValueChange: (String) -> Unit, placeholder: String) {
RadioButton(
selected = value == placeholder,
onClick = { onValueChange(placeholder) }
)
Text(
text = placeholder,
modifier = Modifier.padding(start = 8.dp)
)
}

View File

@@ -0,0 +1,40 @@
package com.sminnovations.prodtrack.screen.settings.ui.settingsComponents
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.vector.ImageVector
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.ClickableCard
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.RadioDialogue
@Composable
fun RadioCard(
title:String,
subtitle:String,
icon: ImageVector,
radioHeader:String,
gotOption:(String)->Unit,
listOptions:List<String>,
selectedOption:String
) {
var showDialog by remember { mutableStateOf(false) }
ClickableCard(
title = title,
subtitle = subtitle,
onClick = {
showDialog = true
},
icon = icon
)
RadioDialogue(
showDialog = showDialog,
listOptions =listOptions,
headText =radioHeader ,
selectedOption = selectedOption,
onClose = { showDialog=false},
gotOption = gotOption
)
}

View File

@@ -0,0 +1,71 @@
package com.sminnovations.prodtrack.screen.settings.ui.settingsComponents
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable
fun SwitchCard(
checkedValue:Boolean,
title:String,
subtitle:String,
onSwitchChange: (Boolean) -> Unit,
icon: ImageVector
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceContainer)
.toggleable(
value = checkedValue,
enabled = true,
role = Role.Switch,
onValueChange = onSwitchChange
)
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = title,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.bodyLarge
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodyMedium,
)
}
Switch(
checked = checkedValue,
onCheckedChange = onSwitchChange
)
}
}

View File

@@ -0,0 +1,78 @@
package com.sminnovations.prodtrack.screen.settings.ui.subSetting
import android.annotation.SuppressLint
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.sminnovations.prodtrack.R
import com.sminnovations.prodtrack.common.data.dataStore.SettingsData
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.ClickableCard
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.CombinedClickableCard
import kotlinx.coroutines.launch
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun AboutSettings(context: Context)
{
val scope = rememberCoroutineScope()
val dataStore = SettingsData(context)
var clickCount by remember { mutableIntStateOf(0) }
if (clickCount > 5) {
scope.launch {
dataStore.setDevMode(true)
}
clickCount = 0
Toast.makeText(context, "Developer mode is On : Scroll Down", Toast.LENGTH_SHORT).show()
}
Column(modifier = Modifier.padding(10.dp,0.dp,10.dp,10.dp)) {
Text(
text = "About",
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.Bold,
modifier = Modifier
.align(Alignment.Start)
.padding(14.dp),
color = MaterialTheme.colorScheme.primary
)
OutlinedCard{
ClickableCard(
title = "Version",
subtitle = "1.0.04",
onClick = {
clickCount++
},
icon = ImageVector.vectorResource(id = R.drawable.round_info_24)
)
CombinedClickableCard(
title = "Developer",
subtitle = "SM Innovations Pvt Ltd",
onClick = {
}, onDoubleClick={
},
icon = ImageVector.vectorResource(id = R.drawable.round_logo_dev_24)
)
}
}
}

View File

@@ -0,0 +1,90 @@
package com.sminnovations.prodtrack.screen.settings.ui.subSetting
import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.sminnovations.prodtrack.R
import com.sminnovations.prodtrack.common.data.dataStore.SettingsData
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.RadioCard
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.SwitchCard
import kotlinx.coroutines.launch
@Composable
fun Appearance (context: Context) {
val scope = rememberCoroutineScope()
val dataStore = SettingsData(context)
val dynamicColorsThemeValue = dataStore.getDynamicColorThemeValue.collectAsState(true)
val blackThemeValue = dataStore.getBlackThemeValue.collectAsState(initial = false)
val theme = dataStore.getThemeMode.collectAsState(initial = "System default")
Column(modifier = Modifier.padding(10.dp, 0.dp, 10.dp, 0.dp)) {
Text(
text = stringResource(R.string.appearance),
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.Bold,
modifier = Modifier
.align(Alignment.Start)
.padding(14.dp),
color = MaterialTheme.colorScheme.primary
)
OutlinedCard{
RadioCard(
title = stringResource(R.string.theme),
subtitle =theme.value ,
icon = ImageVector.vectorResource(id = R.drawable.round_color_lens_24),
radioHeader = stringResource(R.string.select_theme_mode) ,
gotOption = {
scope.launch {
dataStore.saveThemeMode(it)
}
},
listOptions = listOf("Light", "Dark", "System default"),
selectedOption =theme.value
)
SwitchCard(
checkedValue = dynamicColorsThemeValue.value!!,
title = stringResource(R.string.dynamic_theme),
subtitle = stringResource(R.string.theme_based_on_wallpaper_color),
onSwitchChange =
{
scope.launch {
dataStore.setDynamicColorThemeValue(it)
}
},
icon = ImageVector.vectorResource(id = R.drawable.round_auto_mode_24)
)
SwitchCard(
checkedValue = blackThemeValue.value!!,
title = stringResource(R.string.black_theme),
subtitle = stringResource(R.string.black_theme_description),
onSwitchChange =
{
scope.launch {
dataStore.setBlackThemeValue(it)
}
},
icon = ImageVector.vectorResource(id = R.drawable.round_contrast_24)
)
}
}
}

View File

@@ -0,0 +1,77 @@
package com.sminnovations.prodtrack.screen.settings.ui.subSetting
import android.content.Context
import android.os.Build
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.sminnovations.prodtrack.R
import com.sminnovations.prodtrack.common.data.dataStore.SettingsData
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.ClickableCard
import com.sminnovations.prodtrack.screen.settings.ui.settingsComponents.SwitchCard
import kotlinx.coroutines.launch
@Composable
fun Developer_settings(context: Context) {
val scope = rememberCoroutineScope()
val dataStore = SettingsData(context)
val devMode = dataStore.getDevModeToggle.collectAsState(initial = false)
Column(modifier = Modifier.padding(10.dp,0.dp,10.dp,10.dp)) {
Text(
text = stringResource(R.string.developer_options),
style = MaterialTheme.typography.labelLarge,
fontWeight = FontWeight.Bold,
modifier = Modifier
.align(Alignment.Start)
.padding(14.dp),
color = MaterialTheme.colorScheme.primary
)
OutlinedCard {
SwitchCard(
checkedValue = devMode.value!!,
title = "Developer mode",
subtitle = "To toggle developer mode",
onSwitchChange = {
scope.launch {
dataStore.setDevMode(it)
}
},
icon = ImageVector.vectorResource(R.drawable.round_developer_mode_24)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ClickableCard(
title = "Clear the Database",
subtitle = "you will loose every thing 😨",
onClick = {
},
icon = ImageVector.vectorResource(R.drawable.round_delete_sweep_24)
)
}
ClickableCard(
title = "Change the language",
subtitle = "Current language :English",
onClick = {
Toast.makeText(context, "Not implemented yet", Toast.LENGTH_SHORT).show()
},
icon = ImageVector.vectorResource(R.drawable.round_translate_24)
)
}
}
}

View File

@@ -0,0 +1,69 @@
package com.sminnovations.prodtrack.tens
import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
@Composable
fun MainScreen24(viewModel: MainViewModel) {
val context = LocalContext.current
var imageUri by remember { mutableStateOf<Uri?>(null) }
var recognizedNumber by remember { mutableStateOf<Int?>(null) }
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent()
) { uri: Uri? ->
imageUri = uri
uri?.let {
recognizedNumber = viewModel.recognizeNumber(it)
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Button(onClick = { launcher.launch("image/*") }) {
Text("Pick Image")
}
Spacer(modifier = Modifier.height(16.dp))
imageUri?.let {
// Display the selected image here
// You may want to use a library like Coil or Glide for image loading
}
Spacer(modifier = Modifier.height(16.dp))
recognizedNumber?.let {
Text("Recognized Number: $it")
LaunchedEffect(it) {
Toast.makeText(context, "Recognized Number: $it", Toast.LENGTH_SHORT).show()
}
}
}
}

View File

@@ -0,0 +1,102 @@
package com.sminnovations.prodtrack.tens
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import androidx.lifecycle.ViewModel
import com.sminnovations.prodtrack.MyApplication
import org.tensorflow.lite.Interpreter
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.FileChannel
class MainViewModel() : ViewModel() {
val context = MyApplication.instance
private var tflite: Interpreter? = null
private val imageSizeX = 96
private val imageSizeY = 96
private val numChannels = 3
private val numClasses = 11 // Assuming 10 classes for digits 0-9
init {
try {
val modelBuffer = loadModelFile("your_model.tflite")
tflite = Interpreter(modelBuffer)
Log.d("MainViewModel", "TFLite model loaded successfully")
} catch (e: Exception) {
Log.e("MainViewModel", "Error loading TFLite model", e)
}
}
fun recognizeNumber(imageUri: Uri): Int? {
return try {
val bitmap = loadAndPreprocessImage(imageUri)
val inputBuffer = convertBitmapToByteBuffer(bitmap)
val outputBuffer = Array(1) { FloatArray(numClasses) }
val key = tflite?.signatureKeys?.get(0)
tflite?.run(inputBuffer, outputBuffer)
Log.d("hello",key.toString())
val a = tflite?.getSignatureOutputs(key)
Log.d("aaa",a.toString())
Log.d("MainViewModel", "Model output: ${outputBuffer[0].contentToString()}")
// Find the index of the maximum value in the output
val maxIndex = outputBuffer[0].indices.maxByOrNull { outputBuffer[0][it] } ?: -1
Log.d("MainViewModel", "Recognized digit: $maxIndex")
maxIndex
} catch (e: Exception) {
Log.e("MainViewModel", "Error during number recognition", e)
null
}
}
private fun loadAndPreprocessImage(uri: Uri): Bitmap {
val inputStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
inputStream?.close()
val scaledBitmap = Bitmap.createScaledBitmap(bitmap, imageSizeX, imageSizeY, true)
Log.d("MainViewModel", "Image preprocessed: ${scaledBitmap.width}x${scaledBitmap.height}")
return scaledBitmap
}
private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
val byteBuffer = ByteBuffer.allocateDirect(4 * imageSizeX * imageSizeY * numChannels)
byteBuffer.order(ByteOrder.nativeOrder())
val intValues = IntArray(imageSizeX * imageSizeY)
bitmap.getPixels(intValues, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
var pixel = 0
for (i in 0 until imageSizeX) {
for (j in 0 until imageSizeY) {
val value = intValues[pixel++]
byteBuffer.putFloat(((value shr 16 and 0xFF) - 127.5f) / 127.5f)
byteBuffer.putFloat(((value shr 8 and 0xFF) - 127.5f) / 127.5f)
byteBuffer.putFloat(((value and 0xFF) - 127.5f) / 127.5f)
}
}
Log.d("MainViewModel", "ByteBuffer size: ${byteBuffer.capacity()}")
return byteBuffer
}
private fun loadModelFile(modelPath: String): ByteBuffer {
val assetManager = context.assets
val fileDescriptor = assetManager.openFd(modelPath)
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel
val startOffset = fileDescriptor.startOffset
val declaredLength = fileDescriptor.declaredLength
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}
override fun onCleared() {
super.onCleared()
tflite?.close()
}
}

View File

@@ -0,0 +1,75 @@
package com.sminnovations.prodtrack.tenserflow
import android.content.Context
import android.graphics.Bitmap
import org.tensorflow.lite.Interpreter
import org.tensorflow.lite.flex.FlexDelegate
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
class SevenSegmentClassifier(private val context: Context) {
private var interpreter: Interpreter? = null
init {
try {
// Load model file
val modelFile = loadModelFile()
val options = Interpreter.Options()
options.addDelegate(FlexDelegate()) // Add Flex Delegate support if needed
interpreter = Interpreter(modelFile, options)
} catch (e: Exception) {
e.printStackTrace()
}
}
// Load the TFLite model from assets
private fun loadModelFile(): MappedByteBuffer {
val fileDescriptor = context.assets.openFd("seven_segment_model.tflite")
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel
val startOffset = fileDescriptor.startOffset
val declaredLength = fileDescriptor.declaredLength
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}
// Classify the image and return the result
fun classifyImage(bitmap: Bitmap): Float {
// Resize the bitmap to match the input size of the model
val resizedBitmap = Bitmap.createScaledBitmap(bitmap, 28, 28, true)
val inputBuffer = convertBitmapToByteBuffer(resizedBitmap)
// Prepare output buffer for the result
val output = Array(1) { FloatArray(12) }
// Run inference
interpreter?.run(inputBuffer, output)
// Get the result from the output buffer
return output[0][0] // Assuming first output is the detected number
}
// Convert bitmap to ByteBuffer as input for TensorFlow Lite model
private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
val byteBuffer = ByteBuffer.allocateDirect(4 * 28 * 28) // 4 bytes per float
byteBuffer.order(ByteOrder.nativeOrder())
val intValues = IntArray(28 * 28)
bitmap.getPixels(intValues, 0, 28, 0, 0, 28, 28)
for (pixelValue in intValues) {
val normalizedPixelValue = ((pixelValue shr 16 and 0xFF) - 127.5f) / 127.5f
byteBuffer.putFloat(normalizedPixelValue)
}
return byteBuffer
}
// Close the interpreter
fun close() {
interpreter?.close()
}
}

View File

@@ -0,0 +1,91 @@
package com.sminnovations.prodtrack.tenserflow
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.LocalLifecycleOwner
import java.io.File
@Composable
fun CameraPreview(onImageCaptured: (Bitmap) -> Unit) {
val context = LocalContext.current
val lifeycleowner = androidx.compose.ui.platform.LocalLifecycleOwner.current
val previewView = remember { PreviewView(context) }
val imageCapture = remember { ImageCapture.Builder().build() }
// Create a Box layout to stack the preview and the capture button
Box(
contentAlignment = Alignment.BottomCenter,
modifier = Modifier.height(450.dp)
) {
// Camera Preview
AndroidView(
factory = { previewView },
modifier = Modifier.height(600.dp)
) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(
lifeycleowner,
cameraSelector,
preview,
imageCapture
)
}, ContextCompat.getMainExecutor(context))
}
// Capture Button
Button(
onClick = {
val file = File(context.cacheDir, "temp_image.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(file).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e("CameraX", "Image capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
onImageCaptured(bitmap)
}
}
)
},
modifier = Modifier
.padding(16.dp)
.align(Alignment.BottomCenter)
) {
Text("Capture Image")
}
}
}

View File

@@ -0,0 +1,54 @@
package com.sminnovations.prodtrack.tenserflow
import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
@Composable
fun SevenSegmentScreen() {
val context = LocalContext.current
val classifier = remember { SevenSegmentClassifier(context) }
var detectedNumber by remember { mutableStateOf(0.000f) }
Column(
Modifier
.fillMaxSize()
.padding(16.dp)
) {
CameraPreview { bitmap ->
detectedNumber = classifier.classifyImage(bitmap)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Detected Number: $detectedNumber",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
// Clean up the classifier when the composable is destroyed
DisposableEffect(Unit) {
onDispose {
classifier.close()
}
}
}

View File

@@ -0,0 +1,35 @@
package com.sminnovations.prodtrack.ui.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.graphics.ColorUtils
object ThemeColorSchemes {
private val BlackContrastColor = Color.Black
private const val BLACK_BLEND_RATIO = 0.4f
fun toBlackColorScheme(colorScheme: ColorScheme) = colorScheme.copy(
primaryContainer = convertDarkToBlack(colorScheme.primaryContainer),
secondaryContainer = convertDarkToBlack(colorScheme.secondaryContainer),
tertiaryContainer = convertDarkToBlack(colorScheme.tertiaryContainer),
background = BlackContrastColor,
surface = convertDarkToBlack(colorScheme.surface),
surfaceVariant = convertDarkToBlack(colorScheme.surfaceVariant),
surfaceTint = convertDarkToBlack(colorScheme.surfaceTint),
surfaceContainer = convertDarkToBlack(colorScheme.surface),
)
private fun convertDarkToBlack(color: Color): Color {
val argb = ColorUtils.blendARGB(
BlackContrastColor.toArgb(),
color.toArgb(),
BLACK_BLEND_RATIO,
)
return Color(argb)
}
}

View File

@@ -9,7 +9,13 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
import com.sminnovations.prodtrack.common.data.dataStore.SettingsData
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
@@ -35,21 +41,51 @@ private val LightColorScheme = lightColorScheme(
@Composable
fun ProductionTrackerTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val context = LocalContext.current
val dataStore = SettingsData(context)
val dynamicColorsThemeValue = dataStore.getDynamicColorThemeValue.collectAsState(true)
val blackThemeValue = dataStore.getBlackThemeValue.collectAsState(initial = false)
val theme = dataStore.getThemeMode.collectAsState(initial = "System default")
var darkTheme: Boolean = isSystemInDarkTheme()
when (theme.value) {
"Light" -> darkTheme = false
"Dark" -> darkTheme = true
"System default" -> darkTheme = isSystemInDarkTheme()
else -> {}
}
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
dynamicColorsThemeValue.value!! && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
if (darkTheme && blackThemeValue.value == true) {
ThemeColorSchemes.toBlackColorScheme(
dynamicDarkColorScheme(context)
)
} else if (darkTheme) {
dynamicDarkColorScheme(context)
} else {
dynamicLightColorScheme(context)
}
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.background.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,

View File

@@ -0,0 +1,34 @@
package com.sminnovations.prodtrack.utils
import android.content.Context
import android.content.res.AssetManager
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
object FileUtil {
fun saveFile(context: Context, fileName: String, data: ByteArray): File {
val file = File(context.filesDir, fileName)
file.writeBytes(data)
return file
}
fun readFile(context: Context, fileName: String): ByteArray? {
val file = File(context.filesDir, fileName)
return if (file.exists()) file.readBytes() else null
}
fun loadModelFile(assetManager: AssetManager, modelFileName: String): ByteBuffer {
val fileDescriptor = assetManager.openFd(modelFileName)
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel
val startOffset = fileDescriptor.startOffset
val declaredLength = fileDescriptor.declaredLength
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
}
}

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M18.06,2.83c-1.15,-0.77 -2.46,-1.32 -3.86,-1.61C13.58,1.1 13,1.57 13,2.21v0c0,0.46 0.31,0.88 0.76,0.97c1.17,0.23 2.26,0.7 3.21,1.34c0.39,0.26 0.9,0.19 1.23,-0.14l0,0C18.66,3.93 18.59,3.18 18.06,2.83z"/>
<path android:fillColor="@android:color/white" android:pathData="M11,2.21L11,2.21c0,-0.64 -0.58,-1.11 -1.2,-0.99c-1.4,0.29 -2.71,0.84 -3.86,1.61c-0.52,0.35 -0.59,1.1 -0.15,1.54l0,0c0.33,0.33 0.84,0.4 1.23,0.14c0.96,-0.64 2.04,-1.1 3.21,-1.34C10.69,3.09 11,2.67 11,2.21z"/>
<path android:fillColor="@android:color/white" android:pathData="M4.38,5.79L4.38,5.79C3.93,5.34 3.18,5.42 2.84,5.94C2.07,7.09 1.51,8.39 1.23,9.8C1.1,10.42 1.58,11 2.21,11h0c0.46,0 0.88,-0.31 0.97,-0.76c0.23,-1.17 0.7,-2.26 1.34,-3.22C4.77,6.64 4.7,6.12 4.38,5.79z"/>
<path android:fillColor="@android:color/white" android:pathData="M21.79,11L21.79,11c0.63,0 1.11,-0.58 0.98,-1.2c-0.29,-1.4 -0.84,-2.7 -1.61,-3.86c-0.35,-0.52 -1.1,-0.6 -1.54,-0.15l0,0c-0.33,0.33 -0.4,0.84 -0.14,1.23c0.64,0.96 1.1,2.05 1.34,3.22C20.91,10.69 21.33,11 21.79,11z"/>
<path android:fillColor="@android:color/white" android:pathData="M8,12.46l2.44,1.11L11.54,16c0.18,0.39 0.73,0.39 0.91,0l1.11,-2.44L16,12.46c0.39,-0.18 0.39,-0.73 0,-0.91l-2.44,-1.11L12.46,8c-0.18,-0.39 -0.73,-0.39 -0.91,0l-1.11,2.44L8,11.54C7.61,11.72 7.61,12.28 8,12.46z"/>
<path android:fillColor="@android:color/white" android:pathData="M12,21c-3.11,0 -5.85,-1.59 -7.46,-4H6c0.55,0 1,-0.45 1,-1v0c0,-0.55 -0.45,-1 -1,-1H2c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h0c0.55,0 1,-0.45 1,-1v-1.7c1.99,2.84 5.27,4.7 9,4.7c4.45,0 8.27,-2.64 10,-6.43c0.26,-0.57 -0.08,-1.25 -0.69,-1.39l0,0c-0.45,-0.1 -0.93,0.11 -1.12,0.54C18.77,18.83 15.64,21 12,21z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,22c5.52,0 10,-4.48 10,-10S17.52,2 12,2S2,6.48 2,12S6.48,22 12,22zM13,4.07c3.94,0.49 7,3.85 7,7.93s-3.05,7.44 -7,7.93V4.07z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M16,16h2c0.55,0 1,0.45 1,1s-0.45,1 -1,1h-2c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1zM16,8h5c0.55,0 1,0.45 1,1s-0.45,1 -1,1h-5c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1zM16,12h4c0.55,0 1,0.45 1,1s-0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1zM3,18c0,1.1 0.9,2 2,2h6c1.1,0 2,-0.9 2,-2L13,8L3,8v10zM13,5h-2l-0.71,-0.71c-0.18,-0.18 -0.44,-0.29 -0.7,-0.29L6.41,4c-0.26,0 -0.52,0.11 -0.7,0.29L5,5L3,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h10c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M7,5h10v1c0,0.55 0.45,1 1,1s1,-0.45 1,-1L19,3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v3c0,0.55 0.45,1 1,1s1,-0.45 1,-1L7,5zM16.12,15.88l3.17,-3.17c0.39,-0.39 0.39,-1.02 0,-1.41l-3.17,-3.17c-0.39,-0.39 -1.03,-0.39 -1.42,0 -0.39,0.39 -0.39,1.02 0,1.41L17.17,12l-2.47,2.47c-0.39,0.39 -0.39,1.02 0,1.41 0.39,0.39 1.03,0.39 1.42,0zM9.29,14.46L6.83,12l2.46,-2.46c0.39,-0.39 0.39,-1.02 0,-1.41 -0.39,-0.39 -1.03,-0.39 -1.42,0L4.7,11.3c-0.39,0.39 -0.39,1.02 0,1.41l3.17,3.17c0.39,0.39 1.03,0.39 1.42,0 0.4,-0.39 0.39,-1.03 0,-1.42zM17,19L7,19v-1c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v3c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-3c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v1z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,17c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v4c0,0.55 -0.45,1 -1,1zM13,9h-2L11,7h2v2z"/>
</vector>

View File

@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM7.68,14.98H6V9h1.71c1.28,0 1.71,1.03 1.71,1.71l0,2.56C9.42,13.95 9,14.98 7.68,14.98zM12.38,11.46v1.07h-1.18v1.39h1.93v1.07h-2.25c-0.4,0.01 -0.74,-0.31 -0.75,-0.71V9.75c-0.01,-0.4 0.31,-0.74 0.71,-0.75h2.28l0,1.07h-1.92v1.39H12.38zM16.88,14.23c-0.48,1.11 -1.33,0.89 -1.71,0L13.77,9h1.18l1.07,4.11L17.09,9h1.18L16.88,14.23z"/>
<path android:fillColor="@android:color/white" android:pathData="M7.77,10.12H7.14v3.77h0.63c0.14,0 0.28,-0.05 0.42,-0.16c0.14,-0.1 0.21,-0.26 0.21,-0.47v-2.52c0,-0.21 -0.07,-0.37 -0.21,-0.47C8.05,10.17 7.91,10.12 7.77,10.12z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12.65,15.67c0.14,-0.36 0.05,-0.77 -0.23,-1.05l-2.09,-2.06 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53h1.94c0.54,0 0.99,-0.45 0.99,-0.99v-0.02c0,-0.54 -0.45,-0.99 -0.99,-0.99L10,4L10,3c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v1L1.99,4c-0.54,0 -0.99,0.45 -0.99,0.99 0,0.55 0.45,0.99 0.99,0.99h10.18C11.5,7.92 10.44,9.75 9,11.35c-0.81,-0.89 -1.49,-1.86 -2.06,-2.88 -0.16,-0.29 -0.45,-0.47 -0.78,-0.47 -0.69,0 -1.13,0.75 -0.79,1.35 0.63,1.13 1.4,2.21 2.3,3.21L3.3,16.87c-0.4,0.39 -0.4,1.03 0,1.42 0.39,0.39 1.02,0.39 1.42,0L9,14l2.02,2.02c0.51,0.51 1.38,0.32 1.63,-0.35zM17.5,10c-0.6,0 -1.14,0.37 -1.35,0.94l-3.67,9.8c-0.24,0.61 0.22,1.26 0.87,1.26 0.39,0 0.74,-0.24 0.88,-0.61l0.89,-2.39h4.75l0.9,2.39c0.14,0.36 0.49,0.61 0.88,0.61 0.65,0 1.11,-0.65 0.88,-1.26l-3.67,-9.8c-0.22,-0.57 -0.76,-0.94 -1.36,-0.94zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
</vector>

View File

@@ -1,3 +1,15 @@
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">Production Tracker</string>
<string name="cancel" tools:ignore="ButtonCase">CANCEL</string>
<string name="dynamic_theme">Dynamic Theme</string>
<string name="developer_options">Developer Options</string>
<string name="ok">OK</string>
<string name="stats">Stats</string>
<string name="theme_based_on_wallpaper_color">Theme based on wallpaper color</string>
<string name="theme">Theme</string>
<string name="appearance">Appearance</string>
<string name="select_theme_mode">Select Theme Mode</string>
<string name="black_theme">Black Theme</string>
<string name="black_theme_description">Pitch Black Theme</string>
</resources>

View File

@@ -2,4 +2,5 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
id ("com.google.dagger.hilt.android")version "2.50" apply false
}

View File

@@ -8,6 +8,10 @@ espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.6"
activityCompose = "1.9.2"
composeBom = "2024.04.01"
playServicesMlkitTextRecognitionCommon = "19.1.0"
playServicesMlkitTextRecognition = "19.0.1"
litertGpu = "1.0.1"
litert = "1.0.1"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -24,6 +28,10 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
play-services-mlkit-text-recognition-common = { group = "com.google.android.gms", name = "play-services-mlkit-text-recognition-common", version.ref = "playServicesMlkitTextRecognitionCommon" }
play-services-mlkit-text-recognition = { group = "com.google.android.gms", name = "play-services-mlkit-text-recognition", version.ref = "playServicesMlkitTextRecognition" }
litert-gpu = { group = "com.google.ai.edge.litert", name = "litert-gpu", version.ref = "litertGpu" }
litert = { group = "com.google.ai.edge.litert", name = "litert", version.ref = "litert" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }