2024-09-02 ほぼOK

This commit is contained in:
2024-09-02 21:25:19 +09:00
parent dc58dc0584
commit fe46d46ab6
59 changed files with 2006 additions and 677 deletions

View File

@ -1,11 +1,11 @@
# This file tracks properties of this Flutter project. # This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc. # Used by Flutter tool to assess capabilities and perform upgrades etc.
# #
# This file should be version controlled. # This file should be version controlled and should not be manually edited.
version: version:
revision: 2ad6cd72c040113b47ee9055e722606a490ef0da revision: "5874a72aa4c779a02553007c47dacbefba2374dc"
channel: stable channel: "stable"
project_type: app project_type: app
@ -13,11 +13,26 @@ project_type: app
migration: migration:
platforms: platforms:
- platform: root - platform: root
create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
- platform: android
create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
- platform: ios - platform: ios
create_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 2ad6cd72c040113b47ee9055e722606a490ef0da base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
- platform: linux
create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
- platform: macos
create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
- platform: web
create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
- platform: windows
create_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
base_revision: 5874a72aa4c779a02553007c47dacbefba2374dc
# User provided section # User provided section

View File

@ -15,6 +15,29 @@ For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials, [online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference. samples, guidance on mobile development, and a full API reference.
## iOS のマニュアル更新内容
* Info.plist
<key>CFBundleDisplayName</key>
<string>岐阜ナビ</string>
<key>CFBundleName</key>
<string>岐阜ナビ</string>
<key>NSCameraUsageDescription</key>
<string>岐阜ナビはチェックポイントで撮影した写真を写真ライブラリに保存し、通過記録を保持し、競技結果として提出することができます。</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>岐阜ナビはアプリが閉じられているときでも位置情報へのアクセスが必要です。これにより、走行履歴の記録ができ、レビュー時の参考にすることができます。</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>このアプリではバックグラウンドで位置情報にアクセスします。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>このアプリはチェックポイントへのチェックインや走行履歴を記録するために、位置情報にアクセスします。</string>
<key>NSMicrophoneUsageDescription</key>
<string>このアプリではカメラは使用しますが、マイクの使用は当面行いません。</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>撮影した写真はデバイスのアルバムに保存されます。これにより、不具合時の通過記録を安全に担保することができます。</string>
# 更新履歴 # 更新履歴
0. flutter_compass は pub.dev cache で 34 に変更。キャッシュをクリアしたら修正が必要。 0. flutter_compass は pub.dev cache で 34 に変更。キャッシュをクリアしたら修正が必要。
@ -158,3 +181,12 @@ samples, guidance on mobile development, and a full API reference.
大垣駅 35.36701369466119, 136.61783662683948 大垣駅 35.36701369466119, 136.61783662683948
大垣城 35.36182698266251, 136.61558088722234 大垣城 35.36182698266251, 136.61558088722234
関ケ原駅35.36365422752628, 136.47061844402452 関ケ原駅35.36365422752628, 136.47061844402452
高山駅36.14130783620718, 137.25050201764944
ガソリンスタンド36.13826570797936, 137.21513450124928
バグ:
履歴の写真:アクセスエラー
バックアップをイベントごとに保存・レストア
ログインした際に、イベントが選択されていなければ、イベントを選択するように促す。
事前チェックインした写真が履歴に表示されない。

View File

@ -0,0 +1,5 @@
package com.dvox.gifunavi_git
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

3
devtools_options.yaml Normal file
View File

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

Binary file not shown.

View File

@ -4,6 +4,8 @@ PODS:
- connectivity_plus (0.0.1): - connectivity_plus (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- device_info_plus (0.0.1):
- Flutter
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_compass (0.0.1): - flutter_compass (0.0.1):
- Flutter - Flutter
@ -43,6 +45,7 @@ PODS:
DEPENDENCIES: DEPENDENCIES:
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_compass (from `.symlinks/plugins/flutter_compass/ios`) - flutter_compass (from `.symlinks/plugins/flutter_compass/ios`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
@ -68,6 +71,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/camera_avfoundation/ios" :path: ".symlinks/plugins/camera_avfoundation/ios"
connectivity_plus: connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin" :path: ".symlinks/plugins/connectivity_plus/darwin"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_compass: flutter_compass:
@ -102,6 +107,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4 camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855 flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069

View File

@ -8,14 +8,14 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2EAA11F0595B6B1A6B6ADED8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53C8E795FF5969F8C7B6A237 /* Pods_RunnerTests.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
712BA8D3720206B23B820A9B /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4E153E327A403FD75830D36 /* Pods_RunnerTests.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
8B39E7104A3E3C6F3B501CB2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A39AA3BE6DA0B7B42041019 /* Pods_Runner.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
B6A4962F862B554C98AD7E0A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F31FDD2E828C96A459E4AA85 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -46,14 +46,16 @@
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3592C2DA8DF570D2D07650A8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
53C8E795FF5969F8C7B6A237 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 402073277F0CF7D1DE2A9F29 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
6850CF9EE994EB8682BD460F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
6A39AA3BE6DA0B7B42041019 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6FFD6A432600780CEB6D6374 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
84738EC4DA98A3780DDAB81F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; 86D6A3692EBE5943C3C75411 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
89205C63F97F88BDB459168E /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
89EEC485D24E5B1BC0DD7B8E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -61,10 +63,8 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AC42218FD0A0DB137D157E93 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; }; 9E76BC5648643A00E08CF70B /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
D773E9161AA2211A3E2B94DD /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; C4E153E327A403FD75830D36 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DA00EBAA91366A0B5BFDAE96 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
F31FDD2E828C96A459E4AA85 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -72,15 +72,15 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
B6A4962F862B554C98AD7E0A /* Pods_Runner.framework in Frameworks */, 8B39E7104A3E3C6F3B501CB2 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
CEDA9491D04A70CE2A58DF32 /* Frameworks */ = { F68F88FFE2DC7D6754431798 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2EAA11F0595B6B1A6B6ADED8 /* Pods_RunnerTests.framework in Frameworks */, 712BA8D3720206B23B820A9B /* Pods_RunnerTests.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -95,11 +95,24 @@
path = RunnerTests; path = RunnerTests;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3806CD679A8B2BB869428B93 /* Frameworks */ = { 7F718872327E3605229882F1 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F31FDD2E828C96A459E4AA85 /* Pods_Runner.framework */, 402073277F0CF7D1DE2A9F29 /* Pods-Runner.debug.xcconfig */,
53C8E795FF5969F8C7B6A237 /* Pods_RunnerTests.framework */, 3592C2DA8DF570D2D07650A8 /* Pods-Runner.release.xcconfig */,
6FFD6A432600780CEB6D6374 /* Pods-Runner.profile.xcconfig */,
86D6A3692EBE5943C3C75411 /* Pods-RunnerTests.debug.xcconfig */,
6850CF9EE994EB8682BD460F /* Pods-RunnerTests.release.xcconfig */,
9E76BC5648643A00E08CF70B /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
85F630F9575BB5DDC6F6B3B2 /* Frameworks */ = {
isa = PBXGroup;
children = (
6A39AA3BE6DA0B7B42041019 /* Pods_Runner.framework */,
C4E153E327A403FD75830D36 /* Pods_RunnerTests.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -122,8 +135,8 @@
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */, 331C8082294A63A400263BE5 /* RunnerTests */,
B9F40AE3939A8E59418B2C25 /* Pods */, 7F718872327E3605229882F1 /* Pods */,
3806CD679A8B2BB869428B93 /* Frameworks */, 85F630F9575BB5DDC6F6B3B2 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -151,19 +164,6 @@
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B9F40AE3939A8E59418B2C25 /* Pods */ = {
isa = PBXGroup;
children = (
DA00EBAA91366A0B5BFDAE96 /* Pods-Runner.debug.xcconfig */,
D773E9161AA2211A3E2B94DD /* Pods-Runner.release.xcconfig */,
84738EC4DA98A3780DDAB81F /* Pods-Runner.profile.xcconfig */,
AC42218FD0A0DB137D157E93 /* Pods-RunnerTests.debug.xcconfig */,
89205C63F97F88BDB459168E /* Pods-RunnerTests.release.xcconfig */,
89EEC485D24E5B1BC0DD7B8E /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -171,10 +171,10 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = ( buildPhases = (
CD76B1A18202CBE77CBE3DCF /* [CP] Check Pods Manifest.lock */, 7F66DF627A66DF465C5F63D3 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */, 331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */, 331C807F294A63A400263BE5 /* Resources */,
CEDA9491D04A70CE2A58DF32 /* Frameworks */, F68F88FFE2DC7D6754431798 /* Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -190,15 +190,15 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
7EC19810B524B833F58A16B8 /* [CP] Check Pods Manifest.lock */, 9BC3CC202AEB33DDD6358396 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
3CF3DF1DB3C3E48099A705D1 /* [CP] Embed Pods Frameworks */, B576B812EBF41188E37C7BA1 /* [CP] Embed Pods Frameworks */,
9D3C0722FFFB9D74548C2A72 /* [CP] Copy Pods Resources */, 8EF00F2B7B12B1557BEC9D5A /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@ -286,24 +286,61 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
3CF3DF1DB3C3E48099A705D1 /* [CP] Embed Pods Frameworks */ = { 7F66DF627A66DF465C5F63D3 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Embed Pods Frameworks"; inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", );
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
7EC19810B524B833F58A16B8 /* [CP] Check Pods Manifest.lock */ = { 8EF00F2B7B12B1557BEC9D5A /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
9BC3CC202AEB33DDD6358396 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -325,58 +362,21 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
9740EEB61CF901F6004384FC /* Run Script */ = { B576B812EBF41188E37C7BA1 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
9D3C0722FFFB9D74548C2A72 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Embed Pods Frameworks";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
CD76B1A18202CBE77CBE3DCF /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -508,14 +508,14 @@
}; };
331C8088294A63A400263BE5 /* Debug */ = { 331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = AC42218FD0A0DB137D157E93 /* Pods-RunnerTests.debug.xcconfig */; baseConfigurationReference = 86D6A3692EBE5943C3C75411 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gifunavi.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunaviGit.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -526,14 +526,14 @@
}; };
331C8089294A63A400263BE5 /* Release */ = { 331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 89205C63F97F88BDB459168E /* Pods-RunnerTests.release.xcconfig */; baseConfigurationReference = 6850CF9EE994EB8682BD460F /* Pods-RunnerTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gifunavi.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunaviGit.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -542,14 +542,14 @@
}; };
331C808A294A63A400263BE5 /* Profile */ = { 331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 89EEC485D24E5B1BC0DD7B8E /* Pods-RunnerTests.profile.xcconfig */; baseConfigurationReference = 9E76BC5648643A00E08CF70B /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gifunavi.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunaviGit.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@ -560,7 +560,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -617,7 +617,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -24,28 +24,24 @@
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>岐阜ナビはチェックポイントで撮影した写真を写真ライブラリに保存し、通過記録を保持し、競技結果として提出することができます。</string> <string>岐阜ナビはチェックポイントで撮影した写真を写真ライブラリに保存し、通過記録を保持し、競技結果として提出することができます。</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>岐阜ナビはアプリが閉じられているときでも位置情報へのアクセスが必要です。これにより、走行履歴の記録ができ、レビュー時の参考にすることができます。</string> <string>岐阜ナビはアプリが閉じられているときでも位置情報へのアクセスが必要です。これにより、走行履歴の記録ができ、レビュー時の参考にすることができます。</string>
<key>NSLocationAlwaysUsageDescription</key> <key>NSLocationAlwaysUsageDescription</key>
<string>このアプリではバックグラウンドで位置情報にアクセスします。</string> <string>このアプリではバックグラウンドで位置情報にアクセスします。</string>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string>このアプリはチェックポイントへのチェックインや走行履歴を記録するために、位置情報にアクセスします。</string> <string>このアプリはチェックポイントへのチェックインや走行履歴を記録するために、位置情報にアクセスします。</string>
<key>NSMicrophoneUsageDescription</key> <key>NSMicrophoneUsageDescription</key>
<string>このアプリではカメラは使用しますが、マイクの使用は当面行いません。</string> <string>このアプリではカメラは使用しますが、マイクの使用は当面行いません。</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryAddUsageDescription</key>
<string>撮影した写真はデバイスのアルバムに保存されます。これにより、不具合時の通過記録を安全に担保することができます。</string> <string>撮影した写真はデバイスのアルバムに保存されます。これにより、不具合時の通過記録を安全に担保することができます。</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>撮影した写真はデバイスのアルバムに保存されます。これにより、不具合時の通過記録を安全に担保することができます。</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@ -63,9 +59,7 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>LSApplicationCategoryType</key>
<false/> <string></string>
<key>io.flutter.embedded_views_preview</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -38,8 +38,10 @@ import 'pages/permission/permission.dart';
import 'package:gifunavi/services/api_service.dart'; import 'package:gifunavi/services/api_service.dart';
import 'package:gifunavi/provider/cached_tile_provider.dart'; import 'package:gifunavi/provider/cached_tile_provider.dart';
import 'package:gifunavi/pages/entry/entry_controller.dart'; //import 'package:gifunavi/pages/entry/entry_controller.dart';
import 'package:gifunavi/pages/team/team_controller.dart'; //import 'package:gifunavi/pages/team/team_controller.dart';
import 'package:timezone/timezone.dart' as tz;
Map<String, dynamic> deviceInfo = {}; Map<String, dynamic> deviceInfo = {};
@ -56,14 +58,25 @@ void saveGameState() async {
*/ */
// 現在のユーザーのIDも一緒に保存するようにします。 // 現在のユーザーのIDも一緒に保存するようにします。
void saveGameState() async { Future<void> saveGameState() async {
DestinationController destinationController = DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
IndexController indexController = Get.find<IndexController>(); IndexController indexController = Get.find<IndexController>();
SharedPreferences pref = await SharedPreferences.getInstance(); SharedPreferences pref = await SharedPreferences.getInstance();
debugPrint("indexController.currentUser = ${indexController.currentUser}"); debugPrint("ゲームステータス保存  = ${indexController.currentUser}");
if(indexController.currentUser.isNotEmpty) { if(indexController.currentUser.isNotEmpty) {
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]); pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
if(indexController.currentUser[0]["user"]["event_date"]!=null) {
final date = indexController.currentUser[0]["user"]["event_date"];
pref.setString('eventDate', date.toIso8601String());
debugPrint("Saved date is ${date} => ${date.toIso8601String()}");
pref.setString('eventCode', indexController.currentUser[0]["user"]["event_code"]);
pref.setString('teamName', indexController.currentUser[0]["user"]["team_name"]);
pref.setString('group', indexController.currentUser[0]["user"]["group"]);
//final zekken = indexController.currentUser[0]["user"]["zekken_number"];
pref.setInt('zekkenNumber', indexController.currentUser[0]["user"]["zekken_number"]);
}
}else{ }else{
debugPrint("User is empty...."); debugPrint("User is empty....");
} }
@ -73,75 +86,118 @@ void saveGameState() async {
pref.setBool("ready_for_goal", DestinationController.ready_for_goal); pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
} }
/*
void restoreGame() async {
SharedPreferences pref = await SharedPreferences.getInstance();
DestinationController destinationController =
Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value =
pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal =
pref.getBool("ready_for_goal") ?? false;
//print(
// "--restored -- destinationController.isInRog.value ${pref.getBool("is_in_rog")} -- ${pref.getBool("rogaining_counted")}");
}
*/
void restoreGame() async {
// _indexController.currentUser[0]["user"]["event_date"] = entryDate; // 追加2024-8-9
// _indexController.currentUser[0]["user"]["event_code"] = entry.event.eventName;
// _indexController.currentUser[0]["user"]["team_name"] = entry.team.teamName;
// _indexController.currentUser[0]["user"]["group"] = entry.team.category.categoryName;
// _indexController.currentUser[0]["user"]["zekken_number"] = entry.zekkenNumber;
Future<void> restoreGame() async {
SharedPreferences pref = await SharedPreferences.getInstance(); SharedPreferences pref = await SharedPreferences.getInstance();
IndexController indexController = Get.find<IndexController>(); IndexController indexController = Get.find<IndexController>();
int? savedUserId = pref.getInt("user_id"); int? savedUserId = pref.getInt("user_id");
//int? currUserId = indexController.currentUser[0]['user']['id']; //int? currUserId = indexController.currentUser[0]['user']['id'];
//debugPrint("savedUserId=${savedUserId}, currentUser=${currUserId}"); debugPrint("ゲームステータス再現  savedUserId=${savedUserId}");
if (indexController.currentUser.isNotEmpty && if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]["id"] == savedUserId) { indexController.currentUser[0]["user"]["id"] == savedUserId) {
DestinationController destinationController =
Get.find<DestinationController>(); final dateString = pref.getString('eventDate');
if (dateString != null) {
final parsedDate = DateTime.parse(dateString);
final jstDate = tz.TZDateTime.from(parsedDate, tz.getLocation('Asia/Tokyo'));
debugPrint("restore date is ${dateString} => ${jstDate}");
indexController.currentUser[0]["user"]["event_date"] = jstDate;
//indexController.currentUser[0]["user"]["event_date"] = DateTime.parse(dateString);
}
//debugPrint("restore date is ${dateString?} => ${DateTime.parse(dateString)}");
indexController.currentUser[0]["user"]["event_code"] = pref.getString('eventCode');
indexController.currentUser[0]["user"]["team_name"] = pref.getString('teamName');
indexController.currentUser[0]["user"]["group"] = pref.getString('group');
indexController.currentUser[0]["user"]["zekken_number"] = pref.getInt('zekkenNumber');
debugPrint("user = ${indexController.currentUser[0]["user"]}");
DestinationController destinationController = Get.find<DestinationController>();
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false; destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value = destinationController.rogainingCounted.value = pref.getBool("rogaining_counted") ?? false;
pref.getBool("rogaining_counted") ?? false; DestinationController.ready_for_goal = pref.getBool("ready_for_goal") ?? false;
DestinationController.ready_for_goal = //await Get.putAsync(() => ApiService().init());
pref.getBool("ready_for_goal") ?? false;
await Get.putAsync(() => ApiService().init()); if (indexController.currentUser[0]["user"]["event_code"] != null) {
indexController.setSelectedEventName(indexController.currentUser[0]["user"]["event_code"]);
} else {
indexController.setSelectedEventName('未参加');
}
}else{
indexController.setSelectedEventName('未参加');
} }
} }
/*
void restoreGame_new() async {
SharedPreferences pref = await SharedPreferences.getInstance();
IndexController indexController = Get.find<IndexController>();
DestinationController destinationController = Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value = pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal = pref.getBool("ready_for_goal") ?? false;
int? savedUserId = pref.getInt("user_id");
if (indexController.currentUser.isNotEmpty && indexController.currentUser[0]["user"]["id"] == savedUserId) {
final dateString = pref.getString('eventDate');
if (dateString != null) {
indexController.currentUser[0]["user"]["event_date"] = DateTime.parse(dateString);
}
indexController.currentUser[0]["user"]["event_code"] = pref.getString('eventCode');
indexController.currentUser[0]["user"]["team_name"] = pref.getString('teamName');
indexController.currentUser[0]["user"]["group"] = pref.getString('group');
indexController.currentUser[0]["user"]["zekken_number"] = pref.getInt('zekkenNumber');
if (indexController.currentUser[0]["user"]["event_code"] != null) {
indexController.setSelectedEventName(indexController.currentUser[0]["user"]["event_code"]);
} else {
indexController.setSelectedEventName('未参加');
_showEventSelectionWarning();
Get.toNamed(AppPages.EVENT_ENTRY);
}
} else {
indexController.setSelectedEventName('未参加');
_showEventSelectionWarning();
Get.toNamed(AppPages.EVENT_ENTRY);
}
await Get.putAsync(() => ApiService().init());
}
*/
void _showEventSelectionWarning() {
Get.dialog(
AlertDialog(
title: Text('警告'),
content: Text('イベントを選択してください。'),
actions: [
TextButton(
child: Text('OK'),
onPressed: () => Get.back(),
),
],
),
);
}
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
Get.put(LocationController()); final IndexController _indexController;
/*
Get.put(ApiService());
Get.put(TeamController());
Get.put(EntryController());
Get.put(IndexController());
*/
/*
await FlutterMapTileCaching.initialise();
final StoreDirectory instanceA = FMTC.instance('OpenStreetMap (A)');
await instanceA.manage.createAsync();
await instanceA.metadata.addAsync(
key: 'sourceURL',
value: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
);
await instanceA.metadata.addAsync(
key: 'validDuration',
value: '14',
);
await instanceA.metadata.addAsync(
key: 'behaviour',
value: 'cacheFirst',
);
*/
// 新しいキャッシュプロバイダーの初期化
await CacheProvider.initialize();
// 使用不可
//deviceInfo = await DeviceInfoService.getDeviceInfo();
FlutterError.onError = (FlutterErrorDetails details) { FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details); FlutterError.presentError(details);
@ -150,67 +206,98 @@ void main() async {
ErrorService.reportError(details.exception, details.stack ?? StackTrace.current, deviceInfo, LogManager().operationLogs); ErrorService.reportError(details.exception, details.stack ?? StackTrace.current, deviceInfo, LogManager().operationLogs);
}; };
//Get.put(LocationController());
//await PermissionController.checkAndRequestPermissions();
//requestLocationPermission();
// startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810
Get.put(SettingsController()); // これを追加
/*
runZonedGuarded(() {
runApp(const ProviderScope(child: MyApp()));
}, (error, stackTrace) {
ErrorService.reportError(error, stackTrace, deviceInfo);
});
*/
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
debugPrint('Flutter error: ${details.exception}');
debugPrint('Stack trace: ${details.stack}');
};
try { try {
tz.initializeTimeZones();
// ApiServiceを初期化
//await Get.putAsync(() => ApiService().init()); //await Get.putAsync(() => ApiService().init());
await _initApiService();
debugPrint("1: start ApiService");
// すべてのコントローラーとサービスを非同期で初期化
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
debugPrint("2: start IndexController");
// その他のコントローラーを遅延初期化
Get.lazyPut(() => SettingsController());
debugPrint("2: start SettingsController");
Get.lazyPut(() => DestinationController());
debugPrint("3: start DestinationController");
await initServices(); await initServices();
runApp(const ProviderScope(child: MyApp())); runApp(const ProviderScope(child: MyApp()));
//runApp(HomePage()); // MyApp()からHomePage()に変更
//runApp(const MyApp());
}catch(e, stackTrace){ }catch(e, stackTrace){
print('Error during initialization: $e'); print('Error during initialization: $e');
print('Stack trace: $stackTrace'); print('Stack trace: $stackTrace');
// エラーが発生した場合、エラー画面を表示
runApp(ErrorApp(error: e.toString()));
} }
} }
Future<void> initServices() async { Future<void> initServices() async {
print('Starting services ...'); print('Starting services ...');
try { try {
await Get.putAsync(() => ApiService().init());
print('All services started...'); // 非同期処理を並列実行
await Future.wait([
_initTimeZone(),
_initCacheProvider(),
]);
print('=== 5. Initialized TimeZone...');
print('=== 6. CacheProvider started...');
Get.put(PermissionController());
await _checkPermissions();
debugPrint("7: start PermissionController");
}catch(e){ }catch(e){
print('Error initializing ApiService: $e'); print('Error initializing : $e');
} }
try {
Get.put(SettingsController());
print('SettingsController initialized successfully');
} catch (e) {
print('Error initializing SettingsController: $e');
}
print('All services started...'); print('All services started...');
} }
Future<void> _initLocationController() async {
if (!Get.isRegistered<LocationController>()) {
Get.put(LocationController());
}
print('=== 1. LocationController started...');
}
Future<void> _initTimeZone() async {
tz.initializeTimeZones();
}
Future<void> _initCacheProvider() async {
await CacheProvider.initialize();
}
Future<void> _checkPermissions() async {
await PermissionController.checkAndRequestPermissions();
}
Future<void> _initApiService() async {
await Get.putAsync(() => ApiService().init());
//Get.lazyPut(() => ApiService());
}
class ErrorApp extends StatelessWidget {
final String error;
const ErrorApp({super.key, required this.error});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text('アプリの起動中にエラーが発生しました: $error'),
),
),
);
}
}
Future<void> requestLocationPermission() async { Future<void> requestLocationPermission() async {
try { try {
final status = await Permission.locationAlways.request(); final status = await Permission.locationAlways.request();
@ -300,70 +387,80 @@ String team_name="";
String event_code=""; String event_code="";
Future<void> startBackgroundTracking() async { Future<void> startBackgroundTracking() async {
if (Platform.isIOS && background==false) { try {
if (Platform.isIOS && background == false) {
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>();
if(indexController.currentUser.isNotEmpty) { if (indexController.currentUser.isNotEmpty &&
team_name = indexController.currentUser[0]["user"]['team_name']; indexController.currentUser[0]["user"]['team_name'] != null) {
event_code = indexController.currentUser[0]["user"]["event_code"]; team_name = indexController.currentUser[0]["user"]['team_name'];
} event_code = indexController.currentUser[0]["user"]["event_code"];
}
background = true; background = true;
debugPrint("バックグラウンド処理を開始しました。"); debugPrint("バックグラウンド処理を開始しました。");
const LocationSettings locationSettings = LocationSettings( const LocationSettings locationSettings = LocationSettings(
accuracy: LocationAccuracy.high, accuracy: LocationAccuracy.high,
distanceFilter: 100, distanceFilter: 100,
); );
try { try {
positionStream = Geolocator.getPositionStream(locationSettings: locationSettings) positionStream =
.listen((Position? position) async { Geolocator.getPositionStream(locationSettings: locationSettings)
if (position != null) { .listen((Position? position) async {
final lat = position.latitude; if (position != null) {
final lng = position.longitude; final lat = position.latitude;
//final timestamp = DateTime.now(); final lng = position.longitude;
final accuracy = position.accuracy; //final timestamp = DateTime.now();
final accuracy = position.accuracy;
// GPS信号強度がlowの場合はスキップ // GPS信号強度がlowの場合はスキップ
if (accuracy > 100) { if (accuracy > 100) {
debugPrint("GPS signal strength is low. Skipping data saving."); debugPrint(
return; "GPS signal strength is low. Skipping data saving.");
} return;
}
Duration difference = lastGPSCollectedTime.difference(DateTime.now()) Duration difference = lastGPSCollectedTime.difference(
.abs(); DateTime.now())
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過普通に歩くスピード .abs();
//debugPrint("時間差:${difference}"); // 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過普通に歩くスピード
if (difference.inSeconds >= 10 ) { //debugPrint("時間差:${difference}");
debugPrint("バックグラウンドでのGPS取得時の処理(10secおき) count=${difference.inSeconds}, time=${DateTime.now()}"); if (difference.inSeconds >= 10) {
debugPrint(
"バックグラウンドでのGPS取得時の処理(10secおき) count=${difference
.inSeconds}, time=${DateTime.now()}");
// DBにGPSデータを保存 pages/destination/destination_controller.dart // DBにGPSデータを保存 pages/destination/destination_controller.dart
await addGPStoDB(lat, lng); await addGPStoDB(lat, lng);
lastGPSCollectedTime = DateTime.now(); lastGPSCollectedTime = DateTime.now();
} }
} }
}, onError: (error) { }, onError: (error) {
if (error is LocationServiceDisabledException) { if (error is LocationServiceDisabledException) {
print('Location services are disabled'); print('Location services are disabled');
} else if (error is PermissionDeniedException) { } else if (error is PermissionDeniedException) {
print('Location permissions are denied'); print('Location permissions are denied');
} else { } else {
print('Location Error: $error'); print('Location Error: $error');
} }
}); });
} catch (e) { } catch (e) {
print('Error starting background tracking: $e'); print('Error starting background tracking: $e');
} }
}else if (Platform.isAndroid && background == false) { } else if (Platform.isAndroid && background == false) {
background = true; background = true;
debugPrint("バックグラウンド処理を開始しました。"); debugPrint("バックグラウンド処理を開始しました。");
try { try {
// 位置情報の権限が許可されているかを確認 // 位置情報の権限が許可されているかを確認
await PermissionController.checkAndRequestPermissions(); await PermissionController.checkAndRequestPermissions();
}catch(e){ } catch (e) {
print('Error starting background tracking: $e'); print('Error starting background tracking: $e');
}
} }
} catch (e) {
print('Error starting background tracking: $e');
// 再試行するか、エラーを適切に処理
} }
} }
@ -388,20 +485,24 @@ Future<void> addGPStoDB(double la, double ln) async {
} }
Future<void> stopBackgroundTracking() async { Future<void> stopBackgroundTracking() async {
if (Platform.isIOS && background==true) { try {
background=false; if (Platform.isIOS && background == true) {
debugPrint("バックグラウンド処理:停止しました。"); background = false;
await positionStream?.cancel(); debugPrint("バックグラウンド処理:停止しました。");
positionStream = null; await positionStream?.cancel();
}else if(Platform.isAndroid && background==true){ positionStream = null;
background=false; } else if (Platform.isAndroid && background == true) {
debugPrint("バックグラウンド処理:停止しました。"); background = false;
const platform = MethodChannel('location'); debugPrint("バックグラウンド処理:停止しました。");
try { const platform = MethodChannel('location');
await platform.invokeMethod('stopLocationService'); try {
} on PlatformException catch (e) { await platform.invokeMethod('stopLocationService');
print("Failed to stop location service: '${e.message}'."); } on PlatformException catch (e) {
print("Failed to stop location service: '${e.message}'.");
}
} }
} catch(e){
print('Error stopping background tracking: $e');
} }
} }
@ -414,18 +515,22 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> with WidgetsBindingObserver { class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// This widget is the root of your application. // This widget is the root of your application.
late final LocationController _locationController;
late final IndexController _indexController;
late final DestinationController _destinationController;
late final PermissionController _permissionController;
Timer? _memoryCheckTimer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (!Get.isRegistered<LocationController>()) { _initializeControllers();
Get.put(LocationController()); WidgetsBinding.instance.addObserver(this);
} //_startMemoryMonitoring();
if (context.mounted) { if (context.mounted) {
restoreGame(); // _restoreGameAndInitialize();
} }
WidgetsBinding.instance.addObserver(this);
// ウィジェットが構築された後に権限をチェック // ウィジェットが構築された後に権限をチェック
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -435,6 +540,105 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
debugPrint("Start MyAppState..."); debugPrint("Start MyAppState...");
} }
Future<void> _restoreGameAndInitialize() async {
await restoreGame();
// ここに他の初期化処理を追加できます
}
void _initializeControllers() {
/*
if (!Get.isRegistered<IndexController>()) {
_locationController = Get.put(LocationController(), permanent: true);
}
if (!Get.isRegistered<IndexController>()) {
_indexController = Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
}
if (!Get.isRegistered<DestinationController>()) {
_destinationController =
Get.put(DestinationController(), permanent: true);
}
if (!Get.isRegistered<PermissionController>()) {
_permissionController = Get.put(PermissionController());
}
// 他の必要なコントローラーの初期化
*/
}
void _startMemoryMonitoring() {
/*
_memoryCheckTimer = Timer.periodic(const Duration(seconds: 10), (timer) {
_checkMemoryUsage();
});
*/
}
void _checkMemoryUsage() async {
final memoryInfo = await _getMemoryInfo();
//debugPrint('Current memory usage: ${memoryInfo['used']} MB');
if (memoryInfo['used']! > 100) { // 100MB以上使用している場合
_performMemoryCleanup();
}
}
Future<Map<String, int>> _getMemoryInfo() async {
// プラットフォーム固有のメモリ情報取得ロジックを実装
// この例では仮の値を返しています
return {'total': 1024, 'used': 512};
}
void _performMemoryCleanup() {
/*
debugPrint('Performing memory cleanup');
// キャッシュのクリア
Get.deleteAll(force: false); // 永続的なコントローラーを除外してキャッシュをクリア
imageCache.clear();
imageCache.clearLiveImages();
// 大きなオブジェクトの解放
_clearLargeObjects();
// 未使用のリソースの解放
_releaseUnusedResources();
// ガベージコレクションの促進
_forceGarbageCollection();
debugPrint('Performing memory cleanup');
*/
}
void _clearLargeObjects() {
// 大きなリストやマップをクリア
// 例: myLargeList.clear();
}
void _releaseUnusedResources() {
// 使用していないストリームのクローズ
// 例: myStream?.close();
}
void _forceGarbageCollection() {
/*
Timer(const Duration(seconds: 1), () {
debugPrint('Forcing garbage collection');
// ignore: dead_code
bool didRun = false;
assert(() {
didRun = true;
return true;
}());
if (didRun) {
debugPrint('Garbage collection forced in debug mode');
}
});
*/
}
/* /*
void showPermissionRequiredDialog() { void showPermissionRequiredDialog() {
showDialog( showDialog(
@ -474,6 +678,7 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_memoryCheckTimer?.cancel();
super.dispose(); super.dispose();
} }
@ -487,110 +692,118 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
@override @override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async { Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
try { try {
LocationController locationController = Get.find<LocationController>(); if (!Get.isRegistered<IndexController>()) {
_indexController = Get.find<IndexController>();
}
if (!Get.isRegistered<LocationController>()) {
_locationController = Get.find<LocationController>();
}
if (!Get.isRegistered<DestinationController>()) {
_destinationController = Get.find<DestinationController>();
}
DestinationController destinationController = Get.find<
DestinationController>();
//DestinationController destinationController =
// Get.find<DestinationController>();
switch (state) { switch (state) {
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
// 追加 2024.8.13. //await _onResumed();
await stopBackgroundTracking(); await _onResumed();
destinationController.restartGPS();
// 追加 2024.8.13.
// バックグラウンド処理を停止
if (Platform.isIOS && destinationController.isRunningBackgroundGPS) {
// Foreground に戻った時の処理
debugPrint(
" ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました");
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
stopBackgroundTracking();
destinationController.isRunningBackgroundGPS = false;
destinationController.restartGPS();
} else if (Platform.isAndroid) {
if (destinationController.isRunningBackgroundGPS) {
const platform = MethodChannel('location');
platform.invokeMethod('stopLocationService');
destinationController.isRunningBackgroundGPS = false;
destinationController.restartGPS();
debugPrint("stopped android location service..");
}
debugPrint(
"==(Status Changed)==> RESUMED. android フォアグラウンドに戻りました");
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
} else {
debugPrint("==(Status Changed)==> RESUMED 不明状態");
}
break; break;
case AppLifecycleState.inactive: case AppLifecycleState.inactive:
// アプリが非アクティブになったときに発生します。 // アプリが非アクティブになったときに発生します。
await _onInactive();
if (Platform.isIOS && !destinationController
.isRunningBackgroundGPS) { // iOSはバックグラウンドでもフロントの処理が生きている。
// これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
//locationController.resumePositionStream();
// 追加: フロントエンドのGPS信号のlistenを停止
locationController.stopPositionStream();
destinationController.isRunningBackgroundGPS = true;
startBackgroundTracking();
} else if (Platform.isAndroid &&
!destinationController.isRunningBackgroundGPS) {
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
} else {
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
}
saveGameState();
break; break;
case AppLifecycleState.paused: case AppLifecycleState.paused:
// バックグラウンドに移行したときの処理 // バックグラウンドに移行したときの処理
//locationController.resumePositionStream(); //locationController.resumePositionStream();
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。"); await _onPaused();
if (Platform.isIOS && !destinationController.isRunningBackgroundGPS) {
debugPrint(
"iOS already running background GPS processing when it's inactive");
} else if (Platform.isAndroid &&
!destinationController.isRunningBackgroundGPS) {
debugPrint(
" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
locationController.stopPositionStream();
const platform = MethodChannel('location');
platform.invokeMethod('startLocationService');
//platform.invokeMethod('stopLocationService');
destinationController.isRunningBackgroundGPS = true;
//startBackgroundTracking();
}
saveGameState();
break; break;
case AppLifecycleState.detached: case AppLifecycleState.detached:
// アプリが終了する直前に発生します。この状態では、アプリはメモリから解放される予定です。 // アプリが終了する直前に発生します。この状態では、アプリはメモリから解放される予定です。
//locationController.resumePositionStream(); //locationController.resumePositionStream();
debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。"); await _onDetached();
saveGameState();
break; break;
case AppLifecycleState.hidden: case AppLifecycleState.hidden:
// Web用の特殊な状態で、モバイルアプリでは発生しません。 // Web用の特殊な状態で、モバイルアプリでは発生しません。
//locationController.resumePositionStream(); //locationController.resumePositionStream();
debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた"); await _onHidden();
saveGameState();
break; break;
} }
}catch(e){ }catch(e){
print('Error finding LocationController: $e'); print('Error finding didChangeAppLifecycleState: $e');
_initializeControllers();
} }
} }
Future<void> _onResumed() async {
debugPrint("==(Status Changed)==> RESUMED");
try {
_initializeControllers();
await stopBackgroundTracking();
_destinationController.restartGPS();
if (Platform.isIOS && _destinationController.isRunningBackgroundGPS) {
_locationController.resumePositionStream();
await restoreGame();
_destinationController.isRunningBackgroundGPS = false;
} else if (Platform.isAndroid) {
if (_destinationController.isRunningBackgroundGPS) {
const platform = MethodChannel('location');
await platform.invokeMethod('stopLocationService');
_destinationController.isRunningBackgroundGPS = false;
_destinationController.restartGPS();
}
_locationController.resumePositionStream();
await restoreGame();
}
} catch(e) {
print('Error in _onResumed: $e');
// 必要に応じて再試行またはエラー処理
}
}
Future<void> _onInactive() async {
debugPrint("==(Status Changed)==> INACTIVE");
if (Platform.isIOS && !_destinationController.isRunningBackgroundGPS) {
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
_locationController.stopPositionStream();
_destinationController.isRunningBackgroundGPS = true;
await startBackgroundTracking();
} else if (Platform.isAndroid && !_destinationController.isRunningBackgroundGPS) {
// Android特有の処理があれば追加
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
}else{
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
}
await saveGameState();
}
Future<void> _onPaused() async {
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
if (Platform.isAndroid && !_destinationController.isRunningBackgroundGPS) {
debugPrint(" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
_locationController.stopPositionStream();
const platform = MethodChannel('location');
await platform.invokeMethod('startLocationService');
_destinationController.isRunningBackgroundGPS = true;
}
await saveGameState();
}
Future<void> _onDetached() async {
debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。");
await saveGameState();
// アプリ終了時の追加処理
}
Future<void> _onHidden() async {
debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた");
await saveGameState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -14,8 +14,11 @@ import 'package:gifunavi/services/external_service.dart';
import 'package:gifunavi/utils/const.dart'; import 'package:gifunavi/utils/const.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:http/http.dart' as http; // この行を追加 import 'package:http/http.dart' as http;
import '../../routes/app_pages.dart'; // この行を追加
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
// 関数 getTagText は、特定の条件に基づいて文字列から特定の部分を抽出し、返却するためのものです。 // 関数 getTagText は、特定の条件に基づいて文字列から特定の部分を抽出し、返却するためのものです。
// 関数は2つのパラメータを受け取り、条件分岐を通じて結果を返します。 // 関数は2つのパラメータを受け取り、条件分岐を通じて結果を返します。
@ -220,10 +223,67 @@ class CameraPage extends StatelessWidget {
Timer? timer; Timer? timer;
bool isValidEventParticipation() {
final eventCode = indexController.currentUser[0]["user"]["event_code"];
final teamName = indexController.currentUser[0]["user"]["team_name"];
final dateString = indexController.currentUser[0]["user"]["event_date"];
//final parsedDate = DateTime.parse(dateString);
//final eventDate = tz.TZDateTime.from(parsedDate, tz.getLocation('Asia/Tokyo'));
//final today = DateTime.now();
return eventCode != null &&
teamName != null &&
dateString != null ;
// isSameDay(eventDate, today);
}
bool isSameDay(DateTime date1, DateTime date2) {
return date1.year == date2.year &&
date1.month == date2.month &&
date1.day == date2.day;
}
void showEventParticipationWarning(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("警告"),
content: const Text("今日のイベントにまず参加しないと事前チェックインはできません。サブメニューからイベント参加をタップして今日のイベントに参加してください。"),
actions: <Widget>[
TextButton(
child: const Text("キャンセル"),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: const Text("参加する"),
onPressed: () {
Navigator.of(context).pop();
Get.toNamed(AppPages.EVENT_ENTRY);
},
),
],
);
},
);
}
// 現在の状態に基づいて、適切なアクションボタンを返します。 // 現在の状態に基づいて、適切なアクションボタンを返します。
// 要修正:エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。 // 要修正:エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。
// //
Widget getAction(BuildContext context) { Widget getAction(BuildContext context) {
if (!isValidEventParticipation()) {
return ElevatedButton(
onPressed: () => showEventParticipationWarning(context),
child: const Text("チェックイン"),
);
}
if (manulaCheckin == true) { if (manulaCheckin == true) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0), padding: const EdgeInsets.symmetric(horizontal: 16.0),
@ -253,7 +313,9 @@ class CameraPage extends StatelessWidget {
onPressed: () async { onPressed: () async {
await destinationController.makeCheckin(destination, true, await destinationController.makeCheckin(destination, true,
destinationController.photos[0].path); destinationController.photos[0].path);
destinationController.rogainingCounted.value = true; if( destinationController.isInRog.value==true ) {
destinationController.rogainingCounted.value = true; // ロゲ開始後のみ許可
}
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
@ -387,7 +449,9 @@ class CameraPage extends StatelessWidget {
await destinationController.makeBuyPoint( await destinationController.makeBuyPoint(
destination, destinationController.photos[0].path); destination, destinationController.photos[0].path);
Get.back(); Get.back();
destinationController.rogainingCounted.value = true; if( destinationController.isInRog.value==true ) {
destinationController.rogainingCounted.value = true; // ロゲ開始後のみ許可
}
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
Get.snackbar("お買い物加点を行いました。", Get.snackbar("お買い物加点を行いました。",
@ -428,7 +492,9 @@ class CameraPage extends StatelessWidget {
await destinationController.makeBuyPoint( await destinationController.makeBuyPoint(
destination, destinationController.photos[0].path); destination, destinationController.photos[0].path);
Get.back(); Get.back();
destinationController.rogainingCounted.value = true; if( destinationController.isInRog.value==true ) {
destinationController.rogainingCounted.value = true; //ロゲ開始後のみ許可
}
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
Get.snackbar("お買い物加点を行いました。", Get.snackbar("お買い物加点を行いました。",
@ -465,7 +531,9 @@ class CameraPage extends StatelessWidget {
true, true,
destinationController.photos[0].path); destinationController.photos[0].path);
//Get.back(); //Get.back();
destinationController.rogainingCounted.value = true; if( destinationController.isInRog.value==true ) {
destinationController.rogainingCounted.value = true; //ロゲ開始後のみ許可
}
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
@ -496,6 +564,7 @@ class CameraPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
//print("---- photos ${destination.photos} ----"); //print("---- photos ${destination.photos} ----");
if (buyPointPhoto == true) { if (buyPointPhoto == true) {
// buyPointPhotoがtrueの場合は、BuyPointCameraウィジェットを返します。 // buyPointPhotoがtrueの場合は、BuyPointCameraウィジェットを返します。
@ -780,7 +849,9 @@ class BuyPointCamera extends StatelessWidget {
onPressed: () async { onPressed: () async {
await destinationController.cancelBuyPoint(destination); await destinationController.cancelBuyPoint(destination);
Navigator.of(Get.context!).pop(); Navigator.of(Get.context!).pop();
destinationController.rogainingCounted.value = true; if( destinationController.isInRog.value==true ) {
destinationController.rogainingCounted.value = true; // ロゲ開始後のみ許可
}
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
}, },

View File

@ -1,6 +1,8 @@
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:gifunavi/model/destination.dart'; import 'package:gifunavi/model/destination.dart';
@ -8,8 +10,17 @@ import 'package:gifunavi/model/destination.dart';
class CustomCameraView extends StatefulWidget { class CustomCameraView extends StatefulWidget {
final Function(String) onImageCaptured; final Function(String) onImageCaptured;
final Destination? destination; final Destination? destination;
final Function(bool) onCameraStatusChanged;
//const CustomCameraView({super.key, required this.onImageCaptured, required this.destination});
const CustomCameraView({
Key? key,
required this.onImageCaptured,
required this.destination,
required this.onCameraStatusChanged, // 新しいコールバック
}) : super(key: key);
const CustomCameraView({super.key, required this.onImageCaptured, required this.destination});
@override @override
_CustomCameraViewState createState() => _CustomCameraViewState(); _CustomCameraViewState createState() => _CustomCameraViewState();
@ -17,6 +28,8 @@ class CustomCameraView extends StatefulWidget {
class _CustomCameraViewState extends State<CustomCameraView> { class _CustomCameraViewState extends State<CustomCameraView> {
CameraController? _controller; CameraController? _controller;
bool _isCameraAvailable = true;
late List<CameraDescription> _cameras; late List<CameraDescription> _cameras;
int _selectedCameraIndex = 0; int _selectedCameraIndex = 0;
double _currentScale = 1.0; double _currentScale = 1.0;
@ -31,10 +44,25 @@ class _CustomCameraViewState extends State<CustomCameraView> {
} }
Future<void> _initializeCamera() async { Future<void> _initializeCamera() async {
_cameras = await availableCameras(); try {
_controller = CameraController(_cameras[_selectedCameraIndex], ResolutionPreset.medium); _cameras = await availableCameras();
await _controller!.initialize(); if (_cameras.isNotEmpty) {
setState(() {}); _controller = CameraController(
_cameras[_selectedCameraIndex], ResolutionPreset.medium);
await _controller!.initialize();
setState(() {
_isCameraAvailable = true;
});
} else {
throw Exception('Camera is not available');
}
}catch(err){
print("Error initializing camera: $err");
setState(() {
_isCameraAvailable = false;
});
}
widget.onCameraStatusChanged(_isCameraAvailable);
} }
@override @override
@ -82,20 +110,46 @@ class _CustomCameraViewState extends State<CustomCameraView> {
} }
void _captureImage() async { void _captureImage() async {
if (_controller!.value.isInitialized) { if (_isCameraAvailable) {
final Directory appDirectory = await getApplicationDocumentsDirectory(); if (_controller!.value.isInitialized) {
final String imagePath = path.join(appDirectory.path, '${DateTime.now()}.jpg'); final Directory appDirectory = await getApplicationDocumentsDirectory();
final String imagePath = path.join(
appDirectory.path, '${DateTime.now()}.jpg');
final XFile imageFile = await _controller!.takePicture(); final XFile imageFile = await _controller!.takePicture();
await imageFile.saveTo(imagePath); await imageFile.saveTo(imagePath);
widget.onImageCaptured(imagePath);
Navigator.pop(context);
}
}else{
// ダミー画像を使用
final String imagePath = await _saveDummyImage();
widget.onImageCaptured(imagePath); widget.onImageCaptured(imagePath);
Navigator.pop(context); Navigator.pop(context);
} }
} }
Future<String> _saveDummyImage() async {
final Directory appDirectory = await getApplicationDocumentsDirectory();
final String imagePath = path.join(appDirectory.path, 'dummy_${DateTime.now()}.png');
// アセットからダミー画像を読み込む
ByteData data = await rootBundle.load('assets/images/dummy_camera_image.png');
List<int> bytes = data.buffer.asUint8List();
// ダミー画像をファイルとして保存
await File(imagePath).writeAsBytes(bytes);
return imagePath;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!_isCameraAvailable) {
return _buildDummyCameraView();
}
if (_controller == null || !_controller!.value.isInitialized) { if (_controller == null || !_controller!.value.isInitialized) {
return Container(); return Container();
} }
@ -182,4 +236,64 @@ class _CustomCameraViewState extends State<CustomCameraView> {
], ],
); );
} }
Widget _buildDummyCameraView() {
return Stack(
children: [
Container(
color: Colors.black,
child: const Center(
child: Text(
'カメラを利用できません',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
),
Positioned(
bottom: 16.0,
left: 16.0,
right: 16.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.flash_off, color: Colors.white),
iconSize: 32,
),
GestureDetector(
onTap: _captureEmulatedImage,
child: Container(
height: 80,
width: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
border: Border.all(color: Colors.red, width: 4),
),
child: const Icon(Icons.camera_alt, color: Colors.red, size: 40),
),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.flip_camera_ios, color: Colors.white),
iconSize: 32,
),
],
),
),
],
);
}
void _captureEmulatedImage() async {
final Directory appDirectory = await getApplicationDocumentsDirectory();
final String imagePath = path.join(appDirectory.path, '${DateTime.now()}.jpg');
// ダミーの画像ファイルを作成
await File(imagePath).writeAsBytes(Uint8List(0));
widget.onImageCaptured(imagePath);
Navigator.pop(context);
}
} }

View File

@ -29,12 +29,18 @@ import 'dart:async';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:gifunavi/widgets/debug_widget.dart'; import 'package:gifunavi/widgets/debug_widget.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:gifunavi/pages/permission/permission.dart' ; import 'package:gifunavi/pages/permission/permission.dart' ;
// 新しいインポート
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
// 目的地に関連する状態管理とロジックを担当するクラスです。 // 目的地に関連する状態管理とロジックを担当するクラスです。
// //
class DestinationController extends GetxController { class DestinationController extends GetxController {
@ -423,11 +429,11 @@ class DestinationController extends GetxController {
// 写真撮影モードでない場合 // 写真撮影モードでない場合
if (ds.isEmpty) { if (ds.isEmpty) {
debugPrint("* 目的地がない場合 ==> 検知半径=-1の場合"); //debugPrint("* 目的地がない場合 ==> ds=${ds}");
// print("----- in location popup cp - ${d.cp}----"); // print("----- in location popup cp - ${d.cp}----");
if ((d.cp == -1 || d.cp==0 ) && DateTime.now().difference(lastGoalAt).inHours >= 10) { if ((d.cp == -1 || d.cp==0 ) && DateTime.now().difference(lastGoalAt).inHours >= 10) {
debugPrint("**1: 開始CPで、最後にゴールしてから時間経過していれば、"); debugPrint("**1: 目的地がない場合で、スタート地点。開始CPで、最後にゴールしてから時間経過していれば、");
chekcs = 1; chekcs = 1;
//start //start
@ -461,15 +467,15 @@ class DestinationController extends GetxController {
// 以下の条件分岐を追加 // 以下の条件分岐を追加
} else if (ds.isNotEmpty && ds[0].checkedin == true) { } else if (ds.isNotEmpty && ds[0].checkedin == true) {
// 目的地がDBに存在し、すでにチェックインしている場合は自動ポップアップを表示しない // 目的地がDBに存在し、すでにチェックインしている場合は自動ポップアップを表示しない
debugPrint("チェックイン済み"); debugPrint("目的地がない場合で、チェックイン済み");
return; return;
} else if (isInRog.value == true && } else if (isInRog.value == true && // 常にfalse だよ。。
indexController.rogMode.value == 1 && indexController.rogMode.value == 1 && // マップではなくリストページだよ。
(locationAlreadyCheckedIn==false) && (locationAlreadyCheckedIn==false) && // まだチェックインしてないよ。
d.cp != -1 && d.cp != 0 && d.cp != -2) { d.cp != -1 && d.cp != 0 && d.cp != -2) { // スタートでもゴールでもないよ。
debugPrint("**2: 標準CP まだチェックインしていない。"); debugPrint("**2: 目的地がない場合で、標準CP まだチェックインしていない。");
// print("----- in location popup checkin cp - ${d.cp}----"); // print("----- in location popup checkin cp - ${d.cp}----");
chekcs = 2; // 標準CP chekcs = 2; // 標準CP
@ -494,14 +500,20 @@ class DestinationController extends GetxController {
}); });
} }
return; return;
}else{
debugPrint("**Else: isInRog=${isInRog.value}, rogMode=${indexController.rogMode.value},locationAlreadyCheckedIn=${locationAlreadyCheckedIn},d.cp=${d.cp}");
} }
} }
// 以降、検知範囲にある場合。 // 以降、検知範囲にある場合。
//debugPrint("検知範囲にある場合"); debugPrint("検知範囲にある場合だよ...");
debugPrint("---- 検知範囲: ${d.checkin_radious} > 距離:${distance} ----");
debugPrint("---- チェックイン済みか? $locationAlreadyCheckedIn ----");
debugPrint("---- isInRog : ${isInRog.value}, checkingin = ${isCheckingIn.value}");
debugPrint("---- buyPointImageAdded: ${buyPointImageAdded}, ds.isNotEmpty?: ${ds.isNotEmpty},buyPoint:${buyPoint},buyPointCanceled=${buyPointCanceled}");
debugPrint(" ");
// print("---- location checkin radious ${d.checkin_radious} ----");
// print("---- already checked in $locationAlreadyCheckedIn ----");
if ((checkinRadious >= distance || checkinRadious == -1) && if ((checkinRadious >= distance || checkinRadious == -1) &&
locationAlreadyCheckedIn == false && locationAlreadyCheckedIn == false &&
isInRog.value == true && isInRog.value == true &&
@ -515,9 +527,9 @@ class DestinationController extends GetxController {
//print( //print(
// "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ make checkin ${d.sub_loc_id}@@@@@@@@@@@"); // "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ make checkin ${d.sub_loc_id}@@@@@@@@@@@");
makeCheckin(d, true, ""); // チェックインして makeCheckin(d, true, ""); // チェックインして
if (d.cp != -1 && d.cp != -2 && d.cp != 0 ) { //if (d.cp != -1 && d.cp != -2 && d.cp != 0 ) {
rogainingCounted.value = true; // ゴール用チェックイン済み // rogainingCounted.value = true; // ゴール用チェックイン済み
} //}
skipGps = false; skipGps = false;
} }
return; // 戻る return; // 戻る
@ -552,7 +564,9 @@ class DestinationController extends GetxController {
))).whenComplete(() { ))).whenComplete(() {
shouldShowBottomSheet = true; shouldShowBottomSheet = true;
skipGps = false; skipGps = false;
rogainingCounted.value = true; if( isInRog.value==true ) {
rogainingCounted.value = true;
}
chekcs = 0; chekcs = 0;
isInCheckin.value = false; isInCheckin.value = false;
isCheckingIn.value = false; isCheckingIn.value = false;
@ -729,13 +743,15 @@ class DestinationController extends GetxController {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
if (isgoal == false) { if (isgoal == false) {
await db.deleteAllDestinations(); // await db.deleteAllDestinations();
await db.deleteAllRogaining(); // await db.deleteAllRogaining();
await db.deleteAllDestinationsExceptTodayCheckins();
await db.deleteAllRogainingExceptToday();
} }
int? latgoal = await db.latestGoal(); int? latgoal = await db.latestGoal();
lastGoalAt = DateTime.fromMicrosecondsSinceEpoch(latgoal!); lastGoalAt = DateTime.fromMicrosecondsSinceEpoch(latgoal!);
//print("===== last goal : $last_goal_at ====="); debugPrint("===== last goal : $lastGoalAt =====");
dbService.updateDatabase(); dbService.updateDatabase();
} }
@ -743,7 +759,7 @@ class DestinationController extends GetxController {
// //
void deleteAllDestinations() { void deleteAllDestinations() {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) { db.deleteAllDestinationsExceptTodayCheckins().then((value) {
populateDestinations(); populateDestinations();
}); });
} }
@ -760,6 +776,18 @@ class DestinationController extends GetxController {
photos.add(File(imagePath)); photos.add(File(imagePath));
}, },
destination: destination, destination: destination,
onCameraStatusChanged: (isAvailable) {
// カメラの状態が変更されたときの処理
if (!isAvailable) {
// カメラが利用できない場合の処理
Get.snackbar(
'エラー',
'カメラを初期化できませんでした。',
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
},
), ),
/* /*
builder: (_) => CameraCamera( builder: (_) => CameraCamera(
@ -859,7 +887,9 @@ class DestinationController extends GetxController {
dbDest: dss, dbDest: dss,
))).whenComplete(() { ))).whenComplete(() {
skipGps = false; skipGps = false;
rogainingCounted.value = true; if( isInRog.value == true ) { // ロゲ開始していれば、遠くまで来たことにする。
rogainingCounted.value = true;
}
chekcs = 0; chekcs = 0;
isInCheckin.value = false; isInCheckin.value = false;
//Get.back(); //Get.back();
@ -984,6 +1014,7 @@ class DestinationController extends GetxController {
// //
// 2024-4-8 Akira : See 2809 // 2024-4-8 Akira : See 2809
// checkForCheckinメソッドの再帰呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。また、再帰呼び出しの前に一定時間待機するようにしました。 // checkForCheckinメソッドの再帰呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。また、再帰呼び出しの前に一定時間待機するようにしました。
// 2024-8-24 ... 佐伯呼び出しが必要なのか?
// //
Future<void> checkForCheckin() async { Future<void> checkForCheckin() async {
//print("--- Start of checkForCheckin function ---"); //print("--- Start of checkForCheckin function ---");
@ -1022,13 +1053,15 @@ class DestinationController extends GetxController {
} catch (e) { } catch (e) {
print("An error occurred: $e"); print("An error occurred: $e");
// await checkForCheckin(); // await checkForCheckin();
} finally { //} finally {
await Future.delayed(const Duration(seconds: 1)); // 一定時間待機してから再帰呼び出し // await Future.delayed(const Duration(seconds: 1)); // 一定時間待機してから再帰呼び出し
//print("--- End of checkForCheckin function, calling recursively ---"); //print("--- End of checkForCheckin function, calling recursively ---");
unawaited( checkForCheckin() ); //unawaited( checkForCheckin() );
} }
} }
// GPSデータをサーバーにプッシュする関数です。 // GPSデータをサーバーにプッシュする関数です。
// //
Future<void> pushGPStoServer() async { Future<void> pushGPStoServer() async {
@ -1089,55 +1122,46 @@ class DestinationController extends GetxController {
} }
} }
Future<void> _saveImageToGallery(String imagePath) async { Future<String?> _saveImageToGallery(String imagePath) async {
final status = await PermissionController.checkStoragePermission(); final status = await PermissionController.checkStoragePermission();
if(!status){ if(!status){
await PermissionController.requestStoragePermission(); await PermissionController.requestStoragePermission();
} }
/* try {
final status = await Permission.storage.status; final appDir = await getApplicationDocumentsDirectory();
if (!status.isGranted) { //final fileName = path.basename(imagePath);
final result = await Permission.storage.request(); final fileName = 'checkin_${DateTime.now().millisecondsSinceEpoch}.jpg';
if (!result.isGranted) { final savedImage = await File(imagePath).copy('${appDir.path}/$fileName');
// ユーザーがストレージの権限を拒否した場合の処理
showDialog(
context: Get.context!,
builder: (BuildContext context) {
return AlertDialog(
title: Text('ストレージの権限が必要です'),
content: Text(
'画像をギャラリーに保存するには、ストレージの権限が必要です。アプリの設定画面で権限を許可してください。'),
actions: [
TextButton(
child: Text('キャンセル'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('設定'),
onPressed: () {
Navigator.of(context).pop();
openAppSettings(); // アプリの設定画面を開く
},
),
],
);
}
);
return; debugPrint("fileName=${fileName}, appDir=${appDir} => ${savedImage}");
// ギャラリーにも保存
//await ImageGallerySaver.saveFile(savedImage.path);
await Future.delayed(const Duration(seconds: 3), () async {
final result = await ImageGallerySaver.saveFile(savedImage.path);
print("Save result: $result");
}).timeout(const Duration(seconds: 5));
debugPrint('Image saved to: ${savedImage.path}');
return savedImage.path;
/*
final result = await ImageGallerySaver.saveFile(imagePath);
debugPrint('Image saved to gallery: $result');
if (result['isSuccess']) {
return result['filePath'];
}
*/
} catch (e) {
if (e is TimeoutException) {
print("Operation timed out");
} else {
print('Failed to save image to gallery: $e');
} }
} }
*/ return null;
try {
final result = await ImageGallerySaver.saveFile(imagePath);
print('Image saved to gallery: $result');
} catch (e) {
print('Failed to save image to gallery: $e');
}
} }
// 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。 // 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。
@ -1145,13 +1169,10 @@ class DestinationController extends GetxController {
// 買い物ポイントの作成に失敗した場合のエラーハンドリングを追加することを検討してください。 // 買い物ポイントの作成に失敗した場合のエラーハンドリングを追加することを検討してください。
// //
Future<void> makeBuyPoint(Destination destination, String imageurl) async { Future<void> makeBuyPoint(Destination destination, String imageurl) async {
String? savedImagePath = await _saveImageToGallery(imageurl);
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
await db.updateBuyPoint(destination, imageurl); await db.updateBuyPoint(destination, savedImagePath ?? imageurl);
populateDestinations(); populateDestinations();
//await _saveImageFromPath(imageurl);
await _saveImageToGallery(imageurl);
if (indexController.currentUser.isNotEmpty) { if (indexController.currentUser.isNotEmpty) {
double cpNum = destination.cp!; double cpNum = destination.cp!;
@ -1160,6 +1181,7 @@ class DestinationController extends GetxController {
int userId = indexController.currentUser[0]["user"]["id"]; int userId = indexController.currentUser[0]["user"]["id"];
//print("--- Pressed -----"); //print("--- Pressed -----");
debugPrint("user=${indexController.currentUser[0]["user"]}");
String team = indexController.currentUser[0]["user"]['team_name']; String team = indexController.currentUser[0]["user"]['team_name'];
//print("--- _team : ${_team}-----"); //print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"]; String eventCode = indexController.currentUser[0]["user"]["event_code"];
@ -1172,7 +1194,7 @@ class DestinationController extends GetxController {
//print("------ checkin event $eventCode ------"); //print("------ checkin event $eventCode ------");
ExternalService() ExternalService()
.makeCheckpoint(userId, token, formattedDate, team, cpNum.round(), .makeCheckpoint(userId, token, formattedDate, team, cpNum.round(),
eventCode, imageurl) eventCode, savedImagePath ?? imageurl)
.then((value) { .then((value) {
//print("------Ext service check point $value ------"); //print("------Ext service check point $value ------");
}); });
@ -1197,7 +1219,10 @@ class DestinationController extends GetxController {
if (ddd.isEmpty) { if (ddd.isEmpty) {
destination.checkedin = true; destination.checkedin = true;
destination.checkin_image = imageurl; if (imageurl.isNotEmpty) {
String? savedImagePath = await _saveImageToGallery(imageurl);
destination.checkin_image = savedImagePath ?? imageurl;
}
await db.insertDestination(destination); await db.insertDestination(destination);
// print("~~~~ inserted into db ~~~~"); // print("~~~~ inserted into db ~~~~");
} }
@ -1212,7 +1237,8 @@ class DestinationController extends GetxController {
//await _saveImageFromPath(imageurl!); //await _saveImageFromPath(imageurl!);
} }
if (imageurl.isNotEmpty) { if (imageurl.isNotEmpty) {
await _saveImageToGallery(imageurl); String? savedImagePath = await _saveImageToGallery(imageurl);
destination.checkin_image = savedImagePath ?? imageurl;
} }
populateDestinations(); populateDestinations();
@ -1299,11 +1325,11 @@ class DestinationController extends GetxController {
void onInit() async { void onInit() async {
super.onInit(); super.onInit();
/*
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
await PermissionController.checkAndRequestPermissions(); await PermissionController.checkAndRequestPermissions();
}); });
*/
startGPSCheckTimer(); startGPSCheckTimer();
@ -1810,7 +1836,7 @@ class DestinationController extends GetxController {
// //
void deleteDBDestinations() { void deleteDBDestinations() {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) { db.deleteAllDestinationsExceptTodayCheckins().then((value) {
populateDestinations(); populateDestinations();
}); });
dbService.updateDatabase(); dbService.updateDatabase();

View File

@ -2,6 +2,8 @@ import 'package:get/get.dart';
import 'package:gifunavi/pages/entry/entry_controller.dart'; import 'package:gifunavi/pages/entry/entry_controller.dart';
import 'package:gifunavi/services/api_service.dart'; import 'package:gifunavi/services/api_service.dart';
import '../index/index_controller.dart';
class EntryBinding extends Bindings { class EntryBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {

View File

@ -191,18 +191,21 @@ class EntryController extends GetxController {
final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id); final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id);
final zekkenNumber = updatedCategory.categoryNumber.toString(); final zekkenNumber = updatedCategory.categoryNumber.toString();
// selectedDate.value に 9時間を加えてJSTのオフセットを適用
final jstDate = selectedDate.value!.add(const Duration(hours: 9));
final newEntry = await _apiService.createEntry( final newEntry = await _apiService.createEntry(
selectedTeam.value!.id, selectedTeam.value!.id,
selectedEvent.value!.id, selectedEvent.value!.id,
selectedCategory.value!.id, selectedCategory.value!.id,
selectedDate.value!, jstDate, // JSTオフセットが適用された日付を使用
zekkenNumber, zekkenNumber,
); );
entries.add(newEntry); entries.add(newEntry);
Get.back(); Get.back();
} catch (e) { } catch (e) {
print('Error creating entry: $e'); print('Error creating entry: $e');
Get.snackbar('Error', 'Failed to create entry'); Get.snackbar('Error', '$e');
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }

View File

@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
import 'package:gifunavi/model/destination.dart'; import 'package:gifunavi/model/destination.dart';
import 'package:gifunavi/utils/database_helper.dart'; import 'package:gifunavi/utils/database_helper.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
class HistoryPage extends StatefulWidget { class HistoryPage extends StatefulWidget {
const HistoryPage({super.key}); const HistoryPage({super.key});
@ -15,6 +17,48 @@ class HistoryPage extends StatefulWidget {
class _HistoryPageState extends State<HistoryPage> { class _HistoryPageState extends State<HistoryPage> {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("pass_history".tr),
),
body: FutureBuilder<List<Destination>>(
future: db.getDestinations(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(child: Text("no_checkin_yet".tr));
}
final dests = snapshot.data!;
return ListView.builder(
itemCount: dests.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: CustomWidget(
title: dests[index].name ?? 'No Name',
subtitle: "${dests[index].sub_loc_id ?? 'N/A'} : ${dests[index].name ?? 'N/A'}",
image1Path: dests[index].checkin_image,
image2Path: dests[index].buypoint_image,
),
);
},
);
},
),
);
}
}
/*
class _HistoryPageState_old extends State<HistoryPage> {
DatabaseHelper db = DatabaseHelper.instance;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -86,8 +130,97 @@ class _HistoryPageState extends State<HistoryPage> {
); );
} }
} }
*/
class CustomWidget extends StatelessWidget { class CustomWidget extends StatelessWidget {
final String? image1Path;
final String? image2Path;
final String title;
final String subtitle;
const CustomWidget({
super.key,
this.image1Path,
this.image2Path,
required this.title,
required this.subtitle,
});
Widget _buildImage(String? path) {
if (path == null) return const SizedBox.shrink();
return FutureBuilder<String>(
future: _getFullImagePath(path),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return Image.file(
File(snapshot.data!),
width: 50,
height: 100,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
print('Error loading image: $error');
return const Icon(Icons.error);
},
);
} else if (snapshot.hasError) {
print('Error loading image path: ${snapshot.error}');
return const Icon(Icons.error);
} else {
return const CircularProgressIndicator();
}
},
);
}
Future<String> _getFullImagePath(String imagePath) async {
final appDir = await getApplicationDocumentsDirectory();
final fileName = path.basename(imagePath);
final fullPath = path.join(appDir.path, fileName);
debugPrint("Full image path: $fullPath");
return fullPath;
}
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 104,
child: Row(
children: [
_buildImage(image1Path),
if (image1Path != null && image2Path != null) const SizedBox(width: 2),
_buildImage(image2Path),
],
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
maxLines: null,
),
Text(
subtitle,
style: const TextStyle(fontSize: 16),
maxLines: null,
),
],
),
),
],
);
}
}
/*
class CustomWidget_old extends StatelessWidget {
final Image? image1; final Image? image1;
final Image? image2; final Image? image2;
final String title; final String title;
@ -152,3 +285,4 @@ class CustomWidget extends StatelessWidget {
); );
} }
} }
*/

View File

@ -3,12 +3,18 @@ import 'package:gifunavi/pages/destination/destination_controller.dart';
import 'package:gifunavi/pages/index/index_controller.dart'; import 'package:gifunavi/pages/index/index_controller.dart';
import 'package:gifunavi/utils/location_controller.dart'; import 'package:gifunavi/utils/location_controller.dart';
import '../../services/api_service.dart';
class IndexBinding extends Bindings { class IndexBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.lazyPut<IndexController>(() => IndexController()); //Get.lazyPut<IndexController>(() => IndexController());
//Get.put<IndexController>(IndexController()); ////Get.put<IndexController>(IndexController());
Get.put<LocationController>(LocationController()); //Get.put<LocationController>(LocationController());
Get.put<DestinationController>(DestinationController()); //Get.put<DestinationController>(DestinationController());
Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
Get.put(LocationController(), permanent: true);
Get.put(DestinationController(), permanent: true);
} }
} }

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -31,6 +32,8 @@ import 'package:gifunavi/widgets/helper_dialog.dart';
import 'package:timezone/timezone.dart' as tz; import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/data/latest.dart' as tz;
import '../permission/permission.dart';
class IndexController extends GetxController with WidgetsBindingObserver { class IndexController extends GetxController with WidgetsBindingObserver {
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs; List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs; List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
@ -68,9 +71,14 @@ class IndexController extends GetxController with WidgetsBindingObserver {
String? userToken; String? userToken;
//late final ApiService _apiService; //late final ApiService _apiService;
final ApiService _apiService = Get.find<ApiService>(); final ApiService _apiService; // = Get.find<ApiService>();
final DatabaseHelper _dbHelper = DatabaseHelper.instance; final DatabaseHelper _dbHelper = DatabaseHelper.instance;
IndexController({
required ApiService apiService,
}) : _apiService = apiService;
// mode = 0 is map mode, mode = 1 list mode // mode = 0 is map mode, mode = 1 list mode
var mode = 0.obs; var mode = 0.obs;
@ -235,6 +243,10 @@ class IndexController extends GetxController with WidgetsBindingObserver {
initConnectivity(); initConnectivity();
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus); _connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await PermissionController.checkAndRequestPermissions();
});
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
_startLocationService(); // アプリ起動時にLocationServiceを開始する _startLocationService(); // アプリ起動時にLocationServiceを開始する
@ -263,10 +275,10 @@ class IndexController extends GetxController with WidgetsBindingObserver {
connectionStatusName.value = "WiFi"; connectionStatusName.value = "WiFi";
break; break;
case ConnectivityResult.mobile: case ConnectivityResult.mobile:
connectionStatusName.value = "モバイルデータ"; connectionStatusName.value = "mobile";
break; break;
case ConnectivityResult.none: case ConnectivityResult.none:
connectionStatusName.value = "オフライン"; connectionStatusName.value = "offline";
break; break;
default: default:
connectionStatusName.value = "不明"; connectionStatusName.value = "不明";
@ -352,22 +364,53 @@ class IndexController extends GetxController with WidgetsBindingObserver {
} }
void _startLocationService() async { void _startLocationService() async {
const platform = MethodChannel('location'); if (Platform.isAndroid) {
try { const platform = MethodChannel('location');
logManager.addOperationLog("Called start location service."); try {
await platform.invokeMethod('startLocationService'); logManager.addOperationLog("Called start location service.");
} on PlatformException catch (e) { await platform.invokeMethod('startLocationService');
print("Failed to start location service: '${e.message}'."); } on PlatformException catch (e) {
print("Failed to start location service: '${e.message}'.");
}
}else if (Platform.isIOS) {
// iOSの位置情報サービス開始ロジック
// 例: geolocatorプラグインを使用する場合
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// 位置情報サービスが無効の場合の処理
return;
}
// 位置情報の権限確認と取得開始
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// 権限が拒否された場合の処理
return;
}
}
// 位置情報の取得開始
Geolocator.getPositionStream().listen((Position position) {
// 位置情報を使用した処理
});
} catch (e) {
print('Error starting iOS location service: $e');
}
} }
} }
void _stopLocationService() async { void _stopLocationService() async {
const platform = MethodChannel('location'); if (Platform.isAndroid) {
try { const platform = MethodChannel('location');
logManager.addOperationLog("Called stop location service."); try {
await platform.invokeMethod('stopLocationService'); logManager.addOperationLog("Called stop location service.");
} on PlatformException catch (e) { await platform.invokeMethod('stopLocationService');
print("Failed to stop location service: '${e.message}'."); } on PlatformException catch (e) {
print("Failed to stop location service: '${e.message}'.");
}
}else{
debugPrint("stopLocation for iOS");
} }
} }
@ -433,51 +476,75 @@ class IndexController extends GetxController with WidgetsBindingObserver {
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。 // 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
// //
void login(String email, String password, BuildContext context) async{ Future<void> login(String email, String password) async {
try {
AuthService.login(email, password).then((value) async { final value = await AuthService.login(email, password);
print("------- logged in user details ######## $value ###### --------"); if (value.isNotEmpty && value['token'] != null) {
if (value.isNotEmpty) { await changeUser(value);
logManager.addOperationLog("User logged in : $value."); await _initializeUserData();
Get.offAllNamed(AppPages.INDEX);
// Navigator.pop(context);
print("--------- user details login ----- $value");
changeUser(value);
// ログイン成功後、api_serviceを初期化
await Get.putAsync(() => ApiService().init());
// ユーザー情報の完全性をチェック
if (await checkUserInfoComplete()) {
Get.offAllNamed(AppPages.INDEX);
} else {
Get.offAllNamed(AppPages.USER_DETAILS_EDIT);
}
} else { } else {
logManager.addOperationLog("User failed login : $email , $password."); Get.snackbar('Login Failed', 'Invalid credentials');
isLoading.value = false; }
Get.snackbar( } catch (e) {
"login_failed".tr, print('Login error: $e');
"check_login_id_or_password".tr, Get.snackbar('Login Failed', 'An error occurred. Please try again.');
}
}
Future<void> _initializeUserData() async {
try {
await fetchUserEventInfo();
await fetchTeamData();
// 他の必要なデータ取得処理
} catch (e) {
print('Error initializing user data: $e');
Get.snackbar('Error', 'Failed to load user data. Please try again.');
}
}
Future<void> login_old(String email, String password, BuildContext context) async{
try {
AuthService.login(email, password).then((value) async {
print("------- logged in user details ######## $value ###### --------");
if (value.isNotEmpty && value['token']!=null) {
logManager.addOperationLog("User logged in : $value.");
// Navigator.pop(context);
print("--------- user details login ----- $value");
// ログイン成功後、api_serviceを初期化
await Get.putAsync(() => ApiService().init());
// ユーザー情報の完全性をチェック
if (await checkUserInfoComplete()) {
Get.offAllNamed(AppPages.INDEX);
} else {
Get.offAllNamed(AppPages.USER_DETAILS_EDIT);
}
} else {
logManager.addOperationLog("User failed login : $email , $password.");
isLoading.value = false;
Get.snackbar(
"login_failed".tr,
"check_login_id_or_password".tr,
backgroundColor: Colors.red, backgroundColor: Colors.red,
colorText: Colors.white, colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue), icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
//backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
} }
}); });
} catch(e ){
print('Login error: $e');
Get.snackbar('Login Failed', 'An error occurred. Please try again.');
}
} }
Future<bool> checkUserInfoComplete() async {
final user = await ApiService.to.getCurrentUser();
return user.firstname.isNotEmpty &&
user.lastname.isNotEmpty &&
user.dateOfBirth != null;
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。 // 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
// //
@ -532,12 +599,12 @@ class IndexController extends GetxController with WidgetsBindingObserver {
} }
*/ */
void logout() async { Future<void> logout() async {
logManager.addOperationLog("User logout : $currentUser ."); logManager.addOperationLog("User logout : $currentUser .");
saveGameState(); saveGameState();
locations.clear(); locations.clear();
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) { db.deleteAllDestinationsExceptTodayCheckins().then((value) {
DestinationController destinationController = DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
destinationController.populateDestinations(); destinationController.populateDestinations();
@ -619,28 +686,48 @@ class IndexController extends GetxController with WidgetsBindingObserver {
} }
*/ */
void changeUser(Map<String, dynamic> value, {bool replace = true}) async{ Future<void> changeUser(Map<String, dynamic> value, {bool replace = true}) async{
currentUser.clear(); try {
currentUser.add(value); if (value['user'] == null || value['token'] == null) {
if (replace) { throw Exception('Invalid user data');
saveToDevice(currentUser[0]["token"]); }
currentUser.clear();
currentUser.add(value);
if (replace) {
saveToDevice(currentUser[0]["token"]);
}
isLoading.value = false;
// ユーザーのイベント情報を取得
await fetchUserEventInfo();
loadLocationsBound(currentUser[0]["user"]["event_code"]);
if (currentUser.isNotEmpty) {
rogMode.value = 0;
restoreGame();
// チームデータを取得
await fetchTeamData();
} else {
rogMode.value = 1;
}
Get.toNamed(AppPages.INDEX);
} catch( e ){
print('Error in changeUser: $e');
Get.snackbar('Error', 'Failed to update user information');
} }
isLoading.value = false; }
// ユーザーのイベント情報を取得 Future<bool> checkUserInfoComplete() async {
await fetchUserEventInfo(); try {
final user = await ApiService.to.getCurrentUser();
loadLocationsBound( currentUser[0]["user"]["event_code"]); return user.firstname.isNotEmpty &&
if (currentUser.isNotEmpty) { user.lastname.isNotEmpty &&
rogMode.value = 0; user.dateOfBirth != null;
restoreGame(); } catch (e) {
print('Error checking user info: $e');
// チームデータを取得 return false;
await fetchTeamData();
} else {
rogMode.value = 1;
} }
Get.toNamed(AppPages.INDEX);
} }
Future<void> fetchUserEventInfo() async { Future<void> fetchUserEventInfo() async {
@ -697,7 +784,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
Future<void> fetchTeamData() async { Future<void> fetchTeamData() async {
try { try {
Get.put(TeamController()); Get.put(TeamController(apiService:Get.find<ApiService>()));
// \"TeamController\" not found. You need to call \"Get.put(TeamController())\" or \"Get.lazyPut(()=>TeamController())\" // \"TeamController\" not found. You need to call \"Get.put(TeamController())\" or \"Get.lazyPut(()=>TeamController())\"
final teamController = Get.find<TeamController>(); final teamController = Get.find<TeamController>();
await teamController.fetchTeams(); await teamController.fetchTeams();
@ -945,7 +1032,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
Future<void> checkEntryData() async { Future<void> checkEntryData() async {
// エントリーデータの有無をチェックするロジック // エントリーデータの有無をチェックするロジック
final teamController = TeamController(); final teamController = TeamController(apiService:Get.find<ApiService>());
bool hasEntryData = teamController.checkIfUserHasEntryData(); bool hasEntryData = teamController.checkIfUserHasEntryData();
if (!hasEntryData) { if (!hasEntryData) {
await showHelperDialog( await showHelperDialog(

View File

@ -38,10 +38,21 @@ class _IndexPageState extends State<IndexPage> {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
//checkLoginAndShowDialog(); //checkLoginAndShowDialog();
//checkEventAndNavigate();
}); });
} }
void checkLoginAndShowDialog() { void checkEventAndNavigate() async {
if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]["event_code"] == null) {
// イベントコードがない場合、EVENT_ENTRYページに遷移
await Get.toNamed(AppPages.EVENT_ENTRY);
// EVENT_ENTRYページから戻ってきた後に警告を表示
_showEventSelectionWarning();
}
}
void checkLoginAndShowDialog() async {
if (indexController.currentUser.isEmpty) { if (indexController.currentUser.isEmpty) {
showDialog( showDialog(
context: context, context: context,
@ -78,9 +89,30 @@ class _IndexPageState extends State<IndexPage> {
); );
}, },
); );
}else{
if(indexController.currentUser[0]["user"]["event_code"] == null) {
// イベントコードがない場合、EVENT_ENTRYページに遷移
await Get.toNamed(AppPages.EVENT_ENTRY);
// EVENT_ENTRYページから戻ってきた後に警告を表示
_showEventSelectionWarning();
}
} }
} }
void _showEventSelectionWarning() {
Get.dialog(
AlertDialog(
title: Text('警告'),
content: Text('イベントを選択してください。'),
actions: [
TextButton(
child: Text('OK'),
onPressed: () => Get.back(),
),
],
),
);
}
// class IndexPage extends GetView<IndexController> { // class IndexPage extends GetView<IndexController> {
// IndexPage({Key? key}) : super(key: key); // IndexPage({Key? key}) : super(key: key);

View File

@ -119,8 +119,7 @@ class _LoginPageState extends State<LoginPage> {
body: GestureDetector( body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: indexController.currentUser.isEmpty child: indexController.currentUser.isEmpty
? SizedBox( ? SingleChildScrollView(
width: double.infinity,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
@ -216,8 +215,8 @@ class _LoginPageState extends State<LoginPage> {
true; true;
indexController.login( indexController.login(
emailController.text, emailController.text,
passwordController.text, passwordController.text
context); );
}, },
color: Colors.indigoAccent[400], color: Colors.indigoAccent[400],
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -271,37 +270,34 @@ class _LoginPageState extends State<LoginPage> {
), ),
], ],
), ),
Row(
mainAxisAlignment: MainAxisAlignment.center, Padding(
children: [ padding: const EdgeInsets.all(8.0),
Flexible( child: Row(
child: Padding( children: [
padding: const EdgeInsets.all(8.0), Expanded(
child: Text( child: Text(
"app_developed_by_gifu_dx".tr, "app_developed_by_gifu_dx".tr,
style: const TextStyle( style: const TextStyle(fontSize: 10.0),
overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,
fontSize: 10.0),
), ),
), ),
), ],
], ),
), ),
const Row( Padding(
mainAxisAlignment: MainAxisAlignment.center, padding: const EdgeInsets.all(8.0),
children: [ child: Row(
Flexible( children: [
child: Padding( Expanded(
padding: EdgeInsets.all(8.0),
child: Text( child: Text(
"※第8回と第9回は、岐阜県の令和年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています", "※第8回と第9回は、岐阜県の令和年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
style: TextStyle( style: const TextStyle(fontSize: 10.0),
fontSize: 10.0, textAlign: TextAlign.center,
),
), ),
), ),
), ],
], ),
), ),
], ],
), ),

View File

@ -120,8 +120,8 @@ class LoginPopupPage extends StatelessWidget {
true; true;
indexController.login( indexController.login(
emailController.text, emailController.text,
passwordController.text, passwordController.text
context); );
}, },
color: Colors.indigoAccent[400], color: Colors.indigoAccent[400],
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -10,15 +12,6 @@ class PermissionController {
static bool _isRequestingPermission = false; static bool _isRequestingPermission = false;
static Completer<bool>? _permissionCompleter; static Completer<bool>? _permissionCompleter;
static Future<bool> checkLocationPermissions() async {
final locationPermission = await Permission.location.status;
final whenInUsePermission = await Permission.locationWhenInUse.status;
final alwaysPermission = await Permission.locationAlways.status;
return locationPermission == PermissionStatus.granted &&
(whenInUsePermission == PermissionStatus.granted || alwaysPermission == PermissionStatus.granted);
}
static Future<bool> checkAndRequestPermissions() async { static Future<bool> checkAndRequestPermissions() async {
if (_isRequestingPermission) { if (_isRequestingPermission) {
return _permissionCompleter!.future; return _permissionCompleter!.future;
@ -27,13 +20,119 @@ class PermissionController {
_isRequestingPermission = true; _isRequestingPermission = true;
_permissionCompleter = Completer<bool>(); _permissionCompleter = Completer<bool>();
bool hasPermissions = await checkLocationPermissions(); try {
bool hasPermissions = await _checkLocationPermissions();
if (!hasPermissions) {
bool userAgreed = await showLocationDisclosure();
if (userAgreed) {
if (Platform.isAndroid && !await _isAndroid13OrAbove()) {
hasPermissions = await _requestAndroidPreS();
} else {
hasPermissions = await _requestAllLocationPermissions();
}
} else {
print('User did not agree to location usage');
hasPermissions = false;
SystemNavigator.pop();
}
}
_isRequestingPermission = false;
_permissionCompleter!.complete(hasPermissions);
} catch (e) {
print('Error in permission request: $e');
_isRequestingPermission = false;
_permissionCompleter!.complete(false);
}
return _permissionCompleter!.future;
}
static Future<bool> _checkLocationPermissions() async {
final locationPermission = await Permission.location.status;
final whenInUsePermission = await Permission.locationWhenInUse.status;
final alwaysPermission = await Permission.locationAlways.status;
return locationPermission == PermissionStatus.granted &&
(whenInUsePermission == PermissionStatus.granted || alwaysPermission == PermissionStatus.granted);
}
static Future<bool> _requestAllLocationPermissions() async {
await Permission.location.request();
await Permission.locationWhenInUse.request();
final alwaysStatus = await Permission.locationAlways.request();
return alwaysStatus == PermissionStatus.granted;
}
static Future<bool> _requestAndroidPreS() async {
await Permission.location.request();
await Permission.locationWhenInUse.request();
// Android 13以前では、ユーザーに設定画面で権限を許可するように促す
await showDialog(
context: Get.context!,
builder: (context) => AlertDialog(
title: Text('バックグラウンド位置情報の許可'),
content: Text('アプリの設定画面で「常に許可」を選択してください。'),
actions: [
TextButton(
child: Text('設定を開く'),
onPressed: () {
openAppSettings();
Navigator.of(context).pop();
},
),
],
),
);
// 設定画面から戻ってきた後、再度権限をチェック
return await Permission.locationAlways.isGranted;
}
static Future<bool> _isAndroid13OrAbove() async {
if (Platform.isAndroid) {
final androidVersion = int.tryParse(Platform.operatingSystemVersion.split('.').first) ?? 0;
return androidVersion >= 13;
}
return false;
}
static Future<bool> checkLocationPermissions_old() async {
final locationPermission = await Permission.location.status;
if (locationPermission.isDenied) {
await showLocationDisclosure();
final result = await Permission.location.request();
if (result.isDenied) {
await openAppSettings();
}
}
final whenInUsePermission = await Permission.locationWhenInUse.status;
final alwaysPermission = await Permission.locationAlways.status;
return locationPermission == PermissionStatus.granted &&
(whenInUsePermission == PermissionStatus.granted || alwaysPermission == PermissionStatus.granted);
}
static Future<bool> checkAndRequestPermissions_old() async {
if (_isRequestingPermission) {
return _permissionCompleter!.future;
}
_isRequestingPermission = true;
_permissionCompleter = Completer<bool>();
bool hasPermissions = await _checkLocationPermissions();
if (!hasPermissions) { if (!hasPermissions) {
bool userAgreed = await showLocationDisclosure(); bool userAgreed = await showLocationDisclosure();
if (userAgreed) { if (userAgreed) {
try { try {
await requestAllLocationPermissions(); await requestAllLocationPermissions();
hasPermissions = await checkLocationPermissions(); hasPermissions = await _checkLocationPermissions();
} catch (e) { } catch (e) {
print('Error requesting location permissions: $e'); print('Error requesting location permissions: $e');
hasPermissions = false; hasPermissions = false;
@ -48,6 +147,8 @@ class PermissionController {
_isRequestingPermission = false; _isRequestingPermission = false;
_permissionCompleter!.complete(hasPermissions); _permissionCompleter!.complete(hasPermissions);
debugPrint("Finish checkAndRequestPermissions...");
return _permissionCompleter!.future; return _permissionCompleter!.future;
} }
@ -67,35 +168,51 @@ class PermissionController {
} }
static Future<bool> showLocationDisclosure() async { static Future<bool> showLocationDisclosure() async {
return await Get.dialog<bool>( if (Get.context == null) {
AlertDialog( print('Context is null, cannot show dialog');
title: const Text('位置情報の使用について'), return false;
content: const SingleChildScrollView( }
child: ListBody( if (Get.isDialogOpen ?? false) {
children: <Widget>[ print('A dialog is already open');
Text('このアプリでは、以下の目的で位置情報を使用します:'), return false;
Text('• チェックポイントの自動チェックイン(アプリが閉じているときも含む)'), }
Text('• 移動履歴の記録(バックグラウンドでも継続)'),
Text('• 現在地周辺の情報表示'), try {
Text('\nバックグラウンドでも位置情報を継続的に取得します。'), final result = await Get.dialog<bool>(
Text('これにより、バッテリーの消費が増加する可能性があります。'), AlertDialog(
Text('同意しない場合には、アプリは終了します。'), title: const Text('位置情報の使用について'),
], content: const SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('このアプリでは、以下の目的で位置情報を使用します:'),
Text(
'• チェックポイントの自動チェックイン(アプリが閉じているときも含む)'),
Text('• 移動履歴の記録(バックグラウンドでも継続)'),
Text('• 現在地周辺の情報表示'),
Text('\nバックグラウンドでも位置情報を継続的に取得します。'),
Text('これにより、バッテリーの消費が増加する可能性があります。'),
Text('同意しない場合には、アプリは終了します。'),
],
),
), ),
actions: <Widget>[
TextButton(
child: const Text('同意しない'),
onPressed: () => Get.back(result: false),
),
TextButton(
child: const Text('同意する'),
onPressed: () => Get.back(result: true),
),
],
), ),
actions: <Widget>[ barrierDismissible: false,
TextButton( );
child: const Text('同意しない'), return result ?? false;
onPressed: () => Get.back(result: false), }catch(e){
), print('Dialog error: $e');
TextButton( return false;
child: const Text('同意する'), }
onPressed: () => Get.back(result: true),
),
],
),
barrierDismissible: false,
) ?? false;
} }
static void showPermissionDeniedDialog(String title,String message) { static void showPermissionDeniedDialog(String title,String message) {

View File

@ -2,6 +2,8 @@ import 'package:get/get.dart';
import 'package:gifunavi/pages/team/member_controller.dart'; import 'package:gifunavi/pages/team/member_controller.dart';
import 'package:gifunavi/services/api_service.dart'; import 'package:gifunavi/services/api_service.dart';
import '../index/index_controller.dart';
class MemberBinding extends Bindings { class MemberBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {

View File

@ -2,10 +2,15 @@ import 'package:get/get.dart';
import 'package:gifunavi/pages/team/team_controller.dart'; import 'package:gifunavi/pages/team/team_controller.dart';
import 'package:gifunavi/services/api_service.dart'; import 'package:gifunavi/services/api_service.dart';
//import '../entry/entry_controller.dart';
import '../index/index_controller.dart';
class TeamBinding extends Bindings { class TeamBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.lazyPut<ApiService>(() => ApiService()); Get.lazyPut<ApiService>(() => ApiService());
Get.lazyPut<TeamController>(() => TeamController()); Get.lazyPut<TeamController>(() => TeamController(
apiService:Get.find<ApiService>())
);
} }
} }

View File

@ -15,8 +15,14 @@ import 'package:gifunavi/model/event.dart';
class TeamController extends GetxController { class TeamController extends GetxController {
late final ApiService _apiService; final ApiService _apiService;
late final EntryController _entryController; //final EntryController _entryController;
TeamController({
required ApiService apiService,
//required EntryController entryController,
}) : _apiService = apiService;
//_entryController = entryController;
final teams = <Team>[].obs; final teams = <Team>[].obs;
final categories = <NewCategory>[].obs; final categories = <NewCategory>[].obs;
@ -35,12 +41,12 @@ class TeamController extends GetxController {
void onInit() async { void onInit() async {
super.onInit(); super.onInit();
try { try {
_apiService = Get.find<ApiService>(); //_apiService = Get.find<ApiService>();
if (!Get.isRegistered<EntryController>()) { //if (!Get.isRegistered<EntryController>()) {
Get.put(EntryController()); // Get.put(EntryController());
} //}
_entryController = Get.find<EntryController>(); //_entryController = Get.find<EntryController>();
await loadInitialData(); await loadInitialData();
} catch (e) { } catch (e) {

View File

@ -9,6 +9,7 @@ import 'package:gifunavi/model/team.dart';
import 'package:gifunavi/model/category.dart'; import 'package:gifunavi/model/category.dart';
import 'package:gifunavi/model/user.dart'; import 'package:gifunavi/model/user.dart';
import 'package:gifunavi/pages/index/index_controller.dart'; import 'package:gifunavi/pages/index/index_controller.dart';
import '../routes/app_pages.dart';
import '../utils/const.dart'; import '../utils/const.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@ -22,11 +23,15 @@ class ApiService extends GetxService{
Future<ApiService> init() async { Future<ApiService> init() async {
try { try {
//if (!Get.isRegistered<IndexController>()) {
// Get.put(IndexController(apiService: Get.find<ApiService>()));
//}
// ここで必要な初期化処理を行う // ここで必要な初期化処理を行う
serverUrl = ConstValues.currentServer(); serverUrl = ConstValues.currentServer();
baseUrl = '$serverUrl/api'; baseUrl = '$serverUrl/api';
//await Future.delayed(Duration(seconds: 2)); // 仮の遅延(実際の初期化処理に置き換えてください) //await Future.delayed(Duration(seconds: 2)); // 仮の遅延(実際の初期化処理に置き換えてください)
print('ApiService initialized successfully'); print('ApiService initialized successfully . baseUrl = $baseUrl');
return this; return this;
} catch(e) { } catch(e) {
print('Error in ApiService initialization: $e'); print('Error in ApiService initialization: $e');
@ -47,11 +52,13 @@ class ApiService extends GetxService{
注意点として、API のレスポンス形式が変更された場合や、新しいフィールドが追加された場合は、このメソッドも更新する必要があります。そのため、API の変更とクライアントサイドのコードの同期を保つことが重要です。 注意点として、API のレスポンス形式が変更された場合や、新しいフィールドが追加された場合は、このメソッドも更新する必要があります。そのため、API の変更とクライアントサイドのコードの同期を保つことが重要です。
*/ */
String getToken() Future<String> getToken2 () async
{ {
// IndexControllerの初期化を待つ // IndexControllerの初期化を待つ
if (!Get.isRegistered<IndexController>()) {
Get.find<IndexController>();
}
final indexController = Get.find<IndexController>(); final indexController = Get.find<IndexController>();
if (indexController.currentUser.isNotEmpty) { if (indexController.currentUser.isNotEmpty) {
token = indexController.currentUser[0]['token'] ?? ''; token = indexController.currentUser[0]['token'] ?? '';
print("Get token = $token"); print("Get token = $token");
@ -61,11 +68,61 @@ class ApiService extends GetxService{
return token; return token;
} }
String getToken()
{
// IndexControllerの初期化を待つ
if (!Get.isRegistered<IndexController>()) {
Get.find<IndexController>();
}
final indexController = Get.find<IndexController>();
if (indexController.currentUser.isNotEmpty) {
token = indexController.currentUser[0]['token'] ?? '';
print("Get token = $token");
}else{
token = "";
}
return token;
}
Future<dynamic> _handleRequest(Future<http.Response> Function() request) async {
try {
final response = await request();
if (response.statusCode == 200) {
return json.decode(utf8.decode(response.bodyBytes));
} else if (response.statusCode == 401) {
await _handleUnauthorized();
throw Exception('Authentication failed. Please log in again.');
} else {
throw Exception('Request failed with status: ${response.statusCode}');
}
} catch (e) {
print('API request error: $e');
rethrow;
}
}
Future<void> _handleUnauthorized() async {
// トークンをクリアし、ユーザーをログアウトさせる
final indexController = Get.find<IndexController>();
await indexController.logout();
Get.offAllNamed(AppPages.LOGIN);
}
Future<List<Team>> getTeams() async { Future<List<Team>> getTeams() async {
final token = await getToken2();
return _handleRequest(() => http.get(
Uri.parse('$baseUrl/teams/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
)).then((data) => (data as List).map((json) => Team.fromJson(json)).toList());
}
Future<List<Team>> getTeams_old() async {
init(); init();
getToken(); final token = await getToken2();
try { try {
final response = await http.get( final response = await http.get(
Uri.parse('$baseUrl/teams/'), Uri.parse('$baseUrl/teams/'),
headers: {'Authorization': 'Token $token',"Content-Type": "application/json; charset=UTF-8"}, headers: {'Authorization': 'Token $token',"Content-Type": "application/json; charset=UTF-8"},
@ -136,6 +193,14 @@ class ApiService extends GetxService{
} }
Future<NewCategory> getZekkenNumber(int categoryId) async { Future<NewCategory> getZekkenNumber(int categoryId) async {
final token = await getToken2();
return _handleRequest(() => http.post(
Uri.parse('$baseUrl/categories-viewset/$categoryId/get_zekken_number/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
)).then((data) => NewCategory.fromJson(data));
}
Future<NewCategory> getZekkenNumber_old(int categoryId) async {
try { try {
final response = await http.post( final response = await http.post(
Uri.parse('$baseUrl/categories-viewset/$categoryId/get_zekken_number/'), Uri.parse('$baseUrl/categories-viewset/$categoryId/get_zekken_number/'),
@ -157,7 +222,7 @@ class ApiService extends GetxService{
Future<User> getCurrentUser() async { Future<User> getCurrentUser() async {
init(); init();
getToken(); final token = getToken();
try { try {
final response = await http.get( final response = await http.get(
@ -174,6 +239,13 @@ class ApiService extends GetxService{
//_printDataComparison(jsonData, User); //_printDataComparison(jsonData, User);
return User.fromJson(jsonData); return User.fromJson(jsonData);
} else if (response.statusCode == 401) {
// トークンが無効な場合、ログアウトしてログインページにリダイレクト
await Get.find<IndexController>().logout();
//indexController.logout();
Get.offAllNamed(AppPages.LOGIN);
throw Exception('Authentication failed. Please log in again.');
} else { } else {
throw Exception('Failed to get current user. Status code: ${response.statusCode}'); throw Exception('Failed to get current user. Status code: ${response.statusCode}');
} }
@ -244,6 +316,15 @@ class ApiService extends GetxService{
} }
Future<Team> createTeam(String teamName, int categoryId) async { Future<Team> createTeam(String teamName, int categoryId) async {
final token = await getToken2();
return _handleRequest(() => http.post(
Uri.parse('$baseUrl/teams/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
body: json.encode({'team_name': teamName, 'category': categoryId}),
)).then((data) => Team.fromJson(data));
}
Future<Team> createTeam_old(String teamName, int categoryId) async {
init(); init();
getToken(); getToken();
@ -268,6 +349,15 @@ class ApiService extends GetxService{
} }
Future<Team> updateTeam(int teamId, String teamName, int categoryId) async { Future<Team> updateTeam(int teamId, String teamName, int categoryId) async {
final token = await getToken2();
return _handleRequest(() => http.put(
Uri.parse('$baseUrl/teams/$teamId/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
body: json.encode({'team_name': teamName, 'category': categoryId}),
)).then((data) => Team.fromJson(data));
}
Future<Team> updateTeam_old(int teamId, String teamName, int categoryId) async {
init(); init();
getToken(); getToken();
@ -293,6 +383,14 @@ class ApiService extends GetxService{
} }
Future<void> deleteTeam(int teamId) async { Future<void> deleteTeam(int teamId) async {
final token = await getToken2();
await _handleRequest(() => http.delete(
Uri.parse('$baseUrl/teams/$teamId/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
));
}
Future<void> deleteTeamold_(int teamId) async {
init(); init();
getToken(); getToken();
@ -311,6 +409,14 @@ class ApiService extends GetxService{
} }
Future<List<User>> getTeamMembers(int teamId) async { Future<List<User>> getTeamMembers(int teamId) async {
final token = await getToken2();
return _handleRequest(() => http.get(
Uri.parse('$baseUrl/teams/$teamId/members/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
)).then((data) => (data as List).map((json) => User.fromJson(json)).toList());
}
Future<List<User>> getTeamMembers_old(int teamId) async {
init(); init();
getToken(); getToken();
@ -330,7 +436,23 @@ class ApiService extends GetxService{
} }
} }
Future<User> createTeamMember(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth,bool? female) async { Future<User> createTeamMember(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth, bool? female) async {
final token = await getToken2();
String? formattedDateOfBirth = dateOfBirth != null ? DateFormat('yyyy-MM-dd').format(dateOfBirth) : null;
return _handleRequest(() => http.post(
Uri.parse('$baseUrl/teams/$teamId/members/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
body: json.encode({
'email': email,
'firstname': firstname,
'lastname': lastname,
'date_of_birth': formattedDateOfBirth,
'female': female,
}),
)).then((data) => User.fromJson(data));
}
Future<User> createTeamMember_old(int teamId, String? email, String? firstname, String? lastname, DateTime? dateOfBirth,bool? female) async {
init(); init();
getToken(); getToken();
@ -370,7 +492,22 @@ class ApiService extends GetxService{
} }
} }
Future<User> updateTeamMember(int teamId,int? memberId, String firstname, String lastname, DateTime? dateOfBirth,bool? female) async { Future<User> updateTeamMember(int teamId, int? memberId, String firstname, String lastname, DateTime? dateOfBirth, bool? female) async {
final token = await getToken2();
String? formattedDateOfBirth = dateOfBirth != null ? DateFormat('yyyy-MM-dd').format(dateOfBirth) : null;
return _handleRequest(() => http.put(
Uri.parse('$baseUrl/teams/$teamId/members/$memberId/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
body: json.encode({
'firstname': firstname,
'lastname': lastname,
'date_of_birth': formattedDateOfBirth,
'female': female,
}),
)).then((data) => User.fromJson(data));
}
Future<User> updateTeamMember_old(int teamId,int? memberId, String firstname, String lastname, DateTime? dateOfBirth,bool? female) async {
init(); init();
getToken(); getToken();
@ -401,7 +538,15 @@ class ApiService extends GetxService{
} }
} }
Future<void> deleteTeamMember(int teamId,int memberId) async { Future<void> deleteTeamMember(int teamId, int memberId) async {
final token = await getToken2();
await _handleRequest(() => http.delete(
Uri.parse('$baseUrl/teams/$teamId/members/$memberId/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
));
}
Future<void> deleteTeamMember_old(int teamId,int memberId) async {
init(); init();
getToken(); getToken();
@ -442,6 +587,14 @@ class ApiService extends GetxService{
} }
Future<List<Entry>> getEntries() async { Future<List<Entry>> getEntries() async {
final token = await getToken2();
return _handleRequest(() => http.get(
Uri.parse('$baseUrl/entry/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
)).then((data) => (data as List).map((json) => Entry.fromJson(json)).toList());
}
Future<List<Entry>> getEntries_old() async {
init(); init();
getToken(); getToken();
@ -500,7 +653,23 @@ class ApiService extends GetxService{
} }
} }
Future<Entry> createEntry(int teamId, int eventId, int categoryId, DateTime date,String zekkenNumber) async { Future<Entry> createEntry(int teamId, int eventId, int categoryId, DateTime date, String zekkenNumber) async {
final token = await getToken2();
String formattedDate = DateFormat('yyyy-MM-dd').format(date);
return _handleRequest(() => http.post(
Uri.parse('$baseUrl/entry/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
body: json.encode({
'team': teamId,
'event': eventId,
'category': categoryId,
'date': formattedDate,
'zekken_number': zekkenNumber,
}),
)).then((data) => Entry.fromJson(data));
}
Future<Entry> createEntry_old(int teamId, int eventId, int categoryId, DateTime date,String zekkenNumber) async {
init(); init();
getToken(); getToken();
@ -529,11 +698,28 @@ class ApiService extends GetxService{
} else { } else {
final decodedResponse = utf8.decode(response.bodyBytes); final decodedResponse = utf8.decode(response.bodyBytes);
print("decodedResponse = $decodedResponse"); print("decodedResponse = $decodedResponse");
throw Exception('Failed to create entry'); final errorInfo = json.decode(decodedResponse);
throw Exception(errorInfo['error']);
} }
} }
Future<void> updateUserInfo(int userId, Entry entry) async { Future<void> updateUserInfo(int userId, Entry entry) async {
final token = await getToken2();
String formattedDate = DateFormat('yyyy-MM-dd').format(entry.date!);
await _handleRequest(() => http.put(
Uri.parse('$baseUrl/userinfo/$userId/'),
headers: {'Authorization': 'Token $token', "Content-Type": "application/json; charset=UTF-8"},
body: json.encode({
'zekken_number': entry.zekkenNumber,
'event_code': entry.event.eventName,
'group': entry.team.category.categoryName,
'team_name': entry.team.teamName,
'date': formattedDate,
}),
));
}
Future<void> updateUserInfo_old(int userId, Entry entry) async {
init(); init();
getToken(); getToken();
@ -598,7 +784,10 @@ class ApiService extends GetxService{
final decodedResponse = utf8.decode(response.bodyBytes); final decodedResponse = utf8.decode(response.bodyBytes);
final blk = json.decode(decodedResponse); final blk = json.decode(decodedResponse);
throw Exception('Failed to update entry'); Map<String, dynamic> error_dict = blk[0]['error'];
String ? error_message = error_dict['non_field_errors'][0].string;
throw Exception(error_message);
} }
} }

View File

@ -51,8 +51,8 @@ class ExternalService {
//print("--- _team : ${_team}-----"); //print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"]; String eventCode = indexController.currentUser[0]["user"]["event_code"];
if (indexController.connectionStatusName.value != "wifi" && if (indexController.connectionStatusName.value.toLowerCase() != "wifi" &&
indexController.connectionStatusName.value != "mobile") { indexController.connectionStatusName.value.toLowerCase() != "mobile") {
debugPrint("== No network =="); debugPrint("== No network ==");
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
Rog rog = Rog( Rog rog = Rog(
@ -114,8 +114,8 @@ class ExternalService {
//int teamId = indexController.teamController.teams[0]; //int teamId = indexController.teamController.teams[0];
if (indexController.connectionStatusName.value != "wifi" && if (indexController.connectionStatusName.value.toLowerCase() != "wifi" &&
indexController.connectionStatusName.value != "mobile") { indexController.connectionStatusName.value.toLowerCase() != "mobile") {
debugPrint("== checkin without network =="); debugPrint("== checkin without network ==");
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
@ -326,8 +326,8 @@ class ExternalService {
//print("--- _team : ${_team}-----"); //print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"]; String eventCode = indexController.currentUser[0]["user"]["event_code"];
if (indexController.connectionStatusName.value != "wifi" && if (indexController.connectionStatusName.value.toLowerCase() != "wifi" &&
indexController.connectionStatusName.value != "mobile") { indexController.connectionStatusName.value.toLowerCase() != "mobile") {
return Future.value(false); return Future.value(false);
} else { } else {
String serverUrl = ConstValues.currentServer(); String serverUrl = ConstValues.currentServer();
@ -376,8 +376,8 @@ class ExternalService {
List<GpsData> gpsDataList = []; List<GpsData> gpsDataList = [];
if (indexController.connectionStatusName.value != "wifi" && if (indexController.connectionStatusName.value.toLowerCase() != "wifi" &&
indexController.connectionStatusName.value != "mobile") { indexController.connectionStatusName.value.toLowerCase() != "mobile") {
return Future.value(false); return Future.value(false);
} else { } else {
// Step 1: Fetch data from the local database // Step 1: Fetch data from the local database

View File

@ -13,6 +13,11 @@ class DatabaseHelper {
static Database? _database; static Database? _database;
Future<Database> get database async => _database ??= await _initDatabase(); Future<Database> get database async => _database ??= await _initDatabase();
// データベース初期化:
//
// シングルトンパターンを使用してDatabaseHelperのインスタンスを管理しています。
// _initDatabase()メソッドでデータベースを初期化し、必要なテーブルを作成します。
//
Future<Database> _initDatabase() async { Future<Database> _initDatabase() async {
Directory documentDirectory = await getApplicationDocumentsDirectory(); Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path, 'rog.db'); String path = join(documentDirectory.path, 'rog.db');
@ -30,7 +35,10 @@ class DatabaseHelper {
onCreate: _onCreate); onCreate: _onCreate);
} }
// DBを初期化する際に、必要なテーブルを作成します。
//
Future _onCreate(Database db, int version) async { Future _onCreate(Database db, int version) async {
// destinationテーブル: 目的地の情報を保存(位置、名前、住所、連絡先情報など)。
await db.execute(''' await db.execute('''
CREATE TABLE destination( CREATE TABLE destination(
location_id INTEGER PRIMARY KEY, location_id INTEGER PRIMARY KEY,
@ -63,6 +71,7 @@ class DatabaseHelper {
) )
'''); ''');
// rogainingテーブル: ロゲイニングorienteering的なアクティビティの記録を保存。
await db.execute(''' await db.execute('''
CREATE TABLE rogaining( CREATE TABLE rogaining(
rog_id INTEGER PRIMARY KEY AUTOINCREMENT, rog_id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -76,6 +85,7 @@ class DatabaseHelper {
) )
'''); ''');
// rogテーブル: ロゲイニングのチェックポイント情報を保存。
await db.execute(''' await db.execute('''
CREATE TABLE rog( CREATE TABLE rog(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -138,6 +148,22 @@ class DatabaseHelper {
await db.delete('rog'); await db.delete('rog');
} }
Future<void> deleteAllRogainingExceptToday() async {
Database db = await instance.database;
// 今日の開始時刻をエポックミリ秒で取得
final now = DateTime.now();
final startOfDay = DateTime(now.year, now.month, now.day).millisecondsSinceEpoch;
// 今日チェックインしたもの以外を削除
await db.delete(
'rog',
where: 'checkintime < ?',
whereArgs: [startOfDay]
);
}
Future<bool> isRogAlreadyAvailable(int id) async { Future<bool> isRogAlreadyAvailable(int id) async {
Database db = await instance.database; Database db = await instance.database;
var rog = await db.query('rog', where: "id = $id"); var rog = await db.query('rog', where: "id = $id");
@ -229,6 +255,27 @@ class DatabaseHelper {
await db.delete('destination'); await db.delete('destination');
} }
Future<void> deleteAllDestinationsExceptTodayCheckins() async {
Database db = await instance.database;
// 今日の開始時刻をエポックからのミリ秒で取得
final now = DateTime.now();
final startOfDay = DateTime(now.year, now.month, now.day).millisecondsSinceEpoch;
// 今日チェックインされ、buy_pointを持つ目的地を除いて全て削除
await db.rawDelete('''
DELETE FROM destination
WHERE location_id NOT IN (
SELECT d.location_id
FROM destination d
JOIN rog r ON d.location_id = r.cp_number
WHERE date(r.checkintime / 1000, 'unixepoch', 'localtime') = date('now', 'localtime')
AND d.buy_point > 0
AND d.checkedin = 1
)
''', [startOfDay]);
}
Future<bool> isAlreadyAvailable(int locationId) async { Future<bool> isAlreadyAvailable(int locationId) async {
Database db = await instance.database; Database db = await instance.database;
var dest = var dest =

View File

@ -129,20 +129,25 @@ class LocationController extends GetxController {
// 現在位置を調整するメソッドを追加 // 現在位置を調整するメソッドを追加
LatLng? adjustCurrentLocation(Position? position) { LatLng? adjustCurrentLocation(Position? position) {
if (position == null) { if (position == null) { // positionがnullなら、lastValidLocationを使用する。
if( lastValidLocation!=null ) { if( lastValidLocation!=null ) {
debugPrint("== 現在位置なし。最後の位置を使用 ==");
//debugPrint("=== adjustCurrentLocation (Position:Null and using LastValidLocation ${lastValidLocation})==="); //debugPrint("=== adjustCurrentLocation (Position:Null and using LastValidLocation ${lastValidLocation})===");
return LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude); return LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
}else { }else {
print("=== adjustCurrentLocation (Position:Null and No LastValidLocation ... )==="); debugPrint("== 現在位置なし。最後の位置も無し ==");
//print("=== adjustCurrentLocation (Position:Null and No LastValidLocation ... )===");
return null; return null;
} }
//return lastValidLocation ?? LatLng(0, 0); //return lastValidLocation ?? LatLng(0, 0);
} }
final signalStrength = getGpsSignalStrength(position); final signalStrength = getGpsSignalStrength(position);
if (signalStrength == 'high' || signalStrength == 'medium') { if (signalStrength == 'high' || signalStrength == 'medium') {
debugPrint("== 信号強度 ${signalStrength} ==> 最新位置を使用 ==");
//debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )==="); //debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )===");
lastValidLocation = LatLng(position.latitude, position.longitude); lastValidLocation = LatLng(position.latitude, position.longitude);
}else{
debugPrint("== 信号強度 ${signalStrength} ==> 最後の位置を使用 ==");
} }
return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude); return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
} }
@ -169,10 +174,39 @@ class LocationController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
// Start listening to location updates when the controller is initialized // Start listening to location updates when the controller is initialized
startPositionStream(); _initLocationService();
} }
Future<void> _initLocationService() async {
try {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
startPositionStream();
} catch( e ){
print('Error initializing location service: $e');
}
}
// 位置情報のストリームを開始するメソッドです。 // 位置情報のストリームを開始するメソッドです。
// 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。 // 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。
// 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。 // 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。
@ -185,7 +219,26 @@ class LocationController extends GetxController {
// 2024-4-8 Akira : See 2809 // 2024-4-8 Akira : See 2809
// stopPositionStreamメソッドを追加して、既存のストリームをキャンセルするようにしました。また、ストリームが完了したらnullに設定し、エラー発生時にストリームをキャンセルするようにしました。 // stopPositionStreamメソッドを追加して、既存のストリームをキャンセルするようにしました。また、ストリームが完了したらnullに設定し、エラー発生時にストリームをキャンセルするようにしました。
// //
void startPositionStream() async { void startPositionStream() {
positionStream = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 5, //10, 10mから5mに変更
),
).listen((Position position) {
currentPosition.value = position;
//debugPrint("== startPositionStream: ${position} ==");
locationMarkerPositionStreamController.add(
LocationMarkerPosition(
latitude: position.latitude,
longitude: position.longitude,
accuracy: position.accuracy,
),
);
});
}
void startPositionStream_old() async {
// Check for location service and permissions before starting the stream // Check for location service and permissions before starting the stream
// 位置情報サービスの有効性をチェックし、無効な場合はエラーハンドリングを行います。 // 位置情報サービスの有効性をチェックし、無効な場合はエラーハンドリングを行います。
// //

View File

@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:get/get.dart';
import 'dart:async';
void _performMemoryCleanup() {
//debugPrint('Performing memory cleanup');
// キャッシュのクリア
Get.deleteAll(); // GetXを使用している場合、すべてのコントローラをクリア
imageCache.clear(); // 画像キャッシュをクリア
imageCache.clearLiveImages(); // 使用中の画像キャッシュをクリア
// 大きなオブジェクトの解放
_clearLargeObjects();
// 未使用のリソースの解放
_releaseUnusedResources();
// ガベージコレクションの促進
_forceGarbageCollection();
debugPrint('Memory cleanup completed');
}
void _clearLargeObjects() {
// 大きなリストやマップをクリア
// 例: myLargeList.clear();
// 例: myLargeMap.clear();
}
void _releaseUnusedResources() {
// 使用していないストリームのクローズ
// 例: myStream?.close();
// 未使用のアニメーションコントローラーの破棄
// 例: myAnimationController?.dispose();
// テキスト編集コントローラーの破棄
// 例: myTextEditingController?.dispose();
}
void _forceGarbageCollection() {
// Dart VMにガベージコレクションを促す
Timer(const Duration(seconds: 1), () {
//debugPrint('Forcing garbage collection');
// この呼び出しは必ずしもガベージコレクションを即座に実行するわけではありませんが、
// Dart VMにガベージコレクションの実行を強く促します。
// ignore: dead_code
bool didRun = false;
assert(() {
didRun = true;
return true;
}());
if (didRun) {
//debugPrint('Garbage collection forced in debug mode');
}
});
}
// メモリ使用量を監視し、必要に応じてクリーンアップを実行する関数
void startMemoryMonitoring() {
const Duration checkInterval = Duration(minutes: 5);
Timer.periodic(checkInterval, (Timer timer) {
_checkMemoryUsage();
});
}
void _checkMemoryUsage() async {
// ここでメモリ使用量をチェックするロジックを実装
// 例えば、プラットフォーム固有の方法でメモリ使用量を取得する
// 仮の閾値(実際のアプリケーションに応じて調整が必要)
const int memoryThreshold = 100 * 1024 * 1024; // 100 MB
// 仮のメモリ使用量チェック(実際の実装に置き換える必要があります)
int currentMemoryUsage = await _getCurrentMemoryUsage();
if (currentMemoryUsage > memoryThreshold) {
debugPrint('High memory usage detected: $currentMemoryUsage bytes');
_performMemoryCleanup();
}
}
Future<int> _getCurrentMemoryUsage() async {
// プラットフォーム固有のメモリ使用量取得ロジックを実装
// この例では仮の値を返しています
return 150 * 1024 * 1024; // 仮に150MBとする
}

View File

@ -175,7 +175,12 @@ class BottomSheetNew extends GetView<BottomSheetController> {
destinationController.currentLat, destinationController.currentLon), destinationController.currentLat, destinationController.currentLon),
LatLng(cdest.lat!, cdest.lon!)); LatLng(cdest.lat!, cdest.lon!));
// Check conditions to show confirmation dialog // スタートボタン:
// 表示条件:
// 1. 目的地のCP番号が-1または0の場合
// 2. ロゲイニングがまだ開始されていない場合destinationController.isInRog.value == false
// 3. 最後のゴールから10時間以上経過している場合
//
if (destinationController.isInRog.value == false && if (destinationController.isInRog.value == false &&
(destinationController.distanceToStart() <= 100 || destinationController.isGpsSignalWeak() ) && //追加 Akira 2024-4-5 (destinationController.distanceToStart() <= 100 || destinationController.isGpsSignalWeak() ) && //追加 Akira 2024-4-5
(destination.cp == -1 || destination.cp == 0 ) && (destination.cp == -1 || destination.cp == 0 ) &&
@ -294,7 +299,15 @@ class BottomSheetNew extends GetView<BottomSheetController> {
//print("counted ${destinationController.rogainingCounted.value}"); //print("counted ${destinationController.rogainingCounted.value}");
// ゴールボタン:
// 表示条件:
// 1. 目的地のCP番号が0、-2、または-1の場合
// 2. ロゲイニングが開始されている場合destinationController.rogainingCounted.value == true
// 3. スタート地点から500m以内にいる場合、または GPS信号が弱い場合
// 4. ゴール準備完了フラグが立っている場合DestinationController.ready_for_goal == true
//
}else if (destinationController.rogainingCounted.value == true && }else if (destinationController.rogainingCounted.value == true &&
destinationController.isInRog.value == true &&
// destinationController.distanceToStart() <= 500 && ... GPS信号が弱い時でもOKとする。 // destinationController.distanceToStart() <= 500 && ... GPS信号が弱い時でもOKとする。
(destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) && (destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) &&
(destination.cp == 0 || destination.cp == -2 || destination.cp == -1) && (destination.cp == 0 || destination.cp == -2 || destination.cp == -1) &&
@ -334,7 +347,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
} }
: null, : null,
child: Text( child: Text(
"finish_rogaining".tr, "finish_rogaining".tr, // ロゲゴール
style: const TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
)); ));
@ -353,6 +366,9 @@ class BottomSheetNew extends GetView<BottomSheetController> {
destinationController.isCheckingIn.value = true; // ここを追加 destinationController.isCheckingIn.value = true; // ここを追加
Get.back(); Get.back();
Get.back(); Get.back();
if(destinationController.isInRog.value==false && destination.cp == -1){
destinationController.rogainingCounted.value = false;
}
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 500));
await destinationController.callforCheckin(destination); await destinationController.callforCheckin(destination);
destinationController.isCheckingIn.value = false; destinationController.isCheckingIn.value = false;
@ -371,8 +387,8 @@ class BottomSheetNew extends GetView<BottomSheetController> {
}, },
child: Text( child: Text(
destination.cp == -1 && destination.cp == -1 &&
destinationController.isInRog.value == false && destinationController.isInRog.value == false //&&
destinationController.rogainingCounted.value == false //destinationController.rogainingCounted.value == false
? "ロゲ開始" ? "ロゲ開始"
: destinationController.isInRog.value == true && : destinationController.isInRog.value == true &&
destination.cp == -1 destination.cp == -1

View File

@ -151,26 +151,23 @@ class _GameStateWidgetState extends State<GameStateWidget> {
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: StreamBuilder<List<Destination>>( child: StreamBuilder<List<Destination>>(
stream: dbService.destinationUpdatesStream, stream: dbService.destinationUpdatesStream,
initialData: const [], // 初期値を設定
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == if (snapshot.hasError) {
ConnectionState.waiting) { print('Error: ${snapshot.error}');
return const CircularProgressIndicator();
} else if (snapshot.hasError) {
return LocationVisitedWidget(
count: 0,
minimized: !isExpanded,
);
} else if (snapshot.hasData) {
return LocationVisitedWidget(
count: snapshot.data!.length,
minimized: !isExpanded,
);
} else {
return LocationVisitedWidget( return LocationVisitedWidget(
count: 0, count: 0,
minimized: !isExpanded, minimized: !isExpanded,
); );
} }
// データがある場合はそのデータを使用し、ない場合は空のリストを使用
final destinations = snapshot.data ?? [];
return LocationVisitedWidget(
count: destinations.length,
minimized: !isExpanded,
);
}, },
), ),
@ -183,12 +180,12 @@ class _GameStateWidgetState extends State<GameStateWidget> {
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: Obx(() => ConnectionStatusIndicator( child: Obx(() => ConnectionStatusIndicator(
connectionStatus: (indexController connectionStatus: (indexController
.connectionStatusName.value == .connectionStatusName.value.toLowerCase() ==
"wifi" || "wifi" ||
indexController indexController
.connectionStatusName.value == .connectionStatusName.value.toLowerCase() ==
"mobile") "mobile")
? indexController.connectionStatusName.value == ? indexController.connectionStatusName.value.toLowerCase() ==
"wifi" "wifi"
? ConnectionStatus.wifi ? ConnectionStatus.wifi
: ConnectionStatus.mobile : ConnectionStatus.mobile

View File

@ -26,7 +26,8 @@ class _HelperDialogState extends State<HelperDialog> {
Text('ヘルプ'), Text('ヘルプ'),
], ],
), ),
content: Column( content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -42,11 +43,17 @@ class _HelperDialogState extends State<HelperDialog> {
}); });
}, },
), ),
const Text('この画面を二度と表示しない'), const Flexible(
child: Text(
'この画面を二度と表示しない',
overflow: TextOverflow.ellipsis,
),
),
], ],
), ),
], ],
), ),
),
actions: [ actions: [
TextButton( TextButton(
child: const Text('OK'), child: const Text('OK'),
@ -68,6 +75,9 @@ Future<void> showHelperDialog(String message, String screenKey) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final showHelper = prefs.getBool('helper_$screenKey') ?? true; final showHelper = prefs.getBool('helper_$screenKey') ?? true;
if (showHelper) { if (showHelper) {
Get.dialog(HelperDialog(message: message, screenKey: screenKey)); Get.dialog(
HelperDialog(message: message, screenKey: screenKey),
barrierDismissible: false,
);
} }
} }

View File

@ -52,6 +52,12 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
// 追加
WidgetsBinding.instance.addPostFrameCallback((_) {
PermissionController.checkAndRequestPermissions();
});
debugPrint('MapWidget: initState called'); debugPrint('MapWidget: initState called');
SettingsBinding().dependencies(); // これを追加 SettingsBinding().dependencies(); // これを追加
_startIdleTimer(); _startIdleTimer();
@ -84,6 +90,23 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
mapResetController.resetIdleTimer = _resetIdleTimer; mapResetController.resetIdleTimer = _resetIdleTimer;
Get.put(mapResetController); Get.put(mapResetController);
// Add this debug subscription
subscription = locationController.locationMarkerPositionStreamController.stream.listen(
(LocationMarkerPosition? position) {
if (position != null) {
//debugPrint('Location update received: lat=${position.latitude}, lon=${position.longitude}');
} else {
debugPrint('Received null location update');
}
},
onError: (error) {
debugPrint('Error in location stream: $error');
},
onDone: () {
debugPrint('Location stream closed');
},
);
// indexController.mapController = MapController(initCompleter: mapControllerCompleter); // indexController.mapController = MapController(initCompleter: mapControllerCompleter);
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gifunavi/pages/permission/permission.dart'; import 'package:gifunavi/pages/permission/permission.dart';
import 'package:get/get.dart';
class PermissionHandlerScreen extends StatefulWidget { class PermissionHandlerScreen extends StatefulWidget {
const PermissionHandlerScreen({super.key}); const PermissionHandlerScreen({super.key});
@ -24,8 +25,14 @@ class _PermissionHandlerScreenState extends State<PermissionHandlerScreen> {
appBar: AppBar( appBar: AppBar(
title: const Text('権限の確認'), title: const Text('権限の確認'),
), ),
body: const Center( body: Center(
child: Text('権限の確認中...'), child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('権限の確認中...'),
const SizedBox(height: 20),
],
),
), ),
); );
} }

View File

@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation import Foundation
import connectivity_plus import connectivity_plus
import device_info_plus
import file_selector_macos import file_selector_macos
import geolocator_apple import geolocator_apple
import package_info_plus import package_info_plus
@ -17,6 +18,7 @@ import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))

View File

@ -185,6 +185,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.10" version: "0.7.10"
device_info_plus:
dependency: "direct main"
description:
name: device_info_plus
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
url: "https://pub.dev"
source: hosted
version: "10.1.2"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba"
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -802,7 +818,7 @@ packages:
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
path: path:
dependency: transitive dependency: "direct main"
description: description:
name: path name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
@ -1314,10 +1330,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.2.4" version: "14.2.5"
web: web:
dependency: transitive dependency: transitive
description: description:
@ -1366,6 +1382,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.5.4" version: "5.5.4"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6"
url: "https://pub.dev"
source: hosted
version: "1.1.4"
wkt_parser: wkt_parser:
dependency: transitive dependency: transitive
description: description:

View File

@ -44,6 +44,9 @@ dependencies:
# rules and activating additional ones. # rules and activating additional ones.
flutter_lints: ^4.0.0 flutter_lints: ^4.0.0
path: ^1.8.0
device_info_plus: #^8.0.0
win32: ^5.5.3 win32: ^5.5.3
sqflite: ^2.0.1 sqflite: ^2.0.1
get: ^4.6.6 get: ^4.6.6

BIN
テスト端末.xlsx Normal file

Binary file not shown.