Summary page done

This commit is contained in:
warlock
2022-03-02 09:24:26 +05:30
parent 8c7ea9b86e
commit 4576957b4c
16 changed files with 974 additions and 409 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 19 KiB

BIN
images/hourglass_base.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
images/hourglass_half.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'main.dart'; import 'main.dart';
@@ -6,15 +7,15 @@ import 'Data.dart';
import 'User.dart' as User; import 'User.dart' as User;
import 'package:sn_progress_dialog/sn_progress_dialog.dart'; import 'package:sn_progress_dialog/sn_progress_dialog.dart';
class Activities extends StatefulWidget { class Activities extends StatefulWidget {
const Activities({Key? key}) : super(key: key); const Activities({Key? key}) : super(key: key);
@override @override
_ActivitiesState createState() => _ActivitiesState(); _ActivitiesState createState() => _ActivitiesState();
} }
late ProgressDialog progressDialog; late ProgressDialog progressDialog;
class _ActivitiesState extends State<Activities> { class _ActivitiesState extends State<Activities> {
@override @override
void initState() { void initState() {
@@ -26,9 +27,8 @@ class _ActivitiesState extends State<Activities> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
progressDialog=ProgressDialog(context: context); progressDialog = ProgressDialog(context: context);
return Scaffold( return Scaffold(
floatingActionButton: FloatingActionButton.extended( floatingActionButton: FloatingActionButton.extended(
onPressed: () { onPressed: () {
Navigator.of(context) Navigator.of(context)
@@ -39,7 +39,7 @@ class _ActivitiesState extends State<Activities> {
icon: Icon(Icons.add)), icon: Icon(Icons.add)),
appBar: AppBar( appBar: AppBar(
title: Row( title: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row(children: [ Row(children: [
@@ -47,15 +47,29 @@ class _ActivitiesState extends State<Activities> {
SizedBox(width: 10), SizedBox(width: 10),
Text('Activities') Text('Activities')
]), ]),
(selecting)?Row(children: [ (selecting)
InkWell(onTap: (){ ? Row(children: [
DeleteSelectedTasks(); InkWell(
}, child: Icon(Icons.delete,size: 30,)), onTap: () {
SizedBox(width: 20,), DeleteSelectedTasks();
InkWell(onTap: (){setState(() { },
selecting=false; child: Icon(
});}, child: Icon(Icons.close,size: 30),) Icons.delete,
]) : Container(), size: 30,
)),
SizedBox(
width: 20,
),
InkWell(
onTap: () {
setState(() {
selecting = false;
});
},
child: Icon(Icons.close, size: 30),
)
])
: Container(),
], ],
)), )),
drawer: navDrawer(context, 2), drawer: navDrawer(context, 2),
@@ -68,10 +82,14 @@ class _ActivitiesState extends State<Activities> {
} }
void UpdateList() async { 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(); await User.updateActList();
setState(() {}); setState(() {});
try{progressDialog.update(value: 100);}catch(e){} try {
progressDialog.update(value: 100);
} catch (e) {}
} }
List<Widget> PrintTasks() { List<Widget> PrintTasks() {
@@ -79,19 +97,52 @@ class _ActivitiesState extends State<Activities> {
print('Priting cats : ' + User.taskTypes.length.toString()); print('Priting cats : ' + User.taskTypes.length.toString());
String lastDate = ""; String lastDate = "";
DateFormat dFormat = DateFormat("MM/dd"); DateFormat dFormat = DateFormat("MM/dd");
Map<String, int> productivtyActs = <String, int>{};
Map<String, int> unproductivtyActs = <String, int>{};
Map<String, int> totalMinutes = <String, int>{};
for (var element in User.activities) { for (var element in User.activities) {
String thisDate = dFormat.format(element.startTime); String thisDate = dFormat.format(element.startTime);
if(thisDate != lastDate){ int thisMinutes= element.endTime.difference(element.startTime).inMinutes;
_tasks.add(DateSeperator(thisDate)); if(totalMinutes.containsKey(thisDate)){
lastDate=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; String name = element.taskType.name;
if (element.taskType.cat == null) { if (element.taskType.cat == null) {
print('Got some null cat : ${element.taskType.name}'); print('Got some null cat : ${element.taskType.name}');
} else { } 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; 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); _tasks.add(task);
} }
} }
@@ -99,23 +150,65 @@ class _ActivitiesState extends State<Activities> {
return _tasks; return _tasks;
} }
Widget DateSeperator(date){ Widget DateSeperator(date, prodActs, unprodActs) {
double prodPercentage = (prodActs / (prodActs + unprodActs)) * 100;
return Padding( return Padding(
padding: const EdgeInsets.fromLTRB(10, 20, 10, 0), padding: const EdgeInsets.fromLTRB(10, 20, 10, 0),
child: Column(children: [ child: Column(
Row(children: [ children: [
Text(date,style: TextStyle(fontSize: 18),) 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; bool selecting = false;
Widget ActivityCard( Widget ActivityCard(BuildContext context, String name, DateTime sTime,
BuildContext context, String name,DateTime sTime, DateTime eTime, bool productive, Color color, Activity activity) { DateTime eTime, bool productive, Color color, Activity activity,int totalMinutes) {
DateFormat dateFormat = DateFormat("HH:mm"); 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); 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: [ return Row(children: [
// Container(), // Container(),
(selecting) (selecting)
@@ -132,54 +225,84 @@ class _ActivitiesState extends State<Activities> {
Card( Card(
// color: color, // color: color,
elevation:20, elevation: 20,
shadowColor: color, shadowColor: color,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
//Open Respective Category //Open Respective Category
if(selecting){ if (selecting) {
OnItemSelected(activity); OnItemSelected(activity);
} }
setState(() { setState(() {});
});
}, },
onLongPress: () { onLongPress: () {
print('gonna delete'); print('gonna delete');
selecting = !selecting; selecting = !selecting;
selectedActivities = [activity]; selectedActivities = [activity];
setState(() {}); setState(() {});
}, },
child: Container( child: Container(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(15),
child: Column( child: Column(
children: [ children: [
Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(name + " ($timeSpan)", Row(children: [
style: TextStyle(color: Colors.white)), Text(name + " [$timeSpan]",
Text(dateFormat.format(sTime) + " - " + dateFormat.format(eTime)), 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.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( Container(
margin: EdgeInsets.fromLTRB(15, 0, 15, 10), margin: EdgeInsets.fromLTRB(15, 0, 15, 10),
height: 2, 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)) { if (!selectedActivities.contains(activity)) {
selectedActivities.add(activity); selectedActivities.add(activity);
} else { } else {
@@ -187,23 +310,25 @@ class _ActivitiesState extends State<Activities> {
} }
} }
void DeleteSelectedTasks() async{ void DeleteSelectedTasks() async {
progressDialog.show(max: 100, msg: 'Deleteing ${selectedActivities.length} Task Types'); progressDialog.show(
max: 100, msg: 'Deleteing ${selectedActivities.length} Activities');
selectedActivities.forEach((element) async { 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 Future.delayed(Duration(seconds: 2));
await User.UserOperations.executeQueries(); await User.UserOperations.executeQueries();
await User.updateActList(); await User.updateActList();
selectedActivities=[]; selectedActivities = [];
selecting=false; selecting = false;
setState(() { setState(() {
progressDialog.update(value: 100); progressDialog.update(value: 100);
}); });
} }
} }
List<Activity> selectedActivities = []; List<Activity> selectedActivities = [];

View File

@@ -152,7 +152,7 @@ class _CategoriesState extends State<Categories> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(name, Text(name,
style: TextStyle(color: Colors.white)), ),
// Icon(Icons.analytics, color: color, size: 20,), // Icon(Icons.analytics, color: color, size: 20,),
Icon(Icons.circle, Icon(Icons.circle,
color: (productive) color: (productive)

View File

@@ -31,15 +31,17 @@ class TaskType{
class Activity{ class Activity{
Activity(this.taskType, this.startTime, this.endTime); Activity(this.taskType, this.startTime, this.endTime, {this.metadata});
TaskType taskType; TaskType taskType;
DateTime startTime; DateTime startTime;
DateTime endTime; DateTime endTime;
String? metadata;
static String colType = "type"; static String colType = "type";
static String colStartTime = "s_time"; static String colStartTime = "s_time";
static String colEndTime = "e_time"; static String colEndTime = "e_time";
static String colMetadata= "metadata";
} }
class InitialData{ class InitialData{

View File

@@ -85,7 +85,7 @@ class _TasksState extends State<Tasks> {
} else { } else {
Color color = HexColor.fromHex(element.cat?.color ?? '#000000'); Color color = HexColor.fromHex(element.cat?.color ?? '#000000');
bool productive = element.cat?.productive ?? true; 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); _tasks.add(task);
} }
}); });
@@ -95,7 +95,7 @@ class _TasksState extends State<Tasks> {
bool selecting = false; bool selecting = false;
Widget TaskCard( Widget TaskCard(
BuildContext context, String name, bool productive, Color color) { BuildContext context, String name, bool productive, Color color, String catName) {
return Row(children: [ return Row(children: [
// Container(), // Container(),
(selecting) (selecting)
@@ -140,12 +140,16 @@ class _TasksState extends State<Tasks> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(name, Text(name,
style: TextStyle(color: Colors.white)), ),
// Icon(Icons.analytics, color: color, size: 20,), // Icon(Icons.analytics, color: color, size: 20,),
Icon(Icons.circle, Container(
color: (productive) padding: EdgeInsets.symmetric(horizontal: 10),
? Colors.green decoration: BoxDecoration(
: Colors.red) borderRadius: BorderRadius.circular(10),
color: (productive) ? Colors.green : Colors.red
),
child:Text(catName)
)
]), ]),
], ],
)))), )))),

View File

@@ -61,7 +61,7 @@ Future<void> initUserData() async {
print('Going offline mode.'); print('Going offline mode.');
} }
} }
bool userDataInitiated =false;
Future<void> refreshUserData() async{ Future<void> refreshUserData() async{
ShowProgress("Loading data"); ShowProgress("Loading data");
// categories= await GetCategories(true); // categories= await GetCategories(true);
@@ -70,6 +70,7 @@ Future<void> refreshUserData() async{
await updateCatsList(); await updateCatsList();
await updateTasksList(); await updateTasksList();
await updateActList(); await updateActList();
userDataInitiated=true;
HideProgress(); HideProgress();
} }
@@ -122,7 +123,7 @@ void onCacheDatabaseCreate(Database db, int newVersion) async {
await db.execute(TaskTableSQL); await db.execute(TaskTableSQL);
String ActivityTableSQL = 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))'; 'FOREIGN KEY (${Activity.colType}) REFERENCES TaskTypes(id))';
// print(ActivityTableSQL); // print(ActivityTableSQL);
await db.execute(ActivityTableSQL); await db.execute(ActivityTableSQL);
@@ -328,7 +329,7 @@ Future<List<Activity>> GetActivities(bool forceOffline) async{
List<Activity> _activities = []; List<Activity> _activities = [];
if(offline || forceOffline){ if(offline || forceOffline){
//Retreive from cacheDB //Retreive from cacheDB
print('offline, refreshing activities');
}else{ }else{
//Check if server got updated, If not go for cache //Check if server got updated, If not go for cache
var android_id = await Settings.UUID(); var android_id = await Settings.UUID();
@@ -359,14 +360,15 @@ Future<List<Activity>> GetActivities(bool forceOffline) async{
String? type = element[Activity.colType].toString(); String? type = element[Activity.colType].toString();
String? startTime = element[Activity.colStartTime].toString(); String? startTime = element[Activity.colStartTime].toString();
String? endTime = element[Activity.colEndTime].toString(); String? endTime = element[Activity.colEndTime].toString();
String? metadata = element[Activity.colMetadata].toString();
TaskType? taskType = await getTaskFromId(type); TaskType? taskType = await getTaskFromId(type);
if(type==null || startTime==null || endTime==null || taskType==null){ if(type==null || startTime==null || endTime==null || taskType==null){
print("Something is 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}"); print("TaskType:{$type}, Start Time:{$startTime}, endTime:{$endTime}, metadata:${metadata}");
continue; continue;
} }
print("TaskType:{$type}, Start Time:{$startTime}, endTime:{$endTime}"); print("TaskType:{$type}, Start Time:{$startTime}, endTime:{$endTime}, metadata:${metadata}");
_activities.add(Activity(taskType, DateTime.parse(startTime), DateTime.parse(endTime))); _activities.add(Activity(taskType, DateTime.parse(startTime), DateTime.parse(endTime), metadata: metadata));
} }
activities = _activities; activities = _activities;
return activities; return activities;
@@ -395,8 +397,8 @@ Future<void> UpdateActivitiesFromServer() async{
Map<String, dynamic> cat = jsonDecode(value); Map<String, dynamic> cat = jsonDecode(value);
print(cat); print(cat);
await cacheDb.rawInsert( await cacheDb.rawInsert(
"INSERT OR REPLACE INTO Activities (${Activity.colType}, ${Activity.colStartTime}, ${Activity.colEndTime}) " "INSERT OR REPLACE INTO Activities (${Activity.colType}, ${Activity.colStartTime}, ${Activity.colEndTime}, ${Activity.colMetadata}) "
"VALUES ('${cat['task_id']}', '${cat['sTime']}','${cat['eTime']}') "); "VALUES ('${cat['task_id']}', '${cat['sTime']}','${cat['eTime']}', '${cat['metadata']}') ");
} }
}else{ }else{
print("No activities for now"); print("No activities for now");
@@ -410,13 +412,15 @@ Future<void> UpdateActivitiesFromServer() async{
Future<TaskType?> getTaskFromId(String taskId) async{ Future<TaskType?> getTaskFromId(String taskId) async{
// await GetTaskTypes(false); // await GetTaskTypes(false);
TaskType? cat = null; TaskType? cat = null;
taskTypes.forEach((element) async{ for (var element in taskTypes){
if(element.id == taskId){ if(element.id == taskId){
cat= element; cat= element;
cat?.cat = await getCatFromId((cat?.category ?? '')); cat?.cat = await getCatFromId((cat?.category ?? ''));
} }
}); }
if(cat==null){
print('Got null tasktype for ${taskId} after searching on ${taskTypes.length}');
}
return cat; return cat;
} }
@@ -424,11 +428,11 @@ Future<TaskType?> getTaskFromId(String taskId) async{
Future<Category?> getCatFromId(String catId) async{ Future<Category?> getCatFromId(String catId) async{
// await GetTaskTypes(false); // await GetTaskTypes(false);
Category? cat = null; Category? cat = null;
categories.forEach((element) { for (var element in categories) {
if(element.category_id == catId){ if(element.category_id == catId){
cat= element; cat= element;
} }
}); }
return cat; return cat;
} }
@@ -519,7 +523,7 @@ class UserOperations{
} }
} }
static Future<void> addActivity(String type, String sTime,String eTime, {bool bulk = false, Function(int)? onOverlap}) async{ static Future<void> addActivity(String type, String sTime,String eTime, {String metadata = 'null',bool bulk = false, Function(int)? onOverlap}) async{
//Check for timeoverlapse //Check for timeoverlapse
activities= await GetActivities(true); 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')))")); 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(), 'device_id': await Settings.UUID(),
'type' : username+type, 'type' : username+type,
'sTime': sTime, 'sTime': sTime,
'eTime':eTime 'eTime':eTime,
'metadata': metadata
}; };
if(metadata.length > 0){
}
//Add Query //Add Query
Map<String,Object> query = { Map<String,Object> query = {
Queries.colLink: 'add_activity', Queries.colLink: 'add_activity',
@@ -547,12 +556,13 @@ class UserOperations{
//update Cache //update Cache
Map<String,Object> data = { Map<String,Object> data = {
Activity.colType: type, Activity.colType: username+type,
Activity.colStartTime: sTime, Activity.colStartTime: sTime,
Activity.colEndTime: eTime Activity.colEndTime: eTime,
Activity.colMetadata: metadata
}; };
await cacheDb.insert('Activities', data); await cacheDb.insert('Activities', data);
await GetActivities(true); activities= await GetActivities(false);
if(!bulk){ if(!bulk){
//Add to server and refresh Cache //Add to server and refresh Cache
await executeQueries(); await executeQueries();
@@ -688,10 +698,10 @@ class UserOperations{
} }
void ShowProgress(msg){ void ShowProgress(msg){
try{progressDialog?.show(max: 100, msg: msg);}catch(e){} //try{progressDialog?.show(max: 100, msg: msg);}catch(e){}
} }
void HideProgress(){ void HideProgress(){
try{progressDialog?.update(value: 100);}catch(e){} // try{progressDialog?.update(value: 100);}catch(e){}
} }

View File

@@ -1,25 +1,28 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tasktracker/Categories.dart'; import 'package:tasktracker/Categories.dart';
import 'package:tasktracker/Welcome.dart'; import 'package:tasktracker/Welcome.dart';
import 'package:tasktracker/splash.dart'; import 'package:tasktracker/splash.dart';
import 'package:wakelock/wakelock.dart'; import 'package:wakelock/wakelock.dart';
import 'package:charts_flutter/flutter.dart' as charts; import 'Data.dart';
import 'NewTask.dart'; import 'NewTask.dart';
import 'newActivity.dart'; import 'newActivity.dart';
import 'Tasks.dart'; import 'Tasks.dart';
import 'Activities.dart'; import 'Activities.dart';
import 'User.dart' as User; import 'User.dart' as User;
import 'package:sn_progress_dialog/sn_progress_dialog.dart'; import 'package:sn_progress_dialog/sn_progress_dialog.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
late ProgressDialog progressDialog; late ProgressDialog progressDialog;
showAlertDialog(BuildContext context, String title, String message) { showAlertDialog(BuildContext context, String title, String message) {
// set up the button // set up the button
Widget okButton = TextButton( Widget okButton = TextButton(
child: Text("OK"), child: Text("OK"),
onPressed: () { Navigator.of(context).pop(); }, onPressed: () {
Navigator.of(context).pop();
},
); );
// set up the AlertDialog // set up the AlertDialog
@@ -57,26 +60,6 @@ extension HexColor on Color {
'${blue.toRadixString(16).padLeft(2, '0')}'; '${blue.toRadixString(16).padLeft(2, '0')}';
} }
// To keep the screen on:
final List<PopulationData> 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() { void main() {
//Wakelock.enable(); // or Wakelock.toggle(on: true); //Wakelock.enable(); // or Wakelock.toggle(on: true);
runApp(const MyApp()); runApp(const MyApp());
@@ -88,26 +71,52 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Flutter Demo', title: 'Flutter Demo',
theme: ThemeData( theme: ThemeData(accentColor: Colors.redAccent, brightness: Brightness.dark, primaryColor: Colors.amber, fontFamily: 'Noto-Sans'),
accentColor: Colors.redAccent, //home: const MyHomePage(),
brightness: Brightness.dark, initialRoute: '/splash',
primaryColor: Colors.amber, routes: {
fontFamily: 'Noto-Sans'), '/splash': (context) => const SplashScreen(),
//home: const MyHomePage(), '/welcome': (context) => const WelcomePage(),
initialRoute: '/splash', '/': (context) => const MyHomePage(),
routes:{ '/Tasks': (context) => const Tasks(),
'/splash':(context)=> const SplashScreen(), '/Categories': (context) => const Categories(),
'/welcome':(context)=> const WelcomePage(), '/Activities': (context) => const Activities()
'/':(context) => const MyHomePage(), });
'/Tasks':(context)=> const Tasks(),
'/Categories':(context)=>const Categories(),
'/Activities':(context)=>const Activities()
}
);
} }
} }
List<String> days = [];
String curDay = "";
DateTime? firstDay = null;
DateTime? lastDay = null;
DateTimeRange? taskTypeRange = null;
DateTimeRange? catsRange = null;
List<ProductivityMapData> productivityData = <ProductivityMapData>[
ProductivityMapData('02/24', 35),
ProductivityMapData('02/25', 28),
ProductivityMapData('02/26', 34),
ProductivityMapData('02/27', 32),
ProductivityMapData('02/28', 40)
];
List<TaskTypeMapData> taskTypesData = <TaskTypeMapData>[TaskTypeMapData('Eat', 3600, Colors.green), TaskTypeMapData('Play', 300, Colors.blue)];
List<CatMapData> catsData = <CatMapData>[
CatMapData('Jan', 35, Colors.green),
CatMapData('Feb', 28, Colors.blueAccent),
CatMapData('Mar', 34, Colors.yellow),
CatMapData('Apr', 32, Colors.grey),
];
List<CatMapData> dailyData = <CatMapData>[
CatMapData('Jan', 35, Colors.green),
CatMapData('Feb', 28, Colors.blueAccent),
CatMapData('Mar', 34, Colors.yellow),
CatMapData('Apr', 32, Colors.grey),
];
class MyHomePage extends StatefulWidget { class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key); const MyHomePage({Key? key}) : super(key: key);
@@ -116,215 +125,572 @@ class MyHomePage extends StatefulWidget {
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
List<charts.Series<PopulationData, String>> series = [
charts.Series(
id: "Subscribers",
data: data,
domainFn: (PopulationData series, _) => series.name,
measureFn: (PopulationData series, _) => series.value,
colorFn: (PopulationData series, _) => series.barColor)
];
@override @override
void initState() { void initState() {
// TODO: implement initState // TODO: implement initState
super.initState(); super.initState();
User.refreshUserData().then((val) => LoadStats());
showOfflineSnack(); // showOfflineSnack();
progressDialog = ProgressDialog(context: context); LoadStats();
User.progressDialog=progressDialog; // 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<Category, int> catTimeMap = <Category, int>{};
Map<Category, int> catBriefMap = <Category, int>{};
Map<String, int> productivtyActs = <String, int>{};
Map<String, int> unproductivtyActs = <String, int>{};
Map<TaskType, int> taskTypesDuration = <TaskType, int>{};
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 = <CatMapData>[];
productivityData = <ProductivityMapData>[];
taskTypesData = <TaskTypeMapData>[];
catsData = <CatMapData>[];
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)); await Future.delayed(const Duration(seconds: 1));
if(User.offline){ if (User.offline) {
const SnackBar offlineSnack = SnackBar(content: Text('Offline'),duration: Duration(seconds: 100),); const SnackBar offlineSnack = SnackBar(
ScaffoldMessenger.of(context).showSnackBar(offlineSnack); content: Text('Offline'),
duration: Duration(seconds: 100),
);
// ScaffoldMessenger.of(context).showSnackBar(offlineSnack);
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
floatingActionButton: FloatingActionButton.extended(onPressed: (){ floatingActionButton: FloatingActionButton.extended(
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewActivity())); onPressed: () {
}, Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity())).then((value) => {User.refreshUserData().then((va) => LoadStats())});
},
label: Text("New Activity"), label: Text("New Activity"),
icon: Icon(Icons.add) icon: Icon(Icons.add)),
), appBar: AppBar(title: Row(children: [Icon(Icons.article_outlined, color: Theme.of(context).primaryColor), SizedBox(width: 10), Text('Summary')])),
appBar: AppBar(title: Row(children:[Icon(Icons.article_outlined, color: Theme.of(context).primaryColor),SizedBox(width: 10),Text('Summary')])), drawer: navDrawer(context, 0),
drawer: navDrawer(context,0), body: SafeArea(
body: SafeArea( child: SingleChildScrollView(
child: Container( scrollDirection: Axis.vertical,
child: Column( child: Column(
children: [ children: [
Container( (false)
height: 300, ? Container(
padding: EdgeInsets.all(20), padding: EdgeInsets.all(20),
child: Card( child: Row(
elevation: 8, mainAxisAlignment: MainAxisAlignment.spaceBetween,
shadowColor: Colors.blueGrey, mainAxisSize: MainAxisSize.max,
child: Padding( children: [
padding: EdgeInsets.all(8), Text("Good\nMorning!", style: TextStyle(fontSize: 23, fontStyle: FontStyle.italic)),
child: Column( Text(
children: [ "12%",
cardTitle('Daily Average'), style: TextStyle(fontSize: 30),
Expanded( ),
child: charts.BarChart( Column(
series, children: [
animate: true, Text(DateFormat("yy - MM-dd").format(DateTime.now())),
barRendererDecorator: Text(DateFormat("HH:mm").format(DateTime.now()), style: TextStyle(fontSize: 40)),
new charts.BarLabelDecorator( ],
labelPosition: )
charts.BarLabelPosition.inside, ],
labelPadding: 10, ),
labelAnchor: )
charts.BarLabelAnchor.middle), : Container(),
)) Container(
], height: 300,
)))), padding: EdgeInsets.all(10),
Container( child: Card(
height: 300, elevation: 8,
padding: EdgeInsets.all(20), shadowColor: Colors.blueGrey,
child: Card( child: Padding(
elevation: 8, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
shadowColor: Colors.green, child: Column(
child: Padding( children: [
padding: EdgeInsets.all(8), Row(
child: Column( children: [
children: [ SizedBox(
cardTitle('Weekly Average'), width: 10,
Expanded( ),
child: charts.BarChart( Text("Productivity", style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold)),
series, ],
animate: true, ),
barRendererDecorator: Divider(),
new charts.BarLabelDecorator( Row(
labelPosition: mainAxisSize: MainAxisSize.max,
charts.BarLabelPosition.inside, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
labelPadding: 10, children: [
labelAnchor: Row(
charts.BarLabelAnchor.middle), 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<ProductivityMapData, String>>[
LineSeries<ProductivityMapData, String>(
// 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: <CircularSeries>[
// Render pie chart
PieSeries<CatMapData, String>(
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: <ChartSeries<TaskTypeMapData, String>>[
BarSeries<TaskTypeMapData, String>(
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: <ChartSeries<CatMapData, String>>[
BarSeries<CatMapData, String>(
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<String> 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) { Widget moreButton() {
return Row( return MaterialButton(
mainAxisAlignment: MainAxisAlignment.spaceBetween, onPressed: () {},
children:[ color: Colors.green,
Padding(padding: EdgeInsets.all(20),child:Text(title, child: Row(
style: TextStyle(fontWeight: FontWeight.bold))), children: [Text('More'), Icon(Icons.keyboard_arrow_right)],
MaterialButton(onPressed: (){},child:moreButton())]); ));
} }
Widget moreButton(){ Drawer navDrawer(BuildContext context, int pageIndex) {
return MaterialButton( return Drawer(
onPressed: (){ child: ListView(
},
color: Colors.green,
child:Row(
children: [ 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 { class MyPlayerBar extends CustomPainter {
String name; MyPlayerBar(this.max, this.value, {this.background = Colors.lightBlue, this.fill = Colors.blue});
int value;
charts.Color barColor; Color background = Colors.lightBlue;
PopulationData( Color fill = Colors.blue;
{required this.name, required this.value, required this.barColor}); 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){ class CatMapData {
return Drawer( CatMapData(this.name, this.time, this.color);
child: ListView( final String name;
children: [ final int time;
Padding( final Color color;
padding: EdgeInsets.all(16), }
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, class ProductivityMapData {
children:[Text("Time Tracker", ProductivityMapData(this.day, this.productivity);
style: TextStyle(
fontSize: 25, final String day;
color: Theme.of(context).accentColor, final double productivity;
fontWeight: FontWeight.bold)), }
Icon(Icons.more_time,size: 30,),
]) class TaskTypeMapData {
), TaskTypeMapData(this.task, this.time, this.color);
Divider(), final Color color;
ListTile( final String task;
selected: (pageIndex == 0), final int time;
title: Text('Summary'), }
leading: Icon(Icons.article_outlined,color: Theme.of(context).primaryColor),
onTap: () { String MinutesToTimeString(minutes) {
if(pageIndex==0){return;} int hours = (minutes / 60).floor();
Navigator.of(context).pushReplacementNamed('/'); int mins = minutes % 60;
},
), String str = "";
ListTile( if (hours > 0) {
selected: (pageIndex == 1), str += hours.toString() + "h";
title: Text('Analytics'), }
leading: Icon(Icons.analytics_outlined,color: Theme.of(context).primaryColor), if (mins > 0) {
onTap: () { str += ((hours > 0) ? " " : "") + mins.toString() + "m";
if(pageIndex==1){return;} }
// Navigator.of(context).pushReplacementNamed('/');
}, return str;
), }
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: () {
},
),
],
));
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart'; import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'User.dart' as User; import 'User.dart' as User;
@@ -31,6 +32,7 @@ class _NewActivity extends State<NewActivity> {
DateTime startTime = DateTime.now(); DateTime startTime = DateTime.now();
DateTime endTime = DateTime.now().add(Duration(minutes: 30)); DateTime endTime = DateTime.now().add(Duration(minutes: 30));
TextEditingController metadataController = TextEditingController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -50,6 +52,7 @@ class _NewActivity extends State<NewActivity> {
Container( Container(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Text('Task')), child: Text('Task')),
Container( Container(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 12, vertical: 1), horizontal: 12, vertical: 1),
@@ -78,6 +81,25 @@ class _NewActivity extends State<NewActivity> {
selectedCat = _value ?? 'n/a'; 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( Container(
child: Divider( child: Divider(
height: 30, height: 30,
@@ -85,82 +107,107 @@ class _NewActivity extends State<NewActivity> {
Container( Container(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Text('Start Time')), child: Text('Start Time')),
Container( Row(
padding: EdgeInsets.symmetric( mainAxisSize: MainAxisSize.max,
horizontal: 12, vertical: 1), mainAxisAlignment: MainAxisAlignment.spaceEvenly,
decoration: BoxDecoration( children: [
borderRadius: BorderRadius.circular(12), QuickTimeButton('Last', Function: (){
border: Border.all( if(User.activities.length > 0) {
color: Colors.grey, width: 2)), startTime= User.activities[0].endTime;
child: MaterialButton( }
onPressed: () { })
setState(() { ,
DatePicker.showDateTimePicker( Container(
context, padding: EdgeInsets.symmetric(
showTitleActions: true, horizontal: 12, vertical: 1),
onChanged: (date) { decoration: BoxDecoration(
// print('change $date'); borderRadius: BorderRadius.circular(12),
}, onConfirm: (date) { border: Border.all(
color: Colors.grey, width: 2)),
child: MaterialButton(
onPressed: () {
setState(() { setState(() {
if(endTime.compareTo(date) < 0){ DatePicker.showDateTimePicker(
const snackBar = SnackBar( context,
content: Text('You cannot start something after you ended it!'), showTitleActions: true,
); onChanged: (date) {
ScaffoldMessenger.of(context).showSnackBar(snackBar); // print('change $date');
}else { }, onConfirm: (date) {
startTime = 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, child: Text(
locale: LocaleType.en); dateFormat.format(startTime),
}); style: TextStyle(
}, color: Colors.blue)))),
child: Text( QuickTimeButton('Now', Function: (){
dateFormat.format(startTime), startTime = DateTime.now();
style: TextStyle( })
color: Colors.blue)))), ],
),
SizedBox( SizedBox(
height: 10, height: 10,
), ),
Container( Container(
padding: EdgeInsets.all(10), padding: EdgeInsets.all(10),
child: Text('Ended Time')), child: Text('Ended Time')),
Container( Row(
padding: EdgeInsets.symmetric( mainAxisSize: MainAxisSize.max,
horizontal: 12, vertical: 1), mainAxisAlignment: MainAxisAlignment.spaceEvenly,
decoration: BoxDecoration( children: [
borderRadius: BorderRadius.circular(12), Container(width: 60,),
border: Border.all( Container(
color: Colors.grey, width: 2)), padding: EdgeInsets.symmetric(
child: MaterialButton( horizontal: 12, vertical: 1),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.grey, width: 2)),
child: MaterialButton(
onPressed: () { onPressed: () {
setState(() {
DatePicker.showDateTimePicker(
context,
showTitleActions: true,
onChanged: (date) {
// print('change $date');
}, onConfirm: (date) {
setState(() { setState(() {
if(startTime.compareTo(date) > 0){ DatePicker.showDateTimePicker(
const snackBar = SnackBar( context,
content: Text('You cannot end something before you start it!'), showTitleActions: true,
); onChanged: (date) {
ScaffoldMessenger.of(context).showSnackBar(snackBar); // print('change $date');
}else { }, onConfirm: (date) {
endTime = 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, child: Text(dateFormat.format(endTime),
locale: LocaleType.en); style: TextStyle(
}); color: Colors.blue)))),
}, QuickTimeButton('Now', Function: (){
child: Text(dateFormat.format(endTime), endTime = DateTime.now();
style: TextStyle( })
color: Colors.blue)))), ],
),
SizedBox( SizedBox(
height: 30, height: 30,
), ),
@@ -173,6 +220,12 @@ class _NewActivity extends State<NewActivity> {
Divider( Divider(
height: 30, height: 30,
), ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
],
)
]), ]),
], ],
))), ))),
@@ -194,9 +247,7 @@ class _NewActivity extends State<NewActivity> {
shape: StadiumBorder() shape: StadiumBorder()
), ),
onPressed: () { onPressed: () {
setState(() { Navigator.pop(context);
Navigator.pop(context);
});
}, },
child: Text('Back', child: Text('Back',
style: TextStyle(fontSize: 20))))), style: TextStyle(fontSize: 20))))),
@@ -212,10 +263,6 @@ class _NewActivity extends State<NewActivity> {
onPressed: () { onPressed: () {
add_action(); add_action();
setState(() {
});
}, },
child: Text('Add Entry', child: Text('Add Entry',
style: TextStyle(fontSize: 20))))), style: TextStyle(fontSize: 20))))),
@@ -224,14 +271,33 @@ class _NewActivity extends State<NewActivity> {
]))); ])));
} }
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{ void add_action() async{
print('adding Task Type : $selectedCat at $startTime - $endTime'); print('adding Task Type : $selectedCat at $startTime - $endTime');
bool failed=false; 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'); 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; failed=true;
}); });
if(!failed) if(!failed)

View File

@@ -29,20 +29,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.1" 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: clock:
dependency: transitive dependency: transitive
description: description:
@@ -77,7 +63,7 @@ packages:
name: device_info_plus name: device_info_plus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.1" version: "3.2.2"
device_info_plus_linux: device_info_plus_linux:
dependency: transitive dependency: transitive
description: description:
@@ -205,13 +191,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@@ -253,14 +232,14 @@ packages:
name: path_provider_android name: path_provider_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.11" version: "2.0.12"
path_provider_ios: path_provider_ios:
dependency: transitive dependency: transitive
description: description:
name: path_provider_ios name: path_provider_ios
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.7" version: "2.0.8"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@@ -330,7 +309,7 @@ packages:
name: shared_preferences_ios name: shared_preferences_ios
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.10" version: "2.1.0"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@@ -420,6 +399,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" 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: synchronized:
dependency: transitive dependency: transitive
description: description:
@@ -454,7 +447,7 @@ packages:
name: uuid name: uuid
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.5" version: "3.0.6"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -503,7 +496,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.10" version: "2.4.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@@ -30,11 +30,10 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
uuid: ^3.0.5 uuid: ^3.0.5
wakelock: ^0.6.1+1 wakelock: ^0.6.1+1
charts_flutter: ^0.12.0 syncfusion_flutter_charts: ^19.4.53
flutter_datetime_picker: ^1.5.1 flutter_datetime_picker: ^1.5.1
sqflite: ^2.0.2 sqflite: ^2.0.2
intl: ^0.17.0 intl: ^0.17.0