Files
authsec_flutter/lib/screens/Attendance/attendance_storage.dart
T
2026-09-30 09:13:45 +05:30

168 lines
4.8 KiB
Dart

import 'package:path/path.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';
// ---------------------------------------------------------------------------
// Phone-only storage for attendance (no backend yet).
//
// - SharedPreferences keeps ONE value: the currently open session id
// (so UI, background service and reboot recovery all agree).
// - sqflite keeps every session + every GPS dot.
//
// NOTE: the database handle is opened per-isolate (statics are per-isolate),
// so the background service gets its own safe handle.
// ---------------------------------------------------------------------------
class AttendanceStorage {
static const String _dbName = 'attendance.db';
static const String _prefsActiveSessionKey = 'attendance_active_session';
static Database? _db;
static Future<Database> get _database async {
if (_db != null) return _db!;
final String path = join(await getDatabasesPath(), _dbName);
_db = await openDatabase(
path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute('''
CREATE TABLE attendance_sessions(
session_id TEXT PRIMARY KEY,
punch_in TEXT NOT NULL,
punch_out TEXT,
status TEXT NOT NULL,
total_km_haversine REAL DEFAULT 0
)
''');
await db.execute('''
CREATE TABLE attendance_points(
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
lat REAL NOT NULL,
lng REAL NOT NULL,
accuracy REAL NOT NULL,
is_mocked INTEGER NOT NULL DEFAULT 0,
timestamp TEXT NOT NULL
)
''');
await db.execute('''
CREATE INDEX idx_points_session ON attendance_points(session_id)
''');
},
);
return _db!;
}
// ---------------- active session (SharedPreferences) ----------------
static Future<void> setActiveSession(String sessionId) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_prefsActiveSessionKey, sessionId);
}
static Future<String?> getActiveSession() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_prefsActiveSessionKey);
}
static Future<void> clearActiveSession() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(_prefsActiveSessionKey);
}
// ---------------- sessions ----------------
static Future<void> createSession(String sessionId, DateTime punchIn) async {
final db = await _database;
await db.insert('attendance_sessions', {
'session_id': sessionId,
'punch_in': punchIn.toIso8601String(),
'punch_out': null,
'status': 'OPEN',
'total_km_haversine': 0,
});
}
static Future<Map<String, dynamic>?> getSession(String sessionId) async {
final db = await _database;
final rows = await db.query(
'attendance_sessions',
where: 'session_id = ?',
whereArgs: [sessionId],
limit: 1,
);
if (rows.isEmpty) return null;
return rows.first;
}
static Future<void> closeSession(
String sessionId,
DateTime punchOut,
double totalKmHaversine,
) async {
final db = await _database;
await db.update(
'attendance_sessions',
{
'punch_out': punchOut.toIso8601String(),
'status': 'CLOSED',
'total_km_haversine': totalKmHaversine,
},
where: 'session_id = ?',
whereArgs: [sessionId],
);
}
static Future<List<Map<String, dynamic>>> getRecentSessions(
{int limit = 20}) async {
final db = await _database;
return db.query(
'attendance_sessions',
orderBy: 'punch_in DESC',
limit: limit,
);
}
// ---------------- points ----------------
static Future<void> insertPoint({
required String sessionId,
required double lat,
required double lng,
required double accuracy,
required bool isMocked,
required DateTime timestamp,
}) async {
final db = await _database;
await db.insert('attendance_points', {
'session_id': sessionId,
'lat': lat,
'lng': lng,
'accuracy': accuracy,
'is_mocked': isMocked ? 1 : 0,
'timestamp': timestamp.toIso8601String(),
});
}
static Future<List<Map<String, dynamic>>> getPoints(
String sessionId) async {
final db = await _database;
return db.query(
'attendance_points',
where: 'session_id = ?',
whereArgs: [sessionId],
orderBy: 'timestamp ASC',
);
}
static Future<int> countPoints(String sessionId) async {
final db = await _database;
final rows = await db.rawQuery(
'SELECT COUNT(*) AS c FROM attendance_points WHERE session_id = ?',
[sessionId],
);
return (rows.first['c'] as int?) ?? 0;
}
}