diff --git a/android/app/build.gradle b/android/app/build.gradle index af703c5..3c44ea1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -12,6 +12,13 @@ if (localPropertiesFile.exists()) { } } +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + + def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -23,7 +30,7 @@ if (flutterVersionName == null) { } android { - namespace "com.example.super_liquid_galaxy_controller" + namespace "com.aritra.super_liquid_galaxy_controller" compileSdkVersion 34 ndkVersion flutter.ndkVersion @@ -42,7 +49,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.super_liquid_galaxy_controller" + applicationId "com.aritra.super_liquid_galaxy_controller" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion flutter.minSdkVersion @@ -51,13 +58,20 @@ android { versionName flutterVersionName } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig signingConfigs.release } } + } flutter { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8e3be6a..b6db632 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + @@ -30,7 +31,7 @@ /> createState() => _LocationSelectorState(); @@ -356,7 +356,7 @@ class _LocationSelectorState extends State { )); } } - widget.submitData(point); + widget.submitData(point,label); } @@ -368,7 +368,7 @@ class _LocationSelectorState extends State { print(out); if (out.isNotEmpty) { widget.submitData( - Coordinates(latitude: out[0].latitude, longitude: out[0].longitude)); + Coordinates(latitude: out[0].latitude, longitude: out[0].longitude), label); } else { if (!Get.isSnackbarOpen) { diff --git a/lib/components/navisland.dart b/lib/components/navisland.dart index 3444ca4..600063c 100644 --- a/lib/components/navisland.dart +++ b/lib/components/navisland.dart @@ -153,15 +153,9 @@ class NavIsland extends StatelessWidget { color: Colors.transparent, child: InkWell( onTap: () async { - /*LatLng centerPoint = LatLng(37.7749, -122.4194); // Example center point (San Francisco) - double length = 120.0; // Length of the footprint in meters - double width = 70.0; // Width of the footprint in meters - double angle = 0 * (Math.pi / 180); // Angle of rotation in radians (45 degrees in this case) - String kmlOutput = KMLGenerator.generateFootprintBottomPolygon(centerPoint, length, width, angle); - print(kmlOutput);*/ - LatLng start = LatLng(1,6); - LatLng end = LatLng(5, 3); + LatLng start = LatLng(1,5); + LatLng end = LatLng(7,11); double dashLength = 10000.0; // Length of each dash in meters double gapLength = 5000.0; // Length of gap between dashes in meters diff --git a/lib/data_class/coordinate.dart b/lib/data_class/coordinate.dart index 6a93da2..9ae1ef0 100644 --- a/lib/data_class/coordinate.dart +++ b/lib/data_class/coordinate.dart @@ -1,11 +1,17 @@ +import 'package:latlong2/latlong.dart'; + class Coordinates { - double latitude; - double longitude; + late double latitude; + late double longitude; Coordinates({ required this.latitude, required this.longitude, }); + Coordinates.fromLatLng(LatLng point) { + latitude = point.latitude; + longitude = point.longitude; + } // Override toString for better debugging output @override diff --git a/lib/data_class/geo_reversecode_response.dart b/lib/data_class/geo_reversecode_response.dart new file mode 100644 index 0000000..436e8d1 --- /dev/null +++ b/lib/data_class/geo_reversecode_response.dart @@ -0,0 +1,268 @@ +class GeoReverseCodeResponse { + List? results; + Query? query; + + GeoReverseCodeResponse({this.results, this.query}); + + GeoReverseCodeResponse.fromJson(Map json) { + this.results = json["results"] == null ? null : (json["results"] as List).map((e) => Results.fromJson(e)).toList(); + this.query = json["query"] == null ? null : Query.fromJson(json["query"]); + } + + Map toJson() { + final Map data = new Map(); + if (this.results != null) + data["results"] = this.results?.map((e) => e.toJson()).toList(); + if (this.query != null) + data["query"] = this.query?.toJson(); + return data; + } + + @override + String toString() { + return 'GeoReverseCodeResponse{results: $results, query: $query}'; + } +} + +class Query { + double? lat; + double? lon; + String? plusCode; + + Query({this.lat, this.lon, this.plusCode}); + + Query.fromJson(Map json) { + this.lat = (json["lat"])?.toDouble(); + this.lon = (json["lon"])?.toDouble(); + this.plusCode = json["plus_code"]; + } + + Map toJson() { + final Map data = new Map(); + data["lat"] = this.lat; + data["lon"] = this.lon; + data["plus_code"] = this.plusCode; + return data; + } + + @override + String toString() { + return 'Query{lat: $lat, lon: $lon, plusCode: $plusCode}'; + } +} + +class Results { + Datasource? datasource; + String? country; + String? countryCode; + double? lon; + double? lat; + double? distance; + String? resultType; + String? formatted; + String? addressLine1; + String? addressLine2; + String? category; + Timezone? timezone; + String? plusCode; + Rank? rank; + String? placeId; + Bbox? bbox; + + Results( + {this.datasource, + this.country, + this.countryCode, + this.lon, + this.lat, + this.distance, + this.resultType, + this.formatted, + this.addressLine1, + this.addressLine2, + this.category, + this.timezone, + this.plusCode, + this.rank, + this.placeId, + this.bbox}); + + Results.fromJson(Map json) { + this.datasource = json["datasource"] == null ? null : Datasource.fromJson(json["datasource"]); + this.country = json["country"]; + this.countryCode = json["country_code"]; + this.lon = json["lon"]; + this.lat = json["lat"]; + this.distance = json["distance"]?.toDouble(); + this.resultType = json["result_type"]; + this.formatted = json["formatted"]; + this.addressLine1 = json["address_line1"]; + this.addressLine2 = json["address_line2"]; + this.category = json["category"]; + this.timezone = json["timezone"] == null ? null : Timezone.fromJson(json["timezone"]); + this.plusCode = json["plus_code"]; + this.rank = json["rank"] == null ? null : Rank.fromJson(json["rank"]); + this.placeId = json["place_id"]; + this.bbox = json["bbox"] == null ? null : Bbox.fromJson(json["bbox"]); + } + + Map toJson() { + final Map data = new Map(); + if (this.datasource != null) + data["datasource"] = this.datasource?.toJson(); + data["country"] = this.country; + data["country_code"] = this.countryCode; + data["lon"] = this.lon; + data["lat"] = this.lat; + data["distance"] = this.distance; + data["result_type"] = this.resultType; + data["formatted"] = this.formatted; + data["address_line1"] = this.addressLine1; + data["address_line2"] = this.addressLine2; + data["category"] = this.category; + if (this.timezone != null) + data["timezone"] = this.timezone?.toJson(); + data["plus_code"] = this.plusCode; + if (this.rank != null) + data["rank"] = this.rank?.toJson(); + data["place_id"] = this.placeId; + if (this.bbox != null) + data["bbox"] = this.bbox?.toJson(); + return data; + } + + @override + String toString() { + return 'Results{datasource: $datasource, country: $country, countryCode: $countryCode, lon: $lon, lat: $lat, distance: $distance, resultType: $resultType, formatted: $formatted, addressLine1: $addressLine1, addressLine2: $addressLine2, category: $category, timezone: $timezone, plusCode: $plusCode, rank: $rank, placeId: $placeId, bbox: $bbox}'; + } +} + +class Bbox { + double? lon1; + double? lat1; + double? lon2; + double? lat2; + + Bbox({this.lon1, this.lat1, this.lon2, this.lat2}); + + Bbox.fromJson(Map json) { + this.lon1 = json["lon1"]?.toDouble(); + this.lat1 = json["lat1"]?.toDouble(); + this.lon2 = json["lon2"]?.toDouble(); + this.lat2 = json["lat2"]?.toDouble(); + } + + Map toJson() { + final Map data = new Map(); + data["lon1"] = this.lon1; + data["lat1"] = this.lat1; + data["lon2"] = this.lon2; + data["lat2"] = this.lat2; + return data; + } + + @override + String toString() { + return 'Bbox{lon1: $lon1, lat1: $lat1, lon2: $lon2, lat2: $lat2}'; + } +} + +class Rank { + double? importance; + double? popularity; + + Rank({this.importance, this.popularity}); + + Rank.fromJson(Map json) { + this.importance = json["importance"]; + this.popularity = json["popularity"]; + } + + Map toJson() { + final Map data = new Map(); + data["importance"] = this.importance; + data["popularity"] = this.popularity; + return data; + } + + @override + String toString() { + return 'Rank{importance: $importance, popularity: $popularity}'; + } +} + +class Timezone { + String? name; + String? offsetStd; + int? offsetStdSeconds; + String? offsetDst; + int? offsetDstSeconds; + String? abbreviationStd; + String? abbreviationDst; + + Timezone( + {this.name, + this.offsetStd, + this.offsetStdSeconds, + this.offsetDst, + this.offsetDstSeconds, + this.abbreviationStd, + this.abbreviationDst}); + + Timezone.fromJson(Map json) { + this.name = json["name"]; + this.offsetStd = json["offset_STD"]; + this.offsetStdSeconds = json["offset_STD_seconds"]; + this.offsetDst = json["offset_DST"]; + this.offsetDstSeconds = json["offset_DST_seconds"]; + this.abbreviationStd = json["abbreviation_STD"]; + this.abbreviationDst = json["abbreviation_DST"]; + } + + Map toJson() { + final Map data = new Map(); + data["name"] = this.name; + data["offset_STD"] = this.offsetStd; + data["offset_STD_seconds"] = this.offsetStdSeconds; + data["offset_DST"] = this.offsetDst; + data["offset_DST_seconds"] = this.offsetDstSeconds; + data["abbreviation_STD"] = this.abbreviationStd; + data["abbreviation_DST"] = this.abbreviationDst; + return data; + } + + @override + String toString() { + return 'Timezone{name: $name, offsetStd: $offsetStd, offsetStdSeconds: $offsetStdSeconds, offsetDst: $offsetDst, offsetDstSeconds: $offsetDstSeconds, abbreviationStd: $abbreviationStd, abbreviationDst: $abbreviationDst}'; + } +} + +class Datasource { + String? sourcename; + String? attribution; + String? license; + String? url; + + Datasource({this.sourcename, this.attribution, this.license, this.url}); + + Datasource.fromJson(Map json) { + this.sourcename = json["sourcename"]; + this.attribution = json["attribution"]; + this.license = json["license"]; + this.url = json["url"]; + } + + Map toJson() { + final Map data = new Map(); + data["sourcename"] = this.sourcename; + data["attribution"] = this.attribution; + data["license"] = this.license; + data["url"] = this.url; + return data; + } + + @override + String toString() { + return 'Datasource{sourcename: $sourcename, attribution: $attribution, license: $license, url: $url}'; + } +} diff --git a/lib/data_class/geocode_response.dart b/lib/data_class/geocode_response.dart new file mode 100644 index 0000000..b8277ca --- /dev/null +++ b/lib/data_class/geocode_response.dart @@ -0,0 +1,307 @@ +class GeoCodeResponse { + List? results; + Query? query; + + GeoCodeResponse({this.results, this.query}); + + GeoCodeResponse.fromJson(Map json) { + this.results = json["results"] == null ? null : (json["results"] as List).map((e) => Results.fromJson(e)).toList(); + this.query = json["query"] == null ? null : Query.fromJson(json["query"]); + } + + Map toJson() { + final Map data = new Map(); + if (this.results != null) + data["results"] = this.results?.map((e) => e.toJson()).toList(); + if (this.query != null) + data["query"] = this.query?.toJson(); + return data; + } + + @override + String toString() { + return 'GeoCodeResponse{results: $results, query: $query}'; + } +} + +class Query { + String? text; + Parsed? parsed; + + Query({this.text, this.parsed}); + + Query.fromJson(Map json) { + this.text = json["text"]; + this.parsed = json["parsed"] == null ? null : Parsed.fromJson(json["parsed"]); + } + + Map toJson() { + final Map data = new Map(); + data["text"] = this.text; + if (this.parsed != null) + data["parsed"] = this.parsed?.toJson(); + return data; + } + + @override + String toString() { + return 'Query{text: $text, parsed: $parsed}'; + } +} + +class Parsed { + String? state; + String? country; + String? expectedType; + + Parsed({this.state, this.country, this.expectedType}); + + Parsed.fromJson(Map json) { + this.state = json["state"]; + this.country = json["country"]; + this.expectedType = json["expected_type"]; + } + + Map toJson() { + final Map data = new Map(); + data["state"] = this.state; + data["country"] = this.country; + data["expected_type"] = this.expectedType; + return data; + } + + @override + String toString() { + return 'Parsed{state: $state, country: $country, expectedType: $expectedType}'; + } +} + +class Results { + Datasource? datasource; + String? ref; + String? country; + String? countryCode; + String? state; + double? lon; + double? lat; + String? stateCode; + String? resultType; + String? formatted; + String? addressLine1; + String? addressLine2; + String? category; + Timezone? timezone; + String? plusCode; + Rank? rank; + String? placeId; + Bbox? bbox; + + Results( + {this.datasource, + this.ref, + this.country, + this.countryCode, + this.state, + this.lon, + this.lat, + this.stateCode, + this.resultType, + this.formatted, + this.addressLine1, + this.addressLine2, + this.category, + this.timezone, + this.plusCode, + this.rank, + this.placeId, + this.bbox}); + + Results.fromJson(Map json) { + this.datasource = json["datasource"] == null ? null : Datasource.fromJson(json["datasource"]); + this.ref = json["ref"]; + this.country = json["country"]; + this.countryCode = json["country_code"]; + this.state = json["state"]; + this.lon = json["lon"]; + this.lat = json["lat"]; + this.stateCode = json["state_code"]; + this.resultType = json["result_type"]; + this.formatted = json["formatted"]; + this.addressLine1 = json["address_line1"]; + this.addressLine2 = json["address_line2"]; + this.category = json["category"]; + this.timezone = json["timezone"] == null ? null : Timezone.fromJson(json["timezone"]); + this.plusCode = json["plus_code"]; + this.rank = json["rank"] == null ? null : Rank.fromJson(json["rank"]); + this.placeId = json["place_id"]; + this.bbox = json["bbox"] == null ? null : Bbox.fromJson(json["bbox"]); + } + + Map toJson() { + final Map data = new Map(); + if (this.datasource != null) + data["datasource"] = this.datasource?.toJson(); + data["ref"] = this.ref; + data["country"] = this.country; + data["country_code"] = this.countryCode; + data["state"] = this.state; + data["lon"] = this.lon; + data["lat"] = this.lat; + data["state_code"] = this.stateCode; + data["result_type"] = this.resultType; + data["formatted"] = this.formatted; + data["address_line1"] = this.addressLine1; + data["address_line2"] = this.addressLine2; + data["category"] = this.category; + if (this.timezone != null) + data["timezone"] = this.timezone?.toJson(); + data["plus_code"] = this.plusCode; + if (this.rank != null) + data["rank"] = this.rank?.toJson(); + data["place_id"] = this.placeId; + if (this.bbox != null) + data["bbox"] = this.bbox?.toJson(); + return data; + } + + @override + String toString() { + return 'Results{datasource: $datasource, ref: $ref, country: $country, countryCode: $countryCode, state: $state, lon: $lon, lat: $lat, stateCode: $stateCode, resultType: $resultType, formatted: $formatted, addressLine1: $addressLine1, addressLine2: $addressLine2, category: $category, timezone: $timezone, plusCode: $plusCode, rank: $rank, placeId: $placeId, bbox: $bbox}'; + } +} + +class Bbox { + double? lon1; + double? lat1; + double? lon2; + double? lat2; + + Bbox({this.lon1, this.lat1, this.lon2, this.lat2}); + + Bbox.fromJson(Map json) { + this.lon1 = json["lon1"]; + this.lat1 = json["lat1"]; + this.lon2 = json["lon2"]; + this.lat2 = json["lat2"]; + } + + Map toJson() { + final Map data = new Map(); + data["lon1"] = this.lon1; + data["lat1"] = this.lat1; + data["lon2"] = this.lon2; + data["lat2"] = this.lat2; + return data; + } + + @override + String toString() { + return 'Bbox{lon1: $lon1, lat1: $lat1, lon2: $lon2, lat2: $lat2}'; + } +} + +class Rank { + double? importance; + double? popularity; + int? confidence; + String? matchType; + + Rank({this.importance, this.popularity, this.confidence, this.matchType}); + + Rank.fromJson(Map json) { + this.importance = json["importance"]; + this.popularity = json["popularity"]; + this.confidence = json["confidence"]; + this.matchType = json["match_type"]; + } + + Map toJson() { + final Map data = new Map(); + data["importance"] = this.importance; + data["popularity"] = this.popularity; + data["confidence"] = this.confidence; + data["match_type"] = this.matchType; + return data; + } + + @override + String toString() { + return 'Rank{importance: $importance, popularity: $popularity, confidence: $confidence, matchType: $matchType}'; + } +} + +class Timezone { + String? name; + String? offsetStd; + int? offsetStdSeconds; + String? offsetDst; + int? offsetDstSeconds; + String? abbreviationStd; + String? abbreviationDst; + + Timezone( + {this.name, + this.offsetStd, + this.offsetStdSeconds, + this.offsetDst, + this.offsetDstSeconds, + this.abbreviationStd, + this.abbreviationDst}); + + Timezone.fromJson(Map json) { + this.name = json["name"]; + this.offsetStd = json["offset_STD"]; + this.offsetStdSeconds = json["offset_STD_seconds"]; + this.offsetDst = json["offset_DST"]; + this.offsetDstSeconds = json["offset_DST_seconds"]; + this.abbreviationStd = json["abbreviation_STD"]; + this.abbreviationDst = json["abbreviation_DST"]; + } + + Map toJson() { + final Map data = new Map(); + data["name"] = this.name; + data["offset_STD"] = this.offsetStd; + data["offset_STD_seconds"] = this.offsetStdSeconds; + data["offset_DST"] = this.offsetDst; + data["offset_DST_seconds"] = this.offsetDstSeconds; + data["abbreviation_STD"] = this.abbreviationStd; + data["abbreviation_DST"] = this.abbreviationDst; + return data; + } + + @override + String toString() { + return 'Timezone{name: $name, offsetStd: $offsetStd, offsetStdSeconds: $offsetStdSeconds, offsetDst: $offsetDst, offsetDstSeconds: $offsetDstSeconds, abbreviationStd: $abbreviationStd, abbreviationDst: $abbreviationDst}'; + } +} + +class Datasource { + String? sourcename; + String? attribution; + String? license; + String? url; + + Datasource({this.sourcename, this.attribution, this.license, this.url}); + + Datasource.fromJson(Map json) { + this.sourcename = json["sourcename"]; + this.attribution = json["attribution"]; + this.license = json["license"]; + this.url = json["url"]; + } + + Map toJson() { + final Map data = new Map(); + data["sourcename"] = this.sourcename; + data["attribution"] = this.attribution; + data["license"] = this.license; + data["url"] = this.url; + return data; + } + + @override + String toString() { + return 'Datasource{sourcename: $sourcename, attribution: $attribution, license: $license, url: $url}'; + } +} diff --git a/lib/main.dart b/lib/main.dart index d1b846b..443ccbc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,14 +18,7 @@ AndroidMapRenderer mapRenderer = AndroidMapRenderer.platformDefault; void main() { WidgetsFlutterBinding.ensureInitialized(); - /*final GoogleMapsFlutterPlatform mapsImplementation = - GoogleMapsFlutterPlatform.instance; - if (mapsImplementation is GoogleMapsFlutterAndroid) { - // Force Hybrid Composition mode. - mapsImplementation.useAndroidViewSurface = true; - mapRenderer = await mapsImplementation - .initializeWithRenderer(AndroidMapRenderer.latest); - }*/ + Get.lazyPut(() => LGConnection(),fenix: true); Get.lazyPut(() => ApiManager(),fenix: true); Get.lazyPut(() => SpeechController(),fenix: true); diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index dac247d..4f8e128 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -104,7 +104,7 @@ class _DashBoardState extends State { width: screenHeight * 0.1, child: Icon( Icons.settings, - size: 60, + size: screenHeight*0.07, color: Colors.white, ), onTap: () async { diff --git a/lib/screens/kml_builder.dart b/lib/screens/kml_builder.dart index c5ccc31..dba08c4 100644 --- a/lib/screens/kml_builder.dart +++ b/lib/screens/kml_builder.dart @@ -14,7 +14,8 @@ import 'package:super_liquid_galaxy_controller/screens/test.dart'; import 'package:super_liquid_galaxy_controller/utils/galaxy_colors.dart'; import 'package:super_liquid_galaxy_controller/utils/kmlgenerator.dart'; import 'package:super_liquid_galaxy_controller/utils/lg_connection.dart'; - +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; import '../data_class/kml_element.dart'; import '../data_class/map_position.dart'; import '../generated/assets.dart'; @@ -141,7 +142,7 @@ class _KmlUploaderState extends State { fontWeight: FontWeight.w400, fontSize: 25.0), ), - Divider( + const Divider( thickness: 1.0, ), const SizedBox( @@ -252,6 +253,8 @@ class _KmlUploaderState extends State { //await sshClient.clearKml(); File? file = await sshClient.makeFile(filename, KMLGenerator.generateCustomKml('slave_1', kmlList)); + + //Get.to(()=>TestScreen(kml: KMLGenerator.generateCustomKml('slave_1', kmlList))); //String kml = KMLGenerator.generateCustomKml('slave_1', kmlList); print("made successfully"); await sshClient.kmlFileUpload( @@ -266,11 +269,15 @@ class _KmlUploaderState extends State { GalaxyButton( height: screenHeight * 0.1, width: screenWidth * 0.31, - actionText: "VISUALIZE IN MAP", - icon: Icons.map, + actionText: "DOWNLOAD KML", + icon: Icons.save_alt_rounded, isLeading: true, onTap: () async { - await sshClient.clearKml(); + //await sshClient.clearKml(); + String kml = KMLGenerator.generateCustomKml('slave_1', kmlList); + saveStringToExternalStorageWithProgress(kml, 'custom_kml', 'kml', (progress){ + print(progress); + }); }, backgroundColor: GalaxyColors.blue.withOpacity(0.4), ) @@ -444,4 +451,52 @@ class _KmlUploaderState extends State { } } } + Future saveStringToExternalStorageWithProgress( + String content, String filename, String extension, Function(double) onProgress) async { + // Request storage permissions + if (await Permission.manageExternalStorage.request().isGranted) { + // Get the external storage directory + Directory? directory = await getExternalStorageDirectory(); + if (directory != null) { + // Create the file path with the custom extension + String path = '${directory.path}/$filename.$extension'; + // Write the string content to the file in chunks + File file = File(path); + RandomAccessFile raf = await file.open(mode: FileMode.write); + int totalLength = content.length; + int chunkSize = 1024; // Write in chunks of 1KB + int writtenLength = 0; + + for (int i = 0; i < totalLength; i += chunkSize) { + int end = (i + chunkSize < totalLength) ? i + chunkSize : totalLength; + await raf.writeString(content.substring(i, end)); + writtenLength += (end - i); + // Calculate and report progress + double progress = writtenLength / totalLength; + onProgress(progress); + } + + await raf.close(); + print('File saved at $path'); + showSuccessSnackbar("File saved at $path"); + } else { + print('External storage directory not found'); + } + } else { + print('Storage permission denied'); + } + + } + + void showSuccessSnackbar(String message) { + if (!Get.isSnackbarOpen) { + Get.showSnackbar(GetSnackBar( + backgroundColor: Colors.green.shade300, + title: "Download Successful", + message: message, + isDismissible: true, + duration: 5.seconds, + )); + } + } } diff --git a/lib/screens/splashscreen.dart b/lib/screens/splashscreen.dart index 937e0ae..d22bbf1 100644 --- a/lib/screens/splashscreen.dart +++ b/lib/screens/splashscreen.dart @@ -13,10 +13,6 @@ class SplashScreen extends StatefulWidget { } class _SplashScreenState extends State { - - - - @override Widget build(BuildContext context) { return AnimatedSplashScreen.withScreenFunction( diff --git a/lib/screens/tour_builder.dart b/lib/screens/tour_builder.dart index e072c7a..958e994 100644 --- a/lib/screens/tour_builder.dart +++ b/lib/screens/tour_builder.dart @@ -27,7 +27,7 @@ class _TourBuilderState extends State { @override void initState() { tourController = Get.find(); - _determinePosition(); + //_determinePosition(); super.initState(); } @@ -104,7 +104,7 @@ class _TourBuilderState extends State { Colors.grey.withOpacity(0.2) ]), child: ImageIcon( - AssetImage(Assets.iconsSshIndicator), + const AssetImage(Assets.iconsSshIndicator), color: tourController.connectionClient.isConnected.value ? Colors.green @@ -125,7 +125,7 @@ class _TourBuilderState extends State { Colors.grey.withOpacity(0.2), ]), child: ImageIcon( - AssetImage(Assets.iconsApiIndicator), + const AssetImage(Assets.iconsApiIndicator), color: tourController.apiClient.isConnected.value ? Colors.green : Colors.red, @@ -183,8 +183,8 @@ class _TourBuilderState extends State { width: screenWidth * 0.25, iconSize: screenHeight * 0.1, searchSize: screenWidth * 0.85, - submitData: (Coordinates point) { - setSearchAround(point); + submitData: (Coordinates point, String label) { + setSearchAround(point,label); }, ) ], @@ -265,13 +265,13 @@ class _TourBuilderState extends State { //_getCity(locator.latitude, locator.longitude); setState(() { setSearchAround( - Coordinates(latitude: point.latitude, longitude: point.longitude)); + Coordinates(latitude: point.latitude, longitude: point.longitude),'Default'); }); } - void setSearchAround(Coordinates point) { + void setSearchAround(Coordinates point, String label) { setState(() { - tourController.setSearchAround(point); + tourController.setSearchAround(point,label); }); print(tourController.getSearchAround()); } diff --git a/lib/utils/api_manager.dart b/lib/utils/api_manager.dart index bfeba9c..219390a 100644 --- a/lib/utils/api_manager.dart +++ b/lib/utils/api_manager.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart' as getx; import 'package:shared_preferences/shared_preferences.dart'; import 'package:super_liquid_galaxy_controller/data_class/api_error.dart'; +import 'package:super_liquid_galaxy_controller/data_class/coordinate.dart'; +import 'package:super_liquid_galaxy_controller/data_class/geo_reversecode_response.dart'; class ApiManager extends getx.GetxController { late String _placesApiKey; @@ -17,6 +19,8 @@ class ApiManager extends getx.GetxController { //end-points static const autocompleteEndPoint = "/geocode/autocomplete"; static const placesEndPoint = "/places"; + static const geocodeEndPoint = "/geocode/search"; + static const reverseGeoCodeEndPoint = "/geocode/reverse"; /*ApiManager._privateConstructor() { print("instance created"); @@ -60,6 +64,45 @@ class ApiManager extends getx.GetxController { return response; } + Future getGeoCodeResponse(String text, String searchLevel) async { + await _connectApi(1); + var response = await _apiClient.get(geocodeEndPoint, queryParameters: { + 'text': text, + 'apiKey': _placesApiKey.trim(), + 'format': 'json', + 'type': searchLevel + }); + if (response.statusCode != 200) { + handleError(response); + } + return response; + } + + Future getReverseGeoCodeResponse( + Coordinates point, String searchLevel) async { + await _connectApi(1); + bool hasType = searchLevel.compareTo("none") != 0; + print("hasType: $hasType"); + var response = hasType + ? await _apiClient.get(reverseGeoCodeEndPoint, queryParameters: { + 'lat': point.latitude.toString(), + 'lon': point.longitude.toString(), + 'apiKey': _placesApiKey.trim(), + 'format': 'json', + 'type': searchLevel + }) + : await _apiClient.get(reverseGeoCodeEndPoint, queryParameters: { + 'lat': point.latitude, + 'lon': point.longitude, + 'apiKey': _placesApiKey.trim(), + 'format': 'json' + }); + if (response.statusCode != 200) { + handleError(response); + } + return response; + } + void handleError(Response response) { var errorResponse = ApiErrorResponse.fromJson(response.data); print('error : ${response.statusMessage}'); @@ -77,25 +120,49 @@ class ApiManager extends getx.GetxController { testApiKey() async { await _connectApi(1); try { - var response = await _apiClient.get( - autocompleteEndPoint, - queryParameters: {'text': "test", - 'apiKey': _placesApiKey.trim() - }); + var response = await _apiClient.get(autocompleteEndPoint, + queryParameters: {'text': "test", 'apiKey': _placesApiKey.trim()}); if (response.statusCode == 200) { isConnected.value = true; - } - else { + } else { var error = ApiErrorResponse.fromJson(response.data); isConnected.value = false; print('${error.error} - ${error.message}'); } + } catch (e) { + print(e); + isConnected.value = false; } - catch(e) - { - print(e); - isConnected.value = false; + } + + Future tryResponseFromPoint(Coordinates searchAroundCoords, bool isCountry) async { + Response response = await getReverseGeoCodeResponse( + searchAroundCoords, isCountry ? 'country' : 'state'); + if (response.statusCode != 200) { + return ''; + } + var responseObj = GeoReverseCodeResponse.fromJson(response.data); + + if (responseObj.results != null && responseObj.results!.isNotEmpty) { + return responseObj.results![0].placeId!; + } + else + { + Response response2 = await getReverseGeoCodeResponse( + searchAroundCoords, 'none'); + var responseObj2 = GeoReverseCodeResponse.fromJson(response2.data); + print(responseObj2); + if (response2.statusCode != 200) { + return ''; + } + if (responseObj2.results != null && responseObj2.results!.isNotEmpty) { + return responseObj2.results![0].placeId!; } + else + { + return ''; + } + } } } diff --git a/lib/utils/kmlgenerator.dart b/lib/utils/kmlgenerator.dart index d6ae23e..2da7705 100644 --- a/lib/utils/kmlgenerator.dart +++ b/lib/utils/kmlgenerator.dart @@ -1,8 +1,10 @@ +import 'dart:math' as Math; +import 'dart:math'; + +import 'package:latlong2/latlong.dart'; import 'package:super_liquid_galaxy_controller/data_class/coordinate.dart'; import 'package:super_liquid_galaxy_controller/data_class/kml_element.dart'; import 'package:super_liquid_galaxy_controller/utils/geo_utils.dart'; -import 'package:latlong2/latlong.dart'; -import 'dart:math' as Math; class KMLGenerator { static String generateBlank(String id) { @@ -25,14 +27,11 @@ class KMLGenerator { '''; - - static getCoordinateList(List list) - { + static getCoordinateList(List list) { var coordinates = ''; - for(final coordinate in list) - { - coordinates += '${coordinate.longitude},${coordinate.latitude},0 '; - } + for (final coordinate in list) { + coordinates += '${coordinate.longitude},${coordinate.latitude},0 '; + } return '''${coordinates}'''; } @@ -68,37 +67,37 @@ class KMLGenerator { var visList = ''; List coordsList = []; for (final item in list) { - switch (item.index) { case 0: { - Placemark element = item.elementData; - visList+=getPlacemark(element); - coordsList.add(element.coordinate); + Placemark element = item.elementData; + visList += getPlacemark(element); + coordsList.add(element.coordinate); } case 1: { LineString element = item.elementData; - visList+=getLineString(element); + visList += getLineString(element); coordsList.addAll(element.coordinates); } case 2: { PolyGon element = item.elementData; - visList+=getLinearRing(element); + element.coordinates.add(element.coordinates[0]); + visList += getLinearRing(element); coordsList.addAll(element.coordinates); } default: { Placemark element = item.elementData; - visList+=getPlacemark(element); + visList += getPlacemark(element); coordsList.add(element.coordinate); } } } - var lookAt =''; - lookAt+= GeoUtils.calculateLookAt(coordsList,45); + var lookAt = ''; + lookAt += GeoUtils.calculateLookAt(coordsList, 45); return ''' @@ -127,6 +126,7 @@ class KMLGenerator { '''; } + static String generateKml(String id, String kml) { return ''' @@ -141,6 +141,12 @@ class KMLGenerator { 7f00ff00 + + + 1 + + ${start.longitude},${start.latitude},0 + ${end.longitude},${end.latitude},0 + + + + ''');*/ + + var bearing = distance.bearing(start, end); + LatLng roadStart1 = + distance.offset(start, gapLength, bearing - 90); + LatLng roadStart2 = + distance.offset(start, gapLength, bearing + 90); + LatLng roadEnd1 = + distance.offset(end, gapLength, bearing + 90); + LatLng roadEnd2 = + distance.offset(end, gapLength, bearing - 90); + kmlSegments.add(''' + + + + + 1 + + ${getCoordinateList([ + Coordinates.fromLatLng(roadEnd1), + Coordinates.fromLatLng(roadEnd2), + Coordinates.fromLatLng(roadStart1), + Coordinates.fromLatLng(roadStart2), + Coordinates.fromLatLng(roadEnd1), + ])} + + + + + '''); + for (int i = 0; i < numSegments; i++) { // Calculate the start point of the dash - LatLng dashStart = distance.offset(start, i * segmentLength, distance.bearing(start, end)); + LatLng dashStart = distance.offset( + start, i * segmentLength, distance.bearing(start, end)); // Calculate the end point of the dash - LatLng dashEnd = distance.offset(dashStart, dashLength, distance.bearing(start, end)); - - + LatLng dashEnd = + distance.offset(dashStart, dashLength, distance.bearing(start, end)); kmlSegments.add(''' + #rs ${dashStart.longitude},${dashStart.latitude},0 @@ -382,12 +449,59 @@ class KMLGenerator { return generateKml('69', kml); } + static double smoothCurve(double x) { + // Ensure x is within the range [0, 1] + if (x < 0) return 0; + if (x > 1) return 0; + return sin(pi * x); + } + static String generateAirplaneTrack( + LatLng start, LatLng end, double dashLength, double gapLength) { + final Distance distance = Distance(); + final double totalDistance = distance(start, end); + final double segmentLength = dashLength + gapLength; + final int numSegments = (totalDistance / segmentLength).floor(); + + List kmlSegments = []; + const double maxHeight = 500000.0; + var stepCount = numSegments*2; + var stepSize = 1.0/stepCount; + var steps = 0.0; + // var altitude = 0.0; + for (int i = 0; i < numSegments; i++) { + // Calculate the start point of the dash + LatLng dashStart = distance.offset( + start, i * segmentLength, distance.bearing(start, end)); + // Calculate the end point of the dash + LatLng dashEnd = + distance.offset(dashStart, dashLength, distance.bearing(start, end)); + var altitude1 = smoothCurve(steps)*maxHeight; + steps+=stepSize; + var altitude2 = smoothCurve(steps)*maxHeight; + steps+=stepSize; + kmlSegments.add(''' + + + relativeToGround + + ${dashStart.longitude},${dashStart.latitude},$altitude1 + ${dashEnd.longitude},${dashEnd.latitude},$altitude2 + + + + '''); + } + String kml = ''; + kml += kmlSegments.join(); + //kml += ''; + return generateKml('69', kml); + } - static generateFootprints(LatLng start, LatLng end, double dashLength, double gapLength) - { + static generateFootprints( + LatLng start, LatLng end, double dashLength, double gapLength) { final Distance distance = Distance(); final double totalDistance = distance(start, end); final double segmentLength = dashLength + gapLength; @@ -398,18 +512,21 @@ class KMLGenerator { List> pointList = []; List kmlSegments = []; - for(int i =0;i ellipsePoints = []; @@ -429,18 +550,22 @@ class KMLGenerator { print("kml: top point -$topPoint"); print("kml: bottom point -$bottomPoint"); - for (int i = 0; i <=360; i += 5) { + for (int i = 0; i <= 360; i += 5) { double theta = i * (Math.pi / 180); - double rotatedX = (length/2.0) * Math.cos(theta); - double rotatedY = (width/2.0) * Math.sin(theta); + double rotatedX = (length / 2.0) * Math.cos(theta); + double rotatedY = (width / 2.0) * Math.sin(theta); // double rotatedX = x * Math.cos(angle) - y * Math.sin(angle); // double rotatedY = x * Math.sin(angle) + y * Math.cos(angle); - LatLng point = distance.offset(center, Math.sqrt(rotatedX * rotatedX + rotatedY * rotatedY), + LatLng point = distance.offset( + center, + Math.sqrt(rotatedX * rotatedX + rotatedY * rotatedY), Math.atan2(rotatedY, rotatedX) * (180.0 / Math.pi)); - if(((Math.atan2(rotatedY, rotatedX) * (180.0 / Math.pi) )- angle).abs()<=2) - print("kml: point debug - $point, ${Math.atan2(rotatedY, rotatedX) * (180.0 / Math.pi)}"); + if (((Math.atan2(rotatedY, rotatedX) * (180.0 / Math.pi)) - angle) + .abs() <= + 2) + print( + "kml: point debug - $point, ${Math.atan2(rotatedY, rotatedX) * (180.0 / Math.pi)}"); ellipsePoints.add(point); - } String kml = ''' @@ -466,7 +591,5 @@ class KMLGenerator { '''; return kml; - } - } diff --git a/lib/utils/tour_controller.dart b/lib/utils/tour_controller.dart index fa18d59..042ada6 100644 --- a/lib/utils/tour_controller.dart +++ b/lib/utils/tour_controller.dart @@ -1,30 +1,48 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; +import 'package:dio/dio.dart'; +import 'package:get/get.dart' as getx; +import 'package:super_liquid_galaxy_controller/data_class/geocode_response.dart'; import 'package:super_liquid_galaxy_controller/utils/api_manager.dart'; import 'package:super_liquid_galaxy_controller/utils/lg_connection.dart'; import '../data_class/coordinate.dart'; -class TourController extends GetxController -{ +class TourController extends getx.GetxController { late ApiManager apiClient; late LGConnection connectionClient; var _searchAroundCoords = Coordinates(latitude: 12.0, longitude: 12.0).obs; - + String _label = ''; @override void onInit() { - apiClient = Get.find(); - connectionClient = Get.find(); + apiClient = getx.Get.find(); + connectionClient = getx.Get.find(); super.onInit(); } - void setSearchAround(Coordinates point) - { + void setSearchAround(Coordinates point, String label) { _searchAroundCoords.value = point; + _label = label; + getPlaceBounds(); } - Coordinates getSearchAround() - { + + Coordinates getSearchAround() { return _searchAroundCoords.value; } -} \ No newline at end of file + + void getPlaceBounds() async { + + var queryText = _label.split('\n').reversed.join(", "); + bool isCountry = _label.split('\n').where((String str) { + return str.isNotEmpty; + }).length == 1; + if(isCountry) + { + queryText = _label.split('\n')[0]; + } + print(queryText); + String placeID = await apiClient.tryResponseFromPoint( + _searchAroundCoords.value, isCountry); + print("id: $placeID"); + + } +} diff --git a/pubspec.lock b/pubspec.lock index af61ef5..f1aee90 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + change_app_package_name: + dependency: "direct dev" + description: + name: change_app_package_name + sha256: "494e7943d4e8ba6a70970bf0fac293c866a121c50096dc9e027565bfbfc5c7a1" + url: "https://pub.dev" + source: hosted + version: "1.2.0" characters: dependency: transitive description: @@ -592,6 +600,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + permission_handler: + dependency: "direct dev" + description: + name: permission_handler + sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 + url: "https://pub.dev" + source: hosted + version: "10.4.5" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" + url: "https://pub.dev" + source: hosted + version: "10.3.6" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + url: "https://pub.dev" + source: hosted + version: "9.1.4" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + url: "https://pub.dev" + source: hosted + version: "3.12.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + url: "https://pub.dev" + source: hosted + version: "0.1.3" petitparser: dependency: transitive description: @@ -656,30 +704,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - sealed_countries: - dependency: "direct dev" - description: - name: sealed_countries - sha256: e5c7436ebdc6c3fd332c3bc16831fd4e69d5ade6f3123d33791b6be29fcdb91b - url: "https://pub.dev" - source: hosted - version: "1.5.0" - sealed_currencies: - dependency: transitive - description: - name: sealed_currencies - sha256: d02f259ffa25fa1b227478edd00ffb7bc78860168611be97aab4bcab984b9a69 - url: "https://pub.dev" - source: hosted - version: "1.5.0" - sealed_languages: - dependency: transitive - description: - name: sealed_languages - sha256: "060ef61bf1b42cccdf18d065707ed9a75905fe7b78a6e65aec9a2e876489253e" - url: "https://pub.dev" - source: hosted - version: "1.5.0" shared_preferences: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 511fc2c..cb93734 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + change_app_package_name: ^1.2.0 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is @@ -61,8 +62,8 @@ dev_dependencies: path_provider: ^2.1.3 speech_to_text: ^6.6.0 avatar_glow: ^3.0.1 - sealed_countries: ^1.5.0 geocoding: ^3.0.0 + permission_handler: ^10.2.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 94586cc..ce843bc 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { GeolocatorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("GeolocatorWindows")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index f0bcafd..b3ea692 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST geolocator_windows + permission_handler_windows url_launcher_windows )