Commit f293cd60 authored by Shoaib's avatar Shoaib
Browse files

WOrking on offline capability.

No related merge requests found
Showing with 279 additions and 27 deletions
+279 -27
......@@ -21,6 +21,7 @@ class InspectionStatus {
static const String newInspection = 'NEW';
static const String sentForInspection = 'SENTFORINS';
static const String inspectionCompleted = 'INSCOMPLETED';
static const String inspectionPending = 'PENDING';
static const String returned = 'RETURNED';
}
......@@ -28,3 +29,18 @@ class FieldValue {
static const String correct = 'Correct';
static const String inCorrect = 'Incorrect';
}
class AppDatabase {
static const String name = 'smf_db';
static const String applicationsTable = 'applications';
static const String inspectionTable = 'inspections';
}
class Storage {
static const String userId = 'smf_user_id';
static const String username = 'smf_user_username';
static const String email = 'smf_user_email';
static const String firstname = 'smf_user_first_name';
static const String lastname = 'smf_user_last_name';
static const String authtoken = 'smf_user_auth_token';
}
import 'package:smf_mobile/constants/app_constants.dart';
import 'package:sqflite/sqflite.dart' as sql;
import 'package:path/path.dart' as path;
import 'package:sqflite/sqlite_api.dart';
class OfflineModel {
static Future<Database> database() async {
final dbPath = await sql.getDatabasesPath();
return sql.openDatabase(path.join(dbPath, AppDatabase.name),
onCreate: (db, version) async {
const String applicationsTable = AppDatabase.applicationsTable;
const String inspectionTable = AppDatabase.inspectionTable;
await db.execute('''CREATE TABLE $applicationsTable (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
application_data TEXT
)''');
await db.execute('''CREATE TABLE $inspectionTable (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
inspection_data TEXT
)''');
}, version: 1);
}
static Future<void> saveApplications(Map<String, Object> data) async {
final db = await OfflineModel.database();
db.insert(
AppDatabase.applicationsTable,
data,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<Map> getApplications(int userId) async {
final db = await OfflineModel.database();
List<dynamic> whereArgs = [userId];
List<Map> rows = await db.query(AppDatabase.applicationsTable,
where: 'user_id = ?', orderBy: 'id DESC', whereArgs: whereArgs);
return rows[0];
}
static Future<void> deleteApplications(int userId) async {
Database db = await OfflineModel.database();
List<dynamic> whereArgs = [userId];
await db.delete(AppDatabase.applicationsTable,
where: 'user_id = ?', whereArgs: whereArgs);
}
static Future<void> saveInspection(Map<String, Object> data) async {
final db = await OfflineModel.database();
db.insert(
AppDatabase.inspectionTable,
data,
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
static Future<List<Map<String, dynamic>>> getInspections(int userId) async {
final db = await OfflineModel.database();
List<dynamic> whereArgs = [userId];
return db.query(AppDatabase.inspectionTable,
where: 'user_id = ?', whereArgs: whereArgs);
}
static Future<void> deleteInspections(int userId) async {
Database db = await OfflineModel.database();
List<dynamic> whereArgs = [userId];
await db.delete(AppDatabase.inspectionTable,
where: 'user_id = ?', whereArgs: whereArgs);
}
}
{
"login": "Login",
"logout": "Logout",
"emailId": "Email Id",
"getOtp": "GET OTP",
"enterOtp": "Enter OTP",
......@@ -14,6 +15,9 @@
"next": "Next",
"previous": "previous",
"inspectionCompleted": "Inspection completed",
"completed": "Completed",
"pending": "Pending",
"noApplications": "No applications at the moment",
"sessionExpiredMessage": "Your session has expired.",
"isGivenInformationCorrect": "Is the given information found correct?",
"typeHere": "Type here",
......
......@@ -59,7 +59,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage>
}
Future<void> _checkInspectorRole() async {
String id = await Helper.getUser('smf_user_id');
String id = await Helper.getUser(Storage.userId);
int userId = int.parse(id);
if (widget.application.leadInspector.isNotEmpty) {
_leadInspectorId = widget.application.leadInspector[0];
......@@ -67,7 +67,8 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage>
setState(() {
_isleadInspector = true;
});
} else {
} else if (widget.application.status ==
InspectionStatus.inspectionCompleted) {
_inspectionSummary =
widget.application.inspectorSummaryDataObject['Inspection Summary'][
widget.application
......@@ -370,7 +371,7 @@ class _ApplicationDetailsPageState extends State<ApplicationDetailsPage>
4),
),
child: Text(
'Status: ${Helper.getInspectionStatus(widget.application.status)}',
'Status: ${Helper.getInspectionStatus(context, widget.application.status)}',
textAlign: TextAlign
.center,
style: GoogleFonts
......
......@@ -11,6 +11,9 @@ import 'package:smf_mobile/repositories/login_repository.dart';
import 'package:smf_mobile/util/helper.dart';
import 'package:smf_mobile/widgets/application_card.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:smf_mobile/util/connectivity_helper.dart';
// import 'dart:developer' as developer;
......@@ -23,6 +26,8 @@ class HomePage extends StatefulWidget {
}
class _HomePageState extends State<HomePage> {
Map _source = {ConnectivityResult.none: false};
final MyConnectivity _connectivity = MyConnectivity.instance;
List<Application> _allApplications = [];
final List<Application> _pendingApplications = [];
final List<Application> _upcomingApplications = [];
......@@ -30,6 +35,10 @@ class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
setState(() => _source = source);
});
}
void _validateUser() async {
......@@ -42,11 +51,31 @@ class _HomePageState extends State<HomePage> {
}
}
bool _isInternetConnected() {
bool connected;
switch (_source.keys.toList()[0]) {
case ConnectivityResult.mobile:
print('connected to mobile...');
connected = true;
break;
case ConnectivityResult.wifi:
print('connected to wifi...');
connected = true;
break;
case ConnectivityResult.none:
default:
print('offline...');
connected = false;
}
return connected;
}
Future<dynamic> _getApplications() async {
print('_getApplications...');
_validateUser();
_allApplications =
await Provider.of<ApplicationRespository>(context, listen: false)
.getApplications();
.getApplications(_isInternetConnected());
String _errorMessage =
Provider.of<ApplicationRespository>(context, listen: false)
.errorMessage;
......@@ -127,7 +156,7 @@ class _HomePageState extends State<HomePage> {
PopupMenuItem<String>(
value: 'logout',
child: Text(
'Logout',
AppLocalizations.of(context)!.logout,
style: GoogleFonts.lato(
color: AppColors.black87,
fontSize: 14.0,
......@@ -168,7 +197,9 @@ class _HomePageState extends State<HomePage> {
margin: const EdgeInsets.only(
top: 10, bottom: 20),
child: Center(
child: Text('No applications at the moment',
child: Text(
AppLocalizations.of(context)!
.noApplications,
style: GoogleFonts.lato(
color: AppColors.black87,
fontSize: 16.0,
......
import 'dart:async';
import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:smf_mobile/constants/app_constants.dart';
import 'package:smf_mobile/database/offline_model.dart';
import 'package:smf_mobile/models/application_model.dart';
import 'package:smf_mobile/services/application_service.dart';
import 'package:smf_mobile/util/helper.dart';
class ApplicationRespository with ChangeNotifier {
late Map _data;
List<Application> _applications = [];
String _errorMessage = '';
Future<dynamic> getApplications() async {
Future<dynamic> getApplications(bool internetConnected) async {
String rawUserId = await Helper.getUser(Storage.userId);
int userId = int.parse(rawUserId);
try {
final request = await ApplicationService.getApplications();
_data = json.decode(request.body);
if (internetConnected) {
final request = await ApplicationService.getApplications();
_data = json.decode(request.body);
Map<String, Object> data = {
'user_id': userId,
'application_data': request.body
};
await OfflineModel.deleteApplications(userId);
await OfflineModel.saveApplications(data);
} else {
var applications = await OfflineModel.getApplications(userId);
_data = json.decode(applications['application_data']);
}
} catch (_) {
return _;
}
......
import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/widgets.dart';
import 'package:smf_mobile/constants/app_constants.dart';
import 'package:smf_mobile/models/login_model.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:smf_mobile/services/login_service.dart';
......@@ -43,14 +44,12 @@ class LoginRespository with ChangeNotifier {
_errorMessage = _data['statusInfo']['errorMessage'];
} else {
_loginDetails = Login.fromJson(_data['responseData']);
_storage.write(key: 'smf_user_id', value: '${_loginDetails.id}');
_storage.write(key: 'smf_user_username', value: _loginDetails.username);
_storage.write(key: 'smf_user_email', value: _loginDetails.email);
_storage.write(
key: 'smf_user_first_name', value: _loginDetails.firstName);
_storage.write(key: 'smf_user_last_name', value: _loginDetails.lastName);
_storage.write(
key: 'smf_user_auth_token', value: _loginDetails.authToken);
_storage.write(key: Storage.userId, value: '${_loginDetails.id}');
_storage.write(key: Storage.username, value: _loginDetails.username);
_storage.write(key: Storage.email, value: _loginDetails.email);
_storage.write(key: Storage.firstname, value: _loginDetails.firstName);
_storage.write(key: Storage.lastname, value: _loginDetails.lastName);
_storage.write(key: Storage.authtoken, value: _loginDetails.authToken);
_firebaseMessaging.getToken().then((token) async {
final request = await LoginService.updateUserDeviceToken(
token.toString(),
......
import 'dart:io';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
const _storage = FlutterSecureStorage();
import 'package:smf_mobile/constants/app_constants.dart';
import 'package:smf_mobile/util/helper.dart';
abstract class BaseService {
final HttpClient client;
......@@ -12,7 +11,7 @@ abstract class BaseService {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8',
};
var authToken = await _storage.read(key: 'smf_user_auth_token');
var authToken = await Helper.getUser(Storage.authtoken);
if (authToken != '' && authToken != null) {
headers['Authorization'] = authToken;
}
......
import 'dart:io';
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:smf_mobile/constants/api_endpoints.dart';
class MyConnectivity {
MyConnectivity._();
static final _instance = MyConnectivity._();
static MyConnectivity get instance => _instance;
final _connectivity = Connectivity();
final _controller = StreamController.broadcast();
Stream get myStream => _controller.stream;
void initialise() async {
ConnectivityResult result = await _connectivity.checkConnectivity();
_checkStatus(result);
_connectivity.onConnectivityChanged.listen((result) {
_checkStatus(result);
});
}
void _checkStatus(ConnectivityResult result) async {
bool isOnline = false;
try {
final result = await InternetAddress.lookup(ApiUrl.baseUrl);
isOnline = result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} on SocketException catch (_) {
isOnline = false;
}
_controller.sink.add({result: isOnline});
}
void disposeStream() => _controller.close();
}
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:intl/intl.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:smf_mobile/constants/app_constants.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
const _storage = FlutterSecureStorage();
......@@ -37,7 +37,7 @@ class Helper {
static Future<bool> isTokenExpired() async {
bool isTokenExpired = true;
var authToken = await _storage.read(key: 'smf_user_auth_token');
var authToken = await _storage.read(key: Storage.authtoken);
if (authToken != null) {
isTokenExpired = JwtDecoder.isExpired(authToken);
}
......@@ -74,12 +74,12 @@ class Helper {
? '${string[0].toUpperCase()}${string.substring(1).toLowerCase()}'
: '';
static getInspectionStatus(String status) {
static getInspectionStatus(BuildContext context, String status) {
String _inspectionStatus = '';
if (status == InspectionStatus.inspectionCompleted) {
_inspectionStatus = 'Completed';
_inspectionStatus = AppLocalizations.of(context)!.completed;
} else if (status == InspectionStatus.sentForInspection) {
_inspectionStatus = 'Pending';
_inspectionStatus = AppLocalizations.of(context)!.pending;
} else {
_inspectionStatus = capitalize(_inspectionStatus);
}
......
......@@ -93,7 +93,8 @@ class _ApplicationCardState extends State<ApplicationCard> {
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(
Helper.getInspectionStatus(widget.application.status),
Helper.getInspectionStatus(
context, widget.application.status),
style: GoogleFonts.lato(
color: AppColors.black60,
fontSize: 14.0,
......
......@@ -64,6 +64,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
connectivity_plus_linux:
dependency: transitive
description:
name: connectivity_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
connectivity_plus_macos:
dependency: transitive
description:
name: connectivity_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
connectivity_plus_web:
dependency: transitive
description:
name: connectivity_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
connectivity_plus_windows:
dependency: transitive
description:
name: connectivity_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
crypto:
dependency: transitive
description:
......@@ -336,6 +378,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
otp_text_field:
dependency: "direct main"
description:
......@@ -446,6 +495,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
sqflite:
dependency: "direct main"
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
stack_trace:
dependency: transitive
description:
......@@ -467,6 +530,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
term_glyph:
dependency: transitive
description:
......
......@@ -48,6 +48,8 @@ dependencies:
firebase_core: ^1.12.0
flutter_local_notifications: ^9.3.2
unique_identifier: ^0.2.2
sqflite: ^2.0.2
connectivity_plus: ^2.2.1
dev_dependencies:
flutter_test:
......
......@@ -6,9 +6,12 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
}
......@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus_windows
flutter_secure_storage_windows
)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment