new version 3.1.1
This commit is contained in:
77
.idea/other.xml
generated
77
.idea/other.xml
generated
@@ -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" />
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user