Get bridge working, get geolocation working

This commit is contained in:
Pieter Vander Vennet 2024-12-12 00:44:27 +01:00
parent 9102d6ef87
commit b165ec6005
8 changed files with 261 additions and 25 deletions

View file

@ -9,7 +9,7 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-geolocation')
}

View file

@ -11,41 +11,44 @@ import com.getcapacitor.annotation.CapacitorPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
@CapacitorPlugin(name = "Echo")
@CapacitorPlugin(name = "Databridge")
public class Databridge extends Plugin {
private final Map<String, Consumer<PluginCall>> responders;
private static final Map<String, Consumer<PluginCall>> responders = new HashMap<>();
static {
responders.put("meta", Databridge.answer("capacitator-shell 0.0.1;"));
}
private static Consumer<PluginCall> answer(String answer) {
return (PluginCall call) -> Databridge.sendAnswerTo(call, answer);
}
public static void sendAnswerTo(PluginCall call, String answer) {
JSObject ret = new JSObject();
ret.put("value", answer);
Log.i("databridge","Resolving call");
return (PluginCall call) -> call.resolve(ret);
Log.i("databridge", "Resolving call");
call.resolve(ret);
}
/**
* A responder will be activated if the native code asks for it.
* Use call.setKeepAlive(true) for multiple responses
* @param responders
* Use call.setKeepAlive(true) if the responder will send multiple responses
*/
public Databridge(Map<String, Consumer<PluginCall>> responders) {
this.responders = responders;
responders.put("meta", Databridge.answer("capacitator-shell 0.0.1;"));
public static void addResponder(String key, Consumer<PluginCall> responder) {
responders.put(key, responder);
}
@PluginMethod()
public void request(PluginCall call) {
String key = call.getString("key");
Log.i("databridge","Got a call: "+key);
var c= this.responders.get(key);
if(c != null){
Log.i("databridge", "Got a call: " + key);
var c = this.responders.get(key);
if (c != null) {
c.accept(call);
}else{
call.reject("ERROR: no responder installed for "+key);
} else {
call.reject("ERROR: no responder installed for " + key);
}
}
}

View file

@ -0,0 +1,155 @@
package org.mapcomplete;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import androidx.core.location.LocationManagerCompat;
import com.getcapacitor.JSObject;
import com.getcapacitor.PluginCall;
public class GeolocationBridge {
public static final int requestCode = 684198;
private final Context context;
private final MainActivity mainActivity;
GeolocationBridge(Context context, MainActivity mainActivity) {
this.context = context;
this.mainActivity = mainActivity;
Databridge.addResponder("location:watch", pluginCall -> {
pluginCall.setKeepAlive(true);
new LocationUpdateListener(pluginCall, context).requestLocationUpdates(true);
});
}
}
class LocationUpdateListener implements LocationListener {
private final PluginCall callback;
private final Context context;
public LocationUpdateListener(PluginCall callback, Context context) {
this.callback = callback;
this.context = context;
}
private void answer(String answer) {
Databridge.sendAnswerTo(this.callback, answer);
}
private void error(String msg) {
this.callback.reject(msg);
}
/**
* Query the location manager to indicate what providers are available.
* Typical providers are `passive`, `network`, `fused` and `gps`
* <p>
* This method selects the best appropriate
*/
private String getPreferredProvider(boolean enableHighAccuracy) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
var providers = lm.getProviders(true);
if (enableHighAccuracy) {
if (providers.contains("gps")) {
return "gps";
}
if (providers.contains("fused")) {
return "fused";
}
if (providers.contains("network")) {
return "network";
}
} else {
if (providers.contains("network")) {
return "network";
}
if (providers.contains("fused")) {
return "fused";
}
if (providers.contains("gps")) {
return "gps";
}
}
if (providers.contains("passive")) {
return "passive";
}
return null;
}
private Boolean isLocationServicesEnabled() {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return LocationManagerCompat.isLocationEnabled(lm);
}
@SuppressWarnings("MissingPermission")
public void requestLocationUpdates(boolean enableHighAccuracy) {
if (!this.isLocationServicesEnabled()) {
this.error("location disabled");
return;
}
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
var provider = getPreferredProvider(enableHighAccuracy);
if (provider == null) {
this.error("Location unavailable: no providers defined. Note: this is a Google Play Services free implementation");
return;
}
lm.requestLocationUpdates(provider, 1000, 10, this);
}
@Override
public void onLocationChanged(Location location) {
JSObject loc = new JSObject();
loc.put("latitude", location.getLatitude());
loc.put("longitude", location.getLongitude());
if (location.hasAccuracy()) {
loc.put("accuracy", location.getAccuracy());
}
if (location.hasAltitude()) {
loc.put("altitude", location.getAltitude());
}
if (location.hasBearing()) {
// Expected for heading: 0 is north, 90 is east, up till 359° ; see https://developer.mozilla.org/en-US/docs/Web/API/GeolocationCoordinates
// getBearing returns essentially the same
loc.put("heading", location.getBearing());
}
JSObject ret = new JSObject();
ret.put("value", loc);
Log.i("databridge", "Resolving call");
this.callback.resolve(ret);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}

View file

@ -1,15 +1,98 @@
package org.mapcomplete;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import java.util.Objects;
public class MainActivity extends BridgeActivity {
private PluginCall locationRequest = null;
private PluginCall authRequest = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerPlugin(Databridge.class);
new GeolocationBridge(getApplicationContext(), this);
Databridge.addResponder("location:request-permission", pluginCall -> {
this.locationRequest = pluginCall;
this.requestPermission();
});
Databridge.addResponder("request:login", pluginCall -> {
this.authRequest = pluginCall;
});
super.onCreate(savedInstanceState);
}
private void requestPermission() {
if (ContextCompat.checkSelfPermission(
getApplicationContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED) {
// Permission is not granted, request it
ActivityCompat.requestPermissions(
this,
new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
GeolocationBridge.requestCode
);
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
return;
}
Uri url = intent.getData();
if (url == null) {
return;
}
if (Objects.equals(url.getPath(), "/land.html")) {
var code = url.getQueryParameter("code");
var state = url.getQueryParameter("state");
JSObject obj = new JSObject();
obj.put("code", code);
obj.put("state", state);
JSObject res = new JSObject();
res.put("value", obj);
Log.i("main", "Resolving auth call");
this.authRequest.resolve(res);
return;
}
System.out.println("Intercepted URL: " + url);
}
@Override
public void onRequestPermissionsResult(
int requestCode,
String[] permissions,
int[] grantResults
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == GeolocationBridge.requestCode) {
if (this.locationRequest != null) {
// We've only requested "FINE_LOCATION"
var granted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
Log.i("Geolocation", "Got fine location request: " + granted);
Databridge.sendAnswerTo(this.locationRequest, granted ? "granted" : "denied");
}
}
}
}

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<paths>
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
</paths>
</paths>

View file

@ -8,7 +8,6 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.1'
classpath 'com.google.gms:google-services:4.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View file

@ -1,6 +1,3 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-geolocation'
project(':capacitor-geolocation').projectDir = new File('../node_modules/@capacitor/geolocation/android')