Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ group 'io.carius.lars.ar_flutter_plugin'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.5.20'
repositories {
google()
jcenter()
Expand All @@ -25,19 +25,28 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 30
namespace 'io.carius.lars.ar_flutter_plugin'
compileSdkVersion 34

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 24
}
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.google.ar:core:1.22.0"
implementation "com.google.ar:core:1.47.0"

implementation 'com.google.ar.sceneform:core:1.15.0'
implementation 'com.google.ar.sceneform:assets:1.15.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ internal class AndroidARView(
handlerThread.quitSafely();
}, Handler(handlerThread.looper));
}
"enableGeospatialMode" -> {
if(arSceneView.session?.isGeospatialModeSupported(Config.GeospatialMode.ENABLED) == true) {
val config = arSceneView.session?.config
config?.setGeospatialMode(Config.GeospatialMode.ENABLED)
arSceneView.session?.configure(config)
}

}
"dispose" -> {
dispose()
}
Expand Down Expand Up @@ -657,7 +665,23 @@ internal class AndroidARView(
//unselect all nodes as we do not want the selection visualizer
transformationSystem.selectNode(null)
}

if(arSceneView.session?.config?.geospatialMode == Config.GeospatialMode.ENABLED) {
val session = arSceneView.session;
val earth = session?.earth
if (earth?.trackingState == TrackingState.TRACKING) {
val cameraGeospatialPose = earth.cameraGeospatialPose
val map: HashMap<String, Any> = HashMap<String, Any>()
map["latitude"] = cameraGeospatialPose.latitude
map["longitude"] = cameraGeospatialPose.longitude
map["altitude"] = cameraGeospatialPose.altitude
map["eastUpSouthQuaternion"] = cameraGeospatialPose.eastUpSouthQuaternion
map["horizontalAccuracy"] = cameraGeospatialPose.horizontalAccuracy
map["orientationYawAccuracy"] = cameraGeospatialPose.orientationYawAccuracy
map["verticalAccuracy"] = cameraGeospatialPose.verticalAccuracy

sessionManagerChannel.invokeMethod("onCameraGeospatialPoseDetected", map)
}
}
}

private fun addNode(dict_node: HashMap<String, Any>, dict_anchor: HashMap<String, Any>? = null): CompletableFuture<Boolean>{
Expand Down
4 changes: 2 additions & 2 deletions example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.7.0'
repositories {
google()
jcenter()
Expand Down Expand Up @@ -27,6 +27,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# platform :ios, '12.0'

# Override firebase SDK version for compatibility reasons
$FirebaseSDKVersion = '8.7.0'
Expand Down
7 changes: 5 additions & 2 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -166,7 +166,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down Expand Up @@ -212,10 +212,12 @@
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
Expand Down Expand Up @@ -243,6 +245,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UIKit
import Flutter

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
2 changes: 2 additions & 0 deletions example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@
<string>This app needs access to location when in the background.</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
39 changes: 36 additions & 3 deletions ios/Classes/IosARView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Foundation
import ARKit
import Combine
import ARCoreCloudAnchors
import ARCoreGeospatial

class IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureRecognizerDelegate, ARSessionDelegate {
let sceneView: ARSCNView
Expand Down Expand Up @@ -74,7 +75,8 @@ class IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureReco

func onSessionMethodCalled(_ call :FlutterMethodCall, _ result:FlutterResult) {
let arguments = call.arguments as? Dictionary<String, Any>



switch call.method {
case "init":
//self.sessionManagerChannel.invokeMethod("onError", arguments: ["SessionTEST from iOS"])
Expand Down Expand Up @@ -104,6 +106,22 @@ class IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureReco
} else {
result(nil)
}
case "enableGeospatialMode":
arcoreSession = try! GARSession(apiKey: arguments?["apiKey"] as! String, bundleIdentifier: nil)
if(!(arcoreSession?.isGeospatialModeSupported(GARGeospatialMode.enabled) ?? false)) {
result(false)
}

if (arcoreSession != nil) {
let configuration = GARSessionConfiguration();
configuration.geospatialMode = .enabled;
arcoreSession?.setConfiguration(configuration, error: nil);
arcoreMode = true
} else {
sessionManagerChannel.invokeMethod("onError", arguments: ["Error initializing Google AR Session"])
}

break
case "dispose":
onDispose(result)
result(nil)
Expand Down Expand Up @@ -182,7 +200,7 @@ class IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureReco
}
break
case "initGoogleCloudAnchorMode":
arcoreSession = try! GARSession.session()
arcoreSession = try! GARSession.session()

if (arcoreSession != nil){
let configuration = GARSessionConfiguration();
Expand Down Expand Up @@ -366,7 +384,22 @@ class IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureReco
func session(_ session: ARSession, didUpdate frame: ARFrame) {
if (arcoreMode) {
do {
try arcoreSession!.update(frame)
let garFrame = try arcoreSession!.update(frame)
let earth = garFrame.earth
if(earth?.earthState == .enabled) {
let geospatialTransform = earth?.cameraGeospatialTransform
var map = Dictionary<String, Any>();
map["latitude"] = geospatialTransform?.coordinate.latitude
map["longitude"] = geospatialTransform?.coordinate.longitude
map["altitude"] = geospatialTransform?.altitude
var eastUpSouthQuaternion: [Float?] = [geospatialTransform?.eastUpSouthQTarget.imag.x, geospatialTransform?.eastUpSouthQTarget.imag.y, geospatialTransform?.eastUpSouthQTarget.imag.z, geospatialTransform?.eastUpSouthQTarget.real]
map["eastUpSouthQuaternion"] = eastUpSouthQuaternion
map["horizontalAccuracy"] = geospatialTransform?.horizontalAccuracy
map["orientationYawAccuracy"] = geospatialTransform?.orientationYawAccuracy
map["verticalAccuracy"] = geospatialTransform?.verticalAccuracy

sessionManagerChannel.invokeMethod("onCameraGeospatialPoseDetected", arguments: map)
}
} catch {
print(error)
}
Expand Down
3 changes: 2 additions & 1 deletion ios/ar_flutter_plugin.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ A Flutter plugin for shared AR experiences supporting Android and iOS.
s.static_framework = true
#s.dependency 'ARCore/CloudAnchors', '~> 1.12.0'
#s.dependency 'ARCore', '~> 1.2.0'
s.dependency 'ARCore/CloudAnchors', '~> 1.33.0' # Updated from 1.32 to 1.33 to support Apple Silicon, info here: https://github.com/google-ar/arcore-ios-sdk/issues/59#issuecomment-1219756010
s.dependency 'ARCoreNanoPbUpdated/CloudAnchors', '~> 1.46.0.2'
s.dependency 'ARCoreNanoPbUpdated/Geospatial', '~> 1.46.0.2'
s.platform = :ios, '13.0'


Expand Down
30 changes: 30 additions & 0 deletions lib/managers/ar_session_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:typed_data';

import 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';
import 'package:ar_flutter_plugin/models/ar_anchor.dart';
import 'package:ar_flutter_plugin/models/ar_geospatial_pose.dart';
import 'package:ar_flutter_plugin/models/ar_hittest_result.dart';
import 'package:ar_flutter_plugin/utils/json_converters.dart';
import 'package:flutter/material.dart';
Expand All @@ -12,6 +13,9 @@ import 'package:vector_math/vector_math_64.dart';
// Type definitions to enforce a consistent use of the API
typedef ARHitResultHandler = void Function(List<ARHitTestResult> hits);

typedef ARCameraGeospatialPoseHandler = void Function(
ARGeospatialPose cameraGeospatialPose);

/// Manages the session configuration, parameters and events of an [ARView]
class ARSessionManager {
/// Platform channel used for communication from and to [ARSessionManager]
Expand All @@ -29,6 +33,8 @@ class ARSessionManager {
/// Receives hit results from user taps with tracked planes or feature points
late ARHitResultHandler onPlaneOrPointTap;

ARCameraGeospatialPoseHandler? onCameraGeospatialPoseDetected;

ARSessionManager(int id, this.buildContext, this.planeDetectionConfig,
{this.debug = false}) {
_channel = MethodChannel('arsession_$id');
Expand Down Expand Up @@ -103,6 +109,21 @@ class ARSessionManager {
return distance;
}

/// Enables geospatial mode for the application.
///
/// This method invokes a platform channel to enable geospatial mode.
///
/// - [iosApiKey]: (Optional) The API key required for iOS devices.
///
/// Example usage:
/// ```dart
/// enableGeospatialMode(iosApiKey: 'your-ios-api-key'); // Required for iOS
/// enableGeospatialMode(); // Works on Android without API key
/// ```
void enableGeospatialMode({String? iosApiKey}) {
_channel.invokeMethod<bool>('enableGeospatialMode', <dynamic, dynamic>{'apiKey': iosApiKey});
}

Future<void> _platformCallHandler(MethodCall call) {
if (debug) {
print('_platformCallHandler call ${call.method} ${call.arguments}');
Expand All @@ -128,6 +149,15 @@ class ARSessionManager {
onPlaneOrPointTap(hitTestResults);
}
break;
case 'onCameraGeospatialPoseDetected':
if (onCameraGeospatialPoseDetected != null) {
final rawPoseDetected = call.arguments as Map<dynamic, dynamic>;
rawPoseDetected['eastUpSouthQuaternion'] = List<double>.from(
rawPoseDetected['eastUpSouthQuaternion']);
final pose = ARGeospatialPose.fromMap(rawPoseDetected);
onCameraGeospatialPoseDetected!(pose);
}
break;
case 'dispose':
_channel.invokeMethod<void>("dispose");
break;
Expand Down
19 changes: 19 additions & 0 deletions lib/models/ar_geospatial_pose.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class ARGeospatialPose {
double? latitude;
double? longitude;
double? altitude;
List<double?>? eastUpSouthQuaternion;
double? horizontalAccuracy;
double? orientationYawAccuracy;
double? verticalAccuracy;

ARGeospatialPose.fromMap(Map<dynamic, dynamic> map) {
this.latitude = map['latitude'];
this.longitude = map['longitude'];
this.altitude = map['altitude'];
this.eastUpSouthQuaternion = map['eastUpSouthQuaternion'];
this.horizontalAccuracy = map['horizontalAccuracy'];
this.orientationYawAccuracy = map['orientationYawAccuracy'];
this.verticalAccuracy = map['verticalAccuracy'];
}
}
6 changes: 3 additions & 3 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: ar_flutter_plugin
description: Flutter Plugin for creating (collaborative) Augmented Reality experiences - Supports ARKit for iOS and ARCore for Android devices.
version: 0.7.3
version: 0.7.4
homepage: https://lars.carius.io
repository: https://github.com/CariusLars/ar_flutter_plugin

Expand All @@ -11,10 +11,10 @@ environment:
dependencies:
flutter:
sdk: flutter
permission_handler: ^10.1.0
permission_handler: ^11.3.1
vector_math: ^2.1.1
json_annotation: ^4.5.0
geolocator: ^9.0.0
geolocator: ^13.0.2

dev_dependencies:
flutter_test:
Expand Down