This commit is contained in:
Sai Naw Wun
2020-10-07 02:33:06 +06:30
parent 01a2798a74
commit 65dda16fe6
475 changed files with 1543 additions and 90780 deletions

148
lib/helpers/api_helper.dart Normal file
View File

@@ -0,0 +1,148 @@
import 'dart:convert';
import 'dart:io';
import 'package:device_info/device_info.dart';
import 'package:dio/dio.dart';
import 'package:fcs/domain/vo/status.dart';
import 'package:logging/logging.dart';
import '../config.dart';
import 'dev_info.dart';
final log = Logger('requestAPI');
// request makes http request
// if token is null
Future<dynamic> requestAPI(
String path,
method, {
dynamic payload,
String token,
String url,
}) async {
DevInfo devInfo = await DevInfo.getDevInfo();
String deviceName = "${devInfo.model}(${devInfo.id})";
log.info("device:${devInfo.deviceID},deviceName:$deviceName");
Map<String, dynamic> headers = {};
if (token != null) {
headers["Token"] = token;
}
if (devInfo != null && devInfo.deviceID != null && deviceName != null) {
headers["Device"] = devInfo.deviceID + ":" + deviceName;
}
headers["Project-ID"] = Config.instance.reportProjectID;
BaseOptions options = new BaseOptions(
method: method,
baseUrl: url == null ? Config.instance.apiURL : url,
connectTimeout: 10000,
receiveTimeout: 10000,
headers: headers,
);
log.info("baseUrl:${options.baseUrl}, path:$path");
try {
Dio dio = new Dio(options);
Response response = await dio.request(
path,
data: payload,
);
var data = Status.fromJson(response.data);
if (data.status == 'Ok') {
return response.data["data"];
} else {
throw Exception(data.message);
}
} catch (e) {
log.warning("path:$path, api:$e");
throw e;
}
}
// request makes http request
// if token is null
Future<dynamic> requestDownloadAPI(String path, method,
{dynamic payload, String token, String url, String filePath}) async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
String deviceName = "${androidInfo.model}(${androidInfo.id})";
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
var bytes = utf8.encode(payload);
var base64Str = base64.encode(bytes);
String escapePayload = HtmlEscape().convert(base64Str);
try {
String baseUrl = url == null ? Config.instance.apiURL : url;
log.info("Path:$baseUrl$path");
HttpClient client = new HttpClient();
var _downloadData = StringBuffer();
var fileSave = new File(filePath);
var request = await client.getUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID);
request.headers
.set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
if (token != null) {
request.headers.set("Token", token);
}
if (androidInfo.androidId != null) {
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
}
request.headers.set("payload", escapePayload);
var response = await request.close();
print("headers:${response.headers}");
response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
onDone: () {
fileSave.writeAsString(_downloadData.toString());
});
} catch (e) {
log.warning("path:$path, api:$e");
throw e;
}
}
// request makes http request
// if token is null
Future<dynamic> requestDownloadPDFAPI(String path, method,
{dynamic payload, String token, String url, String filePath}) async {
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
String deviceName = "${androidInfo.model}(${androidInfo.id})";
log.info("device:${androidInfo.androidId},deviceName:$deviceName");
var bytes = utf8.encode(payload);
var base64Str = base64.encode(bytes);
String escapePayload = HtmlEscape().convert(base64Str);
try {
String baseUrl = url == null ? Config.instance.apiURL : url;
log.info("Path:$baseUrl$path");
HttpClient client = new HttpClient();
// var _downloadData = StringBuffer();
var fileSave = new File(filePath);
var request = await client.getUrl(Uri.parse("$baseUrl$path"));
request.headers.set("Project-ID", Config.instance.reportProjectID);
if (token != null) {
request.headers.set("Token", token);
}
if (androidInfo.androidId != null) {
request.headers.set("Device", androidInfo.androidId + ":" + deviceName);
}
request.headers.set("payload", escapePayload);
var response = await request.close();
print("headers:${response.headers}");
var _downloadData = List<int>();
response.listen((d) => _downloadData.addAll(d), onDone: () {
fileSave.writeAsBytes(_downloadData);
});
// response.transform(utf8.decoder).listen((d) => _downloadData.write(d),
// onDone: () {
// fileSave.writeAsString(_downloadData.toString());
// });
} catch (e) {
log.warning("path:$path, api:$e");
throw e;
}
}

3
lib/helpers/const.dart Normal file
View File

@@ -0,0 +1,3 @@
const userStatusInvited = "invited";
const userStatusJoined = "joined";
const userStatusRequested = "requested";

32
lib/helpers/dev_info.dart Normal file
View File

@@ -0,0 +1,32 @@
import 'package:device_info/device_info.dart';
import 'dart:io' show Platform;
class DevInfo {
bool isAndroid;
bool isIOS;
String deviceID;
String id;
String model;
static DevInfo _instance;
static Future<DevInfo> getDevInfo() async {
if (_instance != null) return Future.value(_instance);
_instance = DevInfo();
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
_instance.deviceID = androidInfo.androidId;
_instance.id = androidInfo.id;
_instance.model = androidInfo.model;
} else if (Platform.isIOS) {
IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
_instance.deviceID = iosDeviceInfo.identifierForVendor;
_instance.id = iosDeviceInfo.utsname.release;
_instance.model = iosDeviceInfo.model;
}
return Future.value(_instance);
}
}

View File

@@ -0,0 +1,32 @@
import 'dart:io';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:logging/logging.dart';
import 'package:uuid/uuid.dart';
final log = Logger('firebaseHelper');
final FirebaseAuth auth = FirebaseAuth.instance;
Future<String> getToken() async {
FirebaseUser firebaseUser = await auth.currentUser();
IdTokenResult token = await firebaseUser.getIdToken();
return token.token;
}
Future<String> uploadStorage(String path, File file, {String fileName}) async {
if (fileName == null) {
fileName = Uuid().v4();
}
StorageReference storageReference =
FirebaseStorage.instance.ref().child('$path/$fileName');
StorageUploadTask uploadTask = storageReference.putFile(file);
await uploadTask.onComplete;
String downloadUrl = await storageReference.getDownloadURL();
print("name:${await storageReference.getName()}");
print("bucket:${await storageReference.getBucket()}");
print("path:${await storageReference.getPath()}");
print("meta:${await storageReference.getMetadata()}");
return downloadUrl;
}

View File

@@ -0,0 +1,68 @@
import 'dart:async';
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:fcs/config.dart';
import 'package:logging/logging.dart';
import 'api_helper.dart';
class NetworkConnectivity {
final log = Logger('NetworkConnectivity');
static final NetworkConnectivity instance = NetworkConnectivity._internal();
static String hostName;
NetworkConnectivity._internal() {
_initialise();
var uri = Uri.parse(Config.instance.apiURL);
hostName = uri.host;
log.info("host name:$hostName");
}
Connectivity connectivity = Connectivity();
final StreamController _controller = StreamController.broadcast();
Stream get statusStream => _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;
// lookup if connectivity is not none
if (result != ConnectivityResult.none) {
try {
final hostNameLookup = await InternetAddress.lookup(hostName);
if (hostNameLookup.isNotEmpty &&
hostNameLookup[0].rawAddress.isNotEmpty) {
if (await checkHeartbeat()) {
isOnline = true;
}
} else
isOnline = false;
} on SocketException catch (_) {
isOnline = false;
}
}
if (_controller != null && !_controller.isClosed)
_controller.sink.add({"isOnline": isOnline});
}
Future<bool> checkHeartbeat() async {
var result = await requestAPI("/hb", "GET");
var status = result["status"];
if (status != null && status != "") {
return true;
}
return false;
}
void disposeStream() => _controller.close();
}

View File

@@ -0,0 +1,152 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
/*
* PaginationModel load data in page
* and listen to document change based on 'update_time' and 'delete_time' fields
* of the document
*/
class PaginationModel<T> {
final log = Logger('PaginationModel');
List<String> ids = [];
DocumentSnapshot prev;
int rowPerLoad = 10;
bool ended = false;
StreamSubscription<QuerySnapshot> listener;
CollectionReference listeningCol;
Query pageQuery;
PaginationModel(CollectionReference listeningCol, Query pageQuery,
{this.rowPerLoad = 10}) {
this.listeningCol = listeningCol;
this.pageQuery = pageQuery;
initData();
}
void initData() async {
_clearState();
_initListener();
load();
}
void _clearState() {
prev = null;
ids = [];
ended = false;
if (listener != null) listener.cancel();
listener = null;
if (controller != null) controller.close();
}
StreamController<Result> controller;
Stream<Result> listen() {
if (controller != null) {
controller.close();
}
controller = StreamController<Result>(onCancel: _clearState);
return controller.stream;
}
void close() {
_clearState();
}
final String updateTimeField = 'update_time';
final String deleteTimeField = 'delete_time';
void _initListener() {
Query _query =
listeningCol.orderBy(updateTimeField, descending: true).limit(1);
_query.getDocuments(source: Source.server).then((QuerySnapshot snapshot) {
int count = snapshot.documents.length;
int updateTime = 0;
if (count == 1) {
updateTime = snapshot.documents[0].data[updateTimeField];
}
Query _queryListener = listeningCol
.where(updateTimeField, isGreaterThan: updateTime)
.orderBy(updateTimeField, descending: true);
listener =
_queryListener.snapshots(includeMetadataChanges: true).listen((qs) {
qs.documentChanges.forEach((c) {
switch (c.type) {
case DocumentChangeType.added:
log.info("added!! $c");
_update(c.document.documentID, c.document.data);
break;
case DocumentChangeType.modified:
log.info("modified!! $c");
_update(c.document.documentID, c.document.data);
break;
default:
}
});
});
});
}
void _update(String id, Map<String, dynamic> data) {
if (ids.contains(id)) {
var deleted = data[deleteTimeField];
if (deleted > 0) {
ids.remove(id);
controller.add(Result(
id: id,
data: data,
documentChangeType: DocumentChangeType.removed));
} else {
controller.add(Result(
id: id,
data: data,
documentChangeType: DocumentChangeType.modified));
}
} else {
ids.add(id);
controller.add(Result(
id: id, data: data, documentChangeType: DocumentChangeType.added));
}
}
Future<bool> load() async {
Query _query =
prev != null ? pageQuery.startAfterDocument(prev) : pageQuery;
try {
await _query
.where(deleteTimeField, isEqualTo: 0)
.limit(rowPerLoad)
.getDocuments(source: Source.server)
.then((QuerySnapshot snapshot) {
int count = snapshot.documents.length;
ended = count < rowPerLoad;
prev = count > 0 ? snapshot.documents[count - 1] : prev;
snapshot.documents.forEach((e) {
if (!ids.contains(e.documentID)) log.shout("load!! $e");
ids.add(e.documentID);
controller.add(Result(
id: e.documentID,
data: e.data,
documentChangeType: DocumentChangeType.added));
});
if (ended) {
controller.add(Result(isEnded: true));
}
});
} catch (e) {
log.warning("Error!! $e");
}
return ended;
}
}
class Result {
String id;
Map<String, dynamic> data;
DocumentChangeType documentChangeType;
bool isEnded;
Result({this.id, this.data, this.documentChangeType, this.isEnded = false});
}

View File

@@ -0,0 +1,81 @@
import 'dart:convert';
import 'package:fcs/domain/entities/user.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharedPref {
static final SharedPref instance = SharedPref._();
SharedPref._();
static Future<bool> isFirstLaunch() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool('first_launch');
}
static Future<void> finishFirstLaunch() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.setBool('first_launch', false);
}
static Future<String> getLang() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('language');
}
static Future<void> saveLang(String lang) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('language', lang);
}
static Future<void> saveUser(User user) async {
await _save("user", user.toJson());
}
static Future<User> getUser() async {
try {
return User.fromJson(await _read("user"));
} catch (e) {
return null;
}
}
static Future<User> removeUser() async {
return await _remove("user");
}
static Future<void> saveSkippedRecoverEmail(bool skipped) async {
await _save("skipped_recovery_email", skipped);
}
static Future<bool> getSkippedRecoverEmail() async {
try {
bool _skipped = await _read("skipped_recovery_email");
return _skipped;
} catch (e) {
return null;
}
}
static _read(String key) async {
try {
final prefs = await SharedPreferences.getInstance();
return json.decode(prefs.getString(key));
} catch (e) {
print("Error:$e");
}
}
static _save(String key, value) async {
try {
final prefs = await SharedPreferences.getInstance();
prefs.setString(key, json.encode(value));
} catch (e) {
print("Error:$e");
}
}
static _remove(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}
}

115
lib/helpers/theme.dart Normal file
View File

@@ -0,0 +1,115 @@
import 'package:flutter/material.dart';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
const primaryColor = const Color(0xff272262);
const secondaryColor = const Color(0xffff4400);
const thirdColor = const Color(0xf0ff4444);
const nextColor = const Color(0xFFfa833d);
const buttonColor = const Color(0xFFFFFFFF);
const buttonBkColor = const Color(0xFF268944);
const labelColor = const Color(0xFF757575);
const TextStyle labelStyle =
TextStyle(fontSize: 20, color: primaryColor, fontWeight: FontWeight.w500);
const TextStyle labelStyleMM = TextStyle(
fontSize: 20,
color: primaryColor,
fontWeight: FontWeight.w500,
height: 1,
fontFamily: "Myanmar3");
const TextStyle subMenuStyle =
TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500);
const TextStyle subMenuStyleMM =
TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500,
fontFamily: "Myanmar3");
const TextStyle welcomeLabelStyle =
TextStyle(fontSize: 23, color: primaryColor, fontWeight: FontWeight.w500);
const TextStyle welcomeSubLabelStyle =
TextStyle(fontSize: 18, color: primaryColor, fontWeight: FontWeight.w500);
const TextStyle siginButtonStyle =
TextStyle(fontSize: 16, color: Colors.white, fontWeight: FontWeight.w500);
TextStyle newLabelStyle(
{Color color,
double fontSize,
FontWeight fontWeight,
bool underline = false}) {
return TextStyle(
fontSize: fontSize == null ? 14 : fontSize,
color: color == null ? secondaryColor : color,
fontWeight: fontWeight == null ? FontWeight.w500 : fontWeight,
decoration: underline ? TextDecoration.underline : TextDecoration.none);
}
TextStyle newLabelStyleMM(
{Color color,
double fontSize,
FontWeight fontWeight,
bool underline = false}) {
return TextStyle(
fontSize: fontSize == null ? 13 : fontSize,
color: color == null ? secondaryColor : color,
fontWeight: fontWeight == null ? FontWeight.w500 : fontWeight,
decoration: underline ? TextDecoration.underline : TextDecoration.none,
fontFamily: "Myanmar3");
}
const TextStyle photoLabelStyle =
TextStyle(color: Colors.black, fontSize: 13.0);
const TextStyle photoLabelStyleMM = TextStyle(
color: Colors.black, fontSize: 13.0, fontFamily: "Myanmar3");
const TextStyle textStyle =
TextStyle(fontSize: 14, color: Colors.black87, fontWeight: FontWeight.w500);
const TextStyle textStyleOdd = TextStyle(
fontSize: 15, color: Colors.blueAccent, fontWeight: FontWeight.w500);
const TextStyle textStrikeStyle = TextStyle(
fontSize: 15,
color: Colors.black87,
fontWeight: FontWeight.w500,
decoration: TextDecoration.lineThrough,
);
const TextStyle textHighlightRedStyle = TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.w500,
backgroundColor: Colors.red,
);
const TextStyle textHighlightGreenStyle = TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.w500,
backgroundColor: Colors.green,
);
const TextStyle textHighlightBlueStyle = TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.w500,
backgroundColor: Colors.blue,
);
const TextStyle subTitleStyle =
TextStyle(fontSize: 12, color: Colors.black45, fontWeight: FontWeight.w500);
final BoxDecoration tableRowOdd = BoxDecoration(
color: Color(0x1F000000),
);
final BoxDecoration tableRowEven = BoxDecoration(
color: Colors.white54,
);
class LoginColors {
const LoginColors();
static const Color loginGradientStart = const Color(0xfff00a21);
static const Color loginGradientEnd = const Color(0xfff00a21);
static const primaryGradient = const LinearGradient(
colors: const [loginGradientStart, loginGradientEnd],
stops: const [0.0, 1.0],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
);
}