Compare commits
4 Commits
dev
...
dev-ml-imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96c7fea6c7 | ||
|
|
09b46dd318 | ||
|
|
adb5e61662 | ||
|
|
0eb4b3433b |
22
.idea/androidTestResultsUserPreferences.xml
generated
Normal file
22
.idea/androidTestResultsUserPreferences.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidTestResultsUserPreferences">
|
||||||
|
<option name="androidTestResultsTableState">
|
||||||
|
<map>
|
||||||
|
<entry key="120167027">
|
||||||
|
<value>
|
||||||
|
<AndroidTestResultsTableState>
|
||||||
|
<option name="preferredColumnWidths">
|
||||||
|
<map>
|
||||||
|
<entry key="Duration" value="90" />
|
||||||
|
<entry key="Pixel_2_API_27" value="120" />
|
||||||
|
<entry key="Tests" value="360" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</AndroidTestResultsTableState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
.idea/deploymentTargetSelector.xml
generated
11
.idea/deploymentTargetSelector.xml
generated
@@ -4,6 +4,17 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<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>
|
||||||
|
<SelectionState runConfigName="Tests in 'com.sminnovations.prodtrack'">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
|||||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||||
|
|||||||
11
.idea/other.xml
generated
11
.idea/other.xml
generated
@@ -25,6 +25,17 @@
|
|||||||
<option name="screenX" value="1080" />
|
<option name="screenX" value="1080" />
|
||||||
<option name="screenY" value="2160" />
|
<option name="screenY" value="2160" />
|
||||||
</PersistentDeviceSelectionData>
|
</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>
|
<PersistentDeviceSelectionData>
|
||||||
<option name="api" value="31" />
|
<option name="api" value="31" />
|
||||||
<option name="brand" value="samsung" />
|
<option name="brand" value="samsung" />
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
id("kotlin-kapt")
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.jetbrains.kotlin.android)
|
alias(libs.plugins.jetbrains.kotlin.android)
|
||||||
}
|
}
|
||||||
@@ -29,6 +32,11 @@ android {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sourceSets {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
@@ -42,6 +50,9 @@ android {
|
|||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion = "1.5.1"
|
kotlinCompilerExtensionVersion = "1.5.1"
|
||||||
}
|
}
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
}
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
@@ -59,6 +70,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
|
implementation(libs.litert)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
@@ -68,8 +80,42 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
|
|
||||||
|
|
||||||
//firebase dependency
|
//firebase
|
||||||
implementation(platform("com.google.firebase:firebase-bom:33.2.0"))
|
implementation(platform("com.google.firebase:firebase-bom:33.2.0"))
|
||||||
implementation("com.google.firebase:firebase-analytics")
|
implementation("com.google.firebase:firebase-analytics")
|
||||||
implementation ("com.google.firebase:firebase-firestore-ktx:23.0.3")
|
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")
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-feature android:name="android.hardware.camera.any" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".MyApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
|||||||
BIN
app/src/main/assets/7seg_classifier.tflite
Normal file
BIN
app/src/main/assets/7seg_classifier.tflite
Normal file
Binary file not shown.
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,47 +1,64 @@
|
|||||||
package com.sminnovations.prodtrack
|
package com.sminnovations.prodtrack
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
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.foundation.layout.padding
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
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
|
import com.sminnovations.prodtrack.ui.theme.ProductionTrackerTheme
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O_MR1)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
|
val viewModel: MainViewModel = viewModel()
|
||||||
ProductionTrackerTheme {
|
ProductionTrackerTheme {
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
MainScreen24(viewModel)
|
||||||
Greeting(
|
|
||||||
name = "Android",
|
|
||||||
modifier = Modifier.padding(innerPadding)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O_MR1)
|
||||||
@Composable
|
@Composable
|
||||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
fun MainScreen(
|
||||||
Text(
|
) {
|
||||||
text = "Hello $name!",
|
val navController = rememberNavController()
|
||||||
modifier = modifier
|
|
||||||
|
Scaffold(
|
||||||
|
bottomBar = {
|
||||||
|
BottomNavigationBar(navController, navItems)
|
||||||
|
},
|
||||||
|
content = { padding ->
|
||||||
|
Box(modifier = Modifier.padding(padding)) {
|
||||||
|
Navigation(navController = navController, navItems)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
//
|
||||||
@Preview(showBackground = true)
|
//@Composable
|
||||||
@Composable
|
//fun MainScreen2(context: Context) {
|
||||||
fun GreetingPreview() {
|
// val model = remember { TFLiteModel(context) }
|
||||||
ProductionTrackerTheme {
|
//
|
||||||
Greeting("Android")
|
// Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
}
|
// SevenSegmentDetector(model)
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.sminnovations.prodtrack.common.data.dataStore
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.sminnovations.prodtrack.screen.history
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HistoryScreen(){
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package com.sminnovations.prodtrack.screen.home
|
||||||
@@ -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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -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")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(){
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
)
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
69
app/src/main/java/com/sminnovations/prodtrack/tens/com23.kt
Normal file
69
app/src/main/java/com/sminnovations/prodtrack/tens/com23.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
app/src/main/java/com/sminnovations/prodtrack/tens/vm.kt
Normal file
102
app/src/main/java/com/sminnovations/prodtrack/tens/vm.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,13 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
|||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
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.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import com.sminnovations.prodtrack.common.data.dataStore.SettingsData
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Purple80,
|
||||||
@@ -35,21 +41,51 @@ private val LightColorScheme = lightColorScheme(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ProductionTrackerTheme(
|
fun ProductionTrackerTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
|
||||||
// Dynamic color is available on Android 12+
|
|
||||||
dynamicColor: Boolean = true,
|
|
||||||
content: @Composable () -> Unit
|
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 {
|
val colorScheme = when {
|
||||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
|
||||||
val context = LocalContext.current
|
dynamicColorsThemeValue.value!! && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
if (darkTheme && blackThemeValue.value == true) {
|
||||||
|
ThemeColorSchemes.toBlackColorScheme(
|
||||||
|
dynamicDarkColorScheme(context)
|
||||||
|
)
|
||||||
|
} else if (darkTheme) {
|
||||||
|
dynamicDarkColorScheme(context)
|
||||||
|
} else {
|
||||||
|
dynamicLightColorScheme(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
darkTheme -> DarkColorScheme
|
darkTheme -> DarkColorScheme
|
||||||
|
|
||||||
|
|
||||||
else -> LightColorScheme
|
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(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
typography = Typography,
|
typography = Typography,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
app/src/main/res/drawable/round_auto_mode_24.xml
Normal file
15
app/src/main/res/drawable/round_auto_mode_24.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/round_color_lens_24.xml
Normal file
5
app/src/main/res/drawable/round_color_lens_24.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/round_contrast_24.xml
Normal file
5
app/src/main/res/drawable/round_contrast_24.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/round_delete_sweep_24.xml
Normal file
5
app/src/main/res/drawable/round_delete_sweep_24.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/round_developer_mode_24.xml
Normal file
5
app/src/main/res/drawable/round_developer_mode_24.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/round_info_24.xml
Normal file
5
app/src/main/res/drawable/round_info_24.xml
Normal 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>
|
||||||
7
app/src/main/res/drawable/round_logo_dev_24.xml
Normal file
7
app/src/main/res/drawable/round_logo_dev_24.xml
Normal 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>
|
||||||
5
app/src/main/res/drawable/round_translate_24.xml
Normal file
5
app/src/main/res/drawable/round_translate_24.xml
Normal 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>
|
||||||
@@ -1,3 +1,15 @@
|
|||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">Production Tracker</string>
|
<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>
|
</resources>
|
||||||
@@ -2,4 +2,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application) apply false
|
alias(libs.plugins.android.application) apply false
|
||||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||||
|
id ("com.google.dagger.hilt.android")version "2.50" apply false
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,10 @@ espressoCore = "3.6.1"
|
|||||||
lifecycleRuntimeKtx = "2.8.6"
|
lifecycleRuntimeKtx = "2.8.6"
|
||||||
activityCompose = "1.9.2"
|
activityCompose = "1.9.2"
|
||||||
composeBom = "2024.04.01"
|
composeBom = "2024.04.01"
|
||||||
|
playServicesMlkitTextRecognitionCommon = "19.1.0"
|
||||||
|
playServicesMlkitTextRecognition = "19.0.1"
|
||||||
|
litertGpu = "1.0.1"
|
||||||
|
litert = "1.0.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
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-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
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]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user