| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- import 'dart:core';
- import 'dart:typed_data';
- import 'package:fixnum/fixnum.dart';
- import 'package:flutter/material.dart';
- import 'package:get/get.dart';
- import 'package:grpc/grpc.dart';
- import 'package:trackoffical_app/exception/exception.dart';
- import 'package:trackoffical_app/logger.dart';
- import 'package:trackoffical_app/route.dart' as r;
- import 'package:sensor/sensor.dart';
- import 'generated/google/protobuf/timestamp.pb.dart' as pb;
- import 'generated/google/protobuf/duration.pb.dart' as pb;
- import 'package:geolocator/geolocator.dart' as geo;
- import 'model/distance.dart';
- export 'model/distance.dart';
- extension PbDurationExtension on pb.Duration {
- Duration toDuration() {
- final microseconds = hasNanos() ? nanos / 1000 : 0;
- return Duration(
- seconds: seconds.toInt(), microseconds: microseconds.toInt());
- }
- }
- extension DurationExtension on Duration {
- pb.Duration toPb() {
- final nanos =
- (inMicroseconds - inSeconds * Duration.microsecondsPerSecond) * 1000;
- return pb.Duration()..seconds= Int64(inSeconds) ..nanos= nanos;
- }
- String toMinSecondString(){
- final minute = inMinutes;
- var second = inSeconds;
- second = second - minute * Duration.secondsPerMinute;
- return '$minute\'$second\'\'';
- }
- String toAppString() {
- final countDown = this;
- var s = countDown.inSeconds;
- final h = (s / 3600).floor();
- final hStr = h.twoDigits();
- final m = ((s - h * 3600) / 60).floor();
- final mStr = m.twoDigits();
- final sStr = (s - h * 3600 - m * 60).twoDigits();
- return '$hStr:$mStr:$sStr';
- }
- Color paceColor(){
- final duration = inMilliseconds.abs().toDouble() / 60000;
- const maxPace = 5.0;
- const minPace = 15.0;
- var v = duration - maxPace;
- var p = v / (minPace - maxPace);
- if(p > 1){
- p = 1;
- }
- if(p < 0){
- p = 0;
- }
- var r = 0;
- var g = 255;
- if(p <= 0.5){
- r = (255.0 * (p / 0.5)).round();
- }else{
- r = 255;
- }
- if(p > 0.5){
- g = g - (255.0 * ((p-0.5) / 0.5)).round();
- }else{
- g = 255;
- }
- return Color.fromARGB(255, r, g, 34);
- }
- }
- extension DateTimeExtension on DateTime {
- String toAppString() {
- String y =
- (year >= -9999 && year <= 9999) ? _fourDigits(year) : _sixDigits(year);
- String m = _twoDigits(month);
- String d = _twoDigits(day);
- String h = _twoDigits(hour);
- String min = _twoDigits(minute);
- String sec = _twoDigits(second);
- return "$y-$m-$d $h:$min:$sec";
- }
- String toAppStringNoDate() {
- String h = _twoDigits(hour);
- String min = _twoDigits(minute);
- String sec = _twoDigits(second);
- return "$h:$min:$sec";
- }
- }
- extension Uint8ListExtension on Uint8List {
- String toHexString({String separator = ''}) {
- return isEmpty
- ? ""
- : map((e) {
- var s = e.toRadixString(16).toUpperCase();
- if (e < 10) {
- s = '0$s';
- }
- return s;
- }).join(separator);
- }
- }
- const hrPColors = <Color>[
- Color(0xff028FE1),
- Color(0xFF4D28ED),
- Color(0xFF0AB105),
- Color(0xFFFFB308),
- Color(0xFFFF6200),
- Color(0xFFD11122)
- ];
- const userMarkColors = <Color>[
- Color(0xFFD112B7),
- Color(0xFF5312D1),
- Color(0xFF1272D1),
- Color(0xFF21AEAE),
- Color(0xFF612D61),
- Color(0xFFF6DE00),
- Color(0xFFFF7904),
- Color(0xFFFF8E97),
- Color(0xFF003C71),
- Color(0xFF850000),
- ];
- extension IntExtension on int {
- String twoDigits() {
- if (this >= 10) return "$this";
- return "0$this";
- }
- Color toHRPColor() {
- final hrp = this;
- var color = const Color(0xff028FE1);
- if (hrp >= 40 && hrp <= 54) {
- color = const Color(0xFF4D28ED);
- }
- if (hrp >= 55 && hrp <= 69) {
- color = const Color(0xFF0AB105);
- }
- if (hrp >= 70 && hrp <= 79) {
- color = const Color(0xFFFFB308);
- }
- if (hrp >= 80 && hrp <= 89) {
- color = const Color(0xFFFF6200);
- }
- if (hrp >= 90 && hrp <= 9999999) {
- color = const Color(0xFFD11122);
- }
- return color;
- }
- Color toUserMarkColor() {
- final userid = this;
- return userMarkColors[userid % 10];
- }
- }
- String _twoDigits(int n) {
- if (n >= 10) return "$n";
- return "0$n";
- }
- String _fourDigits(int n) {
- int absN = n.abs();
- String sign = n < 0 ? "-" : "";
- if (absN >= 1000) return "$n";
- if (absN >= 100) return "${sign}0$absN";
- if (absN >= 10) return "${sign}00$absN";
- return "${sign}000$absN";
- }
- String _sixDigits(int n) {
- assert(n < -9999 || n > 9999);
- int absN = n.abs();
- String sign = n < 0 ? "-" : "+";
- if (absN >= 100000) return "$sign$absN";
- return "${sign}0$absN";
- }
- extension PbDateTimeExt on pb.Timestamp {
- DateTime? toDateTimeNullable() {
- if (nanos == 0 && seconds == 0) {
- return null;
- }
- return toDateTime(toLocal: true);
- }
- DateTime toModel() {
- return toDateTime(toLocal: true);
- }
- }
- extension DateTimeExt on DateTime? {
- pb.Timestamp toPb() {
- if (this != null) {
- return pb.Timestamp.fromDateTime(this!.toUtc());
- } else {
- return pb.Timestamp();
- }
- }
- String toHHmm() {
- final t = this;
- if (t == null) {
- return "00:00";
- }
- final hStr = t.hour.twoDigits();
- final mStr = t.minute.twoDigits();
- return '$hStr:$mStr';
- }
- }
- extension DoubleExtension on double {
- String kmToStr({int fixed=1}) {
- if(this < 1){
- return '${(this*1000).round()} m';
- }else{
- return '${toStringAsFixed(fixed)} km';
- }
- }
- Color paceColor(){
- var v = this - 5;
- var p = v / 4;
- if(p > 1){
- p = 1;
- }
- if(p < 0){
- p = 0;
- }
- var r = 0;
- var g = 255;
- if(p <= 0.5){
- r = (255.0 * (p / 0.5)).round();
- }else{
- r = 255;
- }
- if(p > 0.5){
- g = g - (255.0 * ((p-0.5) / 0.5)).round();
- }else{
- g = 255;
- }
- return Color.fromARGB(255, r, g, 34);
- }
- }
- class _AskLocationServiceState extends State<AskLocationServiceDialog> with WidgetsBindingObserver {
- @override
- void initState() {
- super.initState();
- //2.页面初始化的时候,添加一个状态的监听者
- WidgetsBinding.instance.addObserver(this);
- }
- @override
- void dispose() {
- super.dispose();
- //3. 页面销毁时,移出监听者
- WidgetsBinding.instance.removeObserver(this);
- }
- var isActive=true;
- //监听程序进入前后台的状态改变的方法
- @override
- void didChangeAppLifecycleState(AppLifecycleState state) {
- super.didChangeAppLifecycleState(state);
- switch (state) {
- //进入应用时候不会触发该状态 应用程序处于可见状态,并且可以响应用户的输入事件。它相当于 Android 中Activity的onResume
- case AppLifecycleState.resumed:
- isActive=true;
- break;
- //应用状态处于闲置状态,并且没有用户的输入事件,
- // 注意:这个状态切换到 前后台 会触发,所以流程应该是先冻结窗口,然后停止UI
- case AppLifecycleState.inactive:
- break;
- //当前页面即将退出
- case AppLifecycleState.detached:
- break;
- // 应用程序处于不可见状态
- case AppLifecycleState.paused:
- break;
- }
- }
- @override
- Widget build(BuildContext context) {
- return AlertDialog(
- icon: const Icon(Icons.location_on),
- title: const Text('需要打开系统定位'),
- content: const Text('用于展示附近商家和辅助定向'),
- actions: [
- TextButton(onPressed: ()=>Get.back(), child: const Text('暂不开启')),
- FilledButton(onPressed: ()async{
- await geo.Geolocator.openLocationSettings();
- isActive=false;
- while(!isActive){
- await 10.milliseconds.delay();
- }
- if(mounted){
- Get.back();
- }
- }, child: const Text('去设置'))
- ],
- );
- }
- }
- class AskLocationServiceDialog extends StatefulWidget{
- const AskLocationServiceDialog({super.key});
- static Future<void> show(){
- return Get.dialog(const AskLocationServiceDialog());
- }
- @override
- State<StatefulWidget> createState() {
- return _AskLocationServiceState();
- }
- }
- Future<bool> isLocationServiceEnabled() {
- return Sensor.isLocationServiceOpen();
- }
- Duration pacePerKm(Distance distance, Duration d){
- if(distance.km==0){
- return Duration.zero;
- }
- final m = d.inMilliseconds.toDouble();
- return (m / distance.km).milliseconds;
- }
- Future<void> checkLocationService() async {
- // Test if location services are enabled.
- var serviceEnabled = await isLocationServiceEnabled();
- if (!serviceEnabled) {
- await AskLocationServiceDialog.show();
- if(!await isLocationServiceEnabled()){
- // Location services are not enabled don't continue
- // accessing the position and request users of the
- // App to enable the location services.
- return Future.error(NoServiceError());
- }
- }
- }
- Future<void> tryCatchApi(Future<void> Function() call, {
- String? errTitle,
- bool Function(GrpcError err)? onError,
- Future<void> Function()? onSuccess,
- VoidCallback? onFinally,
- })async{
- try {
- await call();
- await onSuccess?.call();
- } on GrpcError catch (e) {
- warn(e);
- if(onError!= null){
- if (onError(e)){
- return;
- }
- }
- switch (e.code) {
- case StatusCode.unavailable:
- Get.snackbar('网络错误', "请稍后重试");
- break;
- case StatusCode.unauthenticated:
- if(await r.Route.toLogin(thenBack: true)){
- try{
- await call();
- }catch(e){
- Get.snackbar(errTitle?? "出错了", "未知错误");
- }
- }
- break;
- case StatusCode.unknown:
- Get.snackbar(errTitle?? "出错了", "未知错误");
- break;
- default:
- Get.snackbar(errTitle?? "出错了", e.message??'');
- }
- } catch (e) {
- warn(e);
- Get.snackbar(errTitle?? "出错了", "未知错误");
- }finally{
- onFinally?.call();
- }
- }
- String getFileExtensionFromUrl(String url) {
- String extension = '';
- if (url.isNotEmpty) {
- final path = Uri.parse(url).path;
- if (path.isNotEmpty) {
- int position = path.lastIndexOf('/');
- String fileName = path.substring(position + 1);
- position = fileName.lastIndexOf('.');
- if (position >= 0 && position < fileName.length - 1) {
- extension = fileName.substring(position + 1);
- }
- }
- }
- return extension;
- }
- extension MGetOnNum on num{
- Distance get km=> Distance(km: toDouble());
- Distance get meter=> Distance(m: toDouble());
- int get compassDegrees{
- var d = toInt();
- while (d < 0) {
- d += 360;
- }
- while (d > 360) {
- d -= 360;
- }
- return d;
- }
- }
|