updated PDFGenerator daily.kt

This commit is contained in:
sathwikcs
2025-03-18 17:44:55 +05:30
parent 53149d0361
commit b269993e13

View File

@@ -0,0 +1,500 @@
package `in`.sminnovations.smiadmin.ui.utils
import android.content.Context
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Path
import android.graphics.Shader
import android.graphics.Typeface
import android.graphics.pdf.PdfDocument
import android.os.Environment
import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import `in`.sminnovations.smiadmin.data.Employee
import kotlinx.coroutines.tasks.await
import java.io.File
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
class PDFGenerator_daily(
private val context: Context,
private val companyName: String
){
private lateinit var document: PdfDocument
private lateinit var currentPage: PdfDocument.Page
private lateinit var canvas: android.graphics.Canvas
private var currentY: Float = 0f
// Constants for page layout
private val pageWidth = 595f // A4 width
private val pageHeight = 842f // A4 height
private val margin = 40f
private val contentWidth = pageWidth - (2 * margin)
// Professional color scheme
private val primaryColor = Color.rgb(41, 128, 185) // Professional blue
private val secondaryColor = Color.rgb(44, 62, 80) // Dark blue
private val accentColor = Color.rgb(230, 126, 34) // Orange for highlights
private val backgroundColor = Color.rgb(236, 240, 241) // Light gray
private val borderColor = Color.rgb(189, 195, 199) // Medium gray
private val textColor = Color.rgb(52, 73, 94) // Dark gray for text
private var pageNumber = 1
// Data class for attendance record
data class AttendanceRecord(
val checkInTimes: List<String>,
val checkOutTimes: List<String>,
val isCompleted: Boolean
)
suspend fun generateDailyReport(
employees: List<Employee>,
selectedDate: Calendar,
source: String
): File {
document = PdfDocument()
createNewPage()
// Create paint objects
val titlePaint = Paint().apply {
textSize = 32f
typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
color = primaryColor
textAlign = Paint.Align.CENTER
}
val subtitlePaint = Paint().apply {
textSize = 24f
typeface = Typeface.create("sans-serif-light", Typeface.NORMAL)
color = secondaryColor
textAlign = Paint.Align.CENTER
}
val tableHeaderPaint = Paint().apply {
textSize = 14f
typeface = Typeface.create("sans-serif-medium", Typeface.BOLD)
color = Color.WHITE
}
val regularPaint = Paint().apply {
textSize = 12f
typeface = Typeface.create("sans-serif", Typeface.NORMAL)
color = textColor
}
val highlightPaint = Paint().apply {
textSize = 12f
typeface = Typeface.create("sans-serif-medium", Typeface.BOLD)
color = accentColor
}
// Format date for display and Firebase query
val dateFormat = SimpleDateFormat("dd MMMM yyyy", Locale.getDefault())
val formattedDate = dateFormat.format(selectedDate.time)
val yearStr = selectedDate.get(Calendar.YEAR).toString()
val monthStr = String.format("%02d", selectedDate.get(Calendar.MONTH) + 1)
val dayStr = String.format("%02d", selectedDate.get(Calendar.DAY_OF_MONTH))
val firebaseDateKey = "$dayStr-$monthStr-$yearStr"
// Draw report header
drawReportHeader(formattedDate, titlePaint, subtitlePaint)
// Draw table headers
drawTableHeaders(tableHeaderPaint)
// Process each employee
employees.forEach { employee ->
if (currentY > pageHeight - 100) {
finishCurrentPage()
createNewPage()
drawTableHeaders(tableHeaderPaint)
}
// Get attendance data for this employee for the selected date
val attendanceDoc = Firebase.firestore
.collection("attendance_${employee.Id}")
.document(firebaseDateKey)
.get()
.await()
if (attendanceDoc.exists()) {
// Extract check-in and check-out times
val checkInTimes = mutableListOf<String>()
val checkOutTimes = mutableListOf<String>()
val isCompleted = attendanceDoc.getBoolean("DayCompleted") ?: false
// Get all check-in times
var i = 1
while (true) {
val checkInKey = "checkIn$i"
val checkInTime = attendanceDoc.getString(checkInKey)
if (checkInTime != null) {
checkInTimes.add(checkInTime)
i++
} else {
break
}
}
// Get all check-out times
i = 1
while (true) {
val checkOutKey = "checkOut$i"
val checkOutTime = attendanceDoc.getString(checkOutKey)
if (checkOutTime != null) {
checkOutTimes.add(checkOutTime)
i++
} else {
break
}
}
val attendanceRecord = AttendanceRecord(
checkInTimes = checkInTimes,
checkOutTimes = checkOutTimes,
isCompleted = isCompleted
)
drawEmployeeAttendanceRow(employee, attendanceRecord, regularPaint, highlightPaint)
} else {
// Employee was absent
drawEmployeeAbsentRow(employee, regularPaint, highlightPaint)
}
}
// Add footer with page numbers
drawFooter(regularPaint)
// Finish and save document
finishCurrentPage()
return saveDocument(selectedDate)
}
private fun drawReportHeader(
formattedDate: String,
titlePaint: Paint,
subtitlePaint: Paint
) {
// Draw decorative header background
val headerPath = Path().apply {
moveTo(0f, 0f)
lineTo(pageWidth, 0f)
lineTo(pageWidth, 120f)
quadTo(pageWidth/2, 140f, 0f, 120f)
close()
}
canvas.drawPath(headerPath, Paint().apply {
color = primaryColor
alpha = 20
})
// Draw company name
canvas.drawText(companyName, pageWidth/2, 70f, titlePaint)
// Draw report title
canvas.drawText(
"Daily Attendance Report",
pageWidth/2,
105f,
subtitlePaint
)
canvas.drawText(
formattedDate,
pageWidth/2,
135f,
subtitlePaint
)
currentY = 180f
}
private fun drawTableHeaders(headerPaint: Paint) {
val headerHeight = 45f
// Create gradient for header background
val gradient = LinearGradient(
margin, currentY,
margin, currentY + headerHeight,
primaryColor,
secondaryColor,
Shader.TileMode.CLAMP
)
// Draw header background with gradient
canvas.drawRoundRect(
margin, currentY,
pageWidth - margin,
currentY + headerHeight,
8f, 8f,
Paint().apply { shader = gradient }
)
// Draw column headers
var xPos = margin + 15f
canvas.drawText("Employee Name", xPos, currentY + 30f, headerPaint)
xPos += 150f
canvas.drawText("Status", xPos, currentY + 30f, headerPaint)
xPos += 80f
canvas.drawText("Check-in Times", xPos, currentY + 30f, headerPaint)
xPos += 150f
canvas.drawText("Check-out Times", xPos, currentY + 30f, headerPaint)
xPos += 110f
canvas.drawText("Total Hours", xPos, currentY + 30f, headerPaint)
currentY += headerHeight + 5f
}
private fun drawEmployeeAttendanceRow(
employee: Employee,
attendanceRecord: AttendanceRecord,
regularPaint: Paint,
highlightPaint: Paint
) {
// Calculate row height based on number of check-ins/outs
val entriesCount = maxOf(attendanceRecord.checkInTimes.size, attendanceRecord.checkOutTimes.size)
val rowHeight = 40f + (entriesCount * 20f)
// Draw row background
canvas.drawRoundRect(
margin, currentY,
pageWidth - margin,
currentY + rowHeight,
4f, 4f,
Paint().apply {
color = backgroundColor
}
)
// Draw employee name
canvas.drawText(
employee.Name,
margin + 15f,
currentY + 25f,
regularPaint
)
// Draw status
val status = if (attendanceRecord.isCompleted) "Present" else "Incomplete"
val statusPaint = if (attendanceRecord.isCompleted) {
Paint().apply {
textSize = 12f
typeface = Typeface.create("sans-serif-medium", Typeface.BOLD)
color = Color.rgb(46, 204, 113) // Green for present
}
} else {
Paint().apply {
textSize = 12f
typeface = Typeface.create("sans-serif-medium", Typeface.BOLD)
color = Color.rgb(231, 76, 60) // Red for incomplete
}
}
canvas.drawText(
status,
margin + 165f,
currentY + 25f,
statusPaint
)
// Draw check-in times
var checkInY = currentY + 25f
attendanceRecord.checkInTimes.forEachIndexed { index, time ->
// Extract only the time portion for display
val simplifiedTime = try {
val parsedTime = SimpleDateFormat("dd MMMM yyyy hh:mm:ss a", Locale.getDefault()).parse(time)
SimpleDateFormat("hh:mm:ss a", Locale.getDefault()).format(parsedTime)
} catch (e: Exception) {
time // Fallback if parsing fails
}
canvas.drawText(
"$simplifiedTime",
margin + 245f,
checkInY,
regularPaint
)
checkInY += 20f
}
// Draw check-out times
var checkOutY = currentY + 25f
attendanceRecord.checkOutTimes.forEachIndexed { index, time ->
// Extract only the time portion for display
val simplifiedTime = try {
val parsedTime = SimpleDateFormat("dd MMMM yyyy hh:mm:ss a", Locale.getDefault()).parse(time)
SimpleDateFormat("hh:mm:ss a", Locale.getDefault()).format(parsedTime)
} catch (e: Exception) {
time // Fallback if parsing fails
}
canvas.drawText(
"$simplifiedTime",
margin + 395f,
checkOutY,
regularPaint
)
checkOutY += 20f
}
// Calculate and draw total hours
val totalHours = calculateTotalHours(attendanceRecord.checkInTimes, attendanceRecord.checkOutTimes)
canvas.drawText(
String.format("%.2f hrs", totalHours),
margin + 505f,
currentY + 25f,
highlightPaint
)
currentY += rowHeight + 10f
}
private fun drawEmployeeAbsentRow(
employee: Employee,
regularPaint: Paint,
highlightPaint: Paint
) {
val rowHeight = 40f
// Draw row background with a slight red tint for absent
canvas.drawRoundRect(
margin, currentY,
pageWidth - margin,
currentY + rowHeight,
4f, 4f,
Paint().apply {
color = Color.rgb(245, 233, 233) // Light red tint
}
)
// Draw employee name
canvas.drawText(
employee.Name,
margin + 15f,
currentY + 25f,
regularPaint
)
// Draw absent status
val absentPaint = Paint().apply {
textSize = 12f
typeface = Typeface.create("sans-serif-medium", Typeface.BOLD)
color = Color.rgb(231, 76, 60) // Red for absent
}
canvas.drawText(
"Absent",
margin + 165f,
currentY + 25f,
absentPaint
)
// Draw dashes for check-in and check-out
canvas.drawText(
"",
margin + 245f,
currentY + 25f,
regularPaint
)
canvas.drawText(
"",
margin + 395f,
currentY + 25f,
regularPaint
)
// Draw zero hours
canvas.drawText(
"0.00 hrs",
margin + 505f,
currentY + 25f,
highlightPaint
)
currentY += rowHeight + 10f
}
private fun calculateTotalHours(checkInTimes: List<String>, checkOutTimes: List<String>): Float {
var totalMinutes = 0f
val dateFormat = SimpleDateFormat("dd MMMM yyyy hh:mm:ss a", Locale.getDefault())
// Calculate time for each check-in/check-out pair
for (i in 0 until minOf(checkInTimes.size, checkOutTimes.size)) {
try {
val checkInTime = dateFormat.parse(checkInTimes[i])
val checkOutTime = dateFormat.parse(checkOutTimes[i])
if (checkInTime != null && checkOutTime != null) {
// Calculate difference in minutes
val diffMillis = checkOutTime.time - checkInTime.time
val diffMinutes = diffMillis / (1000 * 60)
totalMinutes += diffMinutes
}
} catch (e: Exception) {
// Skip if there's an error parsing the date
continue
}
}
// Convert minutes to hours
return totalMinutes / 60
}
private fun drawFooter(paint: Paint) {
paint.textAlign = Paint.Align.CENTER
paint.textSize = 10f
val currentDate = SimpleDateFormat("dd MMMM yyyy HH:mm", Locale.getDefault())
.format(Calendar.getInstance().time)
canvas.drawText(
"Generated on: $currentDate | Page $pageNumber",
pageWidth/2,
pageHeight - margin/2,
paint
)
}
private fun saveDocument(selectedDate: Calendar): File {
val dateStr = SimpleDateFormat("yyyy_MM_dd", Locale.getDefault())
.format(selectedDate.time)
val fileName = "Daily_Attendance_Report_$dateStr.pdf"
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
fileName
)
FileOutputStream(file).use { outputStream ->
document.writeTo(outputStream)
}
document.close()
return file
}
private fun createNewPage() {
val pageInfo = PdfDocument.PageInfo.Builder(
pageWidth.toInt(),
pageHeight.toInt(),
pageNumber++
).create()
currentPage = document.startPage(pageInfo)
canvas = currentPage.canvas
currentY = margin
}
private fun finishCurrentPage() {
document.finishPage(currentPage)
}
}