Files
rog_app/lib/pages/destination/destination_controller.dart

1636 lines
60 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:io';
import 'dart:typed_data';
import 'package:camera_camera/camera_camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:geojson_vi/geojson_vi.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:latlong2/latlong.dart';
import 'package:rogapp/main.dart';
import 'package:rogapp/model/destination.dart';
import 'package:rogapp/model/gps_data.dart';
import 'package:rogapp/pages/camera/camera_page.dart';
import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/routes/app_pages.dart';
import 'package:rogapp/services/DatabaseService.dart';
import 'package:rogapp/services/destination_service.dart';
import 'package:rogapp/services/external_service.dart';
import 'package:rogapp/services/location_service.dart';
import 'package:rogapp/services/maxtrix_service.dart';
import 'package:rogapp/services/perfecture_service.dart';
import 'package:rogapp/utils/database_gps.dart';
import 'package:rogapp/utils/database_helper.dart';
import 'package:rogapp/utils/location_controller.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart';
import 'dart:async';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:rogapp/widgets/debug_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
// 目的地に関連する状態管理とロジックを担当するクラスです。
//
class DestinationController extends GetxController {
late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。
//Timer? _GPStimer; // GPSタイマーを保持する変数です。
var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。
List<Destination> destinations = <Destination>[].obs; // 目的地のリストを保持するObservable変数です。
double currentLat = 0.0; // 現在の緯度と経度を保持する変数です。
double currentLon = 0.0;
double lastValidLat = 0.0; // 最後に中・強信号で拾ったGPS位置。
// ロゲ開始を屋内でやったら 0 のままなので、屋外で行うこと。
double lastValidLon = 0.0;
DateTime lastGPSCollectedTime = DateTime.now(); // 最後にGPSデータが収集された時刻を保持する変数です。
bool shouldShowBottomSheet = true; // ボトムシートを表示すべきかどうかを示すフラグです。
static bool gps_push_started = false; // ゲームの状態を示す静的変数です。
static bool game_started = false;
static bool ready_for_goal = false;
bool skip_10s = false; // 10秒間のスキップフラグを示す変数です。
List<Destination> currentSelectedDestinations = <Destination>[].obs; // 現在選択されている目的地のリストを保持するObservable変数です。
var isInCheckin = false.obs; // ゲームの状態を示すReactive変数です。
var isInRog = false.obs;
var isAtStart = false.obs;
var isAtGoal = false.obs;
var isPhotoShoot = false.obs;
DateTime lastGoalAt = DateTime.now().subtract(const Duration(days: 1)); // 最後にゴールした時刻を保持する変数です。
//List<Rogaining> rogainings = <Rogaining>[].obs;
bool checkingIn = false; // チェックイン中かどうかを示すフラグです。
var isGpsSelected = true.obs; // GPSが選択されているかどうかを示すReactive変数です。
BuildContext? context; // ビルドコンテキストを保持する変数です。
List<String> gps = <String>["-- stating --"].obs; // GPSと位置情報の許可に関する情報を保持するObservable変数です。
List<String> locationPermission = <String>[" -- starting -- "].obs;
var travelMode = 0.obs; // 移動モードを保持するReactive変数です。
bool skipGps = false; // GPSをスキップするかどうかを示すフラグです。
bool okToUseGPS = false; // 最新のGPS情報を使用して良いかを示すフラグ。
Map<String, dynamic> matrix = {}; // 行列データを保持する変数です。
final photos = <File>[].obs; // 写真のリストを保持するReactive変数です。
final IndexController indexController = Get.find<IndexController>(); // IndexControllerのインスタンスを保持する変数です。
final LocationController locationController = Get.put(LocationController()); // LocationControllerのインスタンスを保持する変数です。
final DatabaseService dbService = DatabaseService(); // DatabaseServiceのインスタンスを保持する変数です。
int _start = 0; // 開始時刻を保持する変数です。
int chekcs = 0; // チェックポイントの数を保持する変数です。
var rogainingCounted = false.obs; // ロゲイニングがカウントされたかどうかを示すReactive変数です。
// destinationController.rogainingCountedは、現在のロゲイニングセッションでポイントがカウントされたかどうかを管理するフラグです。
//
// このフラグは以下のような状況で使用されます:
//
// ロゲイニングを開始したとき、rogainingCountedはfalseに初期化されます。これは、まだポイントがカウントされていないことを示します。
// チェックポイントに到着し、チェックインが成功したとき、rogainingCountedはtrueに設定されます。これは、そのセッションでポイントがカウントされたことを示します。
// ロゲイニングを終了したとき、rogainingCountedは再びfalseに設定されます。これは、次のセッションに備えてフラグをリセットするためです。
// このフラグは、主に以下の目的で使用されます:
//
// ゴール地点でのロジックの制御rogainingCountedがtrueの場合、つまりポイントがカウントされている場合にのみ、ゴール処理を実行できます。
// UI の更新rogainingCountedの状態に基づいて、適切なメッセージやボタンを表示することができます。
bool isMapControllerReady = false;
LatLng lastValidGPSLocation = LatLng(0, 0);
DateTime lastGPSDataReceivedTime = DateTime.now();
DateTime lastPopupShownTime = DateTime.now().subtract(Duration(minutes: 10));
bool isPopupShown = false;
bool hasReceivedGPSData = false;
/*
//==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5
//
bool kDebugMode = true;
// シミュレーションモードのフラグ
RxBool isSimulationMode = RxBool(true);
// シミュレーションモードを切り替えるための関数
void toggleSimulationMode(bool value) {
isSimulationMode.value = value;
}
// 現在位置の取得メソッドを追加
LatLng getCurrentLocation() {
return LatLng(lastValidLat, lastValidLon);
}
//
// GPS信号の強弱を判断するメソッドを追加します。
//
String getGpsSignalStrength() {
// デバッグモードかつシミュレーションモードの場合は、シミュレートされた信号強度を返す
print("kDebugMode : ${kDebugMode}, isSimulationMode : ${isSimulationMode.value}");
if (kDebugMode && isSimulationMode.value) {
return locationController.getSimulatedSignalStrength();
}
// 通常モードの場合は、実際の信号強度を返す
final accuracy = locationController.currentPosition.value?.accuracy ?? double.infinity;
if (accuracy <= 10) {
return 'high';
} else if (accuracy <= 30) {
return 'medium';
} else {
return 'low';
}
}
//
//==== Akira .. GPS信号シミュレーション用 ======= ここまで
*/
void showGPSDataNotReceivedPopup() {
Get.dialog(
AlertDialog(
title: Text('GPS信号が受信できません'),
content: Text('GPS信号が受信できる場所に移動してください。'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('OK'),
),
],
),
);
}
// 最後に有効なGPSデータを受け取ってから10分以上経過している場合にのみメッセージを表示するようにします。
//
void checkGPSDataReceived() {
if (!hasReceivedGPSData) {
// GPS信号を全く受信していない。
if (!isPopupShown) {
// ポップアップしていない。
showGPSDataNotReceivedPopup();
lastPopupShownTime = DateTime.now();
isPopupShown = true;
}
} else {
if (DateTime.now().difference(lastGPSDataReceivedTime).inSeconds >= 600) {
// 前回GPS信号を受信してから10分経過。
if (!isPopupShown && DateTime.now().difference(lastPopupShownTime).inMinutes >= 3) {
// 前回ポップアップしてから3分経過してなければ
showGPSDataNotReceivedPopup();
lastPopupShownTime = DateTime.now();
isPopupShown = true;
}
} else {
isPopupShown = false;
}
}
}
// 日時をフォーマットされた文字列に変換する関数です。
//
String getFormatedTime(DateTime datetime) {
return DateFormat('yyyy-MM-dd HH:mm:ss').format(datetime);
}
// 追加Akira 2024-4-5
// GPS信号の精度が一定値以上の場合、GPS信号が弱いと判断する
//
bool isGpsSignalWeak() {
final accuracy = locationController.currentPosition.value?.accuracy;
if (accuracy == null) {
return true; // 位置情報が取得できていない場合、GPS信号が弱いと見なす
}
return accuracy > 50;
//return locationController.currentPosition.value?.accuracy ?? double.infinity > 50;
}
//
Destination festuretoDestination(GeoJSONFeature fs) {
GeoJSONMultiPoint mp = fs.geometry as GeoJSONMultiPoint;
LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]);
//print("----- ${indexController.currentFeature[0].properties} -----");
return Destination(
name: fs.properties!["location_name"],
sub_loc_id: fs.properties!["sub_loc_id"],
address: fs.properties!["address"],
phone: fs.properties!["phone"],
email: fs.properties!["email"],
webcontents: fs.properties!["webcontents"],
videos: fs.properties!["videos"],
category: fs.properties!["category"],
series: 1,
lat: pt.latitude,
lon: pt.longitude,
location_id: fs.properties!["location_id"],
list_order: 1,
photos: fs.properties!["photos"],
checkin_radious: fs.properties!["checkin_radius"],
auto_checkin: fs.properties!["auto_checkin"] == true ? 1 : 0,
cp: fs.properties!["cp"],
checkin_point: fs.properties!["checkin_point"],
buy_point: fs.properties!["buy_point"],
selected: false,
checkedin: false,
hidden_location: fs.properties!["hidden_location"] == true ? 1 : 0,
tags: fs.properties!["tags"]);
}
// 指定された目的地の位置情報に基づいてタイマーを開始する関数です。
// CP情報(fs)と現在位置からCPまでの距離distance を引数として渡します。
//
Future<void> startTimerLocation(GeoJSONFeature fs, double distance) async {
//print("---- in startTimer ----");
// print("---- is in rog is $is_in_rog ----");
double checkinRadious = fs.properties!['checkin_radius'] ?? double.infinity;
// CPのcheckin_radiusを取得し、checkinRadius に代入。値がなければinfinityとする。
if (checkinRadious >= distance) {
// checkinRadious以内に入ったら、
indexController.currentFeature.clear();
// indexController.currentFeatureを空にします。
Destination d = festuretoDestination(fs);
// festuretoDestination(fs)を呼び出し、GeoJSONFeatureオブジェクトfsからDestinationオブジェクトdを作成します。
// print("----- destination lenght is ${destinations.length} -----");
indexController.currentFeature.add(fs);
// indexController.currentFeatureにfsを追加します。
//print("---- before calling startTimer ----");
await startTimer(d, distance);
// startTimer(d, distance)を非同期で呼び出し、その完了を待機します。
return;
}
}
// 指定された目的地に対してタイマーを開始する関数です。
// 目的地の位置情報を取得し、チェックイン半径内にいるかどうかを確認します。
// 写真撮影モードの場合は、ボトムシートを表示して写真撮影を行います。
// 目的地がデータベースに存在しない場合は、新しい目的地としてデータベースに挿入します。
// 目的地に応じて、チェックイン、ゴール、買い物ポイントの処理を行います。
//
// 2024-4-8 akira: GPS信号が弱い場合でも、最後に取得した位置情報を使用してチェックインやゴールの処理を続行できるようになります。また、チェックインやゴールの処理では、GPS信号の精度チェックを緩和することで、GPS信号が弱い場合でもボタンを押せるようになります。
//
// 要検討:エラーが発生した場合のエラーハンドリングを追加し、適切なメッセージを表示することを検討してください。
//
// 引数CPオブジェクトと現在地からCPまでの距離を渡す。
//
Future<void> startTimer(Destination d, double distance) async {
//print("=== passed dest is ${d.location_id} ${d.checkedin} ====");
skipGps = true;
//debugPrint("---- in startTimer ----");
DatabaseHelper db = DatabaseHelper.instance;
List<Destination> ds = await db.getDestinationByLatLon(d.lat!, d.lon!);
//指定位置のオブジェクトのリストを取得。
Destination? dss;
if (ds.isNotEmpty) {
dss = ds.first; // 取得したリストが空でない場合、dss変数に最初の要素を代入します。
}
// 変数を計算
double checkinRadious = d.checkin_radious ?? double.infinity; // 反応半径
bool autoCheckin = d.auto_checkin == 0 ? false : true; // 自動チェックイン
bool buyPoint = dss != null && dss.buy_point != null && dss.buy_point! > 0 // 買い物ポイント
? true
: false;
bool buyPointImageAdded = // 買い物画像
dss != null && dss.buypoint_image != null ? true : false;
bool buyPointCanceled = // 買い物キャンセル
dss != null && dss.buy_point != null && dss.buy_point == 0
? true
: false;
bool locationAlreadyCheckedIn = // チェックイン済みか
ds.isNotEmpty && ds[0].checkedin == true ? true : false;
bool isuserLoggedIn = indexController.currentUser.isNotEmpty ? true : false; // ログイン済みか
// 初期化。GPS信号が強くても弱くても
if (checkinRadious >= distance || checkinRadious == -1) { // 反応半径内か、-1(=距離を無視)
//currentSelectedDestinations.add(d);
// 目的地として登録する。
//debugPrint("目的地の初期化");
indexController.currentDestinationFeature.clear();
indexController.currentDestinationFeature.add(d);
// print(
// "---- checked in as ${indexController.currentDestinationFeature[0].checkedin.toString()} ----");
} else {
// ここには来ないのでは?
debugPrint("検出範囲外...");
// GPS信号が弱い場合でも、チェックインやゴールの処理を続行する
// comment out by Akira, 2024-4-5
// skipGps = false;
// return;
// GPS信号が弱い場合、最後に取得した高いまたは中程度の位置情報を使用
if (okToUseGPS) {
double lastValidDistance = Geolocator.distanceBetween(
lastValidLat, lastValidLon,
d.lat!, d.lon!
);
/*
double lastValidDistance = distance.as(
LengthUnit.Meter,
LatLng(lastValidLat, lastValidLon),
LatLng(d.lat!, d.lon!),
);
*/
if (checkinRadious >= lastValidDistance || checkinRadious == -1) { // 反応半径内か、距離無視CPなら
indexController.currentDestinationFeature.clear();
indexController.currentDestinationFeature.add(d);
} else {
skipGps = false;
return;
}
} else {
skipGps = false;
return;
}
}
if (isPhotoShoot.value == true) { // 写真撮影するなら ... isPhotoShoot=True にしてる場所がない。
debugPrint("isPhotoShoot.value == true ... will camera popup");
photos.clear(); // まず既存の写真をクリア
if (shouldShowBottomSheet) { // ボトムシートを使うべきなら
shouldShowBottomSheet = false;
if (d.cp == -1) return; // CPは開始点なら戻る。
// カメラページをポップアップ
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(destination: d)))
.whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
chekcs = 0;
isInCheckin.value = false;
});
}
return;
}
// 写真撮影モードでない場合
if (ds.isEmpty) {
debugPrint("* 目的地がない場合 ==> 検知半径=-1の場合");
// print("----- in location popup cp - ${d.cp}----");
if ((d.cp == -1 || d.cp==0 ) && DateTime.now().difference(lastGoalAt).inHours >= 24) {
debugPrint("**1: 開始CPで、最後にゴールしてから時間経過していれば、");
chekcs = 1;
//start
// print("~~~~ calling start ~~~~");
print("---- in start -----");
chekcs = 1; // スタート地点で前のゴールから24時間経過
isInCheckin.value = true;
isAtStart.value = true;
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false; // bottom_sheet を起動させない。
Widget bottomSheet = BottomSheetNew(destination: d);
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.85)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => bottomSheet)
).whenComplete(() {
shouldShowBottomSheet = true; // bottom_sheet 起動許可
skipGps = false;
chekcs = 0; // ボトムシートモード=1,
isAtStart.value = false;
isInCheckin.value = false;
});
}
return;
} else if (isInRog.value == true &&
indexController.rogMode.value == 1 &&
(locationAlreadyCheckedIn==null || locationAlreadyCheckedIn==false) &&
d.cp != -1 && d.cp != 0 && d.cp != -2) {
debugPrint("**2: 標準CP まだチェックインしていない。");
// print("----- in location popup checkin cp - ${d.cp}----");
chekcs = 2; // 標準CP
isInCheckin.value = true;
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false;
Widget bottomSheet = BottomSheetNew(destination: d);
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => bottomSheet)
).whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
chekcs = 0;
isInCheckin.value = false;
});
}
return;
}
}
// 以降、検知範囲にある場合。
//debugPrint("検知範囲にある場合");
// print("---- location checkin radious ${d.checkin_radious} ----");
// print("---- already checked in $locationAlreadyCheckedIn ----");
if ((checkinRadious >= distance || checkinRadious == -1) &&
locationAlreadyCheckedIn == false &&
isInRog.value == true) {
debugPrint("* 検知範囲または距離無視CPで、ゲーム中でまだチェックインしていない。");
if (autoCheckin) { // 自動チェックインなら
if (!checkingIn) {
debugPrint("** 自動チェックインの場合");
//print(
// "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ make checkin ${d.sub_loc_id}@@@@@@@@@@@");
makeCheckin(d, true, ""); // チェックインして
if (d.cp != -1 && d.cp != -2 && d.cp != 0 ) {
rogainingCounted.value = true; // ゴール用チェックイン済み
}
skipGps = false;
}
return; // 戻る
} else {
// それ以外
debugPrint("* 自動チェックイン以外の場合");
// print("--- hidden loc ${d.hidden_location} ----");
// ask for checkin
if (d.hidden_location != null &&
d.hidden_location == 0 && // 隠しCPフラグ==0 ... 通常CP
isInRog.value == true &&
d.cp != -1 && d.cp != -2 && d.cp != 0) {
// 隠しCPの場合、
debugPrint("**3 通常CPの場合");
chekcs = 3;
isInCheckin.value = true;
photos.clear();
// print("--- calling checkin ---");
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false;
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
destination: d,
))).whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
rogainingCounted.value = true;
chekcs = 0;
isInCheckin.value = false;
});
}
return;
} else if (isInRog.value == true &&
(locationAlreadyCheckedIn==null || locationAlreadyCheckedIn==false) &&
d.cp != -1 && d.cp != -2 && d.cp != 0) {
// 通常CP
debugPrint("**4 通常CP以外の場合....どんな場合?");
chekcs = 4;
isInCheckin.value = true;
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false;
Widget bottomSheet = BottomSheetNew(destination: d);
await showMaterialModalBottomSheet(
expand: true,
context: Get.context!,
backgroundColor: Colors.transparent,
builder: (context) => bottomSheet
).whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
chekcs = 0;
isInCheckin.value = false;
});
}
return;
}
}
} else if ((checkinRadious >= distance || checkinRadious == -1) &&
locationAlreadyCheckedIn == true &&
buyPointImageAdded == false &&
ds.isNotEmpty &&
buyPoint == true &&
buyPointCanceled == false &&
isInRog.value == true) {
// チェックイン後で買い物ポイントの場合。
debugPrint("**5 チェックイン後で買い物ポイントの場合");
chekcs = 5;
isInCheckin.value = true;
photos.clear();
//print("--- open buy point $buyPointImageAdded ${d.buypoint_image} ----");
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false;
if (d.cp == -1 && d.cp != -2 && d.cp != 0) return;
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
destination: d,
buyPointPhoto: true,
dbDest: ds.first,
))).whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
rogainingCounted.value = true;
chekcs = 0;
isInCheckin.value = false;
});
}
return;
}
// print("---- cp --- ${d.cp} -----");
// print("--- at goal $is_at_goal ---");
// print("--- rog counted $rogaining_counted ---");
// print("--- loc already checked in $locationAlreadyCheckedIn ---");
// print(
// "==== date diff is ${DateTime.now().difference(last_goal_at).inHours} ====");
if (isuserLoggedIn &&
(d.cp == -2 || d.cp == 0 ) && // Goal CP
locationAlreadyCheckedIn &&
skip_10s == false) {
//check for rogaining
if (isAtGoal.value == false && rogainingCounted.value) {
//goal
//print("---- in goal -----");
debugPrint("**5 ゴールで時計撮影の場合");
chekcs = 5; // Goal 時計撮影
isAtGoal.value = true;
photos.clear();
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false;
if (d.cp == -1) return;
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
destination: d,
))).whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
chekcs = 0;
isAtGoal.value = false;
});
}
return;
} else if (isInRog.value == false &&
indexController.rogMode.value == 1 &&
DateTime.now().difference(lastGoalAt).inHours >= 24) {
//start
//print("---- in start -----");
debugPrint("**5 スタートの場合で最後のゴールから24時間経過している場合");
chekcs = 6; // start point
isAtStart.value = true;
if (shouldShowBottomSheet) {
shouldShowBottomSheet = false;
if (d.cp != -1 && d.cp != 0) return;
Widget bottomSheet = BottomSheetNew(destination: d);
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => bottomSheet)
).whenComplete(() {
shouldShowBottomSheet = true;
//print("----- finished start -------");
skipGps = false;
chekcs = 0;
isAtStart.value = false;
});
}
return;
}
}
//print("==== _chekcs $chekcs ====");
if (chekcs == 0) {
//debugPrint("いずれにも当てはまらないので、処理スキップ");
skipGps = false;
}
return;
}
// ロゲイニングをリセットする関数です。
// ゲームの状態をリセットし、データベースからデータを削除します。
//
Future<void> resetRogaining({bool isgoal = false}) async {
//print("----- resetting --------");
isInCheckin.value = false;
isInRog.value = false;
isAtStart.value = false;
isAtGoal.value = false;
isGpsSelected.value = true;
skipGps = false;
ready_for_goal = false;
_start = 0;
chekcs = 0;
rogainingCounted.value = false;
DatabaseHelper db = DatabaseHelper.instance;
if (isgoal == false) {
await db.deleteAllDestinations();
await db.deleteAllRogaining();
}
int? latgoal = await db.latestGoal();
if (latgoal != null) {
lastGoalAt = DateTime.fromMicrosecondsSinceEpoch(latgoal);
//print("===== last goal : $last_goal_at =====");
}
dbService.updateDatabase();
}
// すべての目的地を削除する関数です。
//
void deleteAllDestinations() {
DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) {
populateDestinations();
});
}
// カメラを開いて写真を撮影する関数です。
//
void openCamera(BuildContext context, Destination? destination) {
photos.clear();
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CameraCamera(
resolutionPreset: ResolutionPreset.medium,
onFile: (file) {
photos.add(file);
Navigator.pop(context);
//print("----image file is : $file----");
//setState(() {});
},
)));
}
// ルートポイントを取得する関数です。
//
void getRoutePoints() {
indexController.routePoints = [];
indexController.routePointLenght.value = 0;
DestinationService.getDestinationLine(destinations, matrix)?.then((value) {
indexController.routePoints = value;
indexController.routePointLenght.value =
indexController.routePoints.length;
});
}
// 指定された緯度と経度に対応する目的地を取得する関数です。
//
Future<Destination?> getDestinationForLatLong(double lat, double long) async {
for (final d in destinations) {
if (lat == d.lat && long == d.lon) {
return d;
}
}
return null;
}
// チェックインの呼び出しを行う関数です。
// 指定された目的地に対してチェックインの処理を行います。
//
Future<void> callforCheckin(Destination d) async {
bool autoCheckin = d.auto_checkin == 0 ? false : true;
print("---- f- checkin ${d.sub_loc_id} ----");
if (autoCheckin) {
if (!checkingIn) {
makeCheckin(d, true, "");
if (d.cp != -1) {
rogainingCounted.value = true;
}
}
} else {
//print("--- hidden loc ${d.hidden_location} ----");
// ask for checkin
//print("is rog ---- ${is_in_rog.value} ----");
if (d.hidden_location != null &&
d.hidden_location == 0 &&
isInRog.value == true &&
d.cp != -1) {
chekcs = 3;
isInCheckin.value = true;
photos.clear();
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
manulaCheckin: true,
destination: d,
))).whenComplete(() async {
if (d.buy_point != null && d.buy_point! > 0) {
skipGps = true;
photos.clear();
DatabaseHelper db = DatabaseHelper.instance;
List<Destination> ds =
await db.getDestinationByLatLon(d.lat!, d.lon!);
Destination? dss;
if (ds.isNotEmpty) {
dss = ds.first;
}
await showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
buyPointPhoto: true,
destination: d,
dbDest: dss,
))).whenComplete(() {
skipGps = false;
rogainingCounted.value = true;
chekcs = 0;
isInCheckin.value = false;
//Get.back();
});
} else {
skipGps = false;
chekcs = 0;
isInCheckin.value = false;
}
});
} else {
Get.snackbar(
"ロゲが始まっていません",
"ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります",
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
}
}
}
// GPSデータをデータベースに追加する関数です。
//
Future<void> addGPStoDB(double la, double ln, {isCheckin = 0}) async {
//debugPrint("in addGPStoDB ${indexController.currentUser}");
try {
GpsDatabaseHelper db = GpsDatabaseHelper.instance;
if(indexController.currentUser.length>0){
final team_name = indexController.currentUser[0]["user"]['team_name'];
final event_code = indexController.currentUser[0]["user"]["event_code"];
GpsData gps_data = GpsData(
id: 0,
team_name: team_name,
event_code: event_code,
lat: la,
lon: ln,
is_checkin: isCheckin,
created_at: DateTime.now().millisecondsSinceEpoch);
var res = await db.insertGps(gps_data);
}
} catch (err) {
print("errr ready gps ${err}");
return;
}
}
// チェックインを確認する関数です。
// ゲームが開始されていない場合は、ゲームを開始します。
// 目的地のリストを走査し、現在位置がチェックイン半径内にある場合は、チェックインの処理を行います。
// GPSデータの送信を開始します。
//
// 2024-4-8 Akira : See 2809
// checkForCheckinメソッドの再帰呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。また、再帰呼び出しの前に一定時間待機するようにしました。
//
Future<void> checkForCheckin() async {
//print("--- Start of checkForCheckin function ---");
dbService.updateDatabase();
await Future.delayed(const Duration(milliseconds: 3000));
game_started = true;
try {
// ここで、エラー
if( indexController.locations.length>0 ) {
indexController.locations[0].features.forEach((fs) async {
GeoJSONMultiPoint mp = fs!.geometry as GeoJSONMultiPoint;
LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]);
double latFs = pt.latitude;
double lonFs = pt.longitude;
var distanceFs = const Distance();
double distFs = distanceFs.as(LengthUnit.Meter, LatLng(latFs, lonFs),
LatLng(currentLat, currentLon));
Destination des = festuretoDestination(fs);
if (distFs <= des.checkin_radious! && skipGps == false) {
await startTimerLocation(fs, distFs);
// Note: You cannot break out of forEach. If you need to stop processing, you might have to reconsider using forEach.
}
});
if (gps_push_started == false) {
unawaited(pushGPStoServer());
}
}
//print("--- 123 ---- $skip_gps----");
} catch (e) {
print("An error occurred: $e");
// await checkForCheckin();
} finally {
await Future.delayed(const Duration(seconds: 5)); // 一定時間待機してから再帰呼び出し
//print("--- End of checkForCheckin function, calling recursively ---");
unawaited( checkForCheckin() );
}
}
// GPSデータをサーバーにプッシュする関数です。
//
Future<void> pushGPStoServer() async {
// print(
// "^^^^^^^^ ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}");
try {
gps_push_started = true;
ExternalService().pushGPS();
} catch (e) {
//print("An error occurred: $e");
//await pushGPStoServer();
} finally {
//print("--- End of pushGPStoServer function, calling recursively ---");
await Future.delayed(const Duration(seconds: 5 * 60));
await pushGPStoServer();
}
}
// ロゲイニングにデータを追加する関数です。
//
void addToRogaining(double lat, double lon, int destinationId) async {
DatabaseHelper db = DatabaseHelper.instance;
List<Destination> d = await db.getDestinationById(destinationId);
if (d.isEmpty) {
Destination df = festuretoDestination(indexController.currentFeature[0]);
//print("--- made checkin ${df.location_id} ----");
makeCheckin(df, true, "");
}
isInRog.value = true;
saveGameState();
}
// 買い物ポイントをキャンセルする関数です。
//
Future<void> cancelBuyPoint(Destination destination) async {
DatabaseHelper db = DatabaseHelper.instance;
await db.updateCancelBuyPoint(destination);
populateDestinations();
}
// 指定されたパスの画像をギャラリーに保存する関数です。
//
_saveImageFromPath(String imagePath) async {
// Read the image file from the given path
File imageFile = File(imagePath);
Uint8List imageBytes = await imageFile.readAsBytes();
// Save the image to the gallery
final result = await ImageGallerySaver.saveImage(imageBytes);
//print("--- save result --- ${result}");
}
// 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。
//
// 買い物ポイントの作成に失敗した場合のエラーハンドリングを追加することを検討してください。
//
Future<void> makeBuyPoint(Destination destination, String imageurl) async {
DatabaseHelper db = DatabaseHelper.instance;
await db.updateBuyPoint(destination, imageurl);
populateDestinations();
await _saveImageFromPath(imageurl);
if (indexController.currentUser.isNotEmpty) {
double cpNum = destination.cp!;
int userId = indexController.currentUser[0]["user"]["id"];
//print("--- Pressed -----");
String team = indexController.currentUser[0]["user"]['team_name'];
//print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"];
//print("--- _event_code : ${_event_code}-----");
String token = indexController.currentUser[0]["token"];
//print("--- _token : ${_token}-----");
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now);
//print("------ checkin event $eventCode ------");
ExternalService()
.makeCheckpoint(userId, token, formattedDate, team, cpNum.round(),
eventCode, imageurl)
.then((value) {
//print("------Ext service check point $value ------");
});
}
}
// チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。
//
// 要検討:チェックインのリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。
//
Future<void> makeCheckin(
Destination destination, bool action, String imageurl) async {
// print("~~~~ calling checkin function ~~~~");
// print(
// "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ressssss ${destination.sub_loc_id}@@@@@@@@@@@");
DatabaseHelper db = DatabaseHelper.instance;
List<Destination> ddd =
await db.getDestinationByLatLon(destination.lat!, destination.lon!);
if (ddd.isEmpty) {
destination.checkedin = true;
destination.checkin_image = imageurl;
await db.insertDestination(destination);
// print("~~~~ inserted into db ~~~~");
}
await _saveImageFromPath(imageurl);
populateDestinations();
/// post to NATNAT
if (indexController.currentUser.isNotEmpty) {
double cpNum = destination.cp!;
int userId = indexController.currentUser[0]["user"]["id"];
//print("--- Pressed -----");
String team = indexController.currentUser[0]["user"]['team_name'];
//print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"];
//print("--- _event_code : ${_event_code}-----");
String token = indexController.currentUser[0]["token"];
//print("--- _token : ${_token}-----");
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now);
await addGPStoDB(currentLat, currentLon, isCheckin: 1);
// print("------ checkin event $eventCode ------");
ExternalService()
.makeCheckpoint(userId, token, formattedDate, team, cpNum.round(),
eventCode, imageurl)
.then((value) {
// print("------Ext service check point $value ------");
});
}
dbService.updateDatabase();
}
// チェックインを削除する関数です。
//
Future<void> removeCheckin(int cp) {
dbService.updateDatabase();
return ExternalService().removeCheckin(cp);
}
// ゲームを開始する関数です。
//
Future<void> startGame() async {
//print("------ starting game ------");
if (game_started == false) {
await checkForCheckin();
}
}
Timer? gpsCheckTimer; // 一定間隔でGPSデータの受信状態をチェックするタイマー
void startGPSCheckTimer() {
gpsCheckTimer = Timer.periodic(Duration(seconds: 5), (timer) {
checkGPSDataReceived();
});
}
// コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
//
@override
void onInit() async {
super.onInit();
startGPSCheckTimer();
// MapControllerの初期化完了を待機するフラグを設定
WidgetsBinding.instance.addPostFrameCallback((_) {
isMapControllerReady = true;
});
// 要検討:エラーメッセージを表示するなどの適切な処理を追加することを検討してください。
//
// locationController からデバイスの受け取るGPS情報を取得し、
// handleLocationUpdate を呼び出している。
//
locationController.locationMarkerPositionStream.listen(
(locationMarkerPosition) {
//if (locationMarkerPosition != null) {
handleLocationUpdate(locationMarkerPosition);
//}
}, onError: (err) {
print("Location Error: $err");
});
startGame();
checkGPSDataReceived();
}
// コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
//
@override
void onClose() {
gpsCheckTimer?.cancel();
locationController.stopPositionStream();
super.onClose();
}
// 位置情報の更新を処理する関数です。
// 現在位置とスタート地点との距離を計算します。
// 現在位置と前回の位置情報との距離と時間差を確認し、一定の条件を満たす場合はGPSデータをデータベースに追加します。
//
// 要検討GPSデータの追加に失敗した場合のエラーハンドリングを追加することをお勧めします。
//
void handleLocationUpdate(LocationMarkerPosition? position) async {
try {
//final DestinationController destinationController = Get.find<DestinationController>();
//final signalStrength = locationController.getGpsSignalStrength();
okToUseGPS = false;
double prevLat = currentLat; // 直前の位置
double prevLon = currentLon;
if (position != null) {
currentLat = position.latitude;
currentLon = position.longitude;
lastValidGPSLocation = LatLng(currentLat, currentLon);
okToUseGPS = true;
lastGPSDataReceivedTime = DateTime.now();
hasReceivedGPSData = true;
} else {
checkGPSDataReceived();
// 信号強度が低い場合、最後に取得した高いまたは中程度の位置情報を使用
// 但し、最初から高精度のものがない場合、どうするか?
//
// GPSデータが受信できない場合、最後に有効なGPSデータを使用
position = LocationMarkerPosition(
latitude: lastValidGPSLocation.latitude,
longitude: lastValidGPSLocation.longitude,
accuracy: 0,
);
/*
if (lastValidLat != 0.0 && lastValidLon != 0.0) {
currentLat = lastValidLat;
currentLon = lastValidLon;
okToUseGPS = true;
} else {
// GPSの届く場所に行って、信号を拾ってください。とメッセージを出す。
position = null;
print("GPSの届く場所に行って、信号を拾ってください。");
Get.snackbar(
"GPS信号を正確に拾えていません",
"空が大きく見えるところへ行ってGPS信号を拾ってください。",
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
}
*/
}
if (okToUseGPS && position != null) {
// スタート位置から150m離れたら、ready_for_goal
if (distanceToStart() >= 150) {
ready_for_goal = true;
}
var distance = const Distance();
double distanceToDest = distance.as(
LengthUnit.Meter,
LatLng(position.latitude, position.longitude),
LatLng(prevLat, prevLon)
);
Duration difference = lastGPSCollectedTime.difference(DateTime.now())
.abs();
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過普通に歩くスピード
if (difference.inSeconds >= 10 && distanceToDest >= 10) {
// print(
// "^^^^^^^^ GPS data collected ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}, ^^^ ${position.latitude}, ${position.longitude}");
LogManager().addLog(
"GPS : $currentLat, $currentLon - ${DateTime
.now()
.hour}:${DateTime
.now()
.minute}:${DateTime
.now()
.second}:${DateTime
.now()
.microsecond}");
if (isInRog.value) {
await addGPStoDB(position.latitude, position.longitude);
lastGPSCollectedTime = DateTime.now();
}
}
}
} catch(e) {
debugPrint("Error: ${e}");
} finally {
/* Akira , 2024-4-5
if (position != null &&
(position.latitude != 0 && position.longitude != 0)) {
currentLat = position.latitude;
currentLon = position.longitude;
}
*/
if (okToUseGPS) {
// 位置情報が取得できた場合、精度に関わらず最後の位置情報を更新
//currentLat = position.latitude;
//currentLon = position.longitude;
}
}
}
// スタート地点までの距離を計算する関数です。
//
double distanceToStart() {
if (indexController.locations.isEmpty) {
return 1000000000;
}
//print("=== gfs len == ${indexController.locations[0].collection.length}");
double distanceToDest = double.infinity;
if (indexController.locations[0].features.isEmpty) {
return distanceToDest;
}
GeoJSONFeature? gfs = indexController.locations[0].features.firstWhere(
(element) => festuretoDestination(element!).cp == -1,
orElse: () => null, // Provide a null value if no element is found
);
//print("gfs : ${gfs}");
if (gfs == null) {
return distanceToDest;
}
//final currentLocation = getCurrentLocation(); // GPS信号中以上での現在位置
Destination des = festuretoDestination(gfs);
//print("=== gfs == ${des.toMap()}");
var distance = const Distance();
distanceToDest = distance.as(LengthUnit.Meter,
LatLng(currentLat,currentLon), LatLng(des.lat!, des.lon!));
// LatLng(currentLat, currentLon), LatLng(des.lat!, des.lon!));
//print("==== dist==${distanceToDest}");
return distanceToDest;
}
// 強制チェックイン距離を取得する関数です。
//
int getForcedChckinDistance(Destination dest) {
if (dest.checkin_radious == -1) {
return 10000000000000000;
}
int _retValue = 100;
if (dest.cp == -1) {
return 500;
}
Destination? ds;
GeoJSONFeature? gfs = indexController.locations[0].features.firstWhere(
(element) => festuretoDestination(element!).cp == -1,
orElse: () => null, // Provide a null value if no element is found
);
if (gfs == null) {
return _retValue;
}
ds = festuretoDestination(gfs);
var distance = const Distance();
double distanceToDest = distance.as(LengthUnit.Meter,
LatLng(dest.lat!, dest.lon!), LatLng(ds.lat!, ds.lon!));
if (distanceToDest <= 500) {
return 500;
}
//print("==== forced dist ==${distanceToDest}");
return _retValue;
}
// ユーザートークンを読み取る関数です。
//
readUserToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
indexController.userToken = prefs.getString("user_token");
}
// コントローラーの準備完了時に呼び出されるライフサイクルメソッドです。
//
@override
void onReady() async {
await readUserToken();
final token = indexController.userToken;
if (token != null && token.isNotEmpty) {
await indexController.loadUserDetailsForToken(token);
fixMapBound(token);
}else {
Get.toNamed(AppPages.LOGIN)!.then((value) {
if (indexController.currentUser.isNotEmpty) {
final tk = indexController.currentUser[0]["token"];
fixMapBound(tk);
} else {
Get.toNamed(AppPages.TRAVEL);
PerfectureService.getSubExt("9").then((value) {
if (value != null) {
LatLngBounds bnds = LatLngBounds(
LatLng(value[1], value[0]), LatLng(value[3], value[2]));
indexController.mapController
.fitBounds(bnds); //.centerZoomFitBounds(bnds);
}
});
}
});
}
// 地図のイベントリスナーを設定
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
if (mapEvent is MapEventMoveEnd) {
indexController.loadLocationsBound();
}
});
super.onReady();
}
// 地図の境界を修正する関数です。
//
void fixMapBound(String token) {
//String _token = indexController.currentUser[0]["token"];
indexController.switchPage(AppPages.INDEX);
if (isMapControllerReady) {
LocationService.getLocationsExt(token).then((value) {
if (value != null) {
//print("--- loc ext is - $value ----");
LatLngBounds bnds = LatLngBounds(
LatLng(value[1], value[0]), LatLng(value[3], value[2]));
//print("--- bnds is - $bnds ----");
indexController.mapController.fitBounds(
bnds,
);
indexController.currentBound.clear();
indexController.currentBound.add(bnds);
indexController.loadLocationsBound();
centerMapToCurrentLocation();
}
});
} else {
// MapControllerの初期化が完了していない場合は、遅延して再試行
Future.delayed(Duration(milliseconds: 100), () {
fixMapBound(token);
});
}
}
/*
void fixMapBound(String token) {
indexController.switchPage(AppPages.INDEX);
LocationService.getLocationsExt(token).then((value) {
if (value != null) {
LatLngBounds bnds = LatLngBounds(
LatLng(value[1], value[0]),
LatLng(value[3], value[2]),
);
if (indexController.isMapControllerReady.value) {
indexController.mapController.fitBounds(
bnds,
);
indexController.currentBound.clear();
indexController.currentBound.add(bnds);
indexController.loadLocationsBound();
centerMapToCurrentLocation();
} else {
// MapControllerが初期化されるまで待機し、その後fitBoundsを実行
WidgetsBinding.instance.addPostFrameCallback((_) {
indexController.mapController.fitBounds(
bnds,
);
indexController.currentBound.clear();
indexController.currentBound.add(bnds);
indexController.loadLocationsBound();
centerMapToCurrentLocation();
});
}
}
});
}
*/
// 地図を現在位置に中央揃えする関数です。
//
void centerMapToCurrentLocation() {
//print("center is ${currentLat}, ${currentLon}");
// Akira ... 状況によって呼ぶか呼ばないか
if (currentLat != 0 || currentLon != 0) {
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
}
}
// 接続状態が変更されたときに呼び出される関数です。
//
void connectionChanged(String val) {
//print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val');
Map<String, dynamic> res = {};
if (val == "wifi" || val == "mobile") {
String token = indexController.currentUser[0]["token"];
DatabaseHelper db = DatabaseHelper.instance;
db.allRogianing().then((value) {
value.forEach((e) async {
if (e.rog_action_type == 0) {
res = await ExternalService().startRogaining();
} else if (e.rog_action_type == 1) {
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
res = await ExternalService().makeCheckpoint(
e.user_id!,
token,
getFormatedTime(datetime),
e.team_name!,
e.cp_number!,
e.event_code!,
e.image!);
} else if (e.rog_action_type == 2) {
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
res = await ExternalService().makeGoal(
e.user_id!,
token,
e.team_name!,
e.image!,
getFormatedTime(datetime),
e.event_code!);
}
if (res.isNotEmpty) {
db.deleteRogaining(e.id!);
}
});
});
}
}
// 位置情報の許可を確認する関数です。
//
void checkPermission() async {
LocationPermission permission = await Geolocator.checkPermission();
if (permission != LocationPermission.whileInUse ||
permission != LocationPermission.always) {
locationPermission.clear();
locationPermission.add(permission.name);
permission = await Geolocator.requestPermission();
}
}
// IDに基づいて目的地を取得する関数です。
//
Destination? destinationById(int id) {
Destination? d;
//print("--- target des - $id ----");
for (Destination ss in destinations) {
//print("--- des - ${ss.location_id} ----");
if (ss.location_id == id) {
d = ss;
break;
}
}
return d;
}
// 目的地を削除する関数です。
//
void deleteDestination(Destination d) {
//int id = destinations[index].location_id!;
//print("---- index ${destinations[index].location_id!}-----");
for (Destination ss in destinations) {
if (ss.location_id == d.location_id) {
destinations.remove(ss);
break;
}
}
DatabaseHelper db = DatabaseHelper.instance;
db.deleteDestination(d.location_id!).then((value) {
populateDestinations();
});
dbService.updateDatabase();
}
// データベースからすべての目的地を削除する関数です。
//
void deleteDBDestinations() {
DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) {
populateDestinations();
});
dbService.updateDatabase();
}
// ---------- database ------------------///
// 目的地を追加する関数です。
//
void addDestinations(Destination dest) {
DatabaseHelper db = DatabaseHelper.instance;
db.getDestinationByLatLon(dest.lat!, dest.lon!).then((value) {
if (value.isNotEmpty) {
db.deleteDestination(value[0].location_id!).then((value) {
db.insertDestination(dest).then((value) {
//print(
// "----- destination controller deleted and inserted destination id $value ---- :::::");
populateDestinations();
});
});
} else {
db.insertDestination(dest).then((value) {
//print("----- destination controller added as new $value--- :::::");
populateDestinations();
});
}
});
dbService.updateDatabase();
}
// 目的地の選択状態を切り替える関数です。
//
void toggleSelection(Destination dest) async {
try {
DatabaseHelper db = DatabaseHelper.instance;
await db.toggleSelecttion(dest);
destinations.clear();
db.getDestinations().then((value) {
destinationCount.value = 0;
currentSelectedDestinations.clear();
for (Destination d in value) {
//print("------ destination controller populating destination-------- ${d.checkedin}-------- :::::");
//print("-----populated----- ${d.toMap()}");
if (d.selected!) {
currentSelectedDestinations.add(d);
}
destinations.add(d);
}
destinationCount.value = destinations.length;
});
} catch( e ){
print('Error in toggleSelection: $e');
Get.snackbar(
"画面切り替えでエラー",
"画面の切り替えができませんでした",
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
}
}
// ダイアログを表示する関数です。
//
buildShowDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return const Center(
child: CircularProgressIndicator(),
);
});
}
// 現在地点からの目的地の行列を計算する関数です。
//
void destinationMatrixFromCurrentPoint(List<Destination> points) {
//buildShowDialog(Get.context!);
MatrixService.getDestinations(points).then((mat) {
//print(" matrix is ------- $mat");
matrix = mat;
try {
indexController.routePoints = [];
indexController.routePointLenght.value = 0;
DestinationService.getDestinationLine(points, matrix)?.then((value) {
indexController.routePoints = value;
indexController.routePointLenght.value =
indexController.routePoints.length;
//Get.toNamed(AppPages.TRAVEL);
});
destinationCount.value = destinations.length;
} catch (_) {
skipGps = false;
return;
} finally {
//Get.back();
}
});
}
// 目的地のリストを取得してObservable変数を更新する関数です。
//
void populateDestinations() {
DatabaseHelper db = DatabaseHelper.instance;
destinations.clear();
db.getDestinations().then((value) {
destinationCount.value = 0;
for (Destination d in value) {
destinations.add(d);
}
if (destinations.isEmpty) {
rogainingCounted.value = false;
}
});
}
// 目的地の順序を変更する関数です。
//
void makeOrder(Destination d, int dir) {
DatabaseHelper db = DatabaseHelper.instance;
db.updateOrder(d, dir).then((value) {
populateDestinations();
});
}
}