Android のバックグラウンドGPSを組み込み
This commit is contained in:
@ -94,4 +94,5 @@ flutter {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
implementation 'com.google.android.gms:play-services-location:18.0.0' // このバージョンは最新のものにしてください
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.rogapp">
|
package="com.dvox.gifunavi">
|
||||||
<!-- Flutter needs it to communicate with the running application
|
<!-- Flutter needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.example.rogapp">
|
package="com.dvox.gifunavi">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<meta-data android:name="com.google.android.geo.API_KEY"
|
<meta-data android:name="com.google.android.geo.API_KEY"
|
||||||
android:value="AIzaSyAUBI1ablMKuJwGj2-kSuEhvYxvB1A-mOE"/>
|
android:value="AIzaSyAUBI1ablMKuJwGj2-kSuEhvYxvB1A-mOE"/>
|
||||||
<service
|
<service
|
||||||
android:name="com.example.rogapp.LocationService"
|
android:name="com.dvox.gifunavi.LocationService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:foregroundServiceType="location" />
|
android:foregroundServiceType="location" />
|
||||||
|
|||||||
241
android/app/src/main/kotlin/com/dvox/gifunavi/LocationService.kt
Normal file
241
android/app/src/main/kotlin/com/dvox/gifunavi/LocationService.kt
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
package com.dvox.gifunavi // この部分を変更
|
||||||
|
|
||||||
|
import android.location.Location
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.location.LocationManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.google.android.gms.location.FusedLocationProviderClient
|
||||||
|
import com.google.android.gms.location.LocationCallback
|
||||||
|
import com.google.android.gms.location.LocationRequest
|
||||||
|
import com.google.android.gms.location.LocationResult
|
||||||
|
import com.google.android.gms.location.LocationServices
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import android.app.Notification
|
||||||
|
|
||||||
|
data class GpsData(
|
||||||
|
val id: Int,
|
||||||
|
val team_name: String,
|
||||||
|
val event_code: String,
|
||||||
|
val lat: Double,
|
||||||
|
val lon: Double,
|
||||||
|
val is_checkin: Int,
|
||||||
|
val created_at: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
class GpsDatabaseHelper(private val context: Context) {
|
||||||
|
fun insertGps(gpsData: GpsData) {
|
||||||
|
Log.d("LocationService", "Android: insertGps.")
|
||||||
|
|
||||||
|
// ここにデータベースへの挿入処理を実装する
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getInstance(context: Context): GpsDatabaseHelper {
|
||||||
|
Log.d("LocationService", "Android: GpsDatabaseHelper.")
|
||||||
|
return GpsDatabaseHelper(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocationService : Service() {
|
||||||
|
private lateinit var fusedLocationClient: FusedLocationProviderClient
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
Log.d("LocationService", "Android: onCreate.")
|
||||||
|
|
||||||
|
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
|
||||||
|
|
||||||
|
// GPSデバイスが有効になっているか確認する
|
||||||
|
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||||
|
Log.d("LocationService", "GPS is disabled.")
|
||||||
|
// GPSが無効の場合の処理を追加する(例: ユーザーにGPSを有効にするように促すなど)
|
||||||
|
}else{
|
||||||
|
Log.d("LocationService", "GPS is enabled.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// フォアグラウンドサービスの設定
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val CHANNEL_ID = "location"
|
||||||
|
val channel = NotificationChannel(CHANNEL_ID, "Location", NotificationManager.IMPORTANCE_DEFAULT)
|
||||||
|
val notificationManager = getSystemService(NotificationManager::class.java) // この行を追加
|
||||||
|
|
||||||
|
Log.d("LocationService", "Android: Foreground service.")
|
||||||
|
|
||||||
|
notificationManager?.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
val notification = NotificationCompat.Builder(this, "location")
|
||||||
|
.setContentTitle("Tracking location...")
|
||||||
|
.setContentText("Location: null")
|
||||||
|
.setSmallIcon(android.R.drawable.ic_menu_mylocation) // リソースが存在しないため0を設定
|
||||||
|
.setOngoing(true)
|
||||||
|
.build()
|
||||||
|
Log.d("LocationService", "Android: Foreground service 2.")
|
||||||
|
|
||||||
|
startForeground(1, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
Log.d("LocationService", "Android: onDestroy.")
|
||||||
|
fusedLocationClient.removeLocationUpdates(locationCallback)
|
||||||
|
stopForeground(true)
|
||||||
|
stopSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val NOTIFICATION_ID = 1
|
||||||
|
private const val CHANNEL_ID = "location_service_channel"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNotification(): Notification {
|
||||||
|
Log.d("LocationService", "Android: createNotification Notification.")
|
||||||
|
val notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||||
|
.setContentTitle("Location Service")
|
||||||
|
.setContentText("Running...")
|
||||||
|
.setSmallIcon(android.R.drawable.ic_menu_mylocation)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val channelName = "Location Service Channel"
|
||||||
|
val channelDescription = "Channel for Location Service"
|
||||||
|
val importance = NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
val channel = NotificationChannel(CHANNEL_ID, channelName, importance).apply {
|
||||||
|
description = channelDescription
|
||||||
|
}
|
||||||
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return notificationBuilder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNotificationChannel() {
|
||||||
|
Log.d("LocationService", "Android: createNotificationChannel.")
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val channel = NotificationChannel(
|
||||||
|
CHANNEL_ID,
|
||||||
|
"Location Service Channel",
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
)
|
||||||
|
val manager = getSystemService(NotificationManager::class.java)
|
||||||
|
manager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var lastLocation: Location? = null
|
||||||
|
|
||||||
|
private val locationCallback = object : LocationCallback() {
|
||||||
|
override fun onLocationResult(locationResult: LocationResult) {
|
||||||
|
val currentLocation = locationResult.lastLocation
|
||||||
|
//Log.d("LocationService", "Android: onLocationResult.")
|
||||||
|
|
||||||
|
if (currentLocation != null) {
|
||||||
|
val accuracy = currentLocation.accuracy
|
||||||
|
if (accuracy <= 30) {
|
||||||
|
var lastLocation = lastLocation
|
||||||
|
if (lastLocation == null || currentLocation.distanceTo(lastLocation) >= 10) {
|
||||||
|
val lat = currentLocation.latitude
|
||||||
|
val lon = currentLocation.longitude
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
|
||||||
|
// GPS データをデバッグ用に表示
|
||||||
|
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
|
||||||
|
val formattedTime = sdf.format(Date(currentTime))
|
||||||
|
//Log.d("LocationService", "Latitude: $lat, Longitude: $lon, Time: $formattedTime, Accuracy: $accuracy")
|
||||||
|
|
||||||
|
// GPS データをデータベースに保存
|
||||||
|
GlobalScope.launch {
|
||||||
|
addGPStoDB(lat, lon, currentTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLocation = currentLocation
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("LocationService", "Android: GPS accuracy is above 30m. Skipping data saving.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("LocationService", "Android: No GPS signal received.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
Log.d("LocationService", "Android: onStartCommand.")
|
||||||
|
|
||||||
|
// 位置情報の権限チェックとGPS有効化の確認を行う
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||||
|
val locationRequest = LocationRequest.create().apply {
|
||||||
|
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
|
||||||
|
interval = 10000
|
||||||
|
fastestInterval = 5000
|
||||||
|
}
|
||||||
|
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
|
||||||
|
} else {
|
||||||
|
Log.d("LocationService", "GPS is disabled.")
|
||||||
|
// GPSが無効の場合の処理を追加する(例: ユーザーにGPSを有効にするように促すなど)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("LocationService", "Location permission is not granted.")
|
||||||
|
// 位置情報の権限が許可されていない場合の処理を追加する
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foregroundサービスを開始
|
||||||
|
startForeground(NOTIFICATION_ID, createNotification())
|
||||||
|
|
||||||
|
return START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun addGPStoDB(lat: Double, lng: Double, timestamp: Long, isCheckin: Int = 0) {
|
||||||
|
try {
|
||||||
|
val context = applicationContext
|
||||||
|
val preferences = context.getSharedPreferences("RogPreferences", Context.MODE_PRIVATE)
|
||||||
|
val teamName = preferences.getString("team_name", "") ?: ""
|
||||||
|
val eventCode = preferences.getString("event_code", "") ?: ""
|
||||||
|
|
||||||
|
|
||||||
|
if (teamName.isNotEmpty() && eventCode.isNotEmpty()) {
|
||||||
|
val gpsData = GpsData(
|
||||||
|
id = 0,
|
||||||
|
team_name = teamName,
|
||||||
|
event_code = eventCode,
|
||||||
|
lat = lat,
|
||||||
|
lon = lng,
|
||||||
|
is_checkin = isCheckin,
|
||||||
|
created_at = timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
val db = GpsDatabaseHelper.getInstance(context)
|
||||||
|
db.insertGps(gpsData)
|
||||||
|
Log.d("LocationService", "Android: addGPStoDB.")
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("LocationService", "Error adding GPS data to DB", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt
Normal file
114
android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package com.dvox.gifunavi // この部分を変更
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
private val CHANNEL = "location"
|
||||||
|
|
||||||
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
|
||||||
|
call, result ->
|
||||||
|
when (call.method) {
|
||||||
|
"startLocationService" -> {
|
||||||
|
Log.d("MainActivity", "Android: called startLocationService.")
|
||||||
|
//val intent = Intent(this, LocationService::class.java)
|
||||||
|
startLocationService()
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
"stopLocationService" -> {
|
||||||
|
Log.d("MainActivity", "Android: called stopLocationService.")
|
||||||
|
//val intent = Intent(this, LocationService::class.java)
|
||||||
|
stopLocationService()
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Log.d("MainActivity", "Android: onCreate.")
|
||||||
|
|
||||||
|
// 位置情報の権限をリクエストする
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_CODE)
|
||||||
|
} else {
|
||||||
|
// startLocationService() // アプリ起動時にLocationServiceを開始する ==> main.dartで制御する。
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
if (requestCode == PERMISSION_REQUEST_CODE) {
|
||||||
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// startLocationService()
|
||||||
|
|
||||||
|
Log.d("MainActivity", "Android: PERMISSION_GRANTED.")
|
||||||
|
} else {
|
||||||
|
// 位置情報の権限が拒否された場合の処理
|
||||||
|
Toast.makeText(this, "Location permission denied.", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PERMISSION_REQUEST_CODE = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startLocationService() {
|
||||||
|
Log.d("MainActivity", "Android: startLocationService.")
|
||||||
|
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
if (!isServiceRunning(LocationService::class.java)) {
|
||||||
|
val intent = Intent(this, LocationService::class.java)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
Log.d("MainActivity", "startForegroundService")
|
||||||
|
startForegroundService(intent)
|
||||||
|
} else {
|
||||||
|
Log.d("MainActivity", "startService")
|
||||||
|
startService(intent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("MainActivity", "Location service is already running.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d("MainActivity", "Location permission is not granted.")
|
||||||
|
// 位置情報の権限が許可されていない場合の処理を追加する
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLocationService() {
|
||||||
|
Log.d("MainActivity", "Android: stopLocationService.")
|
||||||
|
val intent = Intent(this, LocationService::class.java)
|
||||||
|
stopService(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isServiceRunning(serviceClass: Class<*>): Boolean {
|
||||||
|
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||||
|
for (service in manager.getRunningServices(Int.MAX_VALUE)) {
|
||||||
|
if (serviceClass.name == service.service.className) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,98 +0,0 @@
|
|||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
|
|
||||||
class LocationService : Service() {
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
// フォアグラウンドサービスの設定
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val channel = NotificationChannel(
|
|
||||||
"location",
|
|
||||||
"Location",
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
)
|
|
||||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
notificationManager.createNotificationChannel(channel)
|
|
||||||
}
|
|
||||||
val notification = NotificationCompat.Builder(this, "location")
|
|
||||||
.setContentTitle("Tracking location...")
|
|
||||||
.setContentText("Location: null")
|
|
||||||
.setSmallIcon(R.drawable.ic_launcher)
|
|
||||||
.setOngoing(true)
|
|
||||||
.build()
|
|
||||||
startForeground(1, notification)
|
|
||||||
}
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
// バックグラウンドでの位置情報取得処理を実装
|
|
||||||
val locationSettings = LocationSettings.Builder()
|
|
||||||
.setAccuracy(LocationAccuracy.HIGH)
|
|
||||||
.setDistanceFilter(100.0f)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
try {
|
|
||||||
Geolocator.getPositionStream(locationSettings)
|
|
||||||
.catch { e ->
|
|
||||||
// エラーハンドリング
|
|
||||||
println("Location Error: $e")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
.filterNotNull()
|
|
||||||
.filter { position ->
|
|
||||||
// GPS信号がlow以上の場合のみ記録
|
|
||||||
position.accuracy <= 100
|
|
||||||
}
|
|
||||||
.onEach { position ->
|
|
||||||
val lat = position.latitude
|
|
||||||
val lng = position.longitude
|
|
||||||
val timestamp = System.currentTimeMillis()
|
|
||||||
|
|
||||||
// データベースに位置情報を保存
|
|
||||||
addGPStoDB(lat, lng, timestamp)
|
|
||||||
}
|
|
||||||
.launchIn(CoroutineScope(Dispatchers.IO))
|
|
||||||
} catch (e: Exception) {
|
|
||||||
println("Error starting background tracking: $e")
|
|
||||||
}
|
|
||||||
|
|
||||||
return START_STICKY
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addGPStoDB(lat: Double, lng: Double, isCheckin: Int = 0) {
|
|
||||||
try {
|
|
||||||
val context = applicationContext
|
|
||||||
val preferences = context.getSharedPreferences("RogPreferences", Context.MODE_PRIVATE)
|
|
||||||
val teamName = preferences.getString("team_name", "") ?: ""
|
|
||||||
val eventCode = preferences.getString("event_code", "") ?: ""
|
|
||||||
|
|
||||||
if (teamName.isNotEmpty() && eventCode.isNotEmpty()) {
|
|
||||||
val gpsData = GpsData(
|
|
||||||
id = 0,
|
|
||||||
team_name = teamName,
|
|
||||||
event_code = eventCode,
|
|
||||||
lat = lat,
|
|
||||||
lon = lng,
|
|
||||||
is_checkin = isCheckin,
|
|
||||||
created_at = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
|
|
||||||
GlobalScope.launch {
|
|
||||||
val db = GpsDatabaseHelper.getInstance(context)
|
|
||||||
db.insertGps(gpsData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
println("Error adding GPS data to DB: $e")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
package com.example.rogapp
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull
|
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
|
||||||
import io.flutter.plugin.common.MethodChannel
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
|
||||||
private val CHANNEL = "location"
|
|
||||||
|
|
||||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
|
||||||
super.configureFlutterEngine(flutterEngine)
|
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
|
|
||||||
call, result ->
|
|
||||||
when (call.method) {
|
|
||||||
"startLocationService" -> {
|
|
||||||
val intent = Intent(this, LocationService::class.java)
|
|
||||||
startService(intent)
|
|
||||||
result.success(null)
|
|
||||||
}
|
|
||||||
"stopLocationService" -> {
|
|
||||||
val intent = Intent(this, LocationService::class.java)
|
|
||||||
stopService(intent)
|
|
||||||
result.success(null)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
result.notImplemented()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -5,3 +5,4 @@ RELEASE_STORE_FILE=release-key.keystore
|
|||||||
RELEASE_STORE_PASSWORD=SachikoMiyata123
|
RELEASE_STORE_PASSWORD=SachikoMiyata123
|
||||||
RELEASE_KEY_ALIAS=release_key
|
RELEASE_KEY_ALIAS=release_key
|
||||||
RELEASE_KEY_PASSWORD=SachikoMiyata123
|
RELEASE_KEY_PASSWORD=SachikoMiyata123
|
||||||
|
org.gradle.jvmargs=--add-opens=java.base/java.util=ALL-UNNAMED
|
||||||
@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
|
||||||
|
|||||||
Binary file not shown.
BIN
lib/build/ios/Pods.build/Release-iphonesimulator/FMDB.build/dgph
Normal file
BIN
lib/build/ios/Pods.build/Release-iphonesimulator/FMDB.build/dgph
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -52,7 +52,12 @@ void saveGameState() async {
|
|||||||
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}");
|
||||||
|
if(indexController.currentUser.isNotEmpty) {
|
||||||
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
|
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
|
||||||
|
}else{
|
||||||
|
debugPrint("User is empty....");
|
||||||
|
}
|
||||||
pref.setBool("is_in_rog", destinationController.isInRog.value);
|
pref.setBool("is_in_rog", destinationController.isInRog.value);
|
||||||
pref.setBool(
|
pref.setBool(
|
||||||
"rogaining_counted", destinationController.rogainingCounted.value);
|
"rogaining_counted", destinationController.rogainingCounted.value);
|
||||||
@ -79,6 +84,8 @@ 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'];
|
||||||
|
//debugPrint("savedUserId=${savedUserId}, currentUser=${currUserId}");
|
||||||
if (indexController.currentUser.isNotEmpty &&
|
if (indexController.currentUser.isNotEmpty &&
|
||||||
indexController.currentUser[0]["user"]["id"] == savedUserId) {
|
indexController.currentUser[0]["user"]["id"] == savedUserId) {
|
||||||
DestinationController destinationController =
|
DestinationController destinationController =
|
||||||
@ -127,11 +134,15 @@ void main() async {
|
|||||||
// startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810
|
// startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810
|
||||||
Get.put(SettingsController()); // これを追加
|
Get.put(SettingsController()); // これを追加
|
||||||
|
|
||||||
|
/*
|
||||||
runZonedGuarded(() {
|
runZonedGuarded(() {
|
||||||
runApp(const ProviderScope(child: MyApp()));
|
runApp(const ProviderScope(child: MyApp()));
|
||||||
}, (error, stackTrace) {
|
}, (error, stackTrace) {
|
||||||
ErrorService.reportError(error, stackTrace, deviceInfo);
|
ErrorService.reportError(error, stackTrace, deviceInfo);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
runApp(const ProviderScope(child: MyApp()));
|
||||||
|
|
||||||
//runApp(const MyApp());
|
//runApp(const MyApp());
|
||||||
}
|
}
|
||||||
@ -307,6 +318,11 @@ Future<void> stopBackgroundTracking() async {
|
|||||||
debugPrint("バックグラウンド処理:停止しました。");
|
debugPrint("バックグラウンド処理:停止しました。");
|
||||||
await positionStream?.cancel();
|
await positionStream?.cancel();
|
||||||
positionStream = null;
|
positionStream = null;
|
||||||
|
}else if(Platform.isAndroid && background==true){
|
||||||
|
background=false;
|
||||||
|
debugPrint("バックグラウンド処理:停止しました。");
|
||||||
|
await positionStream?.cancel();
|
||||||
|
positionStream = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,38 +370,54 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
// Get.find<DestinationController>();
|
// Get.find<DestinationController>();
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
|
// バックグラウンド処理を停止
|
||||||
|
if (Platform.isIOS && destinationController.isRunningBackgroundGPS) {
|
||||||
// Foreground に戻った時の処理
|
// Foreground に戻った時の処理
|
||||||
debugPrint(" ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました");
|
debugPrint(" ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました");
|
||||||
locationController.resumePositionStream();
|
locationController.resumePositionStream();
|
||||||
//print("RESUMED");
|
//print("RESUMED");
|
||||||
restoreGame();
|
restoreGame();
|
||||||
|
|
||||||
// バックグラウンド処理を停止
|
|
||||||
if (Platform.isIOS && destinationController.isRunningBackgroundGPS) {
|
|
||||||
stopBackgroundTracking();
|
stopBackgroundTracking();
|
||||||
destinationController.isRunningBackgroundGPS=false;
|
destinationController.isRunningBackgroundGPS=false;
|
||||||
destinationController.restartGPS();
|
destinationController.restartGPS();
|
||||||
|
|
||||||
} else if (Platform.isAndroid) {
|
} else if (Platform.isAndroid ) {
|
||||||
|
if( destinationController.isRunningBackgroundGPS ){
|
||||||
const platform = MethodChannel('location');
|
const platform = MethodChannel('location');
|
||||||
platform.invokeMethod('stopLocationService');
|
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:
|
||||||
// アプリが非アクティブになったときに発生します。
|
// アプリが非アクティブになったときに発生します。
|
||||||
|
|
||||||
|
if (Platform.isIOS && !destinationController.isRunningBackgroundGPS) { // iOSはバックグラウンドでもフロントの処理が生きている。
|
||||||
// これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。
|
// これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。
|
||||||
debugPrint(" ==(Status Changed)==> PAUSED. 非アクティブ処理。");
|
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
|
||||||
//locationController.resumePositionStream();
|
//locationController.resumePositionStream();
|
||||||
|
|
||||||
// 追加: フロントエンドのGPS信号のlistenを停止
|
// 追加: フロントエンドのGPS信号のlistenを停止
|
||||||
locationController.stopPositionStream();
|
locationController.stopPositionStream();
|
||||||
|
|
||||||
if (Platform.isIOS ) { // iOSはバックグラウンドでもフロントの処理が生きている。
|
|
||||||
destinationController.isRunningBackgroundGPS=true;
|
destinationController.isRunningBackgroundGPS=true;
|
||||||
startBackgroundTracking();
|
startBackgroundTracking();
|
||||||
}else if(Platform.isAndroid){
|
}else if(Platform.isAndroid && !destinationController.isRunningBackgroundGPS){
|
||||||
const platform = MethodChannel('location');
|
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
|
||||||
platform.invokeMethod('startLocationService');
|
}else{
|
||||||
|
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
|
||||||
|
|
||||||
}
|
}
|
||||||
saveGameState();
|
saveGameState();
|
||||||
break;
|
break;
|
||||||
@ -393,6 +425,19 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
// バックグラウンドに移行したときの処理
|
// バックグラウンドに移行したときの処理
|
||||||
//locationController.resumePositionStream();
|
//locationController.resumePositionStream();
|
||||||
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
|
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
|
||||||
|
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();
|
saveGameState();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.detached:
|
case AppLifecycleState.detached:
|
||||||
|
|||||||
@ -112,7 +112,7 @@ class DestinationController extends GetxController {
|
|||||||
DateTime lastGPSDataReceivedTime = DateTime.now();
|
DateTime lastGPSDataReceivedTime = DateTime.now();
|
||||||
DateTime lastPopupShownTime = DateTime.now().subtract(Duration(minutes: 10));
|
DateTime lastPopupShownTime = DateTime.now().subtract(Duration(minutes: 10));
|
||||||
bool isPopupShown = false;
|
bool isPopupShown = false;
|
||||||
bool hasReceivedGPSData = false;
|
bool hasReceivedGPSData = true;
|
||||||
|
|
||||||
var isCheckingIn = false.obs; // チェックイン操作中はtrueになり、重複してポップアップが出ないようにするもの。
|
var isCheckingIn = false.obs; // チェックイン操作中はtrueになり、重複してポップアップが出ないようにするもの。
|
||||||
|
|
||||||
@ -161,6 +161,7 @@ class DestinationController extends GetxController {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void showGPSDataNotReceivedPopup() {
|
void showGPSDataNotReceivedPopup() {
|
||||||
|
if (Get.context != null) {
|
||||||
Get.dialog(
|
Get.dialog(
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title: Text('GPS信号が受信できません'),
|
title: Text('GPS信号が受信できません'),
|
||||||
@ -173,13 +174,17 @@ class DestinationController extends GetxController {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// Get.contextがnullの場合の処理を追加
|
||||||
|
print('GPS signal not received, but context is null');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最後に有効なGPSデータを受け取ってから10分以上経過している場合にのみメッセージを表示するようにします。
|
// 最後に有効なGPSデータを受け取ってから10分以上経過している場合にのみメッセージを表示するようにします。
|
||||||
//
|
//
|
||||||
void checkGPSDataReceived() {
|
void checkGPSDataReceived() {
|
||||||
if (!hasReceivedGPSData) {
|
if (!hasReceivedGPSData) {
|
||||||
// GPS信号を全く受信していない。
|
//debugPrint("GPS信号を全く受信していない。");
|
||||||
if (!isPopupShown) {
|
if (!isPopupShown) {
|
||||||
// ポップアップしていない。
|
// ポップアップしていない。
|
||||||
showGPSDataNotReceivedPopup();
|
showGPSDataNotReceivedPopup();
|
||||||
@ -946,7 +951,7 @@ class DestinationController extends GetxController {
|
|||||||
is_checkin: isCheckin,
|
is_checkin: isCheckin,
|
||||||
created_at: DateTime.now().millisecondsSinceEpoch);
|
created_at: DateTime.now().millisecondsSinceEpoch);
|
||||||
var res = await db.insertGps(gps_data);
|
var res = await db.insertGps(gps_data);
|
||||||
//debugPrint("Saved GPS data into DB...");
|
debugPrint("Saved GPS data into DB...:${gps_data}");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
print("errr ready gps ${err}");
|
print("errr ready gps ${err}");
|
||||||
@ -1188,7 +1193,7 @@ class DestinationController extends GetxController {
|
|||||||
// ゲームを開始する関数です。
|
// ゲームを開始する関数です。
|
||||||
//
|
//
|
||||||
Future<void> startGame() async {
|
Future<void> startGame() async {
|
||||||
//print("------ starting game ------");
|
debugPrint("------ starting game ------");
|
||||||
if (game_started == false) {
|
if (game_started == false) {
|
||||||
await checkForCheckin();
|
await checkForCheckin();
|
||||||
}
|
}
|
||||||
@ -1206,6 +1211,7 @@ class DestinationController extends GetxController {
|
|||||||
//
|
//
|
||||||
bool inError=false;
|
bool inError=false;
|
||||||
bool isRunningBackgroundGPS=false;
|
bool isRunningBackgroundGPS=false;
|
||||||
|
int activeEngineCount = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() async {
|
||||||
@ -1215,6 +1221,8 @@ class DestinationController extends GetxController {
|
|||||||
|
|
||||||
// MapControllerの初期化完了を待機するフラグを設定
|
// MapControllerの初期化完了を待機するフラグを設定
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
//checkGPSDataReceived(); removed 2024-5-4
|
||||||
|
|
||||||
isMapControllerReady = true;
|
isMapControllerReady = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1280,7 +1288,7 @@ class DestinationController extends GetxController {
|
|||||||
|
|
||||||
startGame();
|
startGame();
|
||||||
|
|
||||||
checkGPSDataReceived();
|
//checkGPSDataReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
void restartGPS(){
|
void restartGPS(){
|
||||||
@ -1308,8 +1316,11 @@ class DestinationController extends GetxController {
|
|||||||
//
|
//
|
||||||
double prevLat = 0.0; // 直前の位置
|
double prevLat = 0.0; // 直前の位置
|
||||||
double prevLon = 0.0;
|
double prevLon = 0.0;
|
||||||
|
bool gpsDebugMode=false;
|
||||||
|
|
||||||
void handleLocationUpdate(LocationMarkerPosition? position) async {
|
void handleLocationUpdate(LocationMarkerPosition? position) async {
|
||||||
|
//debugPrint("DestinationController.handleLocationUpdate");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//final DestinationController destinationController = Get.find<DestinationController>();
|
//final DestinationController destinationController = Get.find<DestinationController>();
|
||||||
//final signalStrength = locationController.getGpsSignalStrength();
|
//final signalStrength = locationController.getGpsSignalStrength();
|
||||||
@ -1399,7 +1410,7 @@ class DestinationController extends GetxController {
|
|||||||
lastGPSCollectedTime = DateTime.now();
|
lastGPSCollectedTime = DateTime.now();
|
||||||
prevLat = position.latitude;
|
prevLat = position.latitude;
|
||||||
prevLon = position.longitude;
|
prevLon = position.longitude;
|
||||||
debugPrint("フロントエンドでのGPS保存(時間差:${difference.inSeconds}, 距離差:${distanceToDest}) : Time=${lastGPSCollectedTime}");
|
gpsDebugMode ? debugPrint("フロントエンドでのGPS保存(時間差:${difference.inSeconds}, 距離差:${distanceToDest}) : Time=${lastGPSCollectedTime}"):null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,7 @@ class IndexPage extends GetView<IndexController> {
|
|||||||
// タップすることでGPS信号の強弱をシミュレーションできるようにする
|
// タップすることでGPS信号の強弱をシミュレーションできるようにする
|
||||||
// Akira 2024-4-5
|
// Akira 2024-4-5
|
||||||
//
|
//
|
||||||
/*
|
/*
|
||||||
Obx(() {
|
Obx(() {
|
||||||
if (locationController.isSimulationMode) {
|
if (locationController.isSimulationMode) {
|
||||||
return DropdownButton<String>(
|
return DropdownButton<String>(
|
||||||
@ -122,34 +122,11 @@ class IndexPage extends GetView<IndexController> {
|
|||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return InkWell(
|
return Container();
|
||||||
onTap: () {
|
|
||||||
locationController.setSimulatedSignalStrength('high');
|
|
||||||
},
|
|
||||||
child: const Icon(Icons.info),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
*/
|
||||||
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
Obx(() => locationController.isSimulationMode
|
|
||||||
? DropdownButton<String>(
|
|
||||||
value: locationController.getSimulatedSignalStrength(),
|
|
||||||
onChanged: (value) {
|
|
||||||
locationController.setSimulatedSignalStrength(value!);
|
|
||||||
},
|
|
||||||
items: ['low', 'medium', 'high','real']
|
|
||||||
.map<DropdownMenuItem<String>>((String value) {
|
|
||||||
return DropdownMenuItem<String>(
|
|
||||||
value: value,
|
|
||||||
child: Text(value),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// bottomNavigationBar: BottomAppBar(
|
// bottomNavigationBar: BottomAppBar(
|
||||||
|
|||||||
@ -25,24 +25,10 @@ class LoginPage extends StatelessWidget {
|
|||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
),
|
),
|
||||||
/* 2024-04-03 Updated by Akira . See https://wiki.sumasen.net/issues/2796?issue_count=1&issue_position=1
|
|
||||||
appBar: AppBar(
|
|
||||||
elevation: 0,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
leading: IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_back_ios,
|
|
||||||
size: 20,
|
|
||||||
color: Colors.black,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
body: indexController.currentUser.isEmpty
|
body: indexController.currentUser.isEmpty
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -129,30 +115,6 @@ class LoginPage extends StatelessWidget {
|
|||||||
emailController.text,
|
emailController.text,
|
||||||
passwordController.text,
|
passwordController.text,
|
||||||
context);
|
context);
|
||||||
/*
|
|
||||||
try {
|
|
||||||
bool isLoggedIn = await indexController.login(emailController.text, passwordController.text,context);
|
|
||||||
if (isLoggedIn) {
|
|
||||||
Get.offAllNamed(AppPages.INDEX);
|
|
||||||
} else {
|
|
||||||
Get.snackbar(
|
|
||||||
"Login Failed",
|
|
||||||
"Invalid email or password",
|
|
||||||
icon: const Icon(Icons.error, color: Colors.red),
|
|
||||||
snackPosition: SnackPosition.BOTTOM,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
Get.snackbar(
|
|
||||||
"Error",
|
|
||||||
"An error occurred during login",
|
|
||||||
icon: const Icon(Icons.error, color: Colors.red),
|
|
||||||
snackPosition: SnackPosition.BOTTOM,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
indexController.isLoading.value = false;
|
|
||||||
}
|
|
||||||
*/ // ここまで
|
|
||||||
},
|
},
|
||||||
color: Colors.indigoAccent[400],
|
color: Colors.indigoAccent[400],
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@ -165,16 +127,6 @@ class LoginPage extends StatelessWidget {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Colors.white70),
|
color: Colors.white70),
|
||||||
),
|
),
|
||||||
/*
|
|
||||||
child: Obx(
|
|
||||||
() => indexController.isLoading.value
|
|
||||||
? const CircularProgressIndicator(color: Colors.white)
|
|
||||||
: const Text(
|
|
||||||
"ログイン",
|
|
||||||
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white70),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
*/ // ここまで
|
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 5.0,
|
height: 5.0,
|
||||||
@ -223,7 +175,7 @@ class LoginPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 5,
|
height: 3,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -280,6 +232,7 @@ class LoginPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
)
|
)
|
||||||
: TextButton(
|
: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|||||||
@ -7,6 +7,8 @@ import 'package:rogapp/pages/index/index_controller.dart';
|
|||||||
import 'package:rogapp/utils/const.dart';
|
import 'package:rogapp/utils/const.dart';
|
||||||
|
|
||||||
class LocationService {
|
class LocationService {
|
||||||
|
|
||||||
|
|
||||||
static Future<GeoJSONFeatureCollection?> loadLocationsFor(
|
static Future<GeoJSONFeatureCollection?> loadLocationsFor(
|
||||||
String perfecture, String cat) async {
|
String perfecture, String cat) async {
|
||||||
final IndexController indexController = Get.find<IndexController>();
|
final IndexController indexController = Get.find<IndexController>();
|
||||||
|
|||||||
@ -39,6 +39,8 @@ class LocationController extends GetxController {
|
|||||||
LatLng? lastValidLocation;
|
LatLng? lastValidLocation;
|
||||||
DateTime lastGPSDataReceivedTime = DateTime.now(); // 最後にGPSデータを受け取った時刻
|
DateTime lastGPSDataReceivedTime = DateTime.now(); // 最後にGPSデータを受け取った時刻
|
||||||
|
|
||||||
|
bool gpsDebugMode = false;
|
||||||
|
/*
|
||||||
// GPSシミュレーション用のメソッドを追加
|
// GPSシミュレーション用のメソッドを追加
|
||||||
void setSimulationMode(bool value) {
|
void setSimulationMode(bool value) {
|
||||||
isSimulationMode = value;
|
isSimulationMode = value;
|
||||||
@ -52,7 +54,7 @@ class LocationController extends GetxController {
|
|||||||
bool isSimulationMode = false;
|
bool isSimulationMode = false;
|
||||||
|
|
||||||
// GPS信号強度をシミュレートするための変数
|
// GPS信号強度をシミュレートするための変数
|
||||||
final Rx<String> _simulatedSignalStrength = Rx<String>('low');
|
final Rx<String> _simulatedSignalStrength = Rx<String>('high');
|
||||||
|
|
||||||
// GPS信号強度をシミュレートするための関数
|
// GPS信号強度をシミュレートするための関数
|
||||||
void setSimulatedSignalStrength(String strength) {
|
void setSimulatedSignalStrength(String strength) {
|
||||||
@ -72,31 +74,38 @@ class LocationController extends GetxController {
|
|||||||
return _simulatedSignalStrength.value;
|
return _simulatedSignalStrength.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
//
|
//
|
||||||
// ====== Akira , GPS信号強度をシミュレート ==== ここまで
|
// ====== Akira , GPS信号強度をシミュレート ==== ここまで
|
||||||
|
|
||||||
|
|
||||||
// GPS信号が弱い場合のフラグ. 本番では、true,high にする。
|
// GPS信号が弱い場合のフラグ. 本番では、false,high にする。
|
||||||
bool isGpsSignalWeak = true;
|
bool isGpsSignalWeak = false;
|
||||||
RxString latestSignalStrength = 'low'.obs;
|
RxString latestSignalStrength = 'high'.obs;
|
||||||
//final _latestSignalStrength = 'low'.obs; // 初期値を設定
|
//final _latestSignalStrength = 'low'.obs; // 初期値を設定
|
||||||
//String get latestSignalStrength => _latestSignalStrength.value;
|
//String get latestSignalStrength => _latestSignalStrength.value;
|
||||||
Stream<String> get gpsSignalStrengthStream => latestSignalStrength.stream;
|
Stream<String> get gpsSignalStrengthStream => latestSignalStrength.stream;
|
||||||
|
|
||||||
|
bool isRunningBackgroundGPS=false;
|
||||||
|
int activeEngineCount = 0;
|
||||||
|
|
||||||
// GPS信号の強弱を判断するメソッドを追加. 10m 以内:強、30m以内:中、それ以上:弱
|
// GPS信号の強弱を判断するメソッドを追加. 10m 以内:強、30m以内:中、それ以上:弱
|
||||||
//
|
//
|
||||||
String getGpsSignalStrength(Position? position) {
|
String getGpsSignalStrength(Position? position) {
|
||||||
if (position == null) {
|
if (position == null) {
|
||||||
|
gpsDebugMode ? debugPrint("getGpsSignalStrength position is null.") : null;
|
||||||
latestSignalStrength.value = "low";
|
latestSignalStrength.value = "low";
|
||||||
isGpsSignalWeak = true;
|
isGpsSignalWeak = true;
|
||||||
return 'low';
|
return 'low';
|
||||||
}
|
}
|
||||||
final accuracy = position.accuracy;
|
final accuracy = position.accuracy;
|
||||||
//debugPrint("getGpsSignalStrength : ${accuracy}");
|
gpsDebugMode ? debugPrint("getGpsSignalStrength : ${accuracy}") : null;
|
||||||
|
/*
|
||||||
if(isSimulationMode){
|
if(isSimulationMode){
|
||||||
return _simulatedSignalStrength.value; // GPS信号強度シミュレーション
|
return _simulatedSignalStrength.value; // GPS信号強度シミュレーション
|
||||||
}else {
|
}else {
|
||||||
|
*/
|
||||||
if (accuracy <= 10) {
|
if (accuracy <= 10) {
|
||||||
latestSignalStrength.value = "high";
|
latestSignalStrength.value = "high";
|
||||||
isGpsSignalWeak = false;
|
isGpsSignalWeak = false;
|
||||||
@ -110,7 +119,7 @@ class LocationController extends GetxController {
|
|||||||
isGpsSignalWeak = true;
|
isGpsSignalWeak = true;
|
||||||
return 'low';
|
return 'low';
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 現在位置を調整するメソッドを追加
|
// 現在位置を調整するメソッドを追加
|
||||||
@ -130,7 +139,7 @@ class LocationController extends GetxController {
|
|||||||
//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);
|
||||||
}
|
}
|
||||||
return lastValidLocation ?? LatLng(position.latitude, position.longitude);
|
return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
//===== Akira Added 2024-4-9 end
|
//===== Akira Added 2024-4-9 end
|
||||||
@ -256,7 +265,7 @@ class LocationController extends GetxController {
|
|||||||
// 位置情報の設定を行います。z11
|
// 位置情報の設定を行います。z11
|
||||||
// Set up the location options
|
// Set up the location options
|
||||||
const locationOptions =
|
const locationOptions =
|
||||||
LocationSettings(accuracy: LocationAccuracy.high, distanceFilter: 0);
|
LocationSettings(accuracy: LocationAccuracy.medium, distanceFilter: 0);
|
||||||
|
|
||||||
// 既存の位置情報のストリームをキャンセルします。
|
// 既存の位置情報のストリームをキャンセルします。
|
||||||
await positionStream?.cancel();
|
await positionStream?.cancel();
|
||||||
@ -265,11 +274,14 @@ class LocationController extends GetxController {
|
|||||||
//
|
//
|
||||||
positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen(
|
positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen(
|
||||||
(Position? position) {
|
(Position? position) {
|
||||||
|
gpsDebugMode ? debugPrint("Position = ${position}"):null;
|
||||||
final signalStrength = getGpsSignalStrength(position);
|
final signalStrength = getGpsSignalStrength(position);
|
||||||
if (signalStrength == 'low') {
|
if (signalStrength == 'low') {
|
||||||
isGpsSignalWeak = true;
|
isGpsSignalWeak = true;
|
||||||
|
gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null;
|
||||||
} else {
|
} else {
|
||||||
isGpsSignalWeak = false;
|
isGpsSignalWeak = false;
|
||||||
|
gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final DestinationController destinationController = Get.find<DestinationController>();
|
final DestinationController destinationController = Get.find<DestinationController>();
|
||||||
@ -349,7 +361,13 @@ class LocationController extends GetxController {
|
|||||||
|
|
||||||
|
|
||||||
void handleLocationUpdate(LocationMarkerPosition? position) async {
|
void handleLocationUpdate(LocationMarkerPosition? position) async {
|
||||||
|
//debugPrint("locationController.handleLocationUpdate");
|
||||||
|
try {
|
||||||
if (position != null) {
|
if (position != null) {
|
||||||
|
double currentLat = position.latitude;
|
||||||
|
double currentLon = position.longitude;
|
||||||
|
//debugPrint("Flutter: Received GPS signal. Latitude: $currentLat, Longitude: $currentLon");
|
||||||
|
|
||||||
//debugPrint("position = ${position}");
|
//debugPrint("position = ${position}");
|
||||||
/*
|
/*
|
||||||
currentPosition.value = position;
|
currentPosition.value = position;
|
||||||
@ -361,6 +379,11 @@ class LocationController extends GetxController {
|
|||||||
*/
|
*/
|
||||||
lastGPSDataReceivedTime = DateTime.now(); // 最後にGPS信号を受け取った時刻
|
lastGPSDataReceivedTime = DateTime.now(); // 最後にGPS信号を受け取った時刻
|
||||||
locationMarkerPositionStreamController.add(position);
|
locationMarkerPositionStreamController.add(position);
|
||||||
|
}else{
|
||||||
|
gpsDebugMode ? debugPrint("Flutter: No GPS signal received."):null;
|
||||||
|
}
|
||||||
|
} catch( e ) {
|
||||||
|
debugPrint("Flutter: Error in handleLocationUpdate: $e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ class StringValues extends Translations{
|
|||||||
'no_values': 'No Values',
|
'no_values': 'No Values',
|
||||||
'email_and_password_required': 'Email and password required',
|
'email_and_password_required': 'Email and password required',
|
||||||
'rogaining_user_need_tosign_up': "Rogaining participants do need to sign up.",
|
'rogaining_user_need_tosign_up': "Rogaining participants do need to sign up.",
|
||||||
'add_location': 'Add Location',
|
'add_location': 'Gifu',
|
||||||
'select_travel_mode':'Select your travel mode',
|
'select_travel_mode':'Select your travel mode',
|
||||||
'walking':'Walking',
|
'walking':'Walking',
|
||||||
'driving': 'Driving',
|
'driving': 'Driving',
|
||||||
@ -105,7 +105,7 @@ class StringValues extends Translations{
|
|||||||
'no_values': '値なし',
|
'no_values': '値なし',
|
||||||
'email_and_password_required': 'メールとパスワードが必要です',
|
'email_and_password_required': 'メールとパスワードが必要です',
|
||||||
'rogaining_user_need_tosign_up': "ロゲイニング参加者はサインアップの必要はありません。",
|
'rogaining_user_need_tosign_up': "ロゲイニング参加者はサインアップの必要はありません。",
|
||||||
'add_location': '目的地選択',
|
'add_location': '岐阜',
|
||||||
'select_travel_mode':'移動モードを選択してください',
|
'select_travel_mode':'移動モードを選択してください',
|
||||||
'walking':'歩行',
|
'walking':'歩行',
|
||||||
'driving': '自動車利用',
|
'driving': '自動車利用',
|
||||||
|
|||||||
@ -87,6 +87,7 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _resetIdleTimer() {
|
void _resetIdleTimer() {
|
||||||
|
debugPrint("_resetIdleTimer...");
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_startIdleTimer();
|
_startIdleTimer();
|
||||||
}
|
}
|
||||||
@ -101,15 +102,20 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// added by Akira
|
// added by Akira
|
||||||
|
/*
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
debugPrint("MapWidget:didChangeAppLifecycleState...state=${state}");
|
||||||
|
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
_resetTimer();
|
_resetTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// _centerMapOnUser を10秒間でコール
|
// _centerMapOnUser を10秒間でコール
|
||||||
void _startIdleTimer() {
|
void _startIdleTimer() {
|
||||||
|
//debugPrint("_startIdleTimer ....");
|
||||||
final settingsController = Get.find<SettingsController>();
|
final settingsController = Get.find<SettingsController>();
|
||||||
if (!settingsController.autoReturnDisabled.value) {
|
if (!settingsController.autoReturnDisabled.value) {
|
||||||
_timer = Timer(settingsController.timerDuration.value, _centerMapOnUser);
|
_timer = Timer(settingsController.timerDuration.value, _centerMapOnUser);
|
||||||
@ -118,13 +124,16 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
// タイマーをリセットして_startIdleTimer をコール
|
// タイマーをリセットして_startIdleTimer をコール
|
||||||
void _resetTimer() {
|
void _resetTimer() {
|
||||||
|
//debugPrint("_resetTimer ....");
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_startIdleTimer();
|
_startIdleTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// マッぷを現在位置を中心にする。
|
// マッぷを現在位置を中心にする。
|
||||||
void _centerMapOnUser() {
|
void _centerMapOnUser() {
|
||||||
|
//debugPrint("_centerMapOnUser ....");
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
//debugPrint("_centerMapOnUser => centering ....");
|
||||||
destinationController.centerMapToCurrentLocation();
|
destinationController.centerMapToCurrentLocation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -205,10 +205,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84"
|
sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.1"
|
version: "10.1.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -38,13 +38,14 @@ dependencies:
|
|||||||
flutter_map: ^6.0.1
|
flutter_map: ^6.0.1
|
||||||
geolocator: ^10.1.0
|
geolocator: ^10.1.0
|
||||||
permission_handler: ^11.3.1
|
permission_handler: ^11.3.1
|
||||||
|
# flutter_dev_tools: ^0.0.2
|
||||||
# permission_handler: ^11.1.0 <== older
|
# permission_handler: ^11.1.0 <== older
|
||||||
# permission_handler 11.2.0 (11.3.1 available)
|
# permission_handler 11.2.0 (11.3.1 available)
|
||||||
# permission_handler_android 12.0.3 (12.0.5 available)
|
# permission_handler_android 12.0.3 (12.0.5 available)
|
||||||
# permission_handler_apple 9.3.0 (9.4.4 available)
|
# permission_handler_apple 9.3.0 (9.4.4 available)
|
||||||
# permission_handler_platform_interface 4.1.0 (4.2.1 available)
|
# permission_handler_platform_interface 4.1.0 (4.2.1 available)
|
||||||
package_info_plus: ^6.0.0
|
package_info_plus: ^6.0.0
|
||||||
device_info_plus: ^10.0.1
|
device_info_plus: ^10.1.0
|
||||||
google_api_availability: ^5.0.0
|
google_api_availability: ^5.0.0
|
||||||
tuple: ^2.0.0
|
tuple: ^2.0.0
|
||||||
latlong2: ^0.9.0
|
latlong2: ^0.9.0
|
||||||
|
|||||||
Reference in New Issue
Block a user