import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'main.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'Data.dart'; import 'package:sqflite/sqflite.dart'; import 'package:path_provider/path_provider.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:sn_progress_dialog/sn_progress_dialog.dart'; import 'Tasks.dart'; late ProgressDialog? progressDialog; late http.Response loginResponse; late Database cacheDb; late String username; List categories = []; List taskTypes = []; List activities=[]; bool offline = true; bool registered = false; Future login(String _username, String password) async { final prefs = await SharedPreferences.getInstance(); username = _username; var device_id = await Settings.UUID(); try { loginResponse = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/login.php'), body: { "username": _username, "password": password, "device_id": device_id })); if (loginResponse.body.toLowerCase().contains("success")) { offline = false; username = _username; registered =loginResponse.body.toLowerCase().contains("register"); print("registered : $registered"); if(registered){ prefs.setBool("registered", true); } } }catch(e){ offline=true; } return loginResponse; } Future initUserData() async { await initCacheDatabase(); await refreshUserData(); print('Initializing UserData...'); if (offline) { print('Going offline mode.'); } } bool userDataInitiated =false; Future refreshUserData() async{ ShowProgress("Loading data"); // categories= await GetCategories(true); // taskTypes= await GetTaskTypes(true); // activities= await GetActivities(true); await updateCatsList(); await updateTasksList(); await updateActList(); userDataInitiated=true; HideProgress(); } Future cacheDbExist() async{ Directory directory = await getApplicationDocumentsDirectory(); return databaseFactory.databaseExists(directory.path + 'cache.db'); } Future updateCatsList() async{ //print('Updating with localCache'); // categories = await GetCategories(true); print('Checking if can refresh'); categories = await GetCategories(false); } Future updateTasksList() async{ // print('Updating with localCache'); // taskTypes = await GetTaskTypes(true); print('Checking if can refresh'); taskTypes = await GetTaskTypes(false); } Future updateActList() async{ //print('Updating with localCache'); //activities = await GetActivities(true); print('Checking if can refresh'); activities = await GetActivities(false); } Future initCacheDatabase() async { Directory directory = await getApplicationDocumentsDirectory(); print('database at ' + directory.path + 'cache.db'); cacheDb = await openDatabase(directory.path + 'cache.db', version: 1, onCreate: onCacheDatabaseCreate, onUpgrade: onCacheDatabaseUpgrade); await UserOperations.executeQueries(); } void onCacheDatabaseCreate(Database db, int newVersion) async { String CategoriesTableSQL = 'CREATE TABLE Categories(${Category.colCatId} VARCHAR(255) PRIMARY KEY,${Category.colName} TEXT, ${Category.colColor} TEXT, ${Category.colProductive} INTEGER)'; // print(CategoriesTableSQL); await db.execute(CategoriesTableSQL); print("Initiated Categories Table"); String TaskTableSQL = 'CREATE TABLE TaskTypes(id TEXT PRIMARY KEY, ${TaskType.colName} TEXT, ${TaskType.colCategory} TEXT, ' 'FOREIGN KEY (${TaskType.colCategory}) REFERENCES Categories(${Category.colCatId}))'; // print(TaskTableSQL); await db.execute(TaskTableSQL); String ActivityTableSQL = 'CREATE TABLE Activities(id INTEGER PRIMARY KEY AUTOINCREMENT, ${Activity.colType} INT, ${Activity.colStartTime} DATETIME, ${Activity.colEndTime} DATETIME, ${Activity.colMetadata} TEXT, ' 'FOREIGN KEY (${Activity.colType}) REFERENCES TaskTypes(id))'; // print(ActivityTableSQL); await db.execute(ActivityTableSQL); String QueriesTableSQL = 'CREATE TABLE Queries(id INTEGER PRIMARY KEY AUTOINCREMENT, ${Queries.colLink} TEXT,${Queries.colData} TEXT)'; // print(QueriesTableSQL); await db.execute(QueriesTableSQL); final prefs = await SharedPreferences.getInstance(); if(prefs.getBool("registered") ?? false) { addInitialDataToCache(); prefs.setBool("registered", false); } // GetCategories(); } Future addInitialDataToCache() async{ ShowProgress("Initializing User Data"); print("adding init data"); await Future.delayed(const Duration(seconds: 1)); //Insert Initial Entries for(Category element in InitialData.getCategories(username)){ await UserOperations.addCategory(element.name, element.color, element.productive,bulk: true); } for(TaskType element in InitialData.getTaskTypes(username)){ await UserOperations.addTaskType(element.name, element.category, bulk: true); // Map data = { // TaskType.colName: element.name, // TaskType.colCategory: element.category // }; // await cacheDb.insert('TaskTypes', data); } HideProgress(); await UserOperations.executeQueries(); await refreshUserData(); } void onCacheDatabaseUpgrade(Database db, int oldVersion, int newVersion) async { //ValidateCacheDB(); print('Upgrading CacheDB from ver.$oldVersion to ver.$newVersion'); } Future> GetCategories(bool forceOffline) async{ List _categories = []; if(offline || forceOffline){ //Retreive from cacheDB }else{ //Check if server got updated, If not go for cache var android_id = await Settings.UUID(); //Validate device_id to check updates bool catsUpdated = true; try{ http.Response update_response = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/check_update.php'), body: {"username": username, "device_id":android_id})); final data = update_response.body.split(','); catsUpdated = data[0] == '1'; }catch(e){ print(e); } print("Need to update : ${!catsUpdated}"); //Update CacheDB //if(!catsUpdated){ await UpdateCategoriesFromServer(); // } } List cats = await cacheDb.query('Categories'); print(cats.length); for(Map element in cats){ String? catName = element[Category.colName].toString(); String? catColor = element[Category.colColor].toString(); String? catProductive = element[Category.colProductive].toString(); if(catName==null || catColor==null || catProductive==null){ print("Something is null!"); print("name:{$catName}, color:{$catColor}, prod:{$Category.colProductive}"); continue; } print("name:{$catName}, color:{$catColor}, prod:{$catProductive}"); _categories.add(Category(username + catName, catName, catColor, ParseBool(catProductive))); } categories = _categories; return categories; } Future UpdateCategoriesFromServer() async{ print("Updating Categories as $username"); try { http.Response response = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/get_categories.php'), body: { "username": username, "device_id": await Settings.UUID() })); print(response.body); List data = response.body.split(""); // await cacheDb.delete("Categories"); for (var value in data) { Map cat = jsonDecode(value); //print(catData); await cacheDb.rawInsert( "INSERT OR REPLACE INTO Categories (${Category.colCatId},${Category .colName},${Category.colProductive},${Category.colColor}) " "VALUES ('${cat['category_id']}','${cat['name']}',${cat['productive']},'${cat['color']}') "); } }catch(e){ offline=true; } } Future> GetTaskTypes(bool forceOffline) async{ List _taskTypes = []; if(offline || forceOffline){ //Retreive from cacheDB }else{ //Check if server got updated, If not go for cache var android_id = await Settings.UUID(); bool updated =true; try{ //Validate device_id to check updates http.Response update_response = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/check_update.php'), body: {"username": username, "device_id":android_id})); final data = update_response.body.split(','); updated = data[1] == '1'; }catch(e){ print(e); } print("Need to update : ${!updated}"); //Update CacheDB // if(!updated){ await UpdateTaskTypesFromServer(); // } } await Future.delayed(Duration(seconds: 1)); List cats = await cacheDb.query('TaskTypes'); print(cats.length); for(Map element in cats){ String? id = element[TaskType.colId].toString(); String? name = element[TaskType.colName].toString(); String? category = element[TaskType.colCategory].toString(); Category? cat = await getCatFromId(category); if(id==null || name==null || category==null){ print("Something is null!"); print("name:{$name}, cat:{$category}, prod:{$id}"); continue; } print("name:{$name}, cat:{$category}, prod:{$id}"); _taskTypes.add(TaskType(id,name,category,cat)); } taskTypes = _taskTypes; return taskTypes; } Future UpdateTaskTypesFromServer() async{ // await GetCategories(true); print("Updating TaskTypes as $username"); try { http.Response response = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/get_taskTypes.php'), body: { "username": username, "device_id": await Settings.UUID() })); print(response.body); List data = response.body.split(""); await cacheDb.delete("TaskTypes"); for (var value in data) { Map cat = jsonDecode(value); print(cat); await cacheDb.rawInsert( "INSERT OR REPLACE INTO TaskTypes (${TaskType.colId},${TaskType .colName},${TaskType.colCategory}) " "VALUES ('${cat['task_id']}','${cat['name']}','${cat['category_id']}') "); print(await cacheDb.query("TaskTypes")); } }catch(e){ offline=true; } } Future> GetActivities(bool forceOffline) async{ List _activities = []; if(offline || forceOffline){ //Retreive from cacheDB print('offline, refreshing activities'); }else{ //Check if server got updated, If not go for cache var android_id = await Settings.UUID(); bool updated =true; try{ //Validate device_id to check updates http.Response update_response = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/check_update.php'), body: {"username": username, "device_id":android_id})); final data = update_response.body.split(','); updated = data[2] == '1'; }catch(e){ print(e); } print("Need to update activities : ${!updated}"); //Update CacheDB //if(!updated){ await UpdateActivitiesFromServer(); //} } List cats = await cacheDb.rawQuery('SELECT * FROM Activities ORDER BY ${Activity.colStartTime} DESC'); print(cats.length); for(Map element in cats){ String? type = element[Activity.colType].toString(); String? startTime = element[Activity.colStartTime].toString(); String? endTime = element[Activity.colEndTime].toString(); String? metadata = element[Activity.colMetadata].toString(); TaskType? taskType = await getTaskFromId(type); if(type==null || startTime==null || endTime==null || taskType==null){ print("Something is null!\ntype:${type==null}, startTime:${startTime==null}, eTime:${endTime==null}, taskType${taskType==null}"); print("TaskType:{$type}, Start Time:{$startTime}, endTime:{$endTime}, metadata:${metadata}"); continue; } print("TaskType:{$type}, Start Time:{$startTime}, endTime:{$endTime}, metadata:${metadata}"); _activities.add(Activity(taskType, DateTime.parse(startTime), DateTime.parse(endTime), metadata: metadata)); } activities = _activities; return activities; } Future UpdateActivitiesFromServer() async{ print("Updating Activities as $username"); try { http.Response response = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/get_activities.php'), body: { "username": username, "device_id": await Settings.UUID() })); await cacheDb.rawDelete("DELETE FROM Activities"); print('Truncate Activity Table before'); print("Activity response: ${response.body}"); if(response.body.contains("{")){ List data = response.body.split(""); for (var value in data){ Map cat = jsonDecode(value); print(cat); await cacheDb.rawInsert( "INSERT OR REPLACE INTO Activities (${Activity.colType}, ${Activity.colStartTime}, ${Activity.colEndTime}, ${Activity.colMetadata}) " "VALUES ('${cat['task_id']}', '${cat['sTime']}','${cat['eTime']}', '${cat['metadata']}') "); } }else{ print("No activities for now"); } }catch(e){ print("Error : $e @ updating activities"); offline=true; } } Future getTaskFromId(String taskId) async{ // await GetTaskTypes(false); TaskType? cat = null; for (var element in taskTypes){ if(element.id == taskId){ cat= element; cat?.cat = await getCatFromId((cat?.category ?? '')); } } if(cat==null){ print('Got null tasktype for ${taskId} after searching on ${taskTypes.length}'); } return cat; } Future getCatFromId(String catId) async{ // await GetTaskTypes(false); Category? cat = null; for (var element in categories) { if(element.category_id == catId){ cat= element; } } return cat; } //Helpers class Helpers { Future _getId() async { var deviceInfo = DeviceInfoPlugin(); if (Platform.isIOS) { // import 'dart:io' var iosDeviceInfo = await deviceInfo.iosInfo; return iosDeviceInfo.identifierForVendor; // unique ID on iOS } else { var androidDeviceInfo = await deviceInfo.androidInfo; return androidDeviceInfo.androidId; // unique ID on Android } } } bool ParseBool(obj){ return obj.toString().toLowerCase()=="true" || obj.toString()=="1"; } class UserOperations{ static Future addCategory(String name, String color, bool productive, {bool bulk = false}) async{ Map queryBody= { 'username': username, 'device_id': await Settings.UUID(), 'name' : name, 'color':color, 'productive': productive ? '1':'0' }; //Add Query Map query = { Queries.colLink: 'add_category', Queries.colData: jsonEncode(queryBody) }; print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}"); await cacheDb.insert('Queries', query); //update Cache Map data = { Category.colCatId: username+name, Category.colName: name, Category.colColor: color, Category.colProductive: productive }; await cacheDb.insert('Categories', data); await GetCategories(true); if(!bulk){ //Add to server and refresh Cache await executeQueries(); } } static Future addTaskType(String name, String category, {bool bulk = false}) async{ Map queryBody= { 'id':username+name, 'username': username, 'device_id': await Settings.UUID(), 'name' : name, 'category': username + category }; //Add Query Map query = { Queries.colLink: 'add_taskType', Queries.colData: jsonEncode(queryBody) }; print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}"); await cacheDb.insert('Queries', query); //update Cache Map data = { TaskType.colId: username+name, Category.colName: name, Category.colCatId: username + category }; await cacheDb.insert('TaskTypes', data); await GetTaskTypes(true); if(!bulk){ //Add to server and refresh Cache await executeQueries(); } } static Future addActivity(String type, String sTime,String eTime, {String metadata = 'null',bool bulk = false, Function(int)? onOverlap}) async{ //Check for timeoverlapse activities= await GetActivities(true); int? overlapCount = Sqflite.firstIntValue(await cacheDb.rawQuery("SELECT COUNT(*) FROM Activities WHERE (((${Activity.colStartTime} < datetime('$sTime')) AND ((${Activity.colEndTime} > datetime('$eTime')) OR (${Activity.colEndTime} < datetime('$eTime') AND ${Activity.colEndTime} > datetime('$sTime')))) OR (${Activity.colStartTime} > datetime('$sTime') AND ${Activity.colStartTime} < datetime('$eTime')) OR (${Activity.colStartTime}=datetime('$sTime') AND ${Activity.colEndTime}=datetime('$eTime')))")); print("ActivityOverlaps: $overlapCount"); if(overlapCount! > 0){ onOverlap!(overlapCount); } Map queryBody= { 'username': username, 'device_id': await Settings.UUID(), 'type' : username+type, 'sTime': sTime, 'eTime':eTime, 'metadata': metadata }; if(metadata.length > 0){ } //Add Query Map query = { Queries.colLink: 'add_activity', Queries.colData: jsonEncode(queryBody) }; print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}"); await cacheDb.insert('Queries', query); //update Cache Map data = { Activity.colType: username+type, Activity.colStartTime: sTime, Activity.colEndTime: eTime, Activity.colMetadata: metadata }; await cacheDb.insert('Activities', data); activities= await GetActivities(false); if(!bulk){ //Add to server and refresh Cache await executeQueries(); } } static Future deleteTask(String name,{bulk=false}) async{ Map queryBody= { 'id':username+name, 'username': username, 'device_id': await Settings.UUID(), }; //Add Query Map query = { Queries.colLink: 'delete_taskType', Queries.colData: jsonEncode(queryBody) }; print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}"); await cacheDb.insert('Queries', query); //update Cache Map data = { TaskType.colId: username+name, Category.colName: name, }; await cacheDb.rawDelete("DELETE FROM TaskTypes WHERE id='${username+name}'"); await GetTaskTypes(true); //Add to server and refresh Cache if(!bulk) { await executeQueries(); } } static Future deleteCategory(String name,{bulk=false}) async{ Map queryBody= { 'id':username+name, 'username': username, 'device_id': await Settings.UUID() , }; //Add Query Map query = { Queries.colLink: 'delete_category', Queries.colData: jsonEncode(queryBody) }; print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}"); await cacheDb.insert('Queries', query); //update Cache Map data = { TaskType.colId: username+name, Category.colName: name, }; await cacheDb.rawDelete("DELETE FROM Categories WHERE ${Category.colCatId}='${username+name}'"); await GetCategories(true); //Add to server and refresh Cache if(!bulk) { await executeQueries(); } } static Future deleteActivity(Activity activity,{bulk=false}) async{ Map queryBody= { 'username': username, 'device_id': await Settings.UUID(), 'sTime': activity.startTime.toString(), 'eTime':activity.endTime.toString(), }; //Add Query Map query = { Queries.colLink: 'delete_activity', Queries.colData: jsonEncode(queryBody) }; print("adding new query ${query[Queries.colLink]} : ${jsonEncode(queryBody)}"); await cacheDb.insert('Queries', query); //update Cache // Map data = { // TaskType.colId: username+name, // Category.colName: name, // }; // await cacheDb.rawDelete("DELETE FROM Categories WHERE ${Category.colCatId}='${username+name}'"); await GetCategories(true); //Add to server and refresh Cache if(!bulk) { await executeQueries(); } } static Future executeQueries() async{ if(offline){ print("Cannot executre queries, Offline!"); return; } ShowProgress("Syncing"); List> queries = await cacheDb.query('Queries'); for(Map element in queries){ int id = int.parse(element['id'].toString()); String? file = element[Queries.colLink].toString(); String? data = element[Queries.colData].toString(); if(file==null || data==null){ print("Null query, Ignoring..."); continue; } print("Query[\n file:$file, \ndata:$data]"); //Execute the http here Map body = jsonDecode(data); try { http.Response queryResponse = (await http.post( Uri.parse('http://161.97.127.136/task_tracker/$file.php'), body: body)); print("Query executed : Results{${queryResponse.body}"); if (queryResponse.body.toLowerCase().contains("success")) { await cacheDb.rawDelete('DELETE FROM Queries WHERE id=$id'); } offline=false; }catch(e){ offline=true; } } HideProgress(); } } void ShowProgress(msg){ //try{progressDialog?.show(max: 100, msg: msg);}catch(e){} } void HideProgress(){ // try{progressDialog?.update(value: 100);}catch(e){} }