new version 3.1.1

This commit is contained in:
sathwikcs
2025-03-03 14:37:41 +05:30
parent 27058baf99
commit b395df6268
4 changed files with 177 additions and 37 deletions

77
.idea/other.xml generated
View File

@@ -47,6 +47,28 @@
<option name="screenX" value="1840" />
<option name="screenY" value="2944" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="a15" />
<option name="id" value="a15" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="A15" />
<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" />
<option name="codename" value="a35x" />
<option name="id" value="a35x" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="A35" />
<option name="screenDensity" value="450" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="samsung" />
@@ -245,6 +267,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="gts7xllite" />
<option name="id" value="gts7xllite" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="SM-T738U" />
<option name="screenDensity" value="340" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
@@ -256,6 +289,17 @@
<option name="screenX" value="1848" />
<option name="screenY" value="2960" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="gts8wifi" />
<option name="id" value="gts8wifi" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy Tab S8" />
<option name="screenDensity" value="274" />
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
@@ -311,6 +355,17 @@
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="o1q" />
<option name="id" value="o1q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S21" />
<option name="screenDensity" value="421" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="31" />
<option name="brand" value="google" />
@@ -367,6 +422,17 @@
<option name="screenY" value="384" />
<option name="type" value="WEAR_OS" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="r11q" />
<option name="id" value="r11q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="SM-S711U" />
<option name="screenDensity" value="450" />
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="google" />
@@ -389,6 +455,17 @@
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
<option name="codename" value="t2q" />
<option name="id" value="t2q" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy S21 Plus" />
<option name="screenDensity" value="394" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="google" />

View File

@@ -14,9 +14,9 @@ android {
applicationId = "in.sminnovations.attendanceapp.user"
minSdk = 26
targetSdk = 34
versionCode = 4
versionCode = 11
versionName = "3.1.1"
versionName = "3.1.4"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@@ -9,11 +9,11 @@ import com.google.firebase.Timestamp
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import dagger.hilt.android.lifecycle.HiltViewModel
import `in`.sminnovations.attendanceapp.screens.home.data.AttendanceUiState
import `in`.sminnovations.attendanceapp.utils.AppUpdater
import `in`.sminnovations.attendanceapp.utils.NetworkMonitor
import `in`.sminnovations.attendanceapp.utils.UpdateInfo
import `in`.sminnovations.attendanceapp.screens.home.data.AttendanceUiState
import `in`.sminnovations.attendanceapp.utils.UpdateChecker
import `in`.sminnovations.attendanceapp.utils.UpdateInfo
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -31,6 +31,8 @@ import java.util.Locale
import javax.inject.Inject
import kotlin.math.abs
const val timeFormat = "dd MMMM yyyy hh:mm:ss a"
@HiltViewModel
class HomeViewModel @Inject constructor(
applicationContext: Context,
@@ -78,12 +80,15 @@ class HomeViewModel @Inject constructor(
is AppUpdater.UpdaterState.Downloading -> {
_downloadProgress.value = state.progress
}
is AppUpdater.UpdaterState.Success -> {
_downloadProgress.value = 1f
}
is AppUpdater.UpdaterState.Error -> {
_downloadProgress.value = 0f
}
AppUpdater.UpdaterState.Idle -> {
// No op
}
@@ -154,7 +159,8 @@ class HomeViewModel @Inject constructor(
processQRCode(value)
}
}
private fun processQRCode(userId: String) {
private fun processQRCode(userId: String) {
if (userId.isBlank()) return
viewModelScope.launch {
@@ -247,7 +253,7 @@ class HomeViewModel @Inject constructor(
private fun formatTimestamp(timestamp: Timestamp): String {
return SimpleDateFormat(
"dd MMMM yyyy hh:mm:ss a", Locale.getDefault()
timeFormat, Locale.getDefault()
).format(timestamp.toDate())
}
@@ -259,30 +265,59 @@ class HomeViewModel @Inject constructor(
// }
private suspend fun handleExistingAttendance(document: DocumentSnapshot) {
val data = document.data ?: return
val checkIns = data.filterKeys { it.startsWith("checkIn") }.size
val checkOuts = data.filterKeys { it.startsWith("checkOut") }.size
if (checkIns > checkOuts) {
processCheckOut(document, checkIns)
} else {
processCheckIn(document, checkIns + 1)
}
val checkIn = document.getString("checkIn$checkIns")!!
if (isTimeDifferenceGreaterThanSec(givenTimeStr = checkIn, currentTimeStr = formatTimestamp(Timestamp.now()), secVal = 5L)) {
if (!isTimeDifferenceGreaterThanMinutes(
givenTimeStr = checkIn,
currentTimeStr = formatTimestamp(Timestamp.now()),
minutesVal = 5L
)
) {
handleError("Wait 5 minutes before logging off")
} else {
if (checkIns > checkOuts) {
processCheckOut(document, checkIns)
} else {
processCheckIn(document, checkIns + 1)
}
}
}
}
// private fun isTimeDifferenceGreaterThanMinutes(givenTimeStr: String, currentTimeStr: String = formatTimestamp(Timestamp.now()), minutes: Long): Boolean {
// val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy hh:mm:ss a")
//
// val givenTime = LocalDateTime.parse(givenTimeStr, formatter)
// val currentTime = LocalDateTime.parse(currentTimeStr, formatter)
//
// val timeDifference = abs(ChronoUnit.MINUTES.between(givenTime, currentTime))
//
// return timeDifference > minutes
// }
private fun isTimeDifferenceGreaterThanMinutes(
givenTimeStr: String,
currentTimeStr: String = formatTimestamp(Timestamp.now()),
minutesVal: Long
): Boolean {
val formatter = DateTimeFormatter.ofPattern(timeFormat)
val givenTime = LocalDateTime.parse(givenTimeStr, formatter)
val currentTime = LocalDateTime.parse(currentTimeStr, formatter)
val timeDifference = abs(ChronoUnit.MINUTES.between(givenTime, currentTime))
return timeDifference > minutesVal
}
private fun isTimeDifferenceGreaterThanSec(
givenTimeStr: String,
currentTimeStr: String = formatTimestamp(Timestamp.now()),
secVal: Long
): Boolean {
val formatter = DateTimeFormatter.ofPattern(timeFormat)
val givenTime = LocalDateTime.parse(givenTimeStr, formatter)
val currentTime = LocalDateTime.parse(currentTimeStr, formatter)
val timeDifference = abs(ChronoUnit.SECONDS.between(givenTime, currentTime))
return timeDifference > secVal
}
private suspend fun handleNewAttendance(userId: String, currentDate: String) {
try {
@@ -307,7 +342,8 @@ class HomeViewModel @Inject constructor(
private suspend fun processCheckIn(document: DocumentSnapshot, checkInCount: Int) {
try {
document.reference.update("checkIn$checkInCount", formatTimestamp(Timestamp.now())).await()
document.reference.update("checkIn$checkInCount", formatTimestamp(Timestamp.now()))
.await()
_uiState.update {
it.copy(
isCheckedIn = true, qrCodeValue = null
@@ -338,8 +374,8 @@ class HomeViewModel @Inject constructor(
}
}
private fun calculateTotalWork(document: DocumentSnapshot): Pair<String,Boolean> {
val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy hh:mm:ss a", Locale.getDefault())
private fun calculateTotalWork(document: DocumentSnapshot): Pair<String, Boolean> {
val formatter = DateTimeFormatter.ofPattern(timeFormat, Locale.getDefault())
var totalMinutes = 0L
for (i in 1..20) {
val checkIn = document.getString("checkIn$i") ?: break
@@ -351,8 +387,8 @@ class HomeViewModel @Inject constructor(
val hours = totalMinutes / 60
val minutes = totalMinutes % 60
val isDayCompleted = totalMinutes >=420L
return Pair(String.format("%02d:%02d:00", hours, minutes),isDayCompleted)
val isDayCompleted = totalMinutes >= 420L
return Pair(String.format("%02d:%02d:00", hours, minutes), isDayCompleted)
}

View File

@@ -1,10 +1,13 @@
package `in`.sminnovations.attendanceapp
import com.google.firebase.Timestamp
import org.junit.Assert.assertEquals
import org.junit.Test
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.Locale
import kotlin.math.abs
/**
@@ -19,14 +22,15 @@ class ExampleUnitTest {
assertEquals(4, 2 + 2)
}
// @Test
// fun timeTest(){
// val currentTime = "30 January 2025 11:01:55 am"
// val givenTime = "30 January 2025 10:30:55 am"
// val minutes = 20L
// val result = isTimeDifferenceGreaterThanMinutes(givenTime, currentTime, minutes)
// assertEquals(true,result)
// }
@Test
fun timeTest(){
val currentTime = "30 January 2025 10:30:55 am"
val givenTime = "30 January 2025 10:31:01 am"
val minutes = 7L
val result = isTimeDifferenceGreaterThanSec(givenTime, currentTime, minutes)
println(result)
assertEquals(false,result)
}
//
// private fun isTimeDifferenceGreaterThanMinutes(givenTimeStr: String, currentTimeStr: String, minutes: Long): Boolean {
// val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy hh:mm:ss a")
@@ -35,4 +39,27 @@ class ExampleUnitTest {
// val timeDifference = abs(ChronoUnit.MINUTES.between(givenTime, currentTime))
// return timeDifference > minutes
// }
private fun isTimeDifferenceGreaterThanSec(
givenTimeStr: String,
currentTimeStr: String = formatTimestamp(Timestamp.now()),
sec: Long = 5
): Boolean {
val formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy hh:mm:ss a")
val givenTime = LocalDateTime.parse(givenTimeStr, formatter)
val currentTime = LocalDateTime.parse(currentTimeStr, formatter)
val timeDifference = abs(ChronoUnit.SECONDS.between(givenTime, currentTime))
return timeDifference > sec
}
private fun formatTimestamp(timestamp: Timestamp): String {
return SimpleDateFormat(
"dd MMMM yyyy hh:mm:ss a", Locale.getDefault()
).format(timestamp.toDate())
}
}