123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:io';
- import 'dart:math' as math;
- import 'package:flutter/gestures.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:get_it/get_it.dart';
- import 'package:intl/intl.dart';
- import 'package:provider/provider.dart';
- import 'package:shared_preferences/shared_preferences.dart';
- import 'package:sport/application.dart';
- import 'package:sport/bean/game.dart';
- import 'package:sport/bean/game_add_entity.dart';
- import 'package:sport/db/statistics_db.dart';
- import 'package:sport/game/sdk_parse.dart';
- import 'package:sport/pages/web/game_log.dart';
- import 'package:sport/pages/web/statistics.dart';
- import 'package:sport/provider/bluetooth.dart';
- import 'package:sport/provider/user_model.dart';
- import 'package:sport/services/Converter.dart';
- import 'package:sport/services/api/inject_api.dart';
- import 'package:sport/services/api/resp.dart';
- import 'package:sport/services/app_subscription_state.dart';
- import 'package:sport/utils/task_queue.dart';
- import 'package:sport/utils/toast.dart';
- import 'package:sport/widgets/dialog/alert_dialog.dart';
- import 'package:sport/widgets/dialog/ble_wait_dialog.dart';
- import 'package:sport/widgets/dialog/request_dialog.dart';
- import 'package:wakelock/wakelock.dart';
- import 'package:web_socket_channel/io.dart';
- import 'package:webview_flutter/webview_flutter.dart';
- class GamePage extends StatefulWidget {
- final GameInfoData game;
- final bool gameCenter;
- const GamePage({Key? key, required this.game, this.gameCenter = false}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _PageState();
- }
- class _PageState extends State<GamePage> with SubscriptionState, InjectApi {
- WebViewController? _controller;
- GlobalKey _key = GlobalKey();
- late Set<JavascriptChannel> javascriptChannels;
- ValueNotifier<int> progressNotifier = ValueNotifier<int>(0);
- late Bluetooth bluetooth;
- Timer? _timer;
- Timer? _timerGame;
- bool _onPageFinished = false;
- bool _onSdkLoaded = false;
- bool _gamePlaying = false;
- bool _gameFinished = false;
- bool _openGameData = false;
- bool _openGameAtt = false;
- bool _vibrate = false;
- Statistics? _statistics;
- late String playGroup;
- late StatisticsDB _db;
- late SDKApi sdk;
- late int sdkVersion = 0;
- @override
- void initState() {
- super.initState();
- javascriptChannels = Set.of([JavascriptChannel(name: "SDKBridge", onMessageReceived: _onMessageReceived)]);
- playGroup = "${DateTime.now().millisecondsSinceEpoch}";
- bluetooth = GetIt.I();
- sdk = bluetooth.gameSDK();
- _initGame();
- _timer = Timer.periodic(Duration(milliseconds: 16), (timer) {
- if (_onPageFinished != true) return;
- if (appLifecycleState != AppLifecycleState.resumed) return;
- if (_gamePlaying == true) {
- int stepFreq = sdk.getStepFreq();
- int stepCount = sdk.getStepCount();
- // if (_stepCount == stepCount) return;
- if (sdkVersion >= 1) {
- int velocity = sdk.getGameStepVel();
- if (isDebugShoe) print("sdk -- step $stepFreq, $stepCount, $velocity");
- _js("stepData", "$stepFreq,$stepCount,$velocity");
- } else {
- if (isDebugShoe) print("sdk -- step $stepFreq, $stepCount");
- _js("step", "$stepFreq,$stepCount");
- }
- }
- if(_openGameAtt){
- if (isDebugShoe) print("sdk -- att ${sdk.getAttX()} ${-(sdk.getAttX() / 10000.0 * 57.29578)}");
- _js("att", "${sdk.getAttX()},0,0");
- }
- });
- _timerGame = Timer.periodic(Duration(seconds: 5), (timer) {
- if (_gamePlaying == true) _updateGameState();
- if(!bluetooth.isConnected){
- showWaitDialog(context);
- return ;
- }
- if(bluetooth.h5gameRNotifier.value != true){
- bluetooth.setupGameMode4h5(true);
- showWaitDialog(context);
- }
- });
- addSubscription(bluetooth.sdkCmdStream.listen((event) {
- int cmd = event;
- _js("cmd", "0,$cmd");
- if (cmd == 6) {
- bluetooth.vibrate(200, leftOrRight: 1);
- }
- if (cmd == 5) {
- bluetooth.vibrate(200, leftOrRight: 2);
- }
- }));
- addSubscription(bluetooth.sdkMotionDataStream.listen((event) {
- String gameData = event;
- if (gameData.length > 10) {
- if (_openGameData == true) _js("gameData", "[$gameData]");
- if (isDebugShoe) {
- channel?.sink.add("$gameData,0");
- }
- }
- }));
- addSubscription(bluetooth.sdkMotionStream.listen((event) {
- if (_onPageFinished != true) return;
- // if (_gamePlaying == true) {
- List<int> result = event;
- if(result.take(4).any((element) => element > 0)) {
- _js("motion", "0,0,${result[0]},${result[1]},${result[2]},${result[3]}");
- }
- // }
- }));
- Wakelock.enable();
- bluetooth.uploadGameLog();
- if (isDebugShoe) {
- try {
- channel = IOWebSocketChannel.connect(Uri.parse('ws://172.16.14.127/examples/websocket/chat'));
- } catch (e) {
- print(e);
- }
- }
- _db = StatisticsDB();
- SharedPreferences.getInstance().then((prefs) {
- this._vibrate = prefs.getBool("vibrate") ?? true;
- });
- WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
- if ((widget.game.h5 ?? 0) == 1) {
- SystemChrome.setPreferredOrientations(Platform.isIOS ? [DeviceOrientation.landscapeRight] : [DeviceOrientation.landscapeLeft]);
- }
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
- });
- }
- @override
- void dispose() {
- progressNotifier.dispose();
- _gameFinished = true;
- Wakelock.disable();
- bluetooth.setupGameMode4h5(widget.gameCenter);
- if (!widget.gameCenter) {
- if ((widget.game.h5 ?? 0) == 1) {
- SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
- }
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top]);
- }
- _controller = null;
- _onGameEnd(isCancel: 1, forceExit: true);
- _timer?.cancel();
- _timerGame?.cancel();
- super.dispose();
- channel?.sink.close();
- }
- _js(String call, String args) {
- var script = "if(window.SHOES_SDK != undefined && window.SHOES_SDK != null && window.SHOES_SDK['$call'] != undefined){window.SHOES_SDK.$call($args);}";
- // print("sdk -- call $script");
- if (appLifecycleState == AppLifecycleState.resumed) _controller?.runJavascript(script);
- }
- _updateGameState({int level = 0, double score = 0, int record = 0, int mode = 0, int opponentId = 0, int isCancel = 0}) {
- if (_statistics != null) {
- for (var i = 0; i < MOTION_COUNT_TYPE.values.length; i++) {
- _statistics!.data[i] = sdk.getMotionCount(i);
- }
- _statistics!.data[(MOTION_COUNT_TYPE.JUMP_COUNT.index)] += _statistics!.data[(MOTION_COUNT_TYPE.ROCK_COUNT.index)] + _statistics!.data[(MOTION_COUNT_TYPE.SCISSORS_COUNT.index)] + _statistics!.data[(MOTION_COUNT_TYPE.PAPER_COUNT.index)];
- _statistics!.end = DateTime.now().millisecondsSinceEpoch;
- if (level > 0) _statistics!.level = level;
- if (score > 0) _statistics!.score = score;
- if (record > 0) _statistics!.record = record;
- if (mode > 0) _statistics!.mode = mode;
- if (opponentId > 0) _statistics!.opponentId = opponentId;
- if (isCancel > 0) _statistics!.status = isCancel;
- _db.update(_statistics!);
- }
- }
- _initGame() {
- bluetooth.setupGameMode4h5(true);
- sdk.gameInit(widget.game.gameType ?? 0);
- }
- _onGameStart() {
- _initGame();
- _gamePlaying = true;
- _statistics = Statistics.name("${widget.game.id}", DateTime.now().millisecondsSinceEpoch);
- _db.save(_statistics!).then((value) => _statistics!.id = value);
- if (isDebugShoe) {
- bluetooth.createGameLog("${widget.game.name}");
- channel?.sink.add("start");
- }
- }
- _onGameEnd({int level = 0, double score = 0, int record = 0, int mode = 0, int opponentId = 0, int isCancel = 0, bool forceExit = false}) async {
- _gamePlaying = false;
- if (_statistics == null) return;
- _updateGameState(level: level, score: score, record: record, mode: mode, opponentId: opponentId, isCancel: isCancel);
- int jump = _statistics!.data[(MOTION_COUNT_TYPE.JUMP_COUNT.index)] + _statistics!.data[(MOTION_COUNT_TYPE.ROCK_COUNT.index)] + _statistics!.data[(MOTION_COUNT_TYPE.SCISSORS_COUNT.index)] + _statistics!.data[(MOTION_COUNT_TYPE.PAPER_COUNT.index)];
- int down = _statistics!.data[(MOTION_COUNT_TYPE.DOWN_COUNT.index)];
- int step = _statistics!.data[(MOTION_COUNT_TYPE.STEP_COUNT.index)];
- //
- // int consume = widget.game.totalConsume(step, jump, down);
- // int equivalent = (SportUtils.consumeToMinute(consume, 4.3, weight) * 60 * 0.7).round();
- print("sdk -- data ${_statistics?.data}");
- int duration = (_statistics!.end - _statistics!.start) ~/ 1000;
- int id = _statistics?.id ?? 0;
- if (duration < 1) {
- _db.deleteRecord(id);
- return;
- }
- String movements = _statistics?.movements ?? "";
- int start = _statistics!.start;
- _statistics = null;
- ApiCall upload = () async {
- final completer = Completer<RespData<GameAddEntity>>();
- Timer timer = Timer(Duration(seconds: 5), () {
- if (!completer.isCompleted) completer.completeError("timeout");
- });
- var queue = TaskQueue();
- var listen = queue.stream.listen((event) {
- if (event.id == id && event.type == TaskType.GAME.index && event.state == 0) {
- print("sdk -- post succ ${event.id}");
- _db.deleteRecord(id);
- if (!completer.isCompleted) completer.complete(event.result as RespData<GameAddEntity>);
- }
- });
- var time = DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.fromMillisecondsSinceEpoch(start));
- queue.addTask(id, TaskType.GAME, () => api.postAddGame("${widget.game.id}", score, record, level, mode, duration, 0, 0, jump, down, step, 0, opponentId, isCancel, playGroup, movements, time));
- try {
- RespData<GameAddEntity> resp = await completer.future;
- timer.cancel();
- if (resp.code == 0 && resp.data?.record != null) {
- _db.deleteRecord(id);
- GameAddRecord? record = resp.data?.record;
- return "{'consume':${record?.consume},'equivalent':${record?.equivalent},'unit':'${record?.unit}','position':${resp.data?.rankResult.isNotEmpty == true ? resp.data?.rankResult.first.position : 0}}";
- }
- } catch (e) {
- print(e);
- }
- listen.cancel();
- return "{'consume':0,'equivalent':0,'unit':'步','position':0}";
- };
- if (forceExit) {
- upload.call();
- } else {
- request(context, upload).then((value) => _js("callback", "'onGameEnd_callback', $value"));
- }
- bluetooth.uploadGameLog();
- }
- IOWebSocketChannel? channel;
- _onMessageReceived(JavascriptMessage message) async {
- String msg = message.message;
- var data = json.decode(msg);
- String? method = data['method'];
- debugPrint("sdk -- call $method");
- if (method == null) return;
- if (_onSdkLoaded != true) {
- setState(() {
- _onSdkLoaded = true;
- });
- }
- if ("onLoad" == method) {
- var args = data['args'];
- sdkVersion = (args['ver'] as int?) ?? 0;
- // if (Platform.isIOS) {
- var mediaQuery = MediaQuery.of(context);
- var offset = Offset(mediaQuery.size.width / 2, mediaQuery.viewPadding.top + 5);
- GestureBinding.instance!.handlePointerEvent(PointerAddedEvent(pointer: 0, position: offset));
- GestureBinding.instance!.handlePointerEvent(PointerDownEvent(pointer: 0, position: offset));
- GestureBinding.instance!.handlePointerEvent(PointerUpEvent(pointer: 0, position: offset));
- // }
- } else if ("onBackPressed" == method) {
- var args = data['args'];
- var foot = (args['foot'] as bool?) ?? false;
- _controller?.canGoBack().then((value) {
- if (value) {
- _controller?.goBack();
- } else {
- setState(() {
- Navigator.pop(context, foot);
- });
- }
- });
- } else if ("onGameStart" == method) {
- _onGameStart();
- } else if ("onGameEnd" == method) {
- var args = data['args'];
- var level = Converter.toInt(args['level']);
- var score = Converter.toDouble(args['score']);
- var record = Converter.toInt(args['record']);
- var mode = Converter.toInt(args['mode']);
- var opponentId = Converter.toInt(args['opponentId']);
- _onGameEnd(level: level, score: score, record: record, mode: mode, opponentId: opponentId);
- } else if ("getUserInfo_callback" == method) {
- var user = Provider.of<UserModel>(context, listen: false).user;
- _js("callback", "'getUserInfo_callback', ${json.encode(user.toJsonSimple())}");
- if (Platform.isIOS) {
- var mediaQuery = MediaQuery.of(context);
- var offset = Offset(mediaQuery.size.width / 2, mediaQuery.viewPadding.top + 5);
- GestureBinding.instance!.handlePointerEvent(PointerAddedEvent(pointer: 0, position: offset));
- GestureBinding.instance!.handlePointerEvent(PointerDownEvent(pointer: 0, position: offset));
- GestureBinding.instance!.handlePointerEvent(PointerUpEvent(pointer: 0, position: offset));
- }
- } else if ("getRank_callback" == method) {
- var args = data['args'];
- var type = Converter.toInt(args['type']);
- var resp = await api.getRankInfo("${widget.game.id}", scope: type == 0 ? "world" : "friend");
- var info = resp.data;
- List<Map<String, dynamic>> list = [];
- if (info != null && info.records != null) {
- var records = info.records!;
- for (var i = 0; i < records.length; i++) {
- list.add({"user": records[i].toJsonSimple(), "score": records[i].score ?? 0, "rank": (i + 1)});
- }
- }
- _js("callback", "'getRank_callback', [$type, ${json.encode({"list": list})}]");
- } else if ("vibrate" == method) {
- if (this._vibrate != true) return;
- var args = data['args'];
- var duration = Converter.toInt(args['duration']);
- var leftOrRight = Converter.toInt(args['leftOrRight']);
- bluetooth.vibrate(duration, leftOrRight: leftOrRight);
- } else if ("openGameData" == method) {
- var args = data['args'];
- var open = args['open'] as bool;
- _openGameData = open;
- } else if ("openGameAtt" == method) {
- var args = data['args'];
- var open = args['open'] as bool;
- _openGameAtt = open;
- }
- }
- @override
- Widget build(BuildContext context) {
- var size = MediaQuery.of(context).size;
- String url = widget.game.downloadUrl!;
- // url = "http://172.16.14.128:7456/";
- var body = Scaffold(
- backgroundColor: Colors.black,
- body: Stack(
- fit: StackFit.expand,
- children: [
- Center(
- child: AspectRatio(
- aspectRatio: math.max(size.aspectRatio, 1.78),
- child: WebView(
- key: _key,
- backgroundColor: Colors.black,
- initialUrl: "$url",
- gestureNavigationEnabled: true,
- javascriptMode: JavascriptMode.unrestricted,
- initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
- allowsInlineMediaPlayback: true,
- onWebViewCreated: (WebViewController webViewController) {
- _controller = webViewController;
- bluetooth.setupGameMode4h5(true);
- },
- onProgress: (progress) {
- progressNotifier.value = progress;
- },
- onPageFinished: (r) {
- setState(() {
- _onPageFinished = true;
- });
- var sdkVersion = sdk.getVersion();
- if (isDebugShoe) ToastUtil.show("网页加载完成!GameSDK Version: $sdkVersion");
- // Future.delayed(Duration(seconds: 10)).then((value) {
- // if (_gamePlaying != true && _gameFinished != true) _onGameStart();
- // });
- },
- javascriptChannels: javascriptChannels,
- ),
- ),
- ),
- if (_onPageFinished == false)
- ValueListenableBuilder<int>(
- valueListenable: progressNotifier,
- builder: (context, v, _) {
- return Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- SizedBox(
- width: 100,
- height: 100,
- child: CircularProgressIndicator(
- value: v / 100.0,
- strokeWidth: 6,
- )),
- const SizedBox(height: 20.0,),
- Text("运动正在加载...", style: Theme.of(context).textTheme.headline6,)
- ],
- ));
- }),
- if (_onPageFinished == false)
- Positioned(
- top: 8,
- right: 8,
- child: IconButton(
- onPressed: () => Navigator.pop(context),
- icon: Icon(
- Icons.close,
- color: Colors.white,
- ))),
- // if (_onPageFinished == true && _onSdkLoaded == false)
- // Center(
- // child: SizedBox(
- // width: 100,
- // height: 100,
- // child: CircularProgressIndicator(
- // strokeWidth: 6,
- // ))),
- // if ((widget.game.h5 ?? 0) == 1 && MediaQuery.of(context).orientation == Orientation.portrait)
- // Center(
- // child: Column(
- // mainAxisSize: MainAxisSize.min,
- // children: [
- // Image.asset("lib/assets/img/icon_pop_horizontal_screen.png",
- // height: 60,
- // fit: BoxFit.fitHeight,
- // ),
- // const SizedBox(height: 16.0,),
- // Text("为了更好的体验,请将手机横过来", style: Theme.of(context).textTheme.headline6,)
- // ],
- // ),
- // ),
- ],
- ),
- );
- return Platform.isIOS
- ? body
- : WillPopScope(
- onWillPop: () async {
- return await showDialog(
- context: context,
- builder: (context) => CustomAlertDialog(title: '是否退出${widget.game.name}', ok: () => Navigator.of(context).pop(true)),
- ) ==
- true;
- },
- child: body,
- );
- }
- }
|