diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4..ea1014d 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b7..20f60dd 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 09d4391..d330048 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d..7bada5c 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372e..25a2300 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/images/hourglass_base.png b/images/hourglass_base.png new file mode 100644 index 0000000..81b18e1 Binary files /dev/null and b/images/hourglass_base.png differ diff --git a/images/hourglass_half.png b/images/hourglass_half.png new file mode 100644 index 0000000..8fb6882 Binary files /dev/null and b/images/hourglass_half.png differ diff --git a/lib/Activities.dart b/lib/Activities.dart index 23e2691..59af4d7 100644 --- a/lib/Activities.dart +++ b/lib/Activities.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'main.dart'; @@ -6,15 +7,15 @@ import 'Data.dart'; import 'User.dart' as User; import 'package:sn_progress_dialog/sn_progress_dialog.dart'; - - class Activities extends StatefulWidget { const Activities({Key? key}) : super(key: key); @override _ActivitiesState createState() => _ActivitiesState(); } + late ProgressDialog progressDialog; + class _ActivitiesState extends State { @override void initState() { @@ -26,9 +27,8 @@ class _ActivitiesState extends State { @override Widget build(BuildContext context) { - progressDialog=ProgressDialog(context: context); + progressDialog = ProgressDialog(context: context); return Scaffold( - floatingActionButton: FloatingActionButton.extended( onPressed: () { Navigator.of(context) @@ -39,7 +39,7 @@ class _ActivitiesState extends State { icon: Icon(Icons.add)), appBar: AppBar( title: Row( - mainAxisSize: MainAxisSize.max, + mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ @@ -47,15 +47,29 @@ class _ActivitiesState extends State { SizedBox(width: 10), Text('Activities') ]), - (selecting)?Row(children: [ - InkWell(onTap: (){ - DeleteSelectedTasks(); - }, child: Icon(Icons.delete,size: 30,)), - SizedBox(width: 20,), - InkWell(onTap: (){setState(() { - selecting=false; - });}, child: Icon(Icons.close,size: 30),) - ]) : Container(), + (selecting) + ? Row(children: [ + InkWell( + onTap: () { + DeleteSelectedTasks(); + }, + child: Icon( + Icons.delete, + size: 30, + )), + SizedBox( + width: 20, + ), + InkWell( + onTap: () { + setState(() { + selecting = false; + }); + }, + child: Icon(Icons.close, size: 30), + ) + ]) + : Container(), ], )), drawer: navDrawer(context, 2), @@ -68,10 +82,14 @@ class _ActivitiesState extends State { } void UpdateList() async { - try{progressDialog.show(max:100,msg: 'Loading Activities');}catch(e){} + try { + progressDialog.show(max: 100, msg: 'Loading Activities'); + } catch (e) {} await User.updateActList(); setState(() {}); - try{progressDialog.update(value: 100);}catch(e){} + try { + progressDialog.update(value: 100); + } catch (e) {} } List PrintTasks() { @@ -79,19 +97,52 @@ class _ActivitiesState extends State { print('Priting cats : ' + User.taskTypes.length.toString()); String lastDate = ""; DateFormat dFormat = DateFormat("MM/dd"); + Map productivtyActs = {}; + Map unproductivtyActs = {}; + Map totalMinutes = {}; for (var element in User.activities) { String thisDate = dFormat.format(element.startTime); - if(thisDate != lastDate){ - _tasks.add(DateSeperator(thisDate)); - lastDate=thisDate; + int thisMinutes= element.endTime.difference(element.startTime).inMinutes; + if(totalMinutes.containsKey(thisDate)){ + if((totalMinutes[thisDate]??0) < thisMinutes){ + totalMinutes[thisDate] = thisMinutes; + } + }else{ + totalMinutes.putIfAbsent(thisDate, () => thisMinutes); + } + + if (element.taskType.cat?.productive ?? false) { + if (productivtyActs.containsKey(thisDate)) { + productivtyActs[thisDate] = (productivtyActs[thisDate]! + thisMinutes); + } else { + productivtyActs.putIfAbsent(thisDate, () => thisMinutes); + } + } else { + if (unproductivtyActs.containsKey(thisDate)) { + unproductivtyActs[thisDate] = (unproductivtyActs[thisDate]! + thisMinutes); + } else { + unproductivtyActs.putIfAbsent(thisDate, () => thisMinutes); + } + } + } + print(productivtyActs); + for (var element in User.activities) { + String thisDate = dFormat.format(element.startTime); + if (thisDate != lastDate) { + int prodActs = productivtyActs[thisDate] ?? 0; + int unProdActs = unproductivtyActs[thisDate] ?? 0; + _tasks.add(DateSeperator(thisDate, prodActs, unProdActs)); + lastDate = thisDate; } String name = element.taskType.name; if (element.taskType.cat == null) { print('Got some null cat : ${element.taskType.name}'); } else { - Color color = HexColor.fromHex(element.taskType.cat?.color ?? '#000000'); + Color color = + HexColor.fromHex(element.taskType.cat?.color ?? '#000000'); bool productive = element.taskType.cat?.productive ?? true; - Widget task = ActivityCard(context, name,element.startTime,element.endTime, productive, color,element); + Widget task = ActivityCard(context, name, element.startTime, + element.endTime, productive, color, element, totalMinutes[thisDate] ?? 0); _tasks.add(task); } } @@ -99,23 +150,65 @@ class _ActivitiesState extends State { return _tasks; } - Widget DateSeperator(date){ + Widget DateSeperator(date, prodActs, unprodActs) { + double prodPercentage = (prodActs / (prodActs + unprodActs)) * 100; return Padding( padding: const EdgeInsets.fromLTRB(10, 20, 10, 0), - child: Column(children: [ - Row(children: [ - Text(date,style: TextStyle(fontSize: 18),) - ],) - ],), + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + date, + style: TextStyle(fontSize: 18), + ), + Row(children: [ + Row( + children: [ + Container(child:Align(child:Text(MinutesToTimeString(prodActs)), alignment: Alignment.center,),width: (prodPercentage) * 1.7, height: 25,decoration: BoxDecoration(color: Colors.green,borderRadius: BorderRadius.horizontal(left: Radius.circular(10))),), + Container(child:Align(child:Text(MinutesToTimeString(unprodActs)), alignment: Alignment.center,),width: (100-prodPercentage)* 1.7,height: 25,decoration: BoxDecoration(color: Colors.red,borderRadius: BorderRadius.horizontal(right: Radius.circular(10))),), + ], + ), + SizedBox(width: 10,), + Text(prodPercentage.toStringAsFixed(1) + "%",style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold,fontSize: 20)) + ],) + // CustomPaint( + // painter: MyPlayerBar(100, prodPercentage.toInt(), + // background: Colors.green, fill: Colors.deepOrange), + // child: Container( + // alignment: Alignment.center, + // height: 25.0, + // width: 200, + // child: Text( + // "Productivity : ${prodPercentage.toStringAsFixed(1)}%", + // style: TextStyle(fontWeight: FontWeight.bold),), + // + // ), + // ), + ], + ) + ], + ), ); } bool selecting = false; - Widget ActivityCard( - BuildContext context, String name,DateTime sTime, DateTime eTime, bool productive, Color color, Activity activity) { + Widget ActivityCard(BuildContext context, String name, DateTime sTime, + DateTime eTime, bool productive, Color color, Activity activity,int totalMinutes) { DateFormat dateFormat = DateFormat("HH:mm"); + int thisMinutes = (activity.endTime.difference(activity.startTime).inMinutes); + int timePercentage = ((thisMinutes / totalMinutes) * 100).toInt(); + // print("$thisMinutes / $totalMinutes"); + bool containsMetadata = ((activity.metadata ?? 'null') != 'null') && + ((activity.metadata ?? '').isNotEmpty); var _timeSpan = eTime.difference(sTime); - String timeSpan = ((_timeSpan.inHours > 0) ? _timeSpan.inHours.toString()+'h ' : '') + ((_timeSpan.inMinutes %60 > 0) ? (_timeSpan.inMinutes%60).toString()+'m ' : ''); + String timeSpan = + ((_timeSpan.inHours > 0) ? _timeSpan.inHours.toString() + 'h ' : '') + + ((_timeSpan.inMinutes % 60 > 0) + ? (_timeSpan.inMinutes % 60).toString() + 'm' + : ''); return Row(children: [ // Container(), (selecting) @@ -132,54 +225,84 @@ class _ActivitiesState extends State { Card( // color: color, - elevation:20, + elevation: 20, shadowColor: color, child: InkWell( onTap: () { //Open Respective Category - if(selecting){ + if (selecting) { OnItemSelected(activity); } - setState(() { - - }); + setState(() {}); }, onLongPress: () { print('gonna delete'); selecting = !selecting; selectedActivities = [activity]; setState(() {}); - }, child: Container( - padding: EdgeInsets.all(10), + padding: EdgeInsets.all(15), child: Column( children: [ Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(name + " ($timeSpan)", - style: TextStyle(color: Colors.white)), - Text(dateFormat.format(sTime) + " - " + dateFormat.format(eTime)), + Row(children: [ + Text(name + " [$timeSpan]", + style: TextStyle( fontSize: 17)), + if (containsMetadata) + Icon(Icons.arrow_forward_outlined, size: 20,), + if (containsMetadata) + Text(activity.metadata ?? '',overflow: TextOverflow.clip,) + ]), + // Icon(Icons.analytics, color: color, size: 20,), - Icon(Icons.circle, - color: (productive) - ? Colors.green - : Colors.red) ]), + SizedBox( + height: 5, + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(dateFormat.format(sTime) + + " - " + + dateFormat.format(eTime)), + SizedBox( + width: 20, + ), + Container( + padding: EdgeInsets.symmetric( + horizontal: 10, vertical: 2), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: (productive) + ? Colors.green + : Colors.red), + child: Text( + activity.taskType.cat?.name ?? 'n/a')) + // Icon(Icons.circle, + // color: (productive) + // ? Colors.green + // : Colors.red) + ]) ], )))), Container( margin: EdgeInsets.fromLTRB(15, 0, 15, 10), height: 2, - color: color) + child:Row(children: [ + Expanded(flex:timePercentage ,child: Container(color:color)), + Expanded(flex:100-timePercentage,child:Container()) + ],)), ]), ), ]); } - void OnItemSelected(Activity activity){ + void OnItemSelected(Activity activity) { if (!selectedActivities.contains(activity)) { selectedActivities.add(activity); } else { @@ -187,23 +310,25 @@ class _ActivitiesState extends State { } } - void DeleteSelectedTasks() async{ - progressDialog.show(max: 100, msg: 'Deleteing ${selectedActivities.length} Task Types'); + void DeleteSelectedTasks() async { + progressDialog.show( + max: 100, msg: 'Deleteing ${selectedActivities.length} Activities'); selectedActivities.forEach((element) async { - await User.UserOperations.deleteActivity(element, bulk:true); + await User.UserOperations.deleteActivity(element, bulk: true); }); await Future.delayed(Duration(seconds: 2)); await User.UserOperations.executeQueries(); await User.updateActList(); - selectedActivities=[]; - selecting=false; + selectedActivities = []; + selecting = false; setState(() { progressDialog.update(value: 100); }); - } - } + + + List selectedActivities = []; diff --git a/lib/Categories.dart b/lib/Categories.dart index f123343..613e016 100644 --- a/lib/Categories.dart +++ b/lib/Categories.dart @@ -152,7 +152,7 @@ class _CategoriesState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(name, - style: TextStyle(color: Colors.white)), + ), // Icon(Icons.analytics, color: color, size: 20,), Icon(Icons.circle, color: (productive) diff --git a/lib/Data.dart b/lib/Data.dart index 08762d0..557c159 100644 --- a/lib/Data.dart +++ b/lib/Data.dart @@ -31,15 +31,17 @@ class TaskType{ class Activity{ - Activity(this.taskType, this.startTime, this.endTime); + Activity(this.taskType, this.startTime, this.endTime, {this.metadata}); TaskType taskType; DateTime startTime; DateTime endTime; + String? metadata; static String colType = "type"; static String colStartTime = "s_time"; static String colEndTime = "e_time"; + static String colMetadata= "metadata"; } class InitialData{ diff --git a/lib/Tasks.dart b/lib/Tasks.dart index 780f717..58a2a8a 100644 --- a/lib/Tasks.dart +++ b/lib/Tasks.dart @@ -85,7 +85,7 @@ class _TasksState extends State { } else { Color color = HexColor.fromHex(element.cat?.color ?? '#000000'); bool productive = element.cat?.productive ?? true; - Widget task = TaskCard(context, name, productive, color); + Widget task = TaskCard(context, name, productive, color, element.cat?.name ?? 'n/a'); _tasks.add(task); } }); @@ -95,7 +95,7 @@ class _TasksState extends State { bool selecting = false; Widget TaskCard( - BuildContext context, String name, bool productive, Color color) { + BuildContext context, String name, bool productive, Color color, String catName) { return Row(children: [ // Container(), (selecting) @@ -140,12 +140,16 @@ class _TasksState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(name, - style: TextStyle(color: Colors.white)), + ), // Icon(Icons.analytics, color: color, size: 20,), - Icon(Icons.circle, - color: (productive) - ? Colors.green - : Colors.red) + Container( + padding: EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: (productive) ? Colors.green : Colors.red + ), + child:Text(catName) + ) ]), ], )))), diff --git a/lib/User.dart b/lib/User.dart index cfba162..76a7acc 100644 --- a/lib/User.dart +++ b/lib/User.dart @@ -61,7 +61,7 @@ Future initUserData() async { print('Going offline mode.'); } } - +bool userDataInitiated =false; Future refreshUserData() async{ ShowProgress("Loading data"); // categories= await GetCategories(true); @@ -70,6 +70,7 @@ Future refreshUserData() async{ await updateCatsList(); await updateTasksList(); await updateActList(); + userDataInitiated=true; HideProgress(); } @@ -122,7 +123,7 @@ void onCacheDatabaseCreate(Database db, int newVersion) async { await db.execute(TaskTableSQL); String ActivityTableSQL = - 'CREATE TABLE Activities(id INTEGER PRIMARY KEY AUTOINCREMENT, ${Activity.colType} INT, ${Activity.colStartTime} DATETIME, ${Activity.colEndTime} DATETIME, ' + '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); @@ -328,7 +329,7 @@ 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(); @@ -359,14 +360,15 @@ Future> GetActivities(bool forceOffline) async{ 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!"); - print("TaskType:{$type}, Start Time:{$startTime}, endTime:{$endTime}"); + 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}"); - _activities.add(Activity(taskType, DateTime.parse(startTime), DateTime.parse(endTime))); + 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; @@ -395,8 +397,8 @@ Future UpdateActivitiesFromServer() async{ Map cat = jsonDecode(value); print(cat); await cacheDb.rawInsert( - "INSERT OR REPLACE INTO Activities (${Activity.colType}, ${Activity.colStartTime}, ${Activity.colEndTime}) " - "VALUES ('${cat['task_id']}', '${cat['sTime']}','${cat['eTime']}') "); + "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"); @@ -410,13 +412,15 @@ Future UpdateActivitiesFromServer() async{ Future getTaskFromId(String taskId) async{ // await GetTaskTypes(false); TaskType? cat = null; - taskTypes.forEach((element) async{ + 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; } @@ -424,11 +428,11 @@ Future getTaskFromId(String taskId) async{ Future getCatFromId(String catId) async{ // await GetTaskTypes(false); Category? cat = null; - categories.forEach((element) { + for (var element in categories) { if(element.category_id == catId){ cat= element; } - }); + } return cat; } @@ -519,7 +523,7 @@ class UserOperations{ } } - static Future addActivity(String type, String sTime,String eTime, {bool bulk = false, Function(int)? onOverlap}) async{ + 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')))")); @@ -533,8 +537,13 @@ class UserOperations{ 'device_id': await Settings.UUID(), 'type' : username+type, 'sTime': sTime, - 'eTime':eTime + 'eTime':eTime, + 'metadata': metadata }; + + if(metadata.length > 0){ + + } //Add Query Map query = { Queries.colLink: 'add_activity', @@ -547,12 +556,13 @@ class UserOperations{ //update Cache Map data = { - Activity.colType: type, + Activity.colType: username+type, Activity.colStartTime: sTime, - Activity.colEndTime: eTime + Activity.colEndTime: eTime, + Activity.colMetadata: metadata }; await cacheDb.insert('Activities', data); - await GetActivities(true); + activities= await GetActivities(false); if(!bulk){ //Add to server and refresh Cache await executeQueries(); @@ -688,10 +698,10 @@ class UserOperations{ } void ShowProgress(msg){ - try{progressDialog?.show(max: 100, msg: msg);}catch(e){} + //try{progressDialog?.show(max: 100, msg: msg);}catch(e){} } void HideProgress(){ - try{progressDialog?.update(value: 100);}catch(e){} + // try{progressDialog?.update(value: 100);}catch(e){} } diff --git a/lib/main.dart b/lib/main.dart index 0a772f1..87b0c9a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,25 +1,28 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:tasktracker/Categories.dart'; import 'package:tasktracker/Welcome.dart'; import 'package:tasktracker/splash.dart'; import 'package:wakelock/wakelock.dart'; -import 'package:charts_flutter/flutter.dart' as charts; +import 'Data.dart'; import 'NewTask.dart'; import 'newActivity.dart'; import 'Tasks.dart'; import 'Activities.dart'; import 'User.dart' as User; import 'package:sn_progress_dialog/sn_progress_dialog.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; late ProgressDialog progressDialog; showAlertDialog(BuildContext context, String title, String message) { - // set up the button Widget okButton = TextButton( child: Text("OK"), - onPressed: () { Navigator.of(context).pop(); }, + onPressed: () { + Navigator.of(context).pop(); + }, ); // set up the AlertDialog @@ -57,26 +60,6 @@ extension HexColor on Color { '${blue.toRadixString(16).padLeft(2, '0')}'; } -// To keep the screen on: -final List data = [ - PopulationData( - name: "Rocket League", - value: 45, - barColor: charts.ColorUtil.fromDartColor(Colors.blue)), - PopulationData( - name: "CS:GO", - value: 15, - barColor: charts.ColorUtil.fromDartColor(Colors.yellow)), - PopulationData( - name: "Halo", - value: 10, - barColor: charts.ColorUtil.fromDartColor(Colors.grey)), - PopulationData( - name: "SneakyPeaky", - value: 30, - barColor: charts.ColorUtil.fromDartColor(Colors.red)), -]; - void main() { //Wakelock.enable(); // or Wakelock.toggle(on: true); runApp(const MyApp()); @@ -88,26 +71,52 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - accentColor: Colors.redAccent, - brightness: Brightness.dark, - primaryColor: Colors.amber, - fontFamily: 'Noto-Sans'), - //home: const MyHomePage(), - initialRoute: '/splash', - routes:{ - '/splash':(context)=> const SplashScreen(), - '/welcome':(context)=> const WelcomePage(), - '/':(context) => const MyHomePage(), - '/Tasks':(context)=> const Tasks(), - '/Categories':(context)=>const Categories(), - '/Activities':(context)=>const Activities() - } - ); + title: 'Flutter Demo', + theme: ThemeData(accentColor: Colors.redAccent, brightness: Brightness.dark, primaryColor: Colors.amber, fontFamily: 'Noto-Sans'), + //home: const MyHomePage(), + initialRoute: '/splash', + routes: { + '/splash': (context) => const SplashScreen(), + '/welcome': (context) => const WelcomePage(), + '/': (context) => const MyHomePage(), + '/Tasks': (context) => const Tasks(), + '/Categories': (context) => const Categories(), + '/Activities': (context) => const Activities() + }); } } +List days = []; +String curDay = ""; + +DateTime? firstDay = null; +DateTime? lastDay = null; +DateTimeRange? taskTypeRange = null; +DateTimeRange? catsRange = null; + +List productivityData = [ + ProductivityMapData('02/24', 35), + ProductivityMapData('02/25', 28), + ProductivityMapData('02/26', 34), + ProductivityMapData('02/27', 32), + ProductivityMapData('02/28', 40) +]; + +List taskTypesData = [TaskTypeMapData('Eat', 3600, Colors.green), TaskTypeMapData('Play', 300, Colors.blue)]; +List catsData = [ + CatMapData('Jan', 35, Colors.green), + CatMapData('Feb', 28, Colors.blueAccent), + CatMapData('Mar', 34, Colors.yellow), + CatMapData('Apr', 32, Colors.grey), +]; + +List dailyData = [ + CatMapData('Jan', 35, Colors.green), + CatMapData('Feb', 28, Colors.blueAccent), + CatMapData('Mar', 34, Colors.yellow), + CatMapData('Apr', 32, Colors.grey), +]; + class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @@ -116,215 +125,572 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - List> series = [ - charts.Series( - id: "Subscribers", - data: data, - domainFn: (PopulationData series, _) => series.name, - measureFn: (PopulationData series, _) => series.value, - colorFn: (PopulationData series, _) => series.barColor) - ]; - @override void initState() { // TODO: implement initState super.initState(); - - showOfflineSnack(); - progressDialog = ProgressDialog(context: context); - User.progressDialog=progressDialog; + User.refreshUserData().then((val) => LoadStats()); + // showOfflineSnack(); + LoadStats(); + // progressDialog = ProgressDialog(context: context); + // User.progressDialog=progressDialog; } - void showOfflineSnack() async{ + void LoadStats() async { + // return; + + while (!User.userDataInitiated) { + await Future.delayed(const Duration(seconds: 1)); + } + + DateFormat dFormat = DateFormat("MM/dd"); + Map catTimeMap = {}; + Map catBriefMap = {}; + + Map productivtyActs = {}; + Map unproductivtyActs = {}; + Map taskTypesDuration = {}; + firstDay = null; + lastDay = null; + String lastDate = ""; + for (var element in User.activities) { + if (lastDay == null) { + lastDay = element.endTime; + } + if (taskTypeRange == null) { + print("$lastDay - $firstDay"); + taskTypeRange = DateTimeRange(start: lastDay!.subtract(const Duration(days: 1)), end: lastDay!); + } + if (catsRange == null) { + print("$lastDay - $firstDay"); + catsRange = DateTimeRange(start: lastDay!.subtract(const Duration(days: 1)), end: lastDay!); + } + firstDay = element.startTime; + String thisDate = dFormat.format(element.startTime); + int thisMinutes = element.endTime.difference(element.startTime).inMinutes; + if (!days.contains(thisDate)) { + days.add(dFormat.format(element.startTime)); + } + if (curDay == "") { + curDay = dFormat.format(DateTime.now()); + } + + if ((element.startTime.isAfter(taskTypeRange!.start) && element.startTime.isBefore(taskTypeRange!.end)) || + (dFormat.format(element.startTime) == dFormat.format(taskTypeRange!.start) || dFormat.format(element.startTime) == dFormat.format(taskTypeRange!.end))) { + if (taskTypesDuration.containsKey(element.taskType)) { + taskTypesDuration[element.taskType] = taskTypesDuration[element.taskType]! + thisMinutes; + } else { + taskTypesDuration.putIfAbsent(element.taskType, () => thisMinutes); + } + } + + if (element.taskType.cat?.productive ?? false) { + if (productivtyActs.containsKey(thisDate)) { + productivtyActs[thisDate] = (productivtyActs[thisDate]! + thisMinutes); + } else { + productivtyActs.putIfAbsent(thisDate, () => thisMinutes); + } + } else { + if (unproductivtyActs.containsKey(thisDate)) { + unproductivtyActs[thisDate] = (unproductivtyActs[thisDate]! + thisMinutes); + } else { + unproductivtyActs.putIfAbsent(thisDate, () => thisMinutes); + } + } + + if (thisDate == curDay) { + if (element.taskType.cat == null) { + continue; + } + print("Null : ${thisMinutes}"); + if (catTimeMap.containsKey(element.taskType.cat)) { + catTimeMap[element.taskType.cat!] = (catTimeMap[element.taskType.cat]! + thisMinutes); + } else { + catTimeMap.putIfAbsent(element.taskType.cat!, () => thisMinutes); + } + } + + if ((element.startTime.isAfter(catsRange!.start) && element.startTime.isBefore(catsRange!.end)) || + (dFormat.format(element.startTime) == dFormat.format(catsRange!.start) || dFormat.format(element.startTime) == dFormat.format(catsRange!.end))) { + if (element.taskType.cat == null) { + continue; + } + print("Null : ${thisMinutes}"); + if (catBriefMap.containsKey(element.taskType.cat)) { + catBriefMap[element.taskType.cat!] = (catBriefMap[element.taskType.cat]! + thisMinutes); + } else { + catBriefMap.putIfAbsent(element.taskType.cat!, () => thisMinutes); + } + } + } + + //curDay = days[0]; + if (this.mounted) { + setState(() { + dailyData = []; + productivityData = []; + taskTypesData = []; + catsData = []; + + catTimeMap.forEach((key, value) { + print(key.name + " : $value"); + Color barCol = HexColor.fromHex(key.color); + dailyData.add(CatMapData(key.name, value, barCol)); + }); + + for (var element in days) { + // if(productivtyActs.containsKey(element) && unproductivtyActs.containsKey(element)){ + int prodActs = (productivtyActs[element] ?? 0); + int unprodActs = (unproductivtyActs[element] ?? 0); + double prod = (prodActs / (prodActs + unprodActs)) * 100; + productivityData.add(ProductivityMapData(element, prod)); + // } + } + + taskTypesDuration.forEach((key, value) { + print("$key : $value"); + taskTypesData.add(TaskTypeMapData(key.name, value, HexColor.fromHex(key.cat!.color))); + }); + + catBriefMap.forEach((key, value) { + print(key.name + " : $value"); + Color barCol = HexColor.fromHex(key.color); + catsData.add(CatMapData(key.name, value, barCol)); + }); + }); + } + } + + void showOfflineSnack() async { await Future.delayed(const Duration(seconds: 1)); - if(User.offline){ - const SnackBar offlineSnack = SnackBar(content: Text('Offline'),duration: Duration(seconds: 100),); - ScaffoldMessenger.of(context).showSnackBar(offlineSnack); + if (User.offline) { + const SnackBar offlineSnack = SnackBar( + content: Text('Offline'), + duration: Duration(seconds: 100), + ); + // ScaffoldMessenger.of(context).showSnackBar(offlineSnack); } } @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: FloatingActionButton.extended(onPressed: (){ - Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewActivity())); - }, + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity())).then((value) => {User.refreshUserData().then((va) => LoadStats())}); + }, label: Text("New Activity"), - icon: Icon(Icons.add) - ), - appBar: AppBar(title: Row(children:[Icon(Icons.article_outlined, color: Theme.of(context).primaryColor),SizedBox(width: 10),Text('Summary')])), - drawer: navDrawer(context,0), - body: SafeArea( - child: Container( - child: Column( - children: [ - Container( - height: 300, - padding: EdgeInsets.all(20), - child: Card( - elevation: 8, - shadowColor: Colors.blueGrey, - child: Padding( - padding: EdgeInsets.all(8), - child: Column( - children: [ - cardTitle('Daily Average'), - Expanded( - child: charts.BarChart( - series, - animate: true, - barRendererDecorator: - new charts.BarLabelDecorator( - labelPosition: - charts.BarLabelPosition.inside, - labelPadding: 10, - labelAnchor: - charts.BarLabelAnchor.middle), - )) - ], - )))), - Container( - height: 300, - padding: EdgeInsets.all(20), - child: Card( - elevation: 8, - shadowColor: Colors.green, - child: Padding( - padding: EdgeInsets.all(8), - child: Column( - children: [ - cardTitle('Weekly Average'), - Expanded( - child: charts.BarChart( - series, - animate: true, - barRendererDecorator: - new charts.BarLabelDecorator( - labelPosition: - charts.BarLabelPosition.inside, - labelPadding: 10, - labelAnchor: - charts.BarLabelAnchor.middle), - )) - ], - )))) - ], - ), + icon: Icon(Icons.add)), + appBar: AppBar(title: Row(children: [Icon(Icons.article_outlined, color: Theme.of(context).primaryColor), SizedBox(width: 10), Text('Summary')])), + drawer: navDrawer(context, 0), + body: SafeArea( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column( + children: [ + (false) + ? Container( + padding: EdgeInsets.all(20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Text("Good\nMorning!", style: TextStyle(fontSize: 23, fontStyle: FontStyle.italic)), + Text( + "12%", + style: TextStyle(fontSize: 30), + ), + Column( + children: [ + Text(DateFormat("yy - MM-dd").format(DateTime.now())), + Text(DateFormat("HH:mm").format(DateTime.now()), style: TextStyle(fontSize: 40)), + ], + ) + ], + ), + ) + : Container(), + Container( + height: 300, + padding: EdgeInsets.all(10), + child: Card( + elevation: 8, + shadowColor: Colors.blueGrey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20), + child: Column( + children: [ + Row( + children: [ + SizedBox( + width: 10, + ), + Text("Productivity", style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold)), + ], + ), + Divider(), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Text("Today : "), + Text("${(productivityData.length > 0) ? productivityData[0].productivity.toStringAsFixed(1) : 'n/a'}%", + style: TextStyle( + fontSize: 20, + color: (productivityData.length > 1) + ? ((productivityData[0].productivity > productivityData[1].productivity) ? Colors.lightGreenAccent : Colors.red) + : Colors.white)) + ], + ), + Row( + children: [ + Text("Yesterday : "), + Text("${(productivityData.length > 1) ? productivityData[1].productivity.toStringAsFixed(1) : 'n/a'}%", style: TextStyle(fontSize: 18)) + ], + ), + ], + ), + Expanded( + child: SfCartesianChart( + // Initialize category axis + primaryXAxis: CategoryAxis(), + series: >[ + LineSeries( + // Bind data source + dataSource: productivityData.reversed.toList(), + xValueMapper: (ProductivityMapData sales, _) => sales.day, + yValueMapper: (ProductivityMapData sales, _) => sales.productivity, + color: Colors.green) + ]), + ) + ], + ), + )), + ), + Container( + height: 370, + padding: EdgeInsets.all(10), + child: Card( + elevation: 8, + shadowColor: Colors.blueGrey, + child: Padding( + padding: EdgeInsets.all(8), + child: (!days.isEmpty) + ? Column( + children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Padding(padding: EdgeInsets.all(20), child: Text('Daily Briefing', style: TextStyle(fontWeight: FontWeight.bold))), + dayPickerWidget(days, value: curDay, onChange: (_value) { + print('new val : $_value'); + curDay = _value; + setState(() { + LoadStats(); + }); + }), + ]), + Expanded( + child: SfCircularChart(legend: Legend(isVisible: true), series: [ + // Render pie chart + PieSeries( + dataSource: dailyData, + pointColorMapper: (CatMapData data, _) => data.color, + xValueMapper: (CatMapData data, _) => data.name, + yValueMapper: (CatMapData data, _) => data.time, + dataLabelMapper: (CatMapData sales, _) => MinutesToTimeString(sales.time), + dataLabelSettings: DataLabelSettings(isVisible: true, useSeriesColor: true)) + ])) + ], + ) + : Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [CircularProgressIndicator()])))), + Container( + height: 450, + padding: EdgeInsets.all(10), + child: Card( + elevation: 8, + shadowColor: Colors.blueGrey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 25), + child: Column(children: [ + Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text("Task Types", style: TextStyle(fontWeight: FontWeight.bold)), + InkWell( + onTap: () async { + DateTimeRange? value = await showDateRangePicker(context: context, firstDate: firstDay ?? DateTime.now(), lastDate: lastDay ?? DateTime.now()); + if (value != null) { + taskTypeRange = value; + } + + LoadStats(); + }, + child: Text((taskTypeRange != null) ? (DateFormat("MM/dd").format(taskTypeRange!.start) + " - " + DateFormat("MM/dd").format(taskTypeRange!.end)) : 'n/a'), + ) + ]), + Expanded( + // maxHeight: 300, + // maxWidth: 100, + child: SfCartesianChart(primaryXAxis: CategoryAxis(), + //primaryYAxis: NumericAxis(minimum: 0, maximum: 40, interval: 10), + series: >[ + BarSeries( + dataSource: taskTypesData, + xValueMapper: (TaskTypeMapData data, _) => data.task, + yValueMapper: (TaskTypeMapData data, _) => data.time / 60, + pointColorMapper: (TaskTypeMapData data, _) => data.color, + dataLabelMapper: (TaskTypeMapData data, _) => MinutesToTimeString(data.time), + dataLabelSettings: DataLabelSettings(isVisible: true), + color: Color.fromRGBO(8, 142, 255, 1)) + ]), + ) + ])))), + Container( + height: 450, + padding: EdgeInsets.all(10), + child: Card( + elevation: 8, + shadowColor: Colors.blueGrey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 25), + child: Column(children: [ + Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text("Categories", style: TextStyle(fontWeight: FontWeight.bold)), + InkWell( + onTap: () async { + DateTimeRange? value = await showDateRangePicker(context: context, firstDate: firstDay ?? DateTime.now(), lastDate: lastDay ?? DateTime.now()); + if (value != null) { + catsRange = value; + } + + LoadStats(); + }, + child: Text((catsRange != null) ? (DateFormat("MM/dd").format(catsRange!.start) + " - " + DateFormat("MM/dd").format(catsRange!.end)) : 'n/a'), + ) + ]), + Expanded( + // maxHeight: 300, + // maxWidth: 100, + child: SfCartesianChart(primaryXAxis: CategoryAxis(), + //primaryYAxis: NumericAxis(minimum: 0, maximum: 40, interval: 10), + series: >[ + BarSeries( + dataSource: catsData, + xValueMapper: (CatMapData data, _) => data.name, + yValueMapper: (CatMapData data, _) => data.time / 60, + pointColorMapper: (CatMapData data, _) => data.color, + dataLabelMapper: (CatMapData data, _) => MinutesToTimeString(data.time), + dataLabelSettings: DataLabelSettings(isVisible: true), + color: Color.fromRGBO(8, 142, 255, 1)) + ]), + ) + ])))), + ], ), - )); + ), + ), + ); + } + + Widget dayPickerWidget(List list, {required String value, required Function(String value) onChange}) { + if (!list.contains(value)) { + print("resetting"); + onChange(list[0]); + } + bool nextAvailable = (list.indexOf(value) < (list.length - 1)); + bool prevAvailable = (list.indexOf(value) > 0); + return Row( + children: [ + InkWell( + onTap: () { + if (nextAvailable) { + onChange(list[list.indexOf(value) + 1]); + } + }, + child: Container( + height: 40, + width: 40, + child: Icon(Icons.arrow_back_ios, size: 18, color: (nextAvailable) ? Colors.white : Colors.grey), + ), + ), + Text( + value, + ), + InkWell( + onTap: () { + if (prevAvailable) { + onChange(list[list.indexOf(value) - 1]); + } + }, + child: Container( + height: 40, + width: 40, + child: Icon(Icons.arrow_forward_ios, size: 18, color: (prevAvailable) ? Colors.white : Colors.grey), + )) + ], + ); } } -Widget cardTitle(String title) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children:[ - Padding(padding: EdgeInsets.all(20),child:Text(title, - style: TextStyle(fontWeight: FontWeight.bold))), - MaterialButton(onPressed: (){},child:moreButton())]); +Widget moreButton() { + return MaterialButton( + onPressed: () {}, + color: Colors.green, + child: Row( + children: [Text('More'), Icon(Icons.keyboard_arrow_right)], + )); } -Widget moreButton(){ - return MaterialButton( - onPressed: (){ - - }, - color: Colors.green, - child:Row( +Drawer navDrawer(BuildContext context, int pageIndex) { + return Drawer( + child: ListView( children: [ - Text('More'),Icon(Icons.keyboard_arrow_right) + Padding( + padding: EdgeInsets.all(16), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text("Time Tracker", style: TextStyle(fontSize: 25, color: Theme.of(context).accentColor, fontWeight: FontWeight.bold)), + Icon( + Icons.more_time, + size: 30, + ), + ])), + Divider(), + ListTile( + selected: (pageIndex == 0), + title: Text('Summary'), + leading: Icon(Icons.article_outlined, color: Theme.of(context).primaryColor), + onTap: () { + if (pageIndex == 0) { + return; + } + Navigator.of(context).pushReplacementNamed('/'); + }, + ), + // ListTile( + // selected: (pageIndex == 1), + // title: Text('Analytics'), + // leading: Icon(Icons.analytics_outlined, + // color: Theme.of(context).primaryColor), + // onTap: () { + // if (pageIndex == 1) { + // return; + // } + // // Navigator.of(context).pushReplacementNamed('/'); + // }, + // ), + Divider(), + ListTile( + selected: (pageIndex == 2), + title: Text('Activities'), + leading: Icon(Icons.task, color: Theme.of(context).primaryColor), + onTap: () { + if (pageIndex == 2) { + return; + } + Navigator.of(context).pushReplacementNamed('/Activities'); + }, + ), + ListTile( + selected: (pageIndex == 3), + title: Text('Task Types'), + leading: Icon(Icons.task, color: Theme.of(context).primaryColor), + onTap: () { + if (pageIndex == 3) { + return; + } + Navigator.of(context).pushReplacementNamed('/Tasks'); + }, + ), + ListTile( + selected: (pageIndex == 4), + title: Text('Categories'), + leading: Icon(Icons.account_tree_outlined, color: Theme.of(context).primaryColor), + onTap: () { + if (pageIndex == 4) { + return; + } + Navigator.of(context).pushReplacementNamed('/Categories'); + }, + ), + Divider(), + ListTile( + selected: (pageIndex == 5), + title: Text('Settings'), + leading: Icon(Icons.settings, color: Colors.blueGrey), + onTap: () {}, + ), + ListTile( + selected: (pageIndex == 6), + title: Text('About'), + leading: Icon(Icons.help_outline_outlined), + onTap: () {}, + ), ], )); } -class PopulationData { - String name; - int value; - charts.Color barColor; - PopulationData( - {required this.name, required this.value, required this.barColor}); +class MyPlayerBar extends CustomPainter { + MyPlayerBar(this.max, this.value, {this.background = Colors.lightBlue, this.fill = Colors.blue}); + + Color background = Colors.lightBlue; + Color fill = Colors.blue; + final int max; + final int value; + + @override + void paint(Canvas canvas, Size size) { + Paint paint = Paint(); + double cursor = (value * size.width) / max; + Radius cornerRadius = Radius.circular(10.0); + + // Already played half color (your darker orange) + paint.color = background; + + // Painting already played half + canvas.drawRRect(RRect.fromRectAndCorners(Rect.fromLTWH(0.0, 0.0, cursor, size.height), topLeft: cornerRadius, bottomLeft: cornerRadius), paint); + + // Yet to play half color (your lighter orange) + paint.color = fill; + + // Painting the remaining space + canvas.drawRRect(RRect.fromRectAndCorners(Rect.fromLTWH(cursor, 0.0, size.width - cursor, size.height), bottomRight: cornerRadius, topRight: cornerRadius), paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; } -Drawer navDrawer(BuildContext context, int pageIndex){ - return Drawer( - child: ListView( - children: [ - Padding( - padding: EdgeInsets.all(16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children:[Text("Time Tracker", - style: TextStyle( - fontSize: 25, - color: Theme.of(context).accentColor, - fontWeight: FontWeight.bold)), - Icon(Icons.more_time,size: 30,), - ]) - ), - Divider(), - ListTile( - selected: (pageIndex == 0), - title: Text('Summary'), - leading: Icon(Icons.article_outlined,color: Theme.of(context).primaryColor), - onTap: () { - if(pageIndex==0){return;} - Navigator.of(context).pushReplacementNamed('/'); - }, - ), - ListTile( - selected: (pageIndex == 1), - title: Text('Analytics'), - leading: Icon(Icons.analytics_outlined,color: Theme.of(context).primaryColor), - onTap: () { - if(pageIndex==1){return;} - // Navigator.of(context).pushReplacementNamed('/'); - }, - ), - Divider(), - ListTile( - selected: (pageIndex == 2), - title: Text('Activities'), - leading: Icon(Icons.task,color: Theme.of(context).primaryColor), - onTap: () { - if(pageIndex==2){return;} - Navigator.of(context).pushReplacementNamed('/Activities'); - }, - ), - ListTile( - selected: (pageIndex == 3), - title: Text('Task Types'), - leading: Icon(Icons.task,color: Theme.of(context).primaryColor), - onTap: () { - if(pageIndex==3){return;} - Navigator.of(context).pushReplacementNamed('/Tasks'); - }, - ), - ListTile( - selected: (pageIndex == 4), - title: Text('Categories'), - leading: Icon(Icons.account_tree_outlined,color: Theme.of(context).primaryColor), - onTap: () { - if(pageIndex==4){return;} - Navigator.of(context).pushReplacementNamed('/Categories'); - }, - ), - Divider(), - ListTile( - selected: (pageIndex == 5), - title: Text('Settings'), - leading: Icon(Icons.settings,color: Colors.blueGrey), - onTap: () { - - }, - ), - ListTile( - selected: (pageIndex == 6), - title: Text('About'), - leading: Icon(Icons.help_outline_outlined), - onTap: () { - - }, - ), - ], - )); -} \ No newline at end of file +class CatMapData { + CatMapData(this.name, this.time, this.color); + final String name; + final int time; + final Color color; +} + +class ProductivityMapData { + ProductivityMapData(this.day, this.productivity); + + final String day; + final double productivity; +} + +class TaskTypeMapData { + TaskTypeMapData(this.task, this.time, this.color); + final Color color; + final String task; + final int time; +} + +String MinutesToTimeString(minutes) { + int hours = (minutes / 60).floor(); + int mins = minutes % 60; + + String str = ""; + if (hours > 0) { + str += hours.toString() + "h"; + } + if (mins > 0) { + str += ((hours > 0) ? " " : "") + mins.toString() + "m"; + } + + return str; +} diff --git a/lib/newActivity.dart b/lib/newActivity.dart index 143f1ac..5ff6514 100644 --- a/lib/newActivity.dart +++ b/lib/newActivity.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/painting.dart'; import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; import 'package:intl/intl.dart'; import 'User.dart' as User; @@ -31,6 +32,7 @@ class _NewActivity extends State { DateTime startTime = DateTime.now(); DateTime endTime = DateTime.now().add(Duration(minutes: 30)); + TextEditingController metadataController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( @@ -50,6 +52,7 @@ class _NewActivity extends State { Container( padding: EdgeInsets.all(10), child: Text('Task')), + Container( padding: EdgeInsets.symmetric( horizontal: 12, vertical: 1), @@ -78,6 +81,25 @@ class _NewActivity extends State { selectedCat = _value ?? 'n/a'; }); })), + + Container( + padding: EdgeInsets.all(10), + child:Column( + children: [ + + TextField( + controller: metadataController, + decoration: InputDecoration( + hintText: 'Description (optional)', + filled: true, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20) + ) + ), + ), + ], + ) + ), Container( child: Divider( height: 30, @@ -85,82 +107,107 @@ class _NewActivity extends State { Container( padding: EdgeInsets.all(10), child: Text('Start Time')), - Container( - padding: EdgeInsets.symmetric( - horizontal: 12, vertical: 1), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: Colors.grey, width: 2)), - child: MaterialButton( - onPressed: () { - setState(() { - DatePicker.showDateTimePicker( - context, - showTitleActions: true, - onChanged: (date) { - // print('change $date'); - }, onConfirm: (date) { + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + QuickTimeButton('Last', Function: (){ + if(User.activities.length > 0) { + startTime= User.activities[0].endTime; + } + }) + , + Container( + padding: EdgeInsets.symmetric( + horizontal: 12, vertical: 1), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.grey, width: 2)), + child: MaterialButton( + onPressed: () { setState(() { - if(endTime.compareTo(date) < 0){ - const snackBar = SnackBar( - content: Text('You cannot start something after you ended it!'), - ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - }else { - startTime = date; - } + DatePicker.showDateTimePicker( + context, + showTitleActions: true, + onChanged: (date) { + // print('change $date'); + }, onConfirm: (date) { + setState(() { + if(endTime.compareTo(date) < 0){ + const snackBar = SnackBar( + content: Text('You cannot start something after you ended it!'), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }else { + startTime = date; + } + }); + }, + currentTime: startTime, + locale: LocaleType.en); }); }, - currentTime: startTime, - locale: LocaleType.en); - }); - }, - child: Text( - dateFormat.format(startTime), - style: TextStyle( - color: Colors.blue)))), + child: Text( + dateFormat.format(startTime), + style: TextStyle( + color: Colors.blue)))), + QuickTimeButton('Now', Function: (){ + startTime = DateTime.now(); + }) + ], + ), SizedBox( height: 10, ), Container( padding: EdgeInsets.all(10), child: Text('Ended Time')), - Container( - padding: EdgeInsets.symmetric( - horizontal: 12, vertical: 1), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: Colors.grey, width: 2)), - child: MaterialButton( + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container(width: 60,), + Container( + padding: EdgeInsets.symmetric( + horizontal: 12, vertical: 1), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.grey, width: 2)), + child: MaterialButton( - onPressed: () { - setState(() { - DatePicker.showDateTimePicker( - context, - showTitleActions: true, - onChanged: (date) { - // print('change $date'); - }, onConfirm: (date) { + onPressed: () { setState(() { - if(startTime.compareTo(date) > 0){ - const snackBar = SnackBar( - content: Text('You cannot end something before you start it!'), - ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - }else { - endTime = date; - } + DatePicker.showDateTimePicker( + context, + showTitleActions: true, + onChanged: (date) { + // print('change $date'); + }, onConfirm: (date) { + setState(() { + if(startTime.compareTo(date) > 0){ + const snackBar = SnackBar( + content: Text('You cannot end something before you start it!'), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }else { + endTime = date; + } + }); + }, + currentTime: endTime, + locale: LocaleType.en); }); }, - currentTime: endTime, - locale: LocaleType.en); - }); - }, - child: Text(dateFormat.format(endTime), - style: TextStyle( - color: Colors.blue)))), + child: Text(dateFormat.format(endTime), + style: TextStyle( + color: Colors.blue)))), + QuickTimeButton('Now', Function: (){ + endTime = DateTime.now(); + }) + ], + ), SizedBox( height: 30, ), @@ -173,6 +220,12 @@ class _NewActivity extends State { Divider( height: 30, ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + ], + ) ]), ], ))), @@ -194,9 +247,7 @@ class _NewActivity extends State { shape: StadiumBorder() ), onPressed: () { - setState(() { - Navigator.pop(context); - }); + Navigator.pop(context); }, child: Text('Back', style: TextStyle(fontSize: 20))))), @@ -212,10 +263,6 @@ class _NewActivity extends State { onPressed: () { add_action(); - setState(() { - - }); - }, child: Text('Add Entry', style: TextStyle(fontSize: 20))))), @@ -224,14 +271,33 @@ class _NewActivity extends State { ]))); } + Widget QuickTimeButton(text, {Function}){ + return InkWell( + child: Container( + padding:EdgeInsets.symmetric(horizontal: 15), + height: 30, + decoration:BoxDecoration( + color: Colors.blueAccent, + borderRadius: BorderRadius.circular(50) + ), + child:Align(child: Text(text),alignment: Alignment.center,) + ), + + onTap: (){ + Function(); + setState(() { + + }); + },); + } + void add_action() async{ print('adding Task Type : $selectedCat at $startTime - $endTime'); bool failed=false; - await User.UserOperations.addActivity(selectedCat,startTime.toString(), endTime.toString(), onOverlap: (overlapCount){ + await User.UserOperations.addActivity(selectedCat,startTime.toString(), endTime.toString(),metadata:metadataController.text, onOverlap: (overlapCount){ showAlertDialog(context, 'Error adding activity', 'Cannot add activity between ${dateFormat.format(startTime)} - ${dateFormat.format(endTime)}, $overlapCount activities are already added within this time range'); failed=true; - }); if(!failed) diff --git a/pubspec.lock b/pubspec.lock index b7b0b61..92230df 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -29,20 +29,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" - charts_common: - dependency: transitive - description: - name: charts_common - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.0" - charts_flutter: - dependency: "direct main" - description: - name: charts_flutter - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.0" clock: dependency: transitive description: @@ -77,7 +63,7 @@ packages: name: device_info_plus url: "https://pub.dartlang.org" source: hosted - version: "3.2.1" + version: "3.2.2" device_info_plus_linux: dependency: transitive description: @@ -205,13 +191,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.2" matcher: dependency: transitive description: @@ -253,14 +232,14 @@ packages: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.12" path_provider_ios: dependency: transitive description: name: path_provider_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.0.8" path_provider_linux: dependency: transitive description: @@ -330,7 +309,7 @@ packages: name: shared_preferences_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.10" + version: "2.1.0" shared_preferences_linux: dependency: transitive description: @@ -420,6 +399,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + syncfusion_flutter_charts: + dependency: "direct main" + description: + name: syncfusion_flutter_charts + url: "https://pub.dartlang.org" + source: hosted + version: "19.4.53" + syncfusion_flutter_core: + dependency: transitive + description: + name: syncfusion_flutter_core + url: "https://pub.dartlang.org" + source: hosted + version: "19.4.54" synchronized: dependency: transitive description: @@ -454,7 +447,7 @@ packages: name: uuid url: "https://pub.dartlang.org" source: hosted - version: "3.0.5" + version: "3.0.6" vector_math: dependency: transitive description: @@ -503,7 +496,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.3.10" + version: "2.4.1" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9931de2..631705e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,11 +30,10 @@ dependencies: flutter: sdk: flutter - cupertino_icons: ^1.0.2 uuid: ^3.0.5 wakelock: ^0.6.1+1 - charts_flutter: ^0.12.0 + syncfusion_flutter_charts: ^19.4.53 flutter_datetime_picker: ^1.5.1 sqflite: ^2.0.2 intl: ^0.17.0