Fixed Location & Storage issues
This commit is contained in:
@ -218,45 +218,53 @@ class CameraPage extends StatelessWidget {
|
||||
// 要修正:エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。
|
||||
//
|
||||
Widget getAction(BuildContext context) {
|
||||
|
||||
//print("----cccheckin is --- ${dbDest?.checkedin} ----");
|
||||
|
||||
if (manulaCheckin == true) {
|
||||
// manulaCheckinがtrueの場合は、撮影ボタンとチェックインボタンを表示します。
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Wrap(
|
||||
spacing: 16.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
Obx(() => ElevatedButton(
|
||||
onPressed: () {
|
||||
destinationController.openCamera(context, destination);
|
||||
},
|
||||
child: const Text("撮影")),
|
||||
Obx(() => destinationController.photos.isNotEmpty
|
||||
? ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||
onPressed: () async {
|
||||
await destinationController.makeCheckin(destination, true,
|
||||
destinationController.photos[0].path);
|
||||
//Get.back();
|
||||
destinationController.rogainingCounted.value = true;
|
||||
destinationController.skipGps = false;
|
||||
destinationController.isPhotoShoot.value = false;
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
padding: const EdgeInsets.all(20),
|
||||
backgroundColor: destinationController.photos.isEmpty
|
||||
? Colors.red
|
||||
: Colors.grey[300],
|
||||
),
|
||||
child: destinationController.photos.isEmpty
|
||||
? const Text("撮影", style: TextStyle(color: Colors.white))
|
||||
: const Text("再撮影", style: TextStyle(color: Colors.black)),
|
||||
)),
|
||||
Obx(() => destinationController.photos.isNotEmpty
|
||||
? ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
|
||||
onPressed: () async {
|
||||
await destinationController.makeCheckin(destination, true,
|
||||
destinationController.photos[0].path);
|
||||
destinationController.rogainingCounted.value = true;
|
||||
destinationController.skipGps = false;
|
||||
destinationController.isPhotoShoot.value = false;
|
||||
|
||||
Get.snackbar("チェックインしました。",
|
||||
"${destination.sub_loc_id} : ${destination.name}",
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
duration: const Duration(seconds: 2), // 表示時間を1秒に設定
|
||||
);
|
||||
// SnackBarの表示が終了するのを待ってからCameraPageを閉じる
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
Get.snackbar("チェックインしました。",
|
||||
"${destination.sub_loc_id} : ${destination.name}",
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
Navigator.of(context).pop(true); // ここを修正
|
||||
},
|
||||
child: const Text("チェックイン"))
|
||||
: Container())
|
||||
],
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: const Text("チェックイン", style: TextStyle(color: Colors.white)),
|
||||
)
|
||||
: Container())
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -610,6 +618,114 @@ class BuyPointCamera extends StatelessWidget {
|
||||
|
||||
Destination destination;
|
||||
|
||||
DestinationController destinationController =
|
||||
Get.find<DestinationController>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
"${destination.sub_loc_id} : ${destination.name}",
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
child: Obx(
|
||||
() => Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 370,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
// 要修正:getReceiptImage関数の戻り値がnullの場合のエラーハンドリングが不十分です。適切なデフォルト画像を表示するなどの処理を追加してください。
|
||||
//
|
||||
image: getReceiptImage(), fit: BoxFit.cover)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(getTagText(true, destination.tags)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Wrap(
|
||||
spacing: 16.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
Obx(() => ElevatedButton(
|
||||
onPressed: () {
|
||||
destinationController.openCamera(context, destination);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
padding: const EdgeInsets.all(20),
|
||||
backgroundColor: destinationController.photos.isEmpty
|
||||
? Colors.red
|
||||
: Colors.grey[300],
|
||||
),
|
||||
child: destinationController.photos.isEmpty
|
||||
? const Text("撮影",
|
||||
style: TextStyle(color: Colors.white))
|
||||
: const Text("再撮影",
|
||||
style: TextStyle(color: Colors.black)),
|
||||
)),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await destinationController.cancelBuyPoint(destination);
|
||||
Navigator.of(Get.context!).pop();
|
||||
destinationController.rogainingCounted.value = true;
|
||||
destinationController.skipGps = false;
|
||||
destinationController.isPhotoShoot.value = false;
|
||||
},
|
||||
child: const Text("買い物なし")),
|
||||
Obx(() => destinationController.photos.isNotEmpty
|
||||
? ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red),
|
||||
onPressed: () async {
|
||||
await destinationController.makeBuyPoint(
|
||||
destination,
|
||||
destinationController.photos[0].path);
|
||||
Get.back();
|
||||
destinationController.rogainingCounted.value = true;
|
||||
destinationController.skipGps = false;
|
||||
destinationController.isPhotoShoot.value = false;
|
||||
Get.snackbar("お買い物加点を行いました",
|
||||
"${destination.sub_loc_id} : ${destination.name}",
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white);
|
||||
},
|
||||
child: const Text("完了",
|
||||
style: TextStyle(color: Colors.white)))
|
||||
: Container())
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
class BuyPointCamera extends StatelessWidget {
|
||||
BuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
||||
|
||||
Destination destination;
|
||||
|
||||
DestinationController destinationController =
|
||||
Get.find<DestinationController>();
|
||||
|
||||
@ -725,7 +841,7 @@ class BuyPointCamera extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
class QRCodeScannerPage extends StatefulWidget {
|
||||
|
||||
|
||||
163
lib/pages/camera/custom_camera_view.dart
Normal file
163
lib/pages/camera/custom_camera_view.dart
Normal file
@ -0,0 +1,163 @@
|
||||
import 'dart:io';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class CustomCameraView extends StatefulWidget {
|
||||
final Function(String) onImageCaptured;
|
||||
|
||||
const CustomCameraView({Key? key, required this.onImageCaptured}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CustomCameraViewState createState() => _CustomCameraViewState();
|
||||
}
|
||||
|
||||
class _CustomCameraViewState extends State<CustomCameraView> {
|
||||
CameraController? _controller;
|
||||
late List<CameraDescription> _cameras;
|
||||
int _selectedCameraIndex = 0;
|
||||
double _currentScale = 1.0;
|
||||
FlashMode _currentFlashMode = FlashMode.off;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeCamera();
|
||||
}
|
||||
|
||||
Future<void> _initializeCamera() async {
|
||||
_cameras = await availableCameras();
|
||||
_controller = CameraController(_cameras[_selectedCameraIndex], ResolutionPreset.medium);
|
||||
await _controller!.initialize();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _toggleCameraLens() async {
|
||||
final newIndex = (_selectedCameraIndex + 1) % _cameras.length;
|
||||
await _controller!.dispose();
|
||||
|
||||
setState(() {
|
||||
_controller = null;
|
||||
_selectedCameraIndex = newIndex;
|
||||
});
|
||||
|
||||
_controller = CameraController(_cameras[_selectedCameraIndex], ResolutionPreset.medium);
|
||||
await _controller!.initialize();
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _toggleFlashMode() {
|
||||
setState(() {
|
||||
_currentFlashMode = (_currentFlashMode == FlashMode.off) ? FlashMode.torch : FlashMode.off;
|
||||
});
|
||||
_controller!.setFlashMode(_currentFlashMode);
|
||||
}
|
||||
|
||||
void _zoomIn() {
|
||||
setState(() {
|
||||
_currentScale += 0.1;
|
||||
if (_currentScale > 5.0) _currentScale = 5.0;
|
||||
});
|
||||
_controller!.setZoomLevel(_currentScale);
|
||||
}
|
||||
|
||||
void _zoomOut() {
|
||||
setState(() {
|
||||
_currentScale -= 0.1;
|
||||
if (_currentScale < 1.0) _currentScale = 1.0;
|
||||
});
|
||||
_controller!.setZoomLevel(_currentScale);
|
||||
}
|
||||
|
||||
void _captureImage() async {
|
||||
if (_controller!.value.isInitialized) {
|
||||
final Directory appDirectory = await getApplicationDocumentsDirectory();
|
||||
final String imagePath = path.join(appDirectory.path, '${DateTime.now()}.jpg');
|
||||
|
||||
final XFile imageFile = await _controller!.takePicture();
|
||||
await imageFile.saveTo(imagePath);
|
||||
|
||||
widget.onImageCaptured(imagePath);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_controller == null || !_controller!.value.isInitialized) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
CameraPreview(_controller!),
|
||||
Positioned(
|
||||
bottom: 16.0,
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _toggleFlashMode,
|
||||
icon: Icon(
|
||||
(_currentFlashMode == FlashMode.off) ? Icons.flash_off : Icons.flash_on,
|
||||
color: Colors.white,
|
||||
),
|
||||
iconSize: 32,
|
||||
color: Colors.orange,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: _captureImage,
|
||||
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: _toggleCameraLens,
|
||||
icon: const Icon(Icons.flip_camera_ios, color: Colors.white),
|
||||
iconSize: 32,
|
||||
color: Colors.blue,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 16.0,
|
||||
right: 16.0,
|
||||
child: Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _zoomIn,
|
||||
icon: const Icon(Icons.zoom_in, color: Colors.white),
|
||||
iconSize: 32,
|
||||
color: Colors.green,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _zoomOut,
|
||||
icon: const Icon(Icons.zoom_out, color: Colors.white),
|
||||
iconSize: 32,
|
||||
color: Colors.green,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ import 'package:rogapp/main.dart';
|
||||
import 'package:rogapp/model/destination.dart';
|
||||
import 'package:rogapp/model/gps_data.dart';
|
||||
import 'package:rogapp/pages/camera/camera_page.dart';
|
||||
import 'package:rogapp/pages/camera/custom_camera_view.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/services/DatabaseService.dart';
|
||||
@ -119,6 +120,7 @@ class DestinationController extends GetxController {
|
||||
|
||||
var isCheckingIn = false.obs; // チェックイン操作中はtrueになり、重複してポップアップが出ないようにするもの。
|
||||
|
||||
var isRouteShowing = false.obs; // ルートが表示されているかどうかを示すReactive変数
|
||||
/*
|
||||
//==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5
|
||||
//
|
||||
@ -159,10 +161,18 @@ class DestinationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//==== Akira .. GPS信号シミュレーション用 ======= ここまで
|
||||
*/
|
||||
|
||||
// ルートをクリアする関数です。
|
||||
void clearRoute() {
|
||||
indexController.routePoints.clear();
|
||||
indexController.routePointLenght.value = 0;
|
||||
isRouteShowing.value = false;
|
||||
}
|
||||
|
||||
void showGPSDataNotReceivedPopup() {
|
||||
if (Get.context != null) {
|
||||
Get.dialog(
|
||||
@ -750,6 +760,12 @@ class DestinationController extends GetxController {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => CustomCameraView(
|
||||
onImageCaptured: (imagePath) {
|
||||
photos.add(File(imagePath));
|
||||
},
|
||||
),
|
||||
/*
|
||||
builder: (_) => CameraCamera(
|
||||
resolutionPreset: ResolutionPreset.medium,
|
||||
onFile: (file) {
|
||||
@ -758,7 +774,10 @@ class DestinationController extends GetxController {
|
||||
//print("----image file is : $file----");
|
||||
//setState(() {});
|
||||
},
|
||||
)));
|
||||
)
|
||||
*/
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ルートポイントを取得する関数です。
|
||||
@ -1075,6 +1094,12 @@ class DestinationController extends GetxController {
|
||||
}
|
||||
|
||||
Future<void> _saveImageToGallery(String imagePath) async {
|
||||
final status = await PermissionController.checkStoragePermission();
|
||||
if(!status){
|
||||
await PermissionController.requestStoragePermission();
|
||||
}
|
||||
|
||||
/*
|
||||
final status = await Permission.storage.status;
|
||||
if (!status.isGranted) {
|
||||
final result = await Permission.storage.request();
|
||||
@ -1105,9 +1130,11 @@ class DestinationController extends GetxController {
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
try {
|
||||
final result = await ImageGallerySaver.saveFile(imagePath);
|
||||
@ -1873,11 +1900,13 @@ class DestinationController extends GetxController {
|
||||
//Get.toNamed(AppPages.TRAVEL);
|
||||
});
|
||||
destinationCount.value = destinations.length;
|
||||
|
||||
} catch (_) {
|
||||
skipGps = false;
|
||||
return;
|
||||
} finally {
|
||||
//Get.back();
|
||||
isRouteShowing.value = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import 'dart:developer' as developer;
|
||||
|
||||
class PermissionController {
|
||||
|
||||
/*
|
||||
static Future<bool> checkLocationPermissions() async {
|
||||
debugPrint("(gifunavi)== checkLocationPermissions ==");
|
||||
final alwaysPermission = await Permission.locationAlways.status;
|
||||
@ -16,6 +17,29 @@ class PermissionController {
|
||||
|
||||
return (alwaysPermission == PermissionStatus.granted || whenInUsePermission == PermissionStatus.granted) &&
|
||||
(locationPermission == PermissionStatus.granted);
|
||||
}
|
||||
*/
|
||||
|
||||
static Future<bool> checkStoragePermission() async {
|
||||
debugPrint("(gifunavi)== checkStoragePermission ==");
|
||||
final storagePermission = await Permission.storage.status;
|
||||
return storagePermission == PermissionStatus.granted;
|
||||
}
|
||||
|
||||
static Future<void> requestStoragePermission() async {
|
||||
debugPrint("(gifunavi)== requestStoragePermission ==");
|
||||
final storagePermission = await Permission.storage.request();
|
||||
|
||||
if (storagePermission == PermissionStatus.granted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (storagePermission == PermissionStatus.permanentlyDenied) {
|
||||
// リクエストが完了するまで待機
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
showPermissionDeniedDialog('storage_permission_needed_title','storage_permission_needed_main');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static Future<bool> checkLocationBasicPermission() async {
|
||||
@ -45,7 +69,7 @@ class PermissionController {
|
||||
final locationStatus = await Permission.location.request();
|
||||
|
||||
if (locationStatus != PermissionStatus.granted) {
|
||||
showPermissionDeniedDialog();
|
||||
showPermissionDeniedDialog('location_permission_needed_title','location_permission_needed_main');
|
||||
}
|
||||
}
|
||||
}catch (e, stackTrace){
|
||||
@ -68,7 +92,7 @@ class PermissionController {
|
||||
final whenInUseStatus = await Permission.locationWhenInUse.request();
|
||||
|
||||
if (whenInUseStatus != PermissionStatus.granted) {
|
||||
showPermissionDeniedDialog();
|
||||
showPermissionDeniedDialog('location_permission_needed_title','location_permission_needed_main');
|
||||
}else{
|
||||
if( !isLocationServiceRunning ){
|
||||
isLocationServiceRunning=true;
|
||||
@ -100,7 +124,7 @@ class PermissionController {
|
||||
final alwaysStatus = await Permission.locationAlways.request();
|
||||
|
||||
if (alwaysStatus != PermissionStatus.granted) {
|
||||
showPermissionDeniedDialog();
|
||||
showPermissionDeniedDialog('location_permission_needed_title','location_permission_needed_main');
|
||||
}
|
||||
}
|
||||
}catch (e, stackTrace){
|
||||
@ -128,12 +152,14 @@ class PermissionController {
|
||||
}
|
||||
}
|
||||
|
||||
static void showPermissionDeniedDialog() {
|
||||
static void showPermissionDeniedDialog(String title,String message) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('location_permission_needed_title'.tr),
|
||||
//title: Text('location_permission_needed_title'.tr),
|
||||
title: Text(title.tr),
|
||||
// 位置情報への許可が必要です
|
||||
content: Text('location_permission_needed_main'.tr),
|
||||
//content: Text('location_permission_needed_main'.tr),
|
||||
content: Text(message.tr),
|
||||
// 岐阜ロゲでは、位置情報を使用してスタート・チェックイン・ゴール等の通過照明及び移動手段の記録のために、位置情報のトラッキングを行なっています。このためバックグラウンドでもトラッキングができるように位置情報の権限が必要です。
|
||||
// 設定画面で、「岐阜ナビ」に対して、常に位置情報を許可するように設定してください。
|
||||
actions: [
|
||||
|
||||
Reference in New Issue
Block a user