Skip to content

Commit 9296564

Browse files
committed
synchrinization features added
1 parent 94262a2 commit 9296564

22 files changed

+1479
-158
lines changed

.gitignore

+6-1
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@
3030
.pub-cache/
3131
.pub/
3232
/build/
33+
lib/firebase_options.dart
3334

3435
# Web related
35-
lib/generated_plugin_registrant.dart
3636

37+
#credential related
38+
lib/generated_plugin_registrant.dart
39+
android/app/google-services.json
40+
ios/Runner/GoogleService-Info.plist
41+
firebase.json
3742
# Symbolication related
3843
app.*.symbols
3944

android/app/build.gradle

+16-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ if (flutterVersionName == null) {
2222
}
2323

2424
apply plugin: 'com.android.application'
25+
// START: FlutterFire Configuration
26+
apply plugin: 'com.google.gms.google-services'
27+
// END: FlutterFire Configuration
2528
apply plugin: 'kotlin-android'
2629
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
2730

@@ -38,17 +41,25 @@ android {
3841

3942
defaultConfig {
4043
applicationId "com.example.task_management"
41-
minSdkVersion flutter.minSdkVersion
42-
targetSdkVersion 33 // Updated target SDK version
44+
minSdkVersion 33 //updated min SDK version
45+
targetSdkVersion 34 // Updated target SDK version
4346
versionCode flutterVersionCode.toInteger()
4447
versionName flutterVersionName
48+
multiDexEnabled true
4549
}
4650

4751
buildTypes {
4852
release {
4953
signingConfig signingConfigs.debug
5054
}
5155
}
56+
compileOptions {
57+
// Flag to enable support for the new language APIs
58+
coreLibraryDesugaringEnabled true
59+
// Sets Java compatibility to Java 8
60+
sourceCompatibility JavaVersion.VERSION_1_8
61+
targetCompatibility JavaVersion.VERSION_1_8
62+
}
5263
}
5364

5465
flutter {
@@ -57,6 +68,9 @@ flutter {
5768

5869
dependencies {
5970
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // Updated to jdk8
71+
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
72+
implementation 'androidx.window:window:1.0.0'
73+
implementation 'androidx.window:window-java:1.0.0'
6074
}
6175

6276
repositories {

android/app/src/main/AndroidManifest.xml

+12
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
<!-- Permissions -->
55
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
66
<uses-permission android:name="android.permission.VIBRATE"/>
7+
<uses-permission android:name="android.permission.WAKE_LOCK"/>
8+
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
9+
android:maxSdkVersion="32" />
10+
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
711

812
<application
913
android:name="${applicationName}"
@@ -16,6 +20,9 @@
1620
android:exported="true">
1721
<intent-filter>
1822
<action android:name="android.intent.action.BOOT_COMPLETED"/>
23+
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
24+
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
25+
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
1926
</intent-filter>
2027
</receiver>
2128

@@ -24,11 +31,16 @@
2431
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver"
2532
android:exported="true"/>
2633

34+
<!-- Receiver for Notification Clicks -->
35+
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" />
36+
2737
<!-- Main Activity -->
2838
<activity
2939
android:name=".MainActivity"
3040
android:launchMode="singleTop"
3141
android:exported="true"
42+
android:showWhenLocked="true"
43+
android:turnScreenOn="true"
3244
android:theme="@style/LaunchTheme"
3345
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
3446
android:hardwareAccelerated="true"

android/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ buildscript {
66
}
77

88
dependencies {
9+
// START: FlutterFire Configuration
10+
classpath 'com.google.gms:google-services:4.3.15'
11+
// END: FlutterFire Configuration
912
classpath 'com.android.tools.build:gradle:7.3.1'
1013
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1114
}

ios/Runner/AppDelegate.swift

+6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import UIKit
22
import Flutter
3+
import flutter_local_notifications
34

45
@UIApplicationMain
56
@objc class AppDelegate: FlutterAppDelegate {
67
override func application(
78
_ application: UIApplication,
89
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
910
) -> Bool {
11+
12+
FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
13+
GeneratedPluginRegistrant.register(with: registry)}
1014
GeneratedPluginRegistrant.register(with: self)
15+
FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
16+
GeneratedPluginRegistrant.register(with: registry)}
1117
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
1218
}
1319
}

lib/controllers/task_controller.dart

+102-1
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,139 @@
1+
import 'dart:developer';
2+
3+
// import 'package:connectivity_plus/connectivity_plus.dart';
14
import 'package:get/get.dart';
25
import 'package:task_management/db/db_helper.dart';
36
import 'package:task_management/models/task.dart';
7+
import 'package:task_management/services/notification_services.dart';
8+
import 'package:task_management/services/task_services.dart';
49

510
class TaskController extends GetxController {
611
//this will hold the data and update the ui
7-
12+
final TaskServices _taskServices = TaskServices();
813
@override
914
void onReady() {
1015
getTasks();
16+
getRemoteTasks();
17+
syncData();
1118
super.onReady();
1219
}
1320

1421
final RxList<Task> taskList = new RxList<Task>();
1522

23+
final RxList<Task> remoteTaskList = new RxList<Task>();
24+
25+
//create resource
26+
void createResource() async {
27+
await DBHelper.initDb();
28+
}
29+
1630
// add data to table
1731
Future<int> addTask(Task task) async {
32+
await _taskServices.addTask(task);
1833
return await DBHelper.insert(task);
1934
}
2035

36+
// bulk insert data to table
37+
void bulkInsert(List<Task> tasks) async {
38+
await DBHelper.bulkInsert(tasks);
39+
getTasks();
40+
}
41+
2142
// get all the data from table
2243
void getTasks() async {
2344
List<Map<String, dynamic>> tasks = await DBHelper.query();
2445
taskList.assignAll(tasks.map((data) => new Task.fromJson(data)).toList());
2546
}
2647

48+
//get all remote tasks
49+
void getRemoteTasks() async {
50+
remoteTaskList.assignAll(await _taskServices.getTasks());
51+
log(remoteTaskList.length.toString());
52+
}
53+
54+
// sync data
55+
void syncData() async {
56+
// Fetch remote and local task IDs
57+
List<String> remoteTaskIdList = await _taskServices.getTaskIds();
58+
List<String> localTaskIdList =
59+
taskList.map((e) => e.id.toString()).toList();
60+
log(remoteTaskIdList.length.toString());
61+
log(localTaskIdList.length.toString());
62+
// If remote data is empty, push all local data to the remote
63+
if (remoteTaskIdList.isEmpty) {
64+
taskList.forEach((task) {
65+
_taskServices.addTask(task);
66+
});
67+
}
68+
69+
// If local data is empty, pull all remote data into local
70+
if (localTaskIdList.isEmpty) {
71+
List<Task> tasks = await _taskServices.getTasks();
72+
taskList.assignAll(tasks);
73+
bulkInsert(tasks);
74+
}
75+
76+
// Sync data between local and remote
77+
else {
78+
// Find tasks that exist in local but not in remote
79+
List<String> notExistInRemote = localTaskIdList
80+
.where((localId) => !remoteTaskIdList.contains(localId))
81+
.toList();
82+
// Push those tasks to the remote
83+
notExistInRemote.forEach((id) {
84+
Task task =
85+
taskList.firstWhere((element) => element.id.toString() == id);
86+
_taskServices.addTask(task);
87+
log("Added local task to remote");
88+
});
89+
90+
// Find tasks that exist in remote but not in local
91+
List<String> notExistInLocal = remoteTaskIdList
92+
.where((remoteId) => !localTaskIdList.contains(remoteId))
93+
.toList();
94+
if (notExistInLocal.isNotEmpty) {
95+
List<Task> remoteTasks = await _taskServices.getTasks();
96+
List<Task> newLocalTasks = remoteTasks
97+
.where((task) => notExistInLocal.contains(task.id.toString()))
98+
.toList();
99+
taskList.addAll(newLocalTasks);
100+
//schedule notification for new tasks which have todays date
101+
for (Task task in newLocalTasks) {
102+
var selectedDate = DateTime.parse(task.startTime);
103+
selectedDate = DateTime(selectedDate.year, selectedDate.month,
104+
selectedDate.day, selectedDate.hour, selectedDate.minute, 00);
105+
if (selectedDate.isAfter(DateTime.now())) {
106+
await NotifyHelper()
107+
.scheduleNotification(selectedDate, task.title, task.note);
108+
}
109+
bulkInsert(newLocalTasks);
110+
log("Added remote tasks to local");
111+
}
112+
}
113+
// Reload tasks to ensure UI updates
114+
getTasks();
115+
}
116+
}
117+
27118
// delete data from table
28119
void deleteTask(Task task) async {
120+
await _taskServices.deleteTask(task.id.toString());
29121
await DBHelper.delete(task);
30122
getTasks();
31123
}
32124

33125
// update data int table
34126
void markTaskCompleted(int id) async {
127+
Task task = taskList.firstWhere((element) => element.id == id);
128+
task.isCompleted = 1;
129+
await _taskServices.updateTaskStatus(task);
35130
await DBHelper.update(id);
36131
getTasks();
37132
}
133+
134+
//delete all data
135+
void clearStorage() async {
136+
await DBHelper.deleteTableData();
137+
// getTasks();
138+
}
38139
}

lib/db/db_helper.dart

+15-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class DBHelper {
1717
version: _version,
1818
onCreate: (db, version) {
1919
return db.execute(
20-
"CREATE TABLE $_tableName(id INTEGER PRIMARY KEY AUTOINCREMENT, title STRING, note TEXT, date STRING, startTime STRING, endTime STRING, remind INTEGER, repeat STRING, color INTEGER, isCompleted INTEGER)",
20+
"CREATE TABLE $_tableName(id INTEGER PRIMARY KEY AUTOINCREMENT,userId String, title STRING, note TEXT, date STRING, startTime STRING, endTime STRING,priority STRING, remind INTEGER, repeat STRING, color INTEGER, isCompleted INTEGER)",
2121
);
2222
},
2323
);
@@ -31,6 +31,15 @@ class DBHelper {
3131
return await _db!.insert(_tableName, task.toJson());
3232
}
3333

34+
//bulk insert
35+
static Future<void> bulkInsert(List<Task> tasks) async {
36+
final batch = _db!.batch();
37+
tasks.forEach((task) {
38+
batch.insert(_tableName, task.toJson());
39+
});
40+
await batch.commit();
41+
}
42+
3443
static Future<int> delete(Task task) async =>
3544
await _db!.delete(_tableName, where: 'id = ?', whereArgs: [task.id]);
3645

@@ -47,4 +56,9 @@ class DBHelper {
4756
WHERE id = ?
4857
''', [1, id]);
4958
}
59+
60+
// drop table
61+
static Future<void> deleteTableData() async {
62+
await _db!.rawDelete('DELETE FROM $_tableName');
63+
}
5064
}

lib/main.dart

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,63 @@
1+
import 'package:firebase_core/firebase_core.dart';
12
import 'package:flutter/material.dart';
23
import 'package:get/get.dart';
34
import 'package:get_storage/get_storage.dart';
5+
import 'package:shared_preferences/shared_preferences.dart';
46
import 'package:task_management/db/db_helper.dart';
7+
import 'package:task_management/services/notification_services.dart';
58
import 'package:task_management/services/theme_services.dart';
9+
import 'package:task_management/ui/pages/all_task_page.dart';
610
import 'package:task_management/ui/pages/home_page.dart';
11+
import 'package:task_management/ui/pages/login_screen.dart';
12+
import 'package:task_management/ui/pages/sign_up_page.dart';
713
import 'package:task_management/ui/theme.dart';
14+
import 'package:timezone/data/latest.dart' as tz;
815

916
void main() async {
1017
WidgetsFlutterBinding.ensureInitialized();
18+
await Firebase.initializeApp();
1119
await DBHelper.initDb();
20+
await NotifyHelper().initNotification();
1221
await GetStorage.init();
22+
tz.initializeTimeZones();
1323
runApp(MyApp());
1424
}
1525

16-
class MyApp extends StatelessWidget {
26+
class MyApp extends StatefulWidget {
27+
@override
28+
State<MyApp> createState() => _MyAppState();
29+
}
30+
31+
class _MyAppState extends State<MyApp> {
32+
bool isLoggedIn = false;
33+
34+
@override
35+
void initState() {
36+
getUserDetails();
37+
super.initState();
38+
}
39+
40+
getUserDetails() async {
41+
SharedPreferences prefs = await SharedPreferences.getInstance();
42+
setState(() {
43+
isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
44+
});
45+
}
46+
1747
@override
1848
Widget build(BuildContext context) {
1949
return GetMaterialApp(
2050
debugShowCheckedModeBanner: false,
2151
// theme: Themes.light,
2252
darkTheme: Themes.dark,
2353
themeMode: ThemeService().theme,
24-
home: HomePage(),
54+
home: isLoggedIn ? HomePage() : LoginScreen(),
55+
routes: {
56+
'/login': (context) => LoginScreen(),
57+
'/home': (context) => HomePage(),
58+
'/signup': (context) => SignUpPage(),
59+
'/view-all': (context) => AllTaskPage(),
60+
},
2561
);
2662
}
2763
}

0 commit comments

Comments
 (0)