Progress 2
This commit is contained in:
parent
7092f1c308
commit
0c8df7e4c7
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="ASK" />
|
||||
<option name="description" value="" />
|
||||
<component name="EntryPointsManager">
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="androidx.compose.runtime.Composable" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
|
|
|
@ -69,6 +69,17 @@
|
|||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="a16x" />
|
||||
<option name="id" value="a16x" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="A16 5G" />
|
||||
<option name="screenDensity" value="450" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
|
@ -289,6 +300,17 @@
|
|||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="gta9pwifi" />
|
||||
<option name="id" value="gta9pwifi" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-X210" />
|
||||
<option name="screenDensity" value="240" />
|
||||
<option name="screenX" value="1200" />
|
||||
<option name="screenY" value="1920" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
|
@ -521,6 +543,17 @@
|
|||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="35" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tokay" />
|
||||
<option name="id" value="tokay" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
</PersistentDeviceSelectionData>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.google.gms.google.services)
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -63,6 +64,12 @@ dependencies {
|
|||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.firebase.firestore)
|
||||
implementation(libs.firebase.auth)
|
||||
implementation(libs.androidx.credentials)
|
||||
implementation(libs.androidx.credentials.play.services.auth)
|
||||
implementation(libs.googleid)
|
||||
// implementation(libs.firebase.auth.ktx)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
@ -70,4 +77,30 @@ dependencies {
|
|||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
// TensorFlow Lite Core
|
||||
implementation (libs.tensorflow.lite)
|
||||
|
||||
// Opsional: TensorFlow Lite dengan Ops TensorFlow Select
|
||||
implementation (libs.tensorflow.lite.select.tf.ops)
|
||||
|
||||
// Opsional: TensorFlow Lite untuk model yang dikonversi dengan support GPU
|
||||
implementation (libs.tensorflow.lite.gpu)
|
||||
|
||||
// Opsional: TensorFlow Lite Support Library untuk Image Processing
|
||||
implementation (libs.tensorflow.lite.support)
|
||||
|
||||
// Kotlin Coroutines (Jika menggunakan async task)
|
||||
implementation (libs.kotlinx.coroutines.core)
|
||||
implementation (libs.kotlinx.coroutines.android)
|
||||
|
||||
// CameraX Dependencies (Jika menggunakan kamera real-time)
|
||||
implementation(libs.androidx.camera.core)
|
||||
implementation( libs.androidx.camera.camera2)
|
||||
implementation (libs.androidx.camera.lifecycle)
|
||||
implementation (libs.androidx.camera.view)
|
||||
implementation (libs.androidx.camera.extensions)
|
||||
|
||||
// Glide (Untuk memuat gambar dengan cepat)
|
||||
implementation (libs.glide)
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "1003809764619",
|
||||
"project_id": "caloryapp-a3033",
|
||||
"storage_bucket": "caloryapp-a3033.firebasestorage.app"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:1003809764619:android:c254ab536c723e7700de0e",
|
||||
"android_client_info": {
|
||||
"package_name": "com.example.caloryapp"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyDxisqLMzEc1vFjjPd8jpXuNse7BMEt7HE"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
|
@ -1,6 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_MEDIA_IMAGES"
|
||||
tools:ignore="SelectedPhotoAccess" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_MEDIA_VIDEO"
|
||||
tools:ignore="SelectedPhotoAccess" />
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera.any" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
|
@ -4,25 +4,32 @@ import android.os.Bundle
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.caloryapp.navigation.Navigation
|
||||
import com.example.caloryapp.pages.account.ProfileScreen
|
||||
import com.example.caloryapp.pages.dashboard.HomeScreen
|
||||
//import com.example.caloryapp.pages.NavBarScreen
|
||||
import com.example.caloryapp.pages.onboard.LoginScreen
|
||||
import com.example.caloryapp.pages.onboard.OnBoardingScreen
|
||||
import com.example.caloryapp.ui.theme.CaloryAppTheme
|
||||
import com.example.caloryapp.widget.MainScreen
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
// val foodClassifier = FoodClassifier(this)
|
||||
setContent {
|
||||
CaloryAppTheme {
|
||||
// Navigation()
|
||||
// val context = LocalContext.current
|
||||
// var classificationResult by remember { mutableStateOf("Memuat...") }
|
||||
//
|
||||
// Box {
|
||||
// CameraPreview(onImageCaptured = { byteBuffer ->
|
||||
// val result = foodClassifier.classify(byteBuffer)
|
||||
// classificationResult = result
|
||||
//
|
||||
// Toast.makeText(context, "Hasil: $result", Toast.LENGTH_SHORT).show()
|
||||
// })
|
||||
// }
|
||||
Navigation()
|
||||
// LoginScreen(navController = rememberNavController())
|
||||
// ProfileScreen(navController = rememberNavController())
|
||||
MainScreen()
|
||||
// MainScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.example.caloryapp.model
|
||||
|
||||
data class UserModel(
|
||||
val username: String = "",
|
||||
val fullName: String = "",
|
||||
val email: String = "",
|
||||
val password: String = "",
|
||||
val gender: String = "",
|
||||
val weight: String = "",
|
||||
val height: String = "",
|
||||
)
|
|
@ -2,38 +2,52 @@ package com.example.caloryapp.navigation
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.caloryapp.pages.onboard.LoginScreen
|
||||
import com.example.caloryapp.pages.NavBarScreen
|
||||
import com.example.caloryapp.pages.account.ProfileDetailScreen
|
||||
import com.example.caloryapp.pages.onboard.ChangePasswordScreen
|
||||
import com.example.caloryapp.pages.onboard.ForgotPasswordScreen
|
||||
import com.example.caloryapp.pages.onboard.OTPVerificationScreen
|
||||
import com.example.caloryapp.pages.onboard.OnBoardingScreen
|
||||
import com.example.caloryapp.pages.onboard.RegisterScreen
|
||||
import com.example.caloryapp.pages.onboard.SuccessChangePassword
|
||||
import com.example.caloryapp.pages.onboard.SuccessRegister
|
||||
import com.example.caloryapp.widget.MainScreen
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import com.example.caloryapp.pages.MainScreen
|
||||
import com.example.caloryapp.pages.account.ProfileChangePasswordScreen
|
||||
|
||||
@Composable
|
||||
fun Navigation(modifier: Modifier = Modifier) {
|
||||
val navController = rememberNavController()
|
||||
val userViewModel: UserViewModel = viewModel()
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = NavigationScreen.OnBoardingScreen.name
|
||||
startDestination = NavigationScreen.LoginScreen.name
|
||||
) {
|
||||
composable(NavigationScreen.OnBoardingScreen.name) {
|
||||
OnBoardingScreen(navController = navController)
|
||||
}
|
||||
composable(NavigationScreen.LoginScreen.name) {
|
||||
LoginScreen(navController = navController)
|
||||
LoginScreen(navController = navController, viewModel = userViewModel)
|
||||
}
|
||||
composable(NavigationScreen.NavBarScreen.name) {
|
||||
NavBarScreen()
|
||||
composable(NavigationScreen.RegisterScreen.name) {
|
||||
RegisterScreen(navController = navController, viewModel = userViewModel)
|
||||
}
|
||||
composable(NavigationScreen.ProfileDetailScreen.name) {
|
||||
ProfileDetailScreen(navController = navController, viewModel = userViewModel)
|
||||
}
|
||||
composable(NavigationScreen.ProfileChangePasswordScreen.name) {
|
||||
ProfileChangePasswordScreen(navController = navController, viewModel = userViewModel)
|
||||
}
|
||||
// composable(NavigationScreen.HomeScreen.name) {
|
||||
// HomeScreen(navController = navController, userViewModel)
|
||||
// }
|
||||
composable(NavigationScreen.MainScreen.name) {
|
||||
MainScreen()
|
||||
MainScreen(userViewModel)
|
||||
}
|
||||
composable(NavigationScreen.ForgotPasswordScreen.name) {
|
||||
ForgotPasswordScreen(navController = navController)
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.example.caloryapp.navigation
|
|||
enum class NavigationScreen {
|
||||
OnBoardingScreen,
|
||||
LoginScreen,
|
||||
RegisterScreen,
|
||||
NavBarScreen,
|
||||
HomeScreen,
|
||||
MainScreen,
|
||||
|
@ -11,12 +12,15 @@ enum class NavigationScreen {
|
|||
OTPVerificationScreen,
|
||||
SuccessChangePassword,
|
||||
SuccessRegister,
|
||||
ProfileScreen;
|
||||
ProfileScreen,
|
||||
ProfileDetailScreen,
|
||||
ProfileChangePasswordScreen;
|
||||
|
||||
fun fromRoute(route: String): NavigationScreen =
|
||||
when (route.substringBefore("/")) {
|
||||
OnBoardingScreen.name -> OnBoardingScreen
|
||||
LoginScreen.name -> LoginScreen
|
||||
RegisterScreen.name -> RegisterScreen
|
||||
NavBarScreen.name -> NavBarScreen
|
||||
MainScreen.name -> MainScreen
|
||||
HomeScreen.name -> HomeScreen
|
||||
|
@ -26,6 +30,8 @@ enum class NavigationScreen {
|
|||
OTPVerificationScreen.name -> OTPVerificationScreen
|
||||
SuccessChangePassword.name -> SuccessChangePassword
|
||||
SuccessRegister.name -> SuccessRegister
|
||||
ProfileDetailScreen.name -> ProfileDetailScreen
|
||||
ProfileChangePasswordScreen.name -> ProfileChangePasswordScreen
|
||||
|
||||
else -> throw IllegalArgumentException("$route gagal bji")
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
package com.example.caloryapp.widget
|
||||
package com.example.caloryapp.pages
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
@ -21,17 +16,11 @@ import androidx.compose.material3.DrawerValue
|
|||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalDrawerSheet
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
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.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
@ -41,19 +30,21 @@ import androidx.compose.ui.res.vectorResource
|
|||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.caloryapp.R
|
||||
import com.example.caloryapp.navigation.NavigationScreen
|
||||
import com.example.caloryapp.pages.account.ProfileChangePasswordScreen
|
||||
import com.example.caloryapp.pages.account.ProfileDetailScreen
|
||||
import com.example.caloryapp.pages.account.ProfileScreen
|
||||
import com.example.caloryapp.pages.dashboard.HomeScreen
|
||||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.medium
|
||||
import com.example.caloryapp.ui.theme.primary
|
||||
import com.example.caloryapp.ui.theme.semibold
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
sealed class DrawerScreen(val title: String) {
|
||||
|
@ -63,7 +54,9 @@ sealed class DrawerScreen(val title: String) {
|
|||
|
||||
@SuppressLint("UnusedMaterialScaffoldPaddingParameter", "UnusedMaterial3ScaffoldPaddingParameter")
|
||||
@Composable
|
||||
fun MainScreen() {
|
||||
fun MainScreen(
|
||||
userViewModel: UserViewModel
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
@ -71,15 +64,21 @@ fun MainScreen() {
|
|||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
DrawerContent(navController, drawerState, scope)
|
||||
DrawerContent(navController, drawerState, scope, userViewModel)
|
||||
}
|
||||
) {
|
||||
NavHost(navController = navController, startDestination = DrawerScreen.HomeScreen.title) {
|
||||
composable(DrawerScreen.HomeScreen.title) {
|
||||
HomeScreen(navController = navController, drawerState = drawerState, scope = scope)
|
||||
HomeScreen(navController = navController, drawerState = drawerState, scope = scope, userViewModel)
|
||||
}
|
||||
composable(DrawerScreen.ProfileScreen.title) {
|
||||
ProfileScreen(navController = navController, drawerState = drawerState, scope = scope)
|
||||
ProfileScreen(navController = navController, drawerState = drawerState, scope = scope, viewModel = userViewModel)
|
||||
}
|
||||
composable(NavigationScreen.ProfileDetailScreen.name) {
|
||||
ProfileDetailScreen(navController = navController, viewModel = userViewModel)
|
||||
}
|
||||
composable(NavigationScreen.ProfileChangePasswordScreen.name) {
|
||||
ProfileChangePasswordScreen(navController = navController, viewModel = userViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,8 +88,10 @@ fun MainScreen() {
|
|||
fun DrawerContent(
|
||||
navController: NavHostController,
|
||||
drawerState: DrawerState,
|
||||
scope: kotlinx.coroutines.CoroutineScope
|
||||
scope: kotlinx.coroutines.CoroutineScope,
|
||||
viewModel: UserViewModel
|
||||
) {
|
||||
val user = viewModel.user.value
|
||||
ModalDrawerSheet(modifier = Modifier.background(primary)) {
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
Row(
|
||||
|
@ -106,7 +107,7 @@ fun DrawerContent(
|
|||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Text(
|
||||
text = "Naufal Kadhafi",
|
||||
text = user!!.fullName,
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = Color.Black,
|
||||
|
@ -114,7 +115,7 @@ fun DrawerContent(
|
|||
)
|
||||
)
|
||||
Text(
|
||||
text = "@kadhafiinl",
|
||||
text = "@${user.username}",
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
color = Color.Black,
|
|
@ -0,0 +1,175 @@
|
|||
package com.example.caloryapp.pages.account
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||
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.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.example.caloryapp.repository.UserRepository
|
||||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.primary
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import com.example.caloryapp.widget.CustomPasswordTextField
|
||||
|
||||
@Composable
|
||||
fun ProfileChangePasswordScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavController,
|
||||
viewModel: UserViewModel
|
||||
) {
|
||||
var oldPassword by remember { mutableStateOf("") }
|
||||
var newPassword by remember { mutableStateOf("") }
|
||||
val userData = viewModel.user.value
|
||||
var isPasswordVisible by remember { mutableStateOf(false) }
|
||||
val context = LocalContext.current
|
||||
val userRepository = UserRepository()
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(background)
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(horizontal = 25.dp, vertical = 50.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
Row(Modifier.fillMaxWidth(), Arrangement.Start) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowLeft,
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.size(28.dp)
|
||||
.clickable { navController.popBackStack() }
|
||||
)
|
||||
Spacer(modifier = Modifier.width(30.dp))
|
||||
Text(
|
||||
text = "Ubah Password",
|
||||
style = TextStyle(
|
||||
fontSize = 25.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Form untuk memasukkan password lama dan baru
|
||||
Spacer(modifier.height(30.dp))
|
||||
Text(
|
||||
text = "Password Lama",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomPasswordTextField(
|
||||
value = oldPassword,
|
||||
onValueChange = { oldPassword = it },
|
||||
placeholderText = "Masukkan Password Lama",
|
||||
input = true
|
||||
)
|
||||
|
||||
Spacer(modifier.height(18.dp))
|
||||
Text(
|
||||
text = "Password Baru",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomPasswordTextField(
|
||||
value = newPassword,
|
||||
onValueChange = { newPassword = it },
|
||||
placeholderText = "Masukkan Password Baru",
|
||||
input = true
|
||||
)
|
||||
|
||||
Spacer(modifier.height(18.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
if (oldPassword.isEmpty() || newPassword.isEmpty()) {
|
||||
Toast.makeText(context, "Password tidak boleh kosong", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} else {
|
||||
// Panggil fungsi untuk memperbarui password berdasarkan username
|
||||
userData?.username?.let {
|
||||
userRepository.updatePasswordByUsername(
|
||||
it,
|
||||
oldPassword,
|
||||
newPassword
|
||||
) { isSuccess ->
|
||||
if (isSuccess) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Password berhasil diperbarui",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.popBackStack() // Navigasi kembali setelah berhasil
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Gagal memperbarui password",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.width(360.dp)
|
||||
.height(50.dp),
|
||||
colors = androidx.compose.material.ButtonDefaults.buttonColors(backgroundColor = primary),
|
||||
shape = RoundedCornerShape(20.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Ubah",
|
||||
style = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
color = Color.White,
|
||||
fontFamily = MaterialTheme.typography.h1.fontFamily,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,206 @@
|
|||
package com.example.caloryapp.pages.account
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||
import androidx.compose.material3.DrawerState
|
||||
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.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.example.caloryapp.R
|
||||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import com.example.caloryapp.widget.CustomTextField
|
||||
|
||||
@Composable
|
||||
fun ProfileDetailScreen(modifier: Modifier = Modifier) {
|
||||
fun ProfileDetailScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
// drawerState: DrawerState,
|
||||
// scope: kotlinx.coroutines.CoroutineScope,
|
||||
navController: NavController,
|
||||
viewModel: UserViewModel
|
||||
) {
|
||||
val user = viewModel.user.value
|
||||
|
||||
var username by remember { mutableStateOf("") }
|
||||
var gmail by remember { mutableStateOf("") }
|
||||
var fullName by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var selectedGender by remember { mutableStateOf("") }
|
||||
var weight by remember { mutableStateOf("") }
|
||||
var height by remember { mutableStateOf("") }
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(background)
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(horizontal = 25.dp, vertical = 50.dp)
|
||||
.verticalScroll(
|
||||
rememberScrollState()
|
||||
)
|
||||
) {
|
||||
Spacer(modifier.height(50.dp))
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
Arrangement.Start,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowLeft,
|
||||
contentDescription = null,
|
||||
Modifier
|
||||
.size(28.dp)
|
||||
.clickable { navController.popBackStack()}
|
||||
)
|
||||
Spacer(modifier = Modifier.width(30.dp))
|
||||
Text(
|
||||
text = "Profil Saya",
|
||||
style = TextStyle(
|
||||
fontSize = 25.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(64.dp))
|
||||
Row(Modifier.fillMaxWidth(), Arrangement.Center) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_profile_men),
|
||||
contentDescription = null,
|
||||
Modifier.size(100.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier.height(45.dp))
|
||||
Text(
|
||||
text = "Nama Lengkap",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = user!!.fullName,
|
||||
onValueChange = { fullName = it },
|
||||
placeholderText = "Masukkan Nama Lengkap",
|
||||
input = false
|
||||
)
|
||||
|
||||
Spacer(modifier.height(20.dp))
|
||||
Text(
|
||||
text = "Username",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = user.username,
|
||||
onValueChange = { },
|
||||
placeholderText = "Masukkan Nama Lengkap",
|
||||
input = false
|
||||
)
|
||||
|
||||
Spacer(modifier.height(20.dp))
|
||||
Text(
|
||||
text = "Email",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = user.email,
|
||||
onValueChange = { },
|
||||
placeholderText = "Masukkan Nama Lengkap",
|
||||
input = false
|
||||
)
|
||||
|
||||
Spacer(modifier.height(20.dp))
|
||||
Text(
|
||||
text = "Gender",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = user.gender,
|
||||
onValueChange = { },
|
||||
placeholderText = "Masukkan Nama Lengkap",
|
||||
input = false
|
||||
)
|
||||
|
||||
Spacer(modifier.height(20.dp))
|
||||
Text(
|
||||
text = "Berat Badan",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = "${user.weight} kg",
|
||||
onValueChange = { },
|
||||
placeholderText = "Masukkan Nama Lengkap",
|
||||
input = false
|
||||
)
|
||||
|
||||
Spacer(modifier.height(20.dp))
|
||||
Text(
|
||||
text = "Tinggi Badan",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = "${user.height} cm",
|
||||
onValueChange = { },
|
||||
placeholderText = "Masukkan Nama Lengkap",
|
||||
input = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,8 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.core.os.unregisterForAllProfilingResults
|
||||
import androidx.navigation.NavController
|
||||
import com.example.caloryapp.R
|
||||
import com.example.caloryapp.navigation.NavigationScreen
|
||||
import com.example.caloryapp.pages.DrawerScreen
|
||||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.medium
|
||||
|
@ -41,6 +43,7 @@ import com.example.caloryapp.ui.theme.primaryblack
|
|||
import com.example.caloryapp.ui.theme.primarygrey
|
||||
import com.example.caloryapp.ui.theme.primaryred
|
||||
import com.example.caloryapp.ui.theme.regular
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import com.example.caloryapp.widget.SimpleAlertDialog
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -52,8 +55,10 @@ fun ProfileScreen(
|
|||
modifier: Modifier = Modifier,
|
||||
navController: NavController,
|
||||
drawerState: DrawerState,
|
||||
scope: kotlinx.coroutines.CoroutineScope
|
||||
) {
|
||||
scope: kotlinx.coroutines.CoroutineScope,
|
||||
viewModel: UserViewModel,
|
||||
) {
|
||||
val user = viewModel.user.value
|
||||
val openAlertDialog = remember { mutableStateOf(false) }
|
||||
val currentDate = Calendar.getInstance().time
|
||||
val dateFormat = SimpleDateFormat("EEEE, dd MMMM yyyy", Locale("id", "ID"))
|
||||
|
@ -77,7 +82,7 @@ fun ProfileScreen(
|
|||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = "Naufal Kadhafi",
|
||||
text = user!!.fullName,
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = Color.Black,
|
||||
|
@ -85,7 +90,7 @@ fun ProfileScreen(
|
|||
)
|
||||
)
|
||||
Text(
|
||||
text = "@kadhafiinl",
|
||||
text = "@${user.username}",
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
color = Color.Black,
|
||||
|
@ -121,7 +126,7 @@ fun ProfileScreen(
|
|||
)
|
||||
Spacer(modifier.height(10.dp))
|
||||
Divider(color = primary.copy(alpha = 0.2f), thickness = 3.dp)
|
||||
Spacer(modifier.height(20.dp))
|
||||
Spacer(modifier.height(40.dp))
|
||||
Row(Modifier.fillMaxWidth(), Arrangement.SpaceBetween) {
|
||||
Row {
|
||||
Image(
|
||||
|
@ -139,6 +144,7 @@ fun ProfileScreen(
|
|||
)
|
||||
}
|
||||
Image(
|
||||
modifier = Modifier.clickable { navController.navigate(NavigationScreen.ProfileDetailScreen.name) },
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_btn_detail),
|
||||
contentDescription = null
|
||||
)
|
||||
|
@ -185,6 +191,7 @@ fun ProfileScreen(
|
|||
)
|
||||
}
|
||||
Image(
|
||||
modifier = Modifier.clickable { navController.navigate(NavigationScreen.ProfileChangePasswordScreen.name) },
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_btn_detail),
|
||||
contentDescription = null
|
||||
)
|
||||
|
|
|
@ -1,10 +1,49 @@
|
|||
package com.example.caloryapp.pages.camera
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavController
|
||||
|
||||
@Composable
|
||||
fun CameraDetectionScreen(modifier: Modifier = Modifier, navController: NavController) {
|
||||
fun CameraDetectionScreen(foodClassifier: FoodClassifier, areaCoverage: Map<String, Int>,modifier: Modifier = Modifier, navController: NavController) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
//@Composable
|
||||
//fun ValidateFoodPlate(foodClassifier: FoodClassifier, areaCoverage: Map<String, Int>) {
|
||||
// val context = LocalContext.current
|
||||
//
|
||||
// val isPortionValid = foodClassifier.validatePortion(areaCoverage)
|
||||
// if (!isPortionValid) {
|
||||
// Toast.makeText(context, "Porsi makanan TIDAK sesuai! Kebutuhan kalori GAGAL terpenuhi!", Toast.LENGTH_LONG).show()
|
||||
// } else {
|
||||
// Toast.makeText(context, "Porsi makanan sesuai! Kebutuhan kalori TERPENUHI!", Toast.LENGTH_LONG).show()
|
||||
// }
|
||||
//}
|
||||
|
||||
@Composable
|
||||
fun FoodPlateOverlay(isValid: Boolean) {
|
||||
val color = if (isValid) Color.Green else Color.Red
|
||||
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
val width = size.width
|
||||
val height = size.height
|
||||
|
||||
// Visualisasi Food Plate dengan warna berdasarkan validasi porsi
|
||||
drawRect(color, Offset(0f, 0f), Size(width * 0.6f, height * 0.25f))
|
||||
drawRect(Color.Yellow, Offset(0f, height * 0.25f), Size(width * 0.5f, height * 0.5f)) // Karbohidrat
|
||||
drawRect(Color.Red, Offset(width * 0.5f, height * 0.25f), Size(width * 0.5f, height * 0.5f)) // Protein
|
||||
drawRect(Color.Green, Offset(0f, 0f), Size(width * 0.6f, height * 0.25f)) // Sayur
|
||||
drawRect(Color(0xFFFFA500), Offset(width * 0.6f, 0f), Size(width * 0.4f, height * 0.25f)) // Buah
|
||||
drawRect(Color.Blue, Offset(width * 0.75f, height * 0.75f), Size(width * 0.25f, height * 0.25f)) // Lainnya
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package com.example.caloryapp.pages.camera
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.util.Size
|
||||
import android.view.Surface
|
||||
import android.view.ViewGroup
|
||||
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.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
||||
//@Composable
|
||||
//fun CameraPreview(
|
||||
// onImageCaptured: (ByteBuffer) -> Unit,
|
||||
// modifier: Modifier = Modifier
|
||||
//) {
|
||||
// val context = LocalContext.current
|
||||
// val lifecycleOwner = LocalContext.current as LifecycleOwner
|
||||
// val cameraExecutor = remember { Executors.newSingleThreadExecutor() }
|
||||
// val previewView = remember { PreviewView(context) }
|
||||
//
|
||||
// DisposableEffect(context) {
|
||||
//// val cameraProviderFuture = ProcessCameraProvider.getInstance()
|
||||
//// cameraProviderFuture.addListener({
|
||||
//// val cameraProvider = cameraProviderFuture.get()
|
||||
//// val preview = Preview.Builder().build().also {
|
||||
//// it.setSurfaceProvider(previewView.surfaceProvider)
|
||||
//// }
|
||||
//
|
||||
//// val imageAnalysis = ImageAnalysis.Builder()
|
||||
//// .setTargetResolution(Size(128, 128))
|
||||
//// .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
//// .build()
|
||||
////
|
||||
//// imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
|
||||
//// val buffer = imageProxy.planes[0].buffer
|
||||
//// onImageCaptured(buffer)
|
||||
//// imageProxy.close()
|
||||
//// }
|
||||
////
|
||||
//// try {
|
||||
//// cameraProvider.unbindAll()
|
||||
//// cameraProvider.bindToLifecycle(
|
||||
//// lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalysis
|
||||
//// )
|
||||
//// } catch (e: Exception) {
|
||||
//// e.printStackTrace()
|
||||
//// }
|
||||
//// }, ContextCompat.getMainExecutor(context))
|
||||
////
|
||||
//// onDispose { cameraExecutor.shutdown() }
|
||||
//// }
|
||||
////
|
||||
//// AndroidView(
|
||||
//// factory = { previewView },
|
||||
//// modifier = modifier.fillMaxSize()
|
||||
//// )
|
||||
//}
|
||||
|
||||
|
||||
@OptIn(ExperimentalGetImage::class)
|
||||
private fun processImageProxy(imageProxy: ImageProxy, onImageCaptured: (ByteBuffer) -> Unit) {
|
||||
val image = imageProxy.image ?: return
|
||||
val planes = image.planes
|
||||
val buffer: ByteBuffer = planes[0].buffer
|
||||
val data = ByteArray(buffer.remaining())
|
||||
buffer.get(data)
|
||||
|
||||
onImageCaptured(buffer) // Mengirim data gambar untuk klasifikasi
|
||||
|
||||
imageProxy.close()
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.example.caloryapp.pages.camera
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
@Composable
|
||||
fun CameraPreviewWithOverlay(
|
||||
foodClassifier: FoodClassifier,
|
||||
areaCoverage: Map<String, Int>
|
||||
) {
|
||||
var classificationResult by remember { mutableStateOf("Memuat...") }
|
||||
Box {
|
||||
// CameraPreview(onImageCaptured = { byteBuffer ->
|
||||
// val result = foodClassifier.classify(byteBuffer)
|
||||
// classificationResult = result
|
||||
// })
|
||||
//
|
||||
// // Menampilkan hasil klasifikasi dan validasi porsi makanan
|
||||
// ValidateFoodPlate(
|
||||
// classificationResult = classificationResult,
|
||||
// areaCoverage = areaCoverage
|
||||
// )
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ValidateFoodPlate(
|
||||
classificationResult: String,
|
||||
areaCoverage: Map<String, Int>
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val idealPercentage = mapOf(
|
||||
"Karbohidrat" to 25,
|
||||
"Protein" to 25,
|
||||
"Sayur" to 30,
|
||||
"Buah" to 15,
|
||||
"Lainnya" to 5
|
||||
)
|
||||
|
||||
var isValid = true
|
||||
for ((category, ideal) in idealPercentage) {
|
||||
val actual = areaCoverage[category] ?: 0
|
||||
if (actual < ideal * 0.8 || actual > ideal * 1.2) {
|
||||
isValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
Toast.makeText(context, "Porsi makanan TIDAK sesuai!", Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
Toast.makeText(context, "Porsi makanan sesuai kebutuhan kalori harian!", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.example.caloryapp.pages.camera
|
||||
|
||||
import android.content.Context
|
||||
import org.tensorflow.lite.Interpreter
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
class FoodClassifier(context: Context) {
|
||||
private val interpreter: Interpreter
|
||||
private val categories = arrayOf("Karbohidrat", "Protein", "Sayur", "Buah", "Lainnya")
|
||||
|
||||
init {
|
||||
val model = context.assets.open("model_food_plate_densenet.tflite").use { it.readBytes() }
|
||||
interpreter = Interpreter(ByteBuffer.wrap(model))
|
||||
}
|
||||
|
||||
fun classify(imageData: ByteBuffer): String {
|
||||
val output = Array(1) { FloatArray(categories.size) }
|
||||
interpreter.run(imageData, output)
|
||||
val maxIndex = output[0].indices.maxByOrNull { output[0][it] } ?: -1
|
||||
return if (maxIndex >= 0) categories[maxIndex] else "Tidak Terdeteksi"
|
||||
}
|
||||
}
|
|
@ -38,14 +38,24 @@ import com.example.caloryapp.ui.theme.primaryblack
|
|||
import com.example.caloryapp.ui.theme.primarygrey
|
||||
import com.example.caloryapp.widget.FilterBar
|
||||
import kotlinx.coroutines.launch
|
||||
import android.widget.Toast
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.caloryapp.model.UserModel
|
||||
import com.example.caloryapp.pages.camera.FoodClassifier
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
navController: NavController,
|
||||
drawerState: DrawerState,
|
||||
scope: kotlinx.coroutines.CoroutineScope
|
||||
scope: kotlinx.coroutines.CoroutineScope,
|
||||
viewModel: UserViewModel,
|
||||
) {
|
||||
var selectedFilter by remember { mutableStateOf("Semua") }
|
||||
val user = viewModel.user.value
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
@ -61,7 +71,7 @@ fun HomeScreen(
|
|||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.clickable { scope.launch { drawerState.open() } },
|
||||
modifier = Modifier.clickable { },
|
||||
painter = painterResource(id = R.drawable.ic_home_acc),
|
||||
contentDescription = null
|
||||
)
|
||||
|
@ -69,7 +79,7 @@ fun HomeScreen(
|
|||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
Text(
|
||||
text = "Naufal Kadhafi",
|
||||
text = user!!.fullName,
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = Color.Black,
|
||||
|
@ -77,7 +87,7 @@ fun HomeScreen(
|
|||
)
|
||||
)
|
||||
Text(
|
||||
text = "@kadhafiinl",
|
||||
text = "@${user.username}",
|
||||
style = TextStyle(
|
||||
fontSize = 14.sp,
|
||||
color = Color.Black,
|
||||
|
@ -99,7 +109,7 @@ fun HomeScreen(
|
|||
// Teks sapaan
|
||||
Row(Modifier.width(215.dp)) {
|
||||
Text(
|
||||
text = "Hai Naufal, Bagaimana kabar kamu hari ini?",
|
||||
text = "Hai ${user!!.fullName}, Bagaimana kabar kamu hari ini?",
|
||||
style = TextStyle(
|
||||
fontSize = 22.sp,
|
||||
color = Color.Black,
|
||||
|
|
|
@ -66,7 +66,7 @@ fun ChangePasswordScreen(modifier: Modifier = Modifier, navController: NavContro
|
|||
Spacer(modifier.height(16.dp))
|
||||
CustomTextField(
|
||||
value = newPassword,
|
||||
onValueChange = { newPassword = it },
|
||||
onValueChange = { newPassword = it },input = true,
|
||||
placeholderText = "Masukkan Kata Sandi"
|
||||
)
|
||||
Spacer(modifier.height(20.dp))
|
||||
|
@ -81,7 +81,7 @@ fun ChangePasswordScreen(modifier: Modifier = Modifier, navController: NavContro
|
|||
Spacer(modifier.height(16.dp))
|
||||
CustomTextField(
|
||||
value = confirmPassword,
|
||||
onValueChange = { confirmPassword = it },
|
||||
onValueChange = { confirmPassword = it },input = true,
|
||||
placeholderText = "Konfirmasi Kata Sandi"
|
||||
)
|
||||
Spacer(modifier.height(45.dp))
|
||||
|
|
|
@ -79,7 +79,7 @@ fun ForgotPasswordScreen(modifier: Modifier = Modifier, navController: NavContro
|
|||
Spacer(modifier.height(16.dp))
|
||||
CustomTextField(
|
||||
value = gmail,
|
||||
onValueChange = { gmail = it },
|
||||
onValueChange = { gmail = it },input = true,
|
||||
placeholderText = stringResource(R.string.gmail)
|
||||
)
|
||||
Spacer(modifier.height(40.dp))
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
package com.example.caloryapp.pages.onboard
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.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
|
||||
|
@ -23,6 +28,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
|
@ -36,15 +42,35 @@ import com.example.caloryapp.navigation.NavigationScreen
|
|||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.blueunderlined
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.fontGrey
|
||||
import com.example.caloryapp.ui.theme.primary
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.ui.theme.semibold
|
||||
import com.example.caloryapp.viewmodel.LoginState
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import com.example.caloryapp.widget.CustomTextField
|
||||
|
||||
@Composable
|
||||
fun LoginScreen(modifier: Modifier = Modifier, navController: NavController) {
|
||||
fun LoginScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavController,
|
||||
viewModel: UserViewModel
|
||||
) {
|
||||
var username by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
val state = viewModel.loginstate.value
|
||||
val userData = viewModel.user.value
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(key1 = state) {
|
||||
if (state is LoginState.Success) {
|
||||
Toast.makeText(context, "Selamat Datang, ${state.user.fullName}", Toast.LENGTH_SHORT).show()
|
||||
navController.navigate(NavigationScreen.MainScreen.name)
|
||||
} else if (state is LoginState.Error) {
|
||||
Toast.makeText(context, "Username atau Kata Sandi Tidak Sesuai!", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier
|
||||
|
@ -83,7 +109,7 @@ fun LoginScreen(modifier: Modifier = Modifier, navController: NavController) {
|
|||
Spacer(modifier.height(16.dp))
|
||||
CustomTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
onValueChange = { username = it }, input = true,
|
||||
placeholderText = "Username"
|
||||
)
|
||||
Spacer(modifier.height(16.dp))
|
||||
|
@ -98,27 +124,22 @@ fun LoginScreen(modifier: Modifier = Modifier, navController: NavController) {
|
|||
Spacer(modifier.height(16.dp))
|
||||
CustomTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
onValueChange = { password = it }, input = true,
|
||||
placeholderText = "Password"
|
||||
)
|
||||
Spacer(modifier.height(18.dp))
|
||||
Row(
|
||||
modifier
|
||||
.align(Alignment.End)
|
||||
.clickable { navController.navigate(NavigationScreen.ForgotPasswordScreen.name) }) {
|
||||
Text(
|
||||
text = stringResource(R.string.lupa_password),
|
||||
style = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
color = blueunderlined,
|
||||
fontFamily = semibold,
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
)
|
||||
}
|
||||
Spacer(modifier.height(35.dp))
|
||||
Button(
|
||||
onClick = { navController.navigate(NavigationScreen.MainScreen.name) },
|
||||
onClick = {
|
||||
if (username.isEmpty()) {
|
||||
Toast.makeText(context, "Username Tidak Boleh Kosong", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} else if (password.isEmpty()){
|
||||
Toast.makeText(context, "Password Tidak Boleh Kosong", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} else {
|
||||
viewModel.login(username, password)
|
||||
}
|
||||
},
|
||||
modifier
|
||||
.width(360.dp)
|
||||
.height(50.dp),
|
||||
|
@ -130,17 +151,44 @@ fun LoginScreen(modifier: Modifier = Modifier, navController: NavController) {
|
|||
style = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
color = Color.White,
|
||||
fontFamily = bold,
|
||||
fontFamily = MaterialTheme.typography.h1.fontFamily,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Row(
|
||||
Modifier.fillMaxWidth(), Arrangement.Center
|
||||
) {
|
||||
Text(
|
||||
text = "Belum Punya Akun ?",
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
color = fontGrey,
|
||||
fontFamily = semibold
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Text(
|
||||
modifier = Modifier.clickable { navController.navigate(NavigationScreen.RegisterScreen.name) },
|
||||
text = "Daftar",
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
color = blueunderlined,
|
||||
fontFamily = semibold,
|
||||
textDecoration = TextDecoration.Underline
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// if (state is LoginState.Error) {
|
||||
// Text(text = "", color = MaterialTheme.colors.error)
|
||||
// Toast.makeText(context, "Username atau Kata Sandi Tidak Sesuai!", Toast.LENGTH_SHORT)
|
||||
// .show()
|
||||
// }
|
||||
|
||||
Spacer(modifier.height(42.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Preview(showBackground = true)
|
||||
//@Composable
|
||||
//fun Preview(modifier: Modifier = Modifier) {
|
||||
// LoginScreen(navController = rememberNavController())
|
||||
//}
|
|
@ -0,0 +1,302 @@
|
|||
package com.example.caloryapp.pages.onboard
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.example.caloryapp.R
|
||||
import com.example.caloryapp.model.UserModel
|
||||
import com.example.caloryapp.navigation.NavigationScreen
|
||||
import com.example.caloryapp.repository.UserRepository
|
||||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.medium
|
||||
import com.example.caloryapp.ui.theme.primary
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.viewmodel.UserViewModel
|
||||
import com.example.caloryapp.widget.CustomTextField
|
||||
import com.example.caloryapp.widget.GenderDropdown
|
||||
|
||||
@Composable
|
||||
fun RegisterScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
navController: NavController,
|
||||
viewModel: UserViewModel
|
||||
) {
|
||||
var username by remember { mutableStateOf("") }
|
||||
var gmail by remember { mutableStateOf("") }
|
||||
var fullName by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var selectedGender by remember { mutableStateOf("") }
|
||||
var weight by remember { mutableStateOf("") }
|
||||
var height by remember { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
val registerState by viewModel.registerState.collectAsState()
|
||||
val firestoreHelper = UserRepository()
|
||||
|
||||
LaunchedEffect(registerState) {
|
||||
try {
|
||||
if (registerState == true) {
|
||||
|
||||
} else if (registerState == false) {
|
||||
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
if (registerState == true) {
|
||||
Toast.makeText(context, "Registrasi berhasil!", Toast.LENGTH_SHORT).show()
|
||||
// navController.navigate("login") // Arahkan ke halaman login
|
||||
} else if (registerState == false) {
|
||||
Toast.makeText(context, "Registrasi gagal!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(background)
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(horizontal = 25.dp, vertical = 50.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(50.dp))
|
||||
Text(
|
||||
text = "Daftar",
|
||||
style = TextStyle(
|
||||
fontSize = 35.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "Yuk isi biodata kamu \n" +
|
||||
"dulu!",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = medium
|
||||
)
|
||||
)
|
||||
|
||||
// username
|
||||
Spacer(modifier = Modifier.height(40.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.username),
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
placeholderText = "Masukkan Username",
|
||||
input = true
|
||||
)
|
||||
|
||||
// gmail
|
||||
Spacer(modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Email",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = gmail,
|
||||
onValueChange = { gmail = it },
|
||||
input = true,
|
||||
placeholderText = "Masukkan Email"
|
||||
)
|
||||
|
||||
// nama lengkap
|
||||
Spacer(modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Nama Lengkap",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = fullName,
|
||||
onValueChange = { fullName = it }, input = true,
|
||||
placeholderText = "Masukkan Nama Lengkap"
|
||||
)
|
||||
|
||||
// pw
|
||||
|
||||
|
||||
// nama lengkap
|
||||
Spacer(modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Kata Sandi",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it }, input = true,
|
||||
placeholderText = "Masukkan Kata Sandi"
|
||||
)
|
||||
|
||||
// gender
|
||||
Spacer(modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Gender",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
GenderDropdown(selectedGender = selectedGender) {
|
||||
selectedGender = it
|
||||
}
|
||||
|
||||
// berat badan
|
||||
Spacer(modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Berat Badan",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = weight.toString(),
|
||||
onValueChange = { weight = it }, input = true,
|
||||
placeholderText = "Masukkan Berat Badan"
|
||||
)
|
||||
|
||||
// tinggi badan
|
||||
Spacer(modifier.height(16.dp))
|
||||
Text(
|
||||
text = "Tinggi Badan",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = bold
|
||||
)
|
||||
)
|
||||
Spacer(modifier.height(12.dp))
|
||||
CustomTextField(
|
||||
value = height.toString(),
|
||||
onValueChange = { height = it }, input = true,
|
||||
placeholderText = "Masukkan Tinggi Badan"
|
||||
)
|
||||
|
||||
// btn daftar
|
||||
Spacer(modifier.height(35.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
if (username.isEmpty() && password.isEmpty() && gmail.isEmpty() && fullName.isEmpty() && selectedGender.isEmpty() &&
|
||||
weight.isEmpty() && height.isEmpty()
|
||||
) {
|
||||
Toast.makeText(context, "Data Tidak Boleh Kosong!", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} else if (username.isNotEmpty() && password.isNotEmpty() && gmail.isNotEmpty() && fullName.isNotEmpty() && selectedGender.isNotEmpty() &&
|
||||
weight.isNotEmpty() && height.isNotEmpty()
|
||||
) {
|
||||
val user = UserModel(
|
||||
username,
|
||||
fullName,
|
||||
gmail,
|
||||
password,
|
||||
selectedGender,
|
||||
weight,
|
||||
height
|
||||
)
|
||||
firestoreHelper.registerUserWithCustomID(user, gmail) { success ->
|
||||
if (success) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Kamu Berhasil Membuat Akun !",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
navController.navigate(NavigationScreen.SuccessRegister.name)
|
||||
} else {
|
||||
Toast.makeText(context, "Error Registering", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
// viewModel.register(gmail, password, user)
|
||||
// viewModel.registerUser(
|
||||
// username, fullName, password, gmail, selectedGender,
|
||||
// weight, height,
|
||||
// onSuccess = {
|
||||
//// navController.navigate(NavigationScreen.MainScreen.name)
|
||||
// Toast.makeText(context, "Pendaftaran Berhasil!", Toast.LENGTH_SHORT).show()
|
||||
// },
|
||||
// onFailure = {
|
||||
// Log.e("Register", "Pendaftaran gagal")
|
||||
// Toast.makeText(context, "GAGAL COKKK", Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
// )
|
||||
}
|
||||
},
|
||||
modifier
|
||||
.width(360.dp)
|
||||
.height(50.dp),
|
||||
colors = androidx.compose.material.ButtonDefaults.buttonColors(backgroundColor = primary),
|
||||
shape = RoundedCornerShape(10.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Daftar",
|
||||
style = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
color = Color.White,
|
||||
fontFamily = bold,
|
||||
textAlign = TextAlign.Center,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import com.example.caloryapp.R
|
||||
import com.example.caloryapp.navigation.NavigationScreen
|
||||
import com.example.caloryapp.ui.theme.background
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.primary
|
||||
|
@ -48,7 +49,7 @@ fun SuccessRegister(modifier: Modifier = Modifier, navController: NavController)
|
|||
Image(imageVector = ImageVector.vectorResource(id = R.drawable.ic_success), contentDescription = "")
|
||||
Spacer(modifier = Modifier.height(115.dp))
|
||||
Button(
|
||||
onClick = { },
|
||||
onClick = { navController.navigate(NavigationScreen.LoginScreen.name) },
|
||||
modifier
|
||||
.width(360.dp)
|
||||
.height(50.dp),
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package com.example.caloryapp.repository
|
||||
|
||||
import android.util.Log
|
||||
import com.example.caloryapp.model.UserModel
|
||||
import com.google.firebase.auth.FirebaseAuth
|
||||
import com.google.firebase.firestore.FirebaseFirestore
|
||||
import kotlinx.coroutines.tasks.await
|
||||
|
||||
class UserRepository {
|
||||
private val db = FirebaseFirestore.getInstance()
|
||||
|
||||
fun registerUser(user: UserModel, onComplete: (Boolean) -> Unit) {
|
||||
db.collection("users")
|
||||
.add(user)
|
||||
.addOnSuccessListener {
|
||||
onComplete(true)
|
||||
}
|
||||
.addOnFailureListener {
|
||||
onComplete(false)
|
||||
}
|
||||
}
|
||||
|
||||
fun registerUserWithCustomID(user: UserModel, userID: String, onComplete: (Boolean) -> Unit) {
|
||||
db.collection("users")
|
||||
.document(userID)
|
||||
.set(user)
|
||||
.addOnSuccessListener {
|
||||
onComplete(true)
|
||||
}
|
||||
.addOnFailureListener {
|
||||
onComplete(false)
|
||||
}
|
||||
}
|
||||
|
||||
fun getUserByUsername(username: String, onComplete: (UserModel?) -> Unit) {
|
||||
db.collection("users")
|
||||
.whereEqualTo("username", username)
|
||||
.get()
|
||||
.addOnSuccessListener { result ->
|
||||
if (!result.isEmpty) {
|
||||
val user = result.documents[0].toObject(UserModel::class.java)
|
||||
onComplete(user)
|
||||
} else {
|
||||
onComplete(null)
|
||||
}
|
||||
}
|
||||
.addOnFailureListener {
|
||||
onComplete(null)
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePasswordByUsername(username: String, oldPassword: String, newPassword: String, onComplete: (Boolean) -> Unit) {
|
||||
db.collection("users")
|
||||
.whereEqualTo("username", username) // Mencari pengguna berdasarkan username
|
||||
.get()
|
||||
.addOnSuccessListener { result ->
|
||||
if (!result.isEmpty) {
|
||||
val userDocument = result.documents[0]
|
||||
val currentPassword = userDocument.getString("password")
|
||||
|
||||
if (currentPassword == oldPassword) {
|
||||
userDocument.reference.update("password", newPassword)
|
||||
.addOnSuccessListener {
|
||||
onComplete(true) // Update berhasil
|
||||
}
|
||||
.addOnFailureListener {
|
||||
onComplete(false) // Update gagal
|
||||
}
|
||||
} else {
|
||||
// Jika password lama tidak cocok
|
||||
onComplete(false) // Password lama salah
|
||||
}
|
||||
} else {
|
||||
// Username tidak ditemukan
|
||||
onComplete(false)
|
||||
}
|
||||
}
|
||||
.addOnFailureListener {
|
||||
onComplete(false) // Terjadi error dalam pencarian data
|
||||
}
|
||||
}
|
||||
|
||||
// private val db = FirebaseFirestore.getInstance()
|
||||
// private val auth = FirebaseAuth.getInstance()
|
||||
//
|
||||
// suspend fun registerUser(email: String, password: String, userModel: UserModel): Boolean {
|
||||
// val userId = auth.currentUser?.uid ?: return false
|
||||
// return try {
|
||||
// db.collection("users").document(userId).set(userModel).await()
|
||||
// true
|
||||
// } catch (e: Exception) {
|
||||
// Log.e("Firestore", "Error saving user: ", e)
|
||||
// false
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// suspend fun getUser(username: String): UserModel? {
|
||||
// return try {
|
||||
// val document = db.collection("users").document(username).get().await()
|
||||
// document.toObject(UserModel::class.java)
|
||||
// } catch (e: Exception) {
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -8,6 +8,7 @@ var primarygrey = Color(0xffBABABA)
|
|||
var background = Color(0xffF4F4F4)
|
||||
var blueunderlined = Color(0xff0063BF)
|
||||
var primaryred = Color(0xffE85454)
|
||||
val fontGrey = Color(0xFF828282)
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package com.example.caloryapp.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.caloryapp.model.UserModel
|
||||
import com.example.caloryapp.repository.UserRepository
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
sealed class LoginState {
|
||||
data object Loading : LoginState()
|
||||
data class Success(val user: UserModel) : LoginState()
|
||||
data class Error(val message: String) : LoginState()
|
||||
}
|
||||
|
||||
class UserViewModel : ViewModel() {
|
||||
private val repository = UserRepository()
|
||||
|
||||
private val _registerState = MutableStateFlow<Boolean?>(null)
|
||||
val registerState: StateFlow<Boolean?> = _registerState
|
||||
|
||||
// Menyimpan data login state (loading, success, error)
|
||||
private val _loginstate = mutableStateOf<LoginState>(LoginState.Loading)
|
||||
val loginstate = _loginstate
|
||||
|
||||
// Menyimpan data pengguna yang login
|
||||
private val _user = mutableStateOf<UserModel?>(null)
|
||||
val user = _user
|
||||
|
||||
fun login(username: String, password: String) {
|
||||
viewModelScope.launch {
|
||||
_loginstate.value = LoginState.Loading
|
||||
// Fetch user by username from Firestore
|
||||
repository.getUserByUsername(username) { fetchedUser ->
|
||||
if (fetchedUser != null && fetchedUser.password == password) {
|
||||
_user.value = fetchedUser // Store user data in ViewModel
|
||||
_loginstate.value = LoginState.Success(fetchedUser)
|
||||
Log.d("Login", "Fetched user: $fetchedUser")
|
||||
Log.d("Login", "User fullName: ${fetchedUser?.fullName}")
|
||||
Log.d("Login", "User username: ${fetchedUser?.username}")
|
||||
|
||||
} else {
|
||||
_loginstate.value = LoginState.Error("Invalid username or password")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **REGISTER USER**
|
||||
// fun register(email: String, password: String, user: UserModel) {
|
||||
// viewModelScope.launch {
|
||||
// val result = repository.registerUser(email, password, user)
|
||||
// _registerState.value = result
|
||||
// }
|
||||
// }
|
||||
|
||||
// fun registerUser(
|
||||
// username: String,
|
||||
// fullName: String,
|
||||
// email: String,
|
||||
// password: String,
|
||||
// gender: String,
|
||||
// weight: String,
|
||||
// height: String,
|
||||
// onSuccess: () -> Unit,
|
||||
// onFailure: () -> Unit
|
||||
// ) {
|
||||
// val user = UserModel(username, fullName, email, password, gender, weight, height)
|
||||
//
|
||||
// viewModelScope.launch {
|
||||
// val isSuccess = repository.registerUser(user)
|
||||
// if (isSuccess) {
|
||||
// onSuccess()
|
||||
// } else {
|
||||
// onFailure()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.example.caloryapp.widget
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
//noinspection UsingMaterialAndMaterial3Libraries
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
//noinspection UsingMaterialAndMaterial3Libraries
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.caloryapp.R
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.ui.theme.primarygrey
|
||||
import com.example.caloryapp.ui.theme.semibold
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
|
||||
@Composable
|
||||
fun CustomPasswordTextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
placeholderText: String,
|
||||
input: Boolean
|
||||
) {
|
||||
// State untuk toggle password visibility
|
||||
var isPasswordVisible by remember { mutableStateOf(false) }
|
||||
|
||||
OutlinedTextField(
|
||||
textStyle = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = semibold,
|
||||
letterSpacing = 0.5.sp
|
||||
),
|
||||
value = value,
|
||||
enabled = input,
|
||||
onValueChange = onValueChange,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = placeholderText,
|
||||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
color = primarygrey,
|
||||
fontFamily = bold,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.padding(end = 16.dp), // Add padding for the icon
|
||||
visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
|
||||
Icon(
|
||||
imageVector = if (isPasswordVisible) Icons.Filled.Check else Icons.Filled.Check,
|
||||
contentDescription = "Toggle Password Visibility",
|
||||
tint = Color.Gray // Sesuaikan dengan warna ikon pada desain
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
backgroundColor = Color.White, // Warna putih dengan sedikit transparansi
|
||||
focusedBorderColor = Color.Transparent, // Menghilangkan border saat fokus
|
||||
unfocusedBorderColor = Color.Transparent, // Menghilangkan border saat tidak fokus
|
||||
cursorColor = Color.Black // Warna kursor
|
||||
),
|
||||
shape = RoundedCornerShape(10.dp) // Membuat border melengkung
|
||||
)
|
||||
}
|
|
@ -20,15 +20,24 @@ import com.example.caloryapp.R
|
|||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.ui.theme.primarygrey
|
||||
import com.example.caloryapp.ui.theme.semibold
|
||||
|
||||
@Composable
|
||||
fun CustomTextField(
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
placeholderText: String
|
||||
placeholderText: String,
|
||||
input: Boolean
|
||||
) {
|
||||
OutlinedTextField(
|
||||
textStyle = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
color = primaryblack,
|
||||
fontFamily = semibold,
|
||||
letterSpacing = 0.5.sp
|
||||
),
|
||||
value = value,
|
||||
enabled = input,
|
||||
onValueChange = onValueChange,
|
||||
placeholder = {
|
||||
androidx.compose.material.Text(
|
||||
|
@ -36,7 +45,8 @@ fun CustomTextField(
|
|||
style = TextStyle(
|
||||
fontSize = 16.sp,
|
||||
color = primarygrey,
|
||||
fontFamily = bold
|
||||
fontFamily = bold,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
)
|
||||
},
|
||||
|
@ -44,11 +54,11 @@ fun CustomTextField(
|
|||
.fillMaxWidth()
|
||||
.height(50.dp),
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
backgroundColor = Color(0xFFF8F8F8), // Warna putih dengan sedikit transparansi
|
||||
backgroundColor = Color.White, // Warna putih dengan sedikit transparansi
|
||||
focusedBorderColor = Color.Transparent, // Menghilangkan border saat fokus
|
||||
unfocusedBorderColor = Color.Transparent, // Menghilangkan border saat tidak fokus
|
||||
cursorColor = Color.Black // Warna kursor
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp) // Membuat border melengkung
|
||||
shape = RoundedCornerShape(10.dp) // Membuat border melengkung
|
||||
)
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.example.caloryapp.widget
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.example.caloryapp.ui.theme.bold
|
||||
import com.example.caloryapp.ui.theme.primaryblack
|
||||
import com.example.caloryapp.ui.theme.primarygrey
|
||||
|
||||
@Composable
|
||||
fun GenderDropdown(selectedGender: String, onGenderSelected: (String) -> Unit) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val genderOptions = listOf("Pria", "Wanita")
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.border(1.dp, Color.White, shape = RoundedCornerShape(8.dp))
|
||||
.background(Color.White, shape = RoundedCornerShape(8.dp))
|
||||
.clickable { expanded = true },
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = if (selectedGender.isEmpty()) "Pilih Gender" else selectedGender,
|
||||
color = if (selectedGender.isEmpty()) primarygrey else primaryblack,
|
||||
modifier = Modifier.weight(1f),
|
||||
fontSize = 16.sp,
|
||||
fontFamily = bold
|
||||
|
||||
)
|
||||
Icon(Icons.Default.ArrowDropDown, contentDescription = "Dropdown Icon", tint = Color.Gray)
|
||||
}
|
||||
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
genderOptions.forEach { gender ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(gender) },
|
||||
onClick = {
|
||||
onGenderSelected(gender)
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,4 +2,5 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||
alias(libs.plugins.google.gms.google.services) apply false
|
||||
}
|
|
@ -1,21 +1,43 @@
|
|||
[versions]
|
||||
agp = "8.5.2"
|
||||
cameraCore = "1.4.1"
|
||||
cameraCamera2 = "1.4.1"
|
||||
glide = "4.12.0"
|
||||
kotlin = "1.9.0"
|
||||
coreKtx = "1.15.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
kotlinxCoroutinesAndroid = "1.7.3"
|
||||
kotlinxCoroutinesCore = "1.6.4"
|
||||
material = "1.7.7"
|
||||
navigationCompose = "2.8.6"
|
||||
lifecycleRuntimeKtx = "2.8.7"
|
||||
activityCompose = "1.10.0"
|
||||
composeBom = "2025.01.01"
|
||||
ohteepee = "1.0.3"
|
||||
tensorflowLite = "2.12.0"
|
||||
tensorflowLiteSelectTfOps = "2.12.0"
|
||||
tensorflowLiteGpu = "2.12.0"
|
||||
tensorflowLiteSupport = "0.4.3"
|
||||
googleGmsGoogleServices = "4.4.2"
|
||||
firebaseFirestore = "25.1.2"
|
||||
firebaseAuth = "22.3.1"
|
||||
credentials = "1.5.0-rc01"
|
||||
credentialsPlayServicesAuth = "1.3.0"
|
||||
googleid = "1.1.1"
|
||||
#firebaseAuthKtx = "23.2.0"
|
||||
|
||||
[libraries]
|
||||
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCamera2" }
|
||||
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" }
|
||||
androidx-camera-extensions = { module = "androidx.camera:camera-extensions", version.ref = "cameraCore" }
|
||||
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraCore" }
|
||||
androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraCore" }
|
||||
androidx-material = { module = "androidx.compose.material:material", version.ref = "material" }
|
||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
|
@ -29,9 +51,22 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
|
|||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" }
|
||||
ohteepee = { module = "com.github.composeuisuite:ohteepee", version.ref = "ohteepee" }
|
||||
tensorflow-lite-support = { module = "org.tensorflow:tensorflow-lite-support", version.ref = "tensorflowLiteSupport" }
|
||||
tensorflow-lite-gpu = { module = "org.tensorflow:tensorflow-lite-gpu", version.ref = "tensorflowLiteGpu" }
|
||||
tensorflow-lite-select-tf-ops = { module = "org.tensorflow:tensorflow-lite-select-tf-ops", version.ref = "tensorflowLiteSelectTfOps" }
|
||||
tensorflow-lite = { module = "org.tensorflow:tensorflow-lite", version.ref = "tensorflowLite" }
|
||||
firebase-firestore = { group = "com.google.firebase", name = "firebase-firestore", version.ref = "firebaseFirestore" }
|
||||
firebase-auth = { group = "com.google.firebase", name = "firebase-auth", version.ref = "firebaseAuth" }
|
||||
androidx-credentials = { group = "androidx.credentials", name = "credentials", version.ref = "credentials" }
|
||||
androidx-credentials-play-services-auth = { group = "androidx.credentials", name = "credentials-play-services-auth", version.ref = "credentialsPlayServicesAuth" }
|
||||
googleid = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "googleid" }
|
||||
#firebase-auth-ktx = { group = "com.google.firebase", name = "firebase-auth-ktx", version.ref = "firebaseAuthKtx" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" }
|
||||
|
||||
|
|
Loading…
Reference in New Issue