Jouca
Jouca

Reputation: 11

GPS position not refreshed automatically

I'm currently working on a Flutter app for a watch using WearOS, since there is no plugin for targetting GPS position using a WearOS watch, you need to do it manually by using the FusedLocationProviderClient from Google Play Services that you need to handle from the ./app/src/main/com/.../.../MainActivity.kt by using a entire script and a system of events broadcasting.

With the code I've currently wrote, I can manage to get the passive GPS position registered by the watch, since it's better when you're currently loading the application and that you're potentially not connected from Wi-Fi or cellular connection.

PS D:\Codes\Flutter projects\idfm_wearos> flutter run --release
Launching lib\main.dart on sdk gwear x86 64 in release mode...
Running Gradle task 'assembleRelease'...                           75,8s
√ Built build\app\outputs\flutter-apk\app-release.apk (28.4MB)
Installing build\app\outputs\flutter-apk\app-release.apk...         3,1s

Flutter run key commands.
h List all available interactive commands.
c Clear the screen
q Quit (terminate the application on the device).
I/flutter ( 5618): {latitude: 48.808303333333335, longitude: 3.08294}

However, after this GPS position is broadcasted by the event system, I can't manage to get the real GPS position, even if I'm connected or getting reconnected.

Here is the code of MainActivity.kt code :

package com.example.idfm_wearos

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel
import java.util.concurrent.TimeUnit

class MainActivity : FlutterActivity() {
    private val CHANNEL = "location_permission"
    private val LOCATION_CHANNEL = "location_updates"
    private val PERMISSION_REQUEST_CODE = 123
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private var eventSink: EventChannel.EventSink? = null
    private lateinit var locationCallback: LocationCallback

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    }

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "requestPermission" -> {
                        requestLocationPermission(result)
                    }
                    "checkPermission" -> {
                        result.success(checkLocationPermission())
                    }
                    else -> result.notImplemented()
                }
            }

        EventChannel(flutterEngine.dartExecutor.binaryMessenger, LOCATION_CHANNEL)
            .setStreamHandler(object : EventChannel.StreamHandler {
                override fun onListen(arguments: Any?, eventSink: EventChannel.EventSink?) {
                    [email protected] = eventSink
                    startLocationUpdates()
                }

                override fun onCancel(arguments: Any?) {
                    stopLocationUpdates()
                }
            })
    }

    private fun requestLocationPermission(result: MethodChannel.Result) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (ActivityCompat.checkSelfPermission(
                    this,
                    Manifest.permission.ACCESS_FINE_LOCATION
                ) == PackageManager.PERMISSION_GRANTED
            ) {
                result.success(true)
                return
            }
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                PERMISSION_REQUEST_CODE
            )
        }
    }

    private fun checkLocationPermission(): Boolean {
        return ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
    }

    private fun startLocationUpdates() {
        if (checkLocationPermission()) {
            // Use LocationManager to get last known location from passive provider
            val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
            val lastKnownLocation: Location? = locationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER)

            // Check if a valid last known location is available
            if (lastKnownLocation != null) {
                Log.d("Location", "Last known location: $lastKnownLocation")
                eventSink?.success("${lastKnownLocation.latitude}|${lastKnownLocation.longitude}")
            } else {
                Log.d("Location", "No passive last known location available")
            }

            // Now request location updates
            val locationRequest = getLocationRequest()
            locationCallback = object : LocationCallback() {
                override fun onLocationResult(locationResult: LocationResult) {
                    for (location in locationResult.locations) {
                        eventSink?.success("${location.latitude}|${location.longitude}")
                    }
                }
            }
            fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
        }
    }

    private fun stopLocationUpdates() {
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }

    private fun getLocationRequest(): LocationRequest {
        return LocationRequest.create().apply {
            interval = TimeUnit.SECONDS.toMillis(5)
            fastestInterval = TimeUnit.SECONDS.toMillis(3)
            priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
        }
    }

    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) {
                eventSink?.success(true)
                startLocationUpdates() // Start location updates after permission is granted
            } else {
                eventSink?.success(false)
            }
        }
    }
}

Here is the code from the Flutter class I'm using :

import 'package:flutter/services.dart';

class LocationService {
  static const MethodChannel _methodChannel = MethodChannel('location_permission');
  static const EventChannel _eventChannel = EventChannel('location_updates');

  final Map<String, dynamic> _locationData = {
    'latitude': 0.0,
    'longitude': 0.0,
  };

  Future<void> startLocationUpdates() async {
    try {
      await _methodChannel.invokeMethod('requestPermission');
      final bool result = await _methodChannel.invokeMethod('checkPermission');
      if (result) {
        _eventChannel.receiveBroadcastStream().listen((event) {
          // Parse the location data (event is a string)
          final List<String> data = event.split('|');
          _locationData['latitude'] = double.parse(data[0]);
          _locationData['longitude'] = double.parse(data[1]);
          print(_locationData);

          //print("Coordinates: $event");
        });
      }
    } catch (e) {
      print("Failed to start location updates: $e");
    }
  }

  Map<String, dynamic> getLocationData() {
    return _locationData;
  }
}

Timer.periodic(Duration(seconds: 5), (timer) async {
  Map<String, dynamic> location = _gpsLocation.getLocationData();
      
  if (location["latitude"] != _locationData["latitude"] || location["longitude"] != _locationData["longitude"]) {
    _locationData = location;
    await refreshStations();
  }
});

Upvotes: 0

Views: 37

Answers (0)

Related Questions