update
This commit is contained in:
parent
79367aef7f
commit
46a9b5b82e
8
.idea/deploymentTargetSelector.xml
generated
8
.idea/deploymentTargetSelector.xml
generated
@ -4,6 +4,14 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2025-06-12T01:51:13.726519Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/nucha/.android/avd/Medium_Phone_API_33.avd" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@ -41,6 +41,9 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
val composeBom = platform("androidx.compose:compose-bom:2025.05.00")
|
||||||
|
implementation(composeBom)
|
||||||
|
androidTestImplementation(composeBom)
|
||||||
|
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
@ -62,6 +65,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.navigation.fragment.ktx)
|
implementation(libs.androidx.navigation.fragment.ktx)
|
||||||
implementation(libs.androidx.navigation.ui.ktx)
|
implementation(libs.androidx.navigation.ui.ktx)
|
||||||
implementation(libs.ads.mobile.sdk)
|
implementation(libs.ads.mobile.sdk)
|
||||||
|
implementation(libs.accompanist.permissions)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
<?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-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@ -13,6 +14,8 @@
|
|||||||
android:theme="@style/Theme.AttendanceManager"
|
android:theme="@style/Theme.AttendanceManager"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@ -2,22 +2,25 @@ import retrofit2.Retrofit
|
|||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
object ApiClient {
|
object ApiClient {
|
||||||
private const val BASE_URL = "http://10.0.2.2:8000" // ใช้ 10.0.2.2 แทน localhost สำหรับ emulator
|
// กำหนด BASE_URL ของ API ใช้ 10.0.2.2 แทน localhost สำหรับ Android emulator
|
||||||
|
private const val BASE_URL = "http://10.0.2.2:8000"
|
||||||
|
|
||||||
|
// สร้าง Retrofit instance เดียว ใช้ lazy initialization คือสร้างเมื่อเรียกใช้ครั้งแรก
|
||||||
|
private val retrofit: Retrofit by lazy {
|
||||||
|
Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL) // กำหนด URL หลักของ API
|
||||||
|
.addConverterFactory(GsonConverterFactory.create()) // ใช้ Gson แปลง JSON เป็น object และกลับกัน
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
// สร้าง AuthApiService จาก Retrofit instance เดียวกัน
|
||||||
val authApi: AuthApiService by lazy {
|
val authApi: AuthApiService by lazy {
|
||||||
Retrofit.Builder()
|
retrofit.create(AuthApiService::class.java)
|
||||||
.baseUrl(BASE_URL)
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
.create(AuthApiService::class.java)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// สร้าง AttendanceApiService จาก Retrofit instance เดียวกัน
|
||||||
val attendanceApi: AttendanceApiService by lazy {
|
val attendanceApi: AttendanceApiService by lazy {
|
||||||
Retrofit.Builder()
|
retrofit.create(AttendanceApiService::class.java)
|
||||||
.baseUrl(BASE_URL)
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
.create(AttendanceApiService::class.java)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class AttendanceViewModel : ViewModel() {
|
||||||
|
|
||||||
|
// ฟังก์ชัน checkIn รับ token, พิกัด lat,long และ callback สำหรับผลลัพธ์
|
||||||
|
fun checkIn(token: String, lat: Double, long: Double, callback: (Boolean, String) -> Unit) {
|
||||||
|
// สร้าง request body จากพิกัดที่รับเข้ามา
|
||||||
|
val body = CheckInRequest(lat = lat, long = long)
|
||||||
|
|
||||||
|
// ใช้ viewModelScope เพื่อ launch coroutine ทำงานแบบ background
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
// เรียก API check-in ผ่าน Retrofit โดยส่ง token ใน header แบบ Bearer และ body ใน POST
|
||||||
|
val response = ApiClient.attendanceApi.checkIn("Bearer $token", body)
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
// ถ้า response สำเร็จ ดึงข้อความเวลาที่เช็คอินสำเร็จจาก response body
|
||||||
|
val msg = response.body()?.checked_in_at ?: "Check-in สำเร็จ"
|
||||||
|
// ส่ง callback กลับว่า success พร้อมข้อความ
|
||||||
|
callback(true, msg)
|
||||||
|
} else {
|
||||||
|
// กรณี response ล้มเหลว ส่ง callback พร้อมสถานะล้มเหลวและรหัส HTTP
|
||||||
|
callback(false, "Check-in ล้มเหลว: ${response.code()}")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// กรณีเกิดข้อผิดพลาดระหว่างเรียก API เช่น network error
|
||||||
|
callback(false, "เกิดข้อผิดพลาด: ${e.localizedMessage}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,14 @@
|
|||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
|
||||||
interface AuthApiService {
|
interface AuthApiService {
|
||||||
|
|
||||||
|
// ฟังก์ชันสำหรับเรียก API login ด้วย HTTP POST ไปที่ "/auth/login"
|
||||||
|
// ใช้ coroutine (suspend) เพื่อให้สามารถเรียกแบบ asynchronous ได้
|
||||||
|
// รับพารามิเตอร์เป็น LoginRequest (username/password)
|
||||||
|
// และคืนค่ากลับเป็น Response<LoginResponse> ที่มี token หรือข้อมูลอื่นใน response
|
||||||
@POST("/auth/login")
|
@POST("/auth/login")
|
||||||
suspend fun login(@Body request: LoginRequest): Response<LoginResponse>
|
suspend fun login(@Body request: LoginRequest): Response<LoginResponse>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//suspend คือ คีย์เวิร์ดในภาษา Kotlin ที่ใช้สำหรับระบุว่า ฟังก์ชันสามารถหยุดทำงานชั่วคราว (suspend) และ resume ต่อได้ภายหลัง ซึ่งเป็นพื้นฐานของ Coroutine เพื่อให้สามารถทำงานแบบ asynchronous (ไม่บล็อก UI) ได้ง่ายและปลอดภัยกว่าแบบเดิม (เช่น AsyncTask หรือ Thread)
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
data class LoginRequest(
|
data class LoginRequest(
|
||||||
val email: String,
|
val email: String,
|
||||||
@ -17,35 +18,35 @@ class AuthViewModel : ViewModel() {
|
|||||||
callback(false, "Email หรือ Password ว่าง")
|
callback(false, "Email หรือ Password ว่าง")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// // จำลองการเรียก API ที่ใช้เวลาประมาณ 2 วินาที
|
// จำลองการเรียก API ที่ใช้เวลาประมาณ 2 วินาที
|
||||||
// delay(2000)
|
delay(2000)
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // ตัวอย่าง login จำลอง: email = admin@example.com, password = 1234
|
// ตัวอย่าง login จำลอง: email = admin@example.com, password = 1234
|
||||||
// if (email == "admin@example.com" && password == "1234") {
|
if (email == "test@test.com" && password == "1234") {
|
||||||
// callback(true, null)
|
|
||||||
// } else {
|
|
||||||
// callback(false, "อีเมลหรือรหัสผ่านไม่ถูกต้อง")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
try {
|
|
||||||
val response = ApiClient.authApi.login(LoginRequest(email, password))
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
val token = response.body()?.token
|
|
||||||
if (!token.isNullOrBlank()) {
|
|
||||||
// ✅ login สำเร็จ
|
|
||||||
callback(true, null)
|
callback(true, null)
|
||||||
} else {
|
|
||||||
callback(false, "ไม่พบ token")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
callback(false, "อีเมลหรือรหัสผ่านไม่ถูกต้อง")
|
callback(false, "อีเมลหรือรหัสผ่านไม่ถูกต้อง")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
}
|
||||||
callback(false, "เกิดข้อผิดพลาดในการเชื่อมต่อ: ${e.localizedMessage}")
|
// try {
|
||||||
}
|
// val response = ApiClient.authApi.login(LoginRequest(email, password))
|
||||||
}
|
// if (response.isSuccessful) {
|
||||||
|
// val token = response.body()?.token
|
||||||
|
// if (!token.isNullOrBlank()) {
|
||||||
|
// // ✅ login สำเร็จ
|
||||||
|
// callback(true, null)
|
||||||
|
// } else {
|
||||||
|
// callback(false, "ไม่พบ token")
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// callback(false, "อีเมลหรือรหัสผ่านไม่ถูกต้อง")
|
||||||
|
// }
|
||||||
|
// } catch (e: Exception) {
|
||||||
|
// callback(false, "เกิดข้อผิดพลาดในการเชื่อมต่อ: ${e.localizedMessage}")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,126 @@
|
|||||||
import androidx.compose.foundation.layout.Column
|
// นำเข้า permission, location และ library ที่เกี่ยวข้อง
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import android.Manifest
|
||||||
import androidx.compose.foundation.layout.padding
|
import android.content.pm.PackageManager
|
||||||
import androidx.compose.material3.Text
|
import android.widget.Toast
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.google.android.gms.location.LocationServices
|
||||||
|
import com.google.accompanist.permissions.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ข้อมูลที่ได้จากการตอบกลับของ API หลังจาก Check In สำเร็จ
|
||||||
|
*/
|
||||||
|
data class CheckInResponse(
|
||||||
|
val checked_in_at: String
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ข้อมูลที่ต้องส่งให้ API เพื่อทำการ Check In
|
||||||
|
*/
|
||||||
|
data class CheckInRequest(
|
||||||
|
val lat: Double,
|
||||||
|
val long: Double
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable ที่ใช้แสดงหน้าจอ Check In/Out และจัดการการร้องขอ permission
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalPermissionsApi::class) // ใช้ API ที่ยังเป็น experimental จาก Accompanist
|
||||||
@Composable
|
@Composable
|
||||||
fun CheckInOutScreen() {
|
fun CheckInOutScreen() {
|
||||||
|
// ใช้ Accompanist ในการจัดการ permission สำหรับ location
|
||||||
|
val locationPermissionState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
|
|
||||||
|
// Context ของแอป สำหรับแสดง Toast หรือใช้ API อื่น ๆ//
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
|
||||||
|
// ใช้ remember เก็บข้อความที่ใช้แสดงผลลัพธ์จาก server
|
||||||
|
val message = remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
// ดึง AttendanceViewModel เพื่อเรียกใช้ฟังก์ชัน checkIn
|
||||||
|
val viewModel: AttendanceViewModel = viewModel()
|
||||||
|
|
||||||
|
// token จำลองไว้เรียก API
|
||||||
|
val token = "fake-jwt-token"
|
||||||
|
|
||||||
|
// สร้าง fusedLocationClient สำหรับเข้าถึง location ล่าสุดของผู้ใช้
|
||||||
|
val fusedLocationClient = remember {
|
||||||
|
LocationServices.getFusedLocationProviderClient(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ฟังก์ชันเรียก permission และเรียกใช้ fusedLocationClient เพื่อดึงตำแหน่ง
|
||||||
|
* แล้วส่งไปยัง API สำหรับ Check In
|
||||||
|
*/
|
||||||
|
fun getLocationAndCheckIn() {
|
||||||
|
// ถ้ายังไม่ได้รับ permission ให้เรียกขอ permission แล้วออกจากฟังก์ชันก่อน
|
||||||
|
if (locationPermissionState.status != PermissionStatus.Granted) {
|
||||||
|
locationPermissionState.launchPermissionRequest()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// เมื่อได้รับ permission แล้ว ใช้ fusedLocationClient ดึงตำแหน่งล่าสุด
|
||||||
|
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
|
||||||
|
if (location != null) {
|
||||||
|
val lat = location.latitude
|
||||||
|
val long = location.longitude
|
||||||
|
|
||||||
|
// เรียก ViewModel เพื่อติดต่อ API และส่งพิกัดไป
|
||||||
|
viewModel.checkIn(token, lat, long) { success, msg ->
|
||||||
|
message.value = msg
|
||||||
|
Toast.makeText(ctx, msg, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(ctx, "ไม่สามารถดึงพิกัดได้", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// ถ้าเกิด SecurityException แสดงว่า permission ไม่ได้เปิดไว้จริง
|
||||||
|
Toast.makeText(ctx, "ไม่มีสิทธิ์เข้าถึงตำแหน่ง", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Layout แบบ Column
|
||||||
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||||
Text("Check In / Out Screen")
|
// แสดงหัวข้อของหน้าจอ
|
||||||
|
Text("Check In / Out Screen", style = MaterialTheme.typography.headlineMedium)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// ตรวจสอบสถานะ permission และแสดง UI ที่เหมาะสม
|
||||||
|
when (val status = locationPermissionState.status) {
|
||||||
|
is PermissionStatus.Granted -> {
|
||||||
|
// ถ้าอนุญาตแล้ว แสดงปุ่ม Check In
|
||||||
|
Button(onClick = { getLocationAndCheckIn() }) {
|
||||||
|
Text("Check In")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is PermissionStatus.Denied -> {
|
||||||
|
if (status.shouldShowRationale) {
|
||||||
|
// ผู้ใช้ปฏิเสธแบบชั่วคราว ให้แสดงเหตุผลและปุ่มขอใหม่
|
||||||
|
Text("แอปต้องขออนุญาตเข้าถึงตำแหน่ง เพื่อทำการ Check In")
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Button(onClick = { locationPermissionState.launchPermissionRequest() }) {
|
||||||
|
Text("ขออนุญาต")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ผู้ใช้ปฏิเสธถาวร (Don't ask again) แจ้งให้ไปเปิดใน Settings
|
||||||
|
Text("กรุณาเปิดสิทธิ์ตำแหน่งในตั้งค่าเครื่อง")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// แสดงข้อความตอบกลับจาก server ถ้ามี
|
||||||
|
message.value?.let {
|
||||||
|
Text("Server Response: $it")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,12 +4,20 @@ 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ปุ่มแบบกำหนดเอง (CustomButton) สำหรับใช้ซ้ำในหลาย ๆ ส่วนของ UI
|
||||||
|
*
|
||||||
|
* @param text ข้อความที่จะแสดงบนปุ่ม
|
||||||
|
* @param onClick ฟังก์ชันที่จะถูกเรียกเมื่อผู้ใช้กดปุ่ม
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun CustomButton(text: String, onClick: () -> Unit) {
|
fun CustomButton(text: String, onClick: () -> Unit) {
|
||||||
|
// ปุ่มจาก Material3 ที่ใช้คลิกได้
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick, // ฟังก์ชันที่จะถูกเรียกเมื่อกดปุ่ม
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth() // ปุ่มขยายเต็มความกว้างของหน้าจอ
|
||||||
) {
|
) {
|
||||||
|
// ข้อความที่จะแสดงอยู่ในปุ่ม
|
||||||
Text(text)
|
Text(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,6 +17,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.yourcompany.attendancemanager.R
|
import com.yourcompany.attendancemanager.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -29,7 +30,10 @@ fun LoginScreen(onLoginSuccess: () -> Unit) {
|
|||||||
val loginFailedText = stringResource(R.string.login_failed)
|
val loginFailedText = stringResource(R.string.login_failed)
|
||||||
val isLoading = remember { mutableStateOf(false) }
|
val isLoading = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
|
||||||
|
Column(modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp)) {
|
||||||
Text(text = loginText, style = MaterialTheme.typography.headlineMedium)
|
Text(text = loginText, style = MaterialTheme.typography.headlineMedium)
|
||||||
InputField(label = stringResource(R.string.email), text = email.value) { email.value = it }
|
InputField(label = stringResource(R.string.email), text = email.value) { email.value = it }
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@ -50,5 +54,6 @@ fun LoginScreen(onLoginSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,9 +25,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
LoginScreen(onLoginSuccess = { navController.navigate("check") })
|
LoginScreen(onLoginSuccess = { navController.navigate("check") })
|
||||||
}
|
}
|
||||||
composable("check") { CheckInOutScreen() }
|
composable("check") { CheckInOutScreen() }
|
||||||
composable("leave") { LeaveRequestScreen() }
|
// composable("leave") { LeaveRequestScreen() }
|
||||||
composable("leaveSummary"){ LeaveSummaryScreen() }
|
// composable("leaveSummary"){ LeaveSummaryScreen() }
|
||||||
composable("attendanceSummary"){ AttendanceSummaryScreen() }
|
// composable("attendanceSummary"){ AttendanceSummaryScreen() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ constraintlayout = "2.2.1"
|
|||||||
navigationFragmentKtx = "2.9.0"
|
navigationFragmentKtx = "2.9.0"
|
||||||
navigationUiKtx = "2.9.0"
|
navigationUiKtx = "2.9.0"
|
||||||
adsMobileSdk = "0.16.0-alpha01"
|
adsMobileSdk = "0.16.0-alpha01"
|
||||||
|
accompanist = "0.31.5-beta"
|
||||||
|
|
||||||
[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" }
|
||||||
@ -48,6 +49,7 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const
|
|||||||
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
|
androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
|
||||||
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" }
|
||||||
ads-mobile-sdk = { group = "com.google.android.libraries.ads.mobile.sdk", name = "ads-mobile-sdk", version.ref = "adsMobileSdk" }
|
ads-mobile-sdk = { group = "com.google.android.libraries.ads.mobile.sdk", name = "ads-mobile-sdk", version.ref = "adsMobileSdk" }
|
||||||
|
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user