Code changes by azmat
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user