prem / lib /services /storage_service.dart
Nitishkumar-ai's picture
Deploy source code to Hugging Face without binaries
c25dcd7
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class StorageService {
static final StorageService _instance = StorageService._internal();
factory StorageService() => _instance;
StorageService._internal();
Database? _database;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB();
return _database!;
}
Future<Database> _initDB() async {
String path = join(await getDatabasesPath(), 'premithius.db');
return await openDatabase(
path,
version: 3,
onCreate: _createTables,
onUpgrade: _onUpgrade,
);
}
Future<void> _createTables(Database db, int version) async {
await db.execute('''
CREATE TABLE keyboard_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
app_context TEXT,
original_text TEXT,
action TEXT,
result_text TEXT,
accepted INTEGER DEFAULT 0,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE scam_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender TEXT,
message_preview TEXT,
category TEXT,
confidence REAL,
threat_level TEXT,
user_feedback TEXT,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE scam_patterns (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pattern TEXT NOT NULL,
category TEXT,
hit_count INTEGER DEFAULT 1,
user_confirmed INTEGER DEFAULT 0,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE whitelist (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sender TEXT UNIQUE,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE upi_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
vpa TEXT,
amount REAL,
fraud_type TEXT,
confidence REAL,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE shield_warnings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
warning_type TEXT,
app_context TEXT,
user_action TEXT,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE permission_anomalies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
app_package TEXT,
permission TEXT,
access_count INTEGER,
time_window TEXT,
severity TEXT,
created_at INTEGER
)
''');
await db.execute('''
CREATE TABLE festival_calendar (
id INTEGER PRIMARY KEY,
name TEXT,
start_date TEXT,
end_date TEXT,
sensitivity_multiplier REAL
)
''');
await _createAppLockTables(db);
await _createVpnTables(db);
await _seedScamDomains(db);
}
Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async {
if (oldVersion < 2) {
await _createAppLockTables(db);
}
if (oldVersion < 3) {
await _createVpnTables(db);
await _seedScamDomains(db);
}
}
Future<void> _createAppLockTables(Database db) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS locked_apps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
package_name TEXT UNIQUE NOT NULL,
app_name TEXT NOT NULL,
auth_method TEXT DEFAULT 'biometric',
locked_at INTEGER NOT NULL,
access_attempts INTEGER DEFAULT 0,
temporary_unlock_until INTEGER DEFAULT 0
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS access_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
package_name TEXT NOT NULL,
app_name TEXT,
was_allowed INTEGER DEFAULT 0,
attempted_at INTEGER NOT NULL
)
''');
}
// --- Helpers for Scam Logging ---
Future<int> insertScamLog(Map<String, dynamic> row) async {
Database db = await database;
return await db.insert('scam_log', row);
}
Future<List<Map<String, dynamic>>> getScamLogs() async {
Database db = await database;
return await db.query('scam_log', orderBy: 'created_at DESC');
}
// --- Helpers for Pattern Learning ---
Future<int> insertKeyboardLog(Map<String, dynamic> row) async {
Database db = await database;
return await db.insert('keyboard_history', row);
}
Future<Map<String, dynamic>?> checkLocalPattern(String text) async {
Database db = await database;
// VERY simplified exact phrase matching for hackathon scale.
// Real implementation would do tokenized LIKE matching.
final List<Map<String, dynamic>> maps = await db.query('scam_patterns');
for (var row in maps) {
if (text.toLowerCase().contains((row['pattern'] as String).toLowerCase())) {
return row;
}
}
return null;
}
// --- App Lock Helpers ---
Future<void> insertLockedApp({
required String packageName,
required String appName,
String authMethod = 'biometric',
}) async {
final db = await database;
await db.insert(
'locked_apps',
{
'package_name': packageName,
'app_name': appName,
'auth_method': authMethod,
'locked_at': DateTime.now().millisecondsSinceEpoch,
'access_attempts': 0,
'temporary_unlock_until': 0,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<void> removeLockedApp(String packageName) async {
final db = await database;
await db.delete('locked_apps', where: 'package_name = ?', whereArgs: [packageName]);
}
Future<List<Map<String, dynamic>>> getLockedApps() async {
final db = await database;
return db.query('locked_apps', orderBy: 'locked_at DESC');
}
Future<bool> isAppLocked(String packageName) async {
final db = await database;
final rows = await db.query(
'locked_apps',
where: 'package_name = ?',
whereArgs: [packageName],
);
if (rows.isEmpty) return false;
final tempUntil = rows.first['temporary_unlock_until'] as int? ?? 0;
return tempUntil == 0 || DateTime.now().millisecondsSinceEpoch > tempUntil;
}
Future<void> setTemporaryUnlock(String packageName, {required DateTime expires}) async {
final db = await database;
await db.update(
'locked_apps',
{'temporary_unlock_until': expires.millisecondsSinceEpoch},
where: 'package_name = ?',
whereArgs: [packageName],
);
}
Future<void> logAccessAttempt({
required String packageName,
required bool wasAllowed,
String? appName,
}) async {
final db = await database;
await db.insert('access_log', {
'package_name': packageName,
'app_name': appName,
'was_allowed': wasAllowed ? 1 : 0,
'attempted_at': DateTime.now().millisecondsSinceEpoch,
});
}
Future<int> getAccessAttemptsTodayCount(String packageName) async {
final db = await database;
final todayStart = DateTime.now()
.subtract(const Duration(hours: 24))
.millisecondsSinceEpoch;
final result = await db.rawQuery(
'SELECT COUNT(*) as cnt FROM access_log WHERE package_name = ? AND attempted_at > ? AND was_allowed = 0',
[packageName, todayStart],
);
return result.first['cnt'] as int? ?? 0;
}
Future<void> _createVpnTables(Database db) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS scam_domains (
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
category TEXT NOT NULL,
source TEXT NOT NULL,
hit_count INTEGER DEFAULT 0,
last_hit INTEGER,
created_at INTEGER NOT NULL
)
''');
await db.execute('''
CREATE TABLE IF NOT EXISTS vpn_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_type TEXT NOT NULL,
domain TEXT,
category TEXT,
app_package TEXT,
created_at INTEGER NOT NULL
)
''');
}
Future<void> _seedScamDomains(Database db) async {
try {
final String contents = await rootBundle.loadString('assets/blocklist/india_scam_domains.txt');
final lines = const LineSplitter().convert(contents);
Batch batch = db.batch();
String currentCategory = 'Known Scam Domain';
for (var line in lines) {
final clean = line.trim().toLowerCase();
if (clean.isEmpty) continue;
if (clean.startsWith('#')) {
currentCategory = clean.substring(1).trim();
continue;
}
batch.insert('scam_domains', {
'domain': clean,
'category': currentCategory,
'source': 'seed',
'hit_count': 0,
'created_at': DateTime.now().millisecondsSinceEpoch,
}, conflictAlgorithm: ConflictAlgorithm.ignore);
}
await batch.commit(noResult: true);
} catch (e) {
print('Error seeding scam domains: $e');
}
}
}