From c7c96f913f44f0e4a300179869cbc215582f110e Mon Sep 17 00:00:00 2001 From: Zohaib Iqbal Kambrani <> Date: Tue, 30 Mar 2021 17:05:15 +0300 Subject: [PATCH] no message --- .../.docs/images/homeScreen.jpg | Bin 0 -> 124669 bytes hms-plugins/flutter-hms-location/CHANGELOG.md | 12 + hms-plugins/flutter-hms-location/LICENSE | 53 + hms-plugins/flutter-hms-location/README.md | 1737 +++++++++++++++++ .../flutter-hms-location/android/build.gradle | 45 + .../android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter-hms-location/android/gradlew | 183 ++ .../flutter-hms-location/android/gradlew.bat | 103 + .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 5 + .../hms/flutter/location/LocationPlugin.java | 201 ++ .../flutter/location/constants/Action.java | 24 + .../flutter/location/constants/Channel.java | 40 + .../hms/flutter/location/constants/Error.java | 33 + .../ActivityConversionStreamHandler.java | 53 + .../ActivityIdentificationMethodHandler.java | 141 ++ .../ActivityIdentificationStreamHandler.java | 53 + .../handlers/FusedLocationMethodHandler.java | 265 +++ .../handlers/FusedLocationStreamHandler.java | 51 + .../handlers/GeofenceMethodHandler.java | 119 ++ .../handlers/GeofenceStreamHandler.java | 53 + .../handlers/HMSLoggerMethodHandler.java | 51 + .../handlers/LocationCallbackHandler.java | 71 + .../location/handlers/PermissionHandler.java | 192 ++ .../listeners/DefaultFailureListener.java | 47 + .../listeners/DefaultSuccessListener.java | 79 + .../LocationSettingsFailureListener.java | 62 + .../RemoveUpdatesSuccessListener.java | 50 + .../RequestUpdatesFailureListener.java | 55 + .../RequestUpdatesSuccessListener.java | 45 + .../flutter/location/logger/HMSLogger.java | 449 +++++ .../ActivityConversionBroadcastReceiver.java | 45 + ...tivityIdentificationBroadcastReceiver.java | 45 + .../FusedLocationBroadcastReceiver.java | 46 + .../receivers/GeofenceBroadcastReceiver.java | 44 + .../flutter/location/utils/ActivityUtils.java | 140 ++ .../flutter/location/utils/GeofenceUtils.java | 98 + .../flutter/location/utils/LocationUtils.java | 309 +++ .../flutter/location/utils/ObjectUtils.java | 31 + .../flutter/location/utils/ValueGetter.java | 112 ++ .../flutter-hms-location/ios/Assets/.gitkeep | 0 .../ios/Classes/LocationPlugin.h | 4 + .../ios/Classes/LocationPlugin.m | 20 + .../ios/huawei_location.podspec | 23 + .../activity/activity_conversion_data.dart | 76 + .../activity/activity_conversion_info.dart | 73 + .../activity_conversion_response.dart | 71 + .../activity_identification_data.dart | 94 + .../activity_identification_response.dart | 106 + .../activity_identification_service.dart | 98 + .../lib/geofence/geofence.dart | 113 ++ .../lib/geofence/geofence_data.dart | 88 + .../lib/geofence/geofence_request.dart | 91 + .../lib/geofence/geofence_service.dart | 72 + .../fused_location_provider_client.dart | 158 ++ .../lib/location/hwlocation.dart | 207 ++ .../lib/location/location.dart | 141 ++ .../lib/location/location_availability.dart | 85 + .../lib/location/location_callback.dart | 31 + .../lib/location/location_request.dart | 231 +++ .../lib/location/location_result.dart | 98 + .../location/location_settings_request.dart | 81 + .../location/location_settings_states.dart | 107 + .../lib/location/navigation_request.dart | 82 + .../lib/location/navigation_result.dart | 53 + .../lib/logger/hmslogger.dart | 30 + .../lib/permission/permission_handler.dart | 63 + hms-plugins/flutter-hms-location/pubspec.yaml | 24 + lib/pages/TestPage.dart | 41 + lib/routes.dart | 3 + lib/uitl/location_util.dart | 126 +- lib/uitl/utils.dart | 17 +- pubspec.yaml | 4 + 74 files changed, 7638 insertions(+), 25 deletions(-) create mode 100644 hms-plugins/flutter-hms-location/.docs/images/homeScreen.jpg create mode 100644 hms-plugins/flutter-hms-location/CHANGELOG.md create mode 100644 hms-plugins/flutter-hms-location/LICENSE create mode 100644 hms-plugins/flutter-hms-location/README.md create mode 100644 hms-plugins/flutter-hms-location/android/build.gradle create mode 100644 hms-plugins/flutter-hms-location/android/gradle.properties create mode 100644 hms-plugins/flutter-hms-location/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 hms-plugins/flutter-hms-location/android/gradlew create mode 100644 hms-plugins/flutter-hms-location/android/gradlew.bat create mode 100644 hms-plugins/flutter-hms-location/android/settings.gradle create mode 100644 hms-plugins/flutter-hms-location/android/src/main/AndroidManifest.xml create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/LocationPlugin.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Action.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Channel.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Error.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityConversionStreamHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationMethodHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationStreamHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationMethodHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationStreamHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceMethodHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceStreamHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/HMSLoggerMethodHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/LocationCallbackHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/PermissionHandler.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultFailureListener.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultSuccessListener.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/LocationSettingsFailureListener.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RemoveUpdatesSuccessListener.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesFailureListener.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesSuccessListener.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/logger/HMSLogger.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityConversionBroadcastReceiver.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityIdentificationBroadcastReceiver.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/FusedLocationBroadcastReceiver.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/GeofenceBroadcastReceiver.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ActivityUtils.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/GeofenceUtils.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/LocationUtils.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ObjectUtils.java create mode 100644 hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ValueGetter.java create mode 100644 hms-plugins/flutter-hms-location/ios/Assets/.gitkeep create mode 100644 hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.h create mode 100644 hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.m create mode 100644 hms-plugins/flutter-hms-location/ios/huawei_location.podspec create mode 100644 hms-plugins/flutter-hms-location/lib/activity/activity_conversion_data.dart create mode 100644 hms-plugins/flutter-hms-location/lib/activity/activity_conversion_info.dart create mode 100644 hms-plugins/flutter-hms-location/lib/activity/activity_conversion_response.dart create mode 100644 hms-plugins/flutter-hms-location/lib/activity/activity_identification_data.dart create mode 100644 hms-plugins/flutter-hms-location/lib/activity/activity_identification_response.dart create mode 100644 hms-plugins/flutter-hms-location/lib/activity/activity_identification_service.dart create mode 100644 hms-plugins/flutter-hms-location/lib/geofence/geofence.dart create mode 100644 hms-plugins/flutter-hms-location/lib/geofence/geofence_data.dart create mode 100644 hms-plugins/flutter-hms-location/lib/geofence/geofence_request.dart create mode 100644 hms-plugins/flutter-hms-location/lib/geofence/geofence_service.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/fused_location_provider_client.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/hwlocation.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location_availability.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location_callback.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location_request.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location_result.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location_settings_request.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/location_settings_states.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/navigation_request.dart create mode 100644 hms-plugins/flutter-hms-location/lib/location/navigation_result.dart create mode 100644 hms-plugins/flutter-hms-location/lib/logger/hmslogger.dart create mode 100644 hms-plugins/flutter-hms-location/lib/permission/permission_handler.dart create mode 100644 hms-plugins/flutter-hms-location/pubspec.yaml create mode 100644 lib/pages/TestPage.dart diff --git a/hms-plugins/flutter-hms-location/.docs/images/homeScreen.jpg b/hms-plugins/flutter-hms-location/.docs/images/homeScreen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..abfc5b99f54713a6df86253e40f5c67dc43f763d GIT binary patch literal 124669 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 literal 0 HcmV?d00001 diff --git a/hms-plugins/flutter-hms-location/CHANGELOG.md b/hms-plugins/flutter-hms-location/CHANGELOG.md new file mode 100644 index 00000000..8c43b7b8 --- /dev/null +++ b/hms-plugins/flutter-hms-location/CHANGELOG.md @@ -0,0 +1,12 @@ +## 5.0.0+301 + +* Added the getNavigationContextState function. +* Added the enableLogger and disableLogger methods. + +## 4.0.4+1/4.0.4+300 + +* Fix minor issues. + +## 4.0.4 + +* Initial release. diff --git a/hms-plugins/flutter-hms-location/LICENSE b/hms-plugins/flutter-hms-location/LICENSE new file mode 100644 index 00000000..7783de53 --- /dev/null +++ b/hms-plugins/flutter-hms-location/LICENSE @@ -0,0 +1,53 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and +You must cause any modified files to carry prominent notices stating that You changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/hms-plugins/flutter-hms-location/README.md b/hms-plugins/flutter-hms-location/README.md new file mode 100644 index 00000000..cb69c34a --- /dev/null +++ b/hms-plugins/flutter-hms-location/README.md @@ -0,0 +1,1737 @@ +# Huawei Location Kit Flutter Plugin + +## Table of Contents +* [Introduction](#introduction) +* [Installation Guide](#installation-guide) + * [Creating Project in App Gallery Connect](#creating-project-in-app-gallery-connect) + * [Configuring the Signing Certificate Fingerprint](#configuring-the-signing-certificate-fingerprint) + * [Integrating the Flutter Location Plugin](#integrating-the-flutter-location-plugin) +* [API Reference](#api-reference) + * [Overview](#overview) + * [FusedLocationProviderClient](#fusedlocationproviderclient) + * [Location](#location) + * [HWLocation](#hwlocation) + * [LocationAvailability](#locationavailability) + * [LocationCallback](#locationcallback) + * [LocationRequest](#locationrequest) + * [LocationResult](#locationresult) + * [LocationSettingsRequest](#locationsettingsrequest) + * [LocationSettingsStates](#locationsettingsstates) + * [NavigationResult](#navigationresult) + * [NavigationRequest](#navigationrequest) + * [ActivityIdentificationService](#activityidentificationservice) + * [ActivityConversionData](#activityconversiondata) + * [ActivityConversionInfo](#activityconversioninfo) + * [ActivityConversionResponse](#activityconversionresponse) + * [ActivityIdentificationData](#activityidentificationdata) + * [ActivityIdentificationResponse](#activityidentificationresponse) + * [Geofence Service](#geofence-service) + * [GeofenceData](#geofencedata) + * [GeofenceRequest](#geofencerequest) + * [PermissionHandler](#permissionhandler) + * [HMSLogger](#hmslogger) + +* [Configuration Description](#configuration-description) +* [Sample Project](#sample-project) +* [Questions or Issues](#questions-or-issues) +* [Licensing and Terms](#licensing-and-terms) + +## Introduction + +This plugin enables communication between Huawei Location SDK and Flutter platform. Huawei Location Kit combines the GPS, Wi-Fi, and base station locations to help you quickly obtain precise user locations, build up global positioning capabilities, and reach a wide range of users around the globe.Currently, it provides the four main capabilities: fused location, location semantics, activity identification, and geofence. You can call relevant capabilities as needed. + +Huawei Location Kit provides the following core capabilities: +- **Fused location**: Provides a set of simple and easy-to-use APIs for you to quickly obtain the device location based on the GPS, Wi-Fi, and base station location data. +- **Activity identification**: Identifies user motion status through the acceleration sensor, cellular network information, and magnetometer, helping you adjust your app based on user behavior. +- **Geofence**: Allows you to set an interested area through an API so that your app can receive a notification when a specified action (such as leaving, entering, or lingering in the area) occurs. + +## Installation Guide + +- Before you get started, you must register as a HUAWEI developer and complete identity verification on the [HUAWEI Developer](https://developer.huawei.com/consumer/en/) website. For details, please refer to [Register a HUAWEI ID](https://developer.huawei.com/consumer/en/doc/10104). + +- Create an app in your project is required in AppGallery Connect in order to communicate with Huawei services. To create an app, perform the following steps: + +### Creating Project in App Gallery Connect + +**Step 1.** Sign in to [AppGallery Connect](https://developer.huawei.com/consumer/en) and select My projects. + +**Step 2.** Click your project from the project list. + +**Step 3.** Go to **Project Setting** > **General information**, and click **Add app**. +If an app exists in the project, and you need to add a new one, expand the app selection area on the top of the page and click **Add app**. + +**Step 4.** On the **Add app** page, enter app information, and click **OK**. + +- A signing certificate fingerprint is used to verify the authenticity of an app when it attempts to access an HMS Core service through the HMS Core SDK. Before using HMS Core (APK), you must locally generate a signing certificate fingerprint and configure it in AppGallery Connect. Ensure that the JDK has been installed on your computer. + +### Configuring the Signing Certificate Fingerprint +**Step 1:** Go to **Project Setting** > **General information**. In the **App information** field, click the icon next to SHA-256 certificate fingerprint, and enter the obtained **SHA256 certificate fingerprint**. + +**Step 2:** After completing the configuration, click **OK**. (Check mark icon) + +### Integrating Flutter Location Plugin + +**Step 1:** Sign in [AppGallery Connect](https://developer.huawei.com/consumer/en/service/josp/agc/index.html) to AppGallery Connect and select **My projects**. + +**Step 2:** Find your app project, and click the desired app name. + +**Step 3:** Go to **Project Setting > > General information**. In the **App information** field, click **agconnect-service.json** to download configuration file. + +**Step 4:** Create a Flutter project if you do not have one. + +**Step 5:** Copy the **agconnect-service.json** file to the **android/app** directory of your Flutter project. + +**Step 6:** Copy the signature file that is generated in **Generating a Signature File** to the android/app directory of your Flutter project. + +**Step 7:** Check whether the **agconnect-services.json** file and signature file are successfully added to the **android/app** directory of the Flutter project. + + + +**Step 8:** Open the **build.gradle** file in the **android** directory of your Flutter project. + - Go to **buildscript** then configure the Maven repository address and agconnect plugin for the HMS SDK. + + ```gradle + buildscript { + repositories { + google() + jcenter() + maven { url 'https://developer.huawei.com/repo/' } + } + + dependencies { + /* + * + */ + classpath 'com.huawei.agconnect:agcp:1.4.1.300' + } + } + ``` + + - Go to **allprojects** then configure the Maven repository address for the HMS SDK. + + ```gradle + allprojects { + repositories { + google() + jcenter() + maven { url 'https://developer.huawei.com/repo/' } + } + } + ``` + +**Step 9:** Open the **build.gradle** file in the **android/app** directory. +- Add `apply plugin: 'com.huawei.agconnect'` line after other `apply` entries. + + ```gradle + apply plugin: 'com.android.application' + apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + apply plugin: 'com.huawei.agconnect' + ``` + + - Set your package name in **defaultConfig** > **applicationId** and set **minSdkVersion** to **19** or **higher**. + Package name must match with the **package_name** entry in **agconnect-services.json** file. + + ```gradle + defaultConfig { + applicationId "" + minSdkVersion 17 + /* + * + */ + } + ``` + - Copy the signature file that generated in [Generating a Signing Certificate](https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#3) to **android/app** directory. + + - Configure the signature in **android** according to the signature file information. + +```gradle +signingConfigs { + config { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } +} +buildTypes { + debug { + signingConfig signingConfigs.config + } + release { + signingConfig signingConfigs.config + } +} +``` + +**Step 10:** On your Flutter project directory find and open your **pubspec.yaml** file and add library to dependencies. For more details please refer the [Using packages](https://flutter.dev/docs/development/packages-and-plugins/using-packages#dependencies-on-unpublished-packages) document. + + ```yaml + dependencies: + huawei_location: + # Replace {library path} with actual library path of Huawei Push Kit Location for Flutter. + path: {library path} + ``` + +-**Step 11:** Run following command to update package info. + +``` +[project_path]> flutter pub get +``` + +**Step 12:** Run following command to start the app. + +``` +[project_path]> flutter run +``` + +## API Reference + +### Overview + +| Module | Description | +|----------------------------------|------------------------------------------------------------------------------------------------| +| FusedLocationProviderClient | With this module you can check the device location settings, get the last known location information once or continuously, set mock location and others. | +| ActivityIdentificationService | If your app needs to obtain the activity status of the user's device (for example, walking, running, or bicycling) or your app needs to detect activity status change of a user, you can use this module. | +| GeofenceService | If you are interested in a place, you can create a geofence based on the place. When the device enters the geofence or stays for a duration of time, a notification can be sent to your app. | + +### FusedLocationProviderClient + +#### Public Constructor Summary + +| Constructor | Description | +|----------------------------------|------------------------------------------------------------------------------------------------ +| FusedLocationProviderClient() | Default constructor. | + +#### Public Method Summary + +| Method | Return Type | Description | +|------------------------------------------------------------------------------------------------|-----------------------------------|-------------| +| checkLocationSettings(LocationSettingsRequest locationSettingsRequest) | Future\ | This API is used to check location settings of the device. | +| getLastLocation() | Future\ | This API is used to obtain the last requested available location. This API does not proactively request the location. Instead, it uses the location cached for the last request. | +| getLastLocationWithAddress(LocationRequest locationRequest) | Future\ | This API is used to obtain the available location of the last request, including the detailed address information. | +| getLocationAvailability() | Future\ | This API is used to check whether the location data is available. | +| setMockMode(bool mockMode) | Future\ | This API is used to specify whether the location provider uses the location mock mode. If yes, the GPS or network location is not used and the location set through setMockLocation (Location) is directly returned. | +| setMockLocation(Location location) | Future\ | This API is used to set the specific mock location. You must call the setMockMode(boolean) method and set the flag to true before calling this method. | +| requestLocationUpdates(LocationRequest locationRequest) | Future\ | This API is used to continuously request location updates. | +| requestLocationUpdatesCb(LocationRequest locationRequest, LocationCallback locationCallback) | Future\ | This API is used to request location updates using the callback. | +| requestLocationUpdatesExCb(LocationRequest locationRequest, LocationCallback locationCallback) | Future\ | This API is an extended location information service API. It supports high-precision location and is compatible with common location APIs. If the device does not support high-precision location or the app does not request the high-precision location permission, this API returns common location information similar to that returned by the requestLocationUpdates API. | +| removeLocationUpdates(int requestCode) | Future\ | This API is used to remove location updates from the specified requestCode. | +| removeLocationUpdatesCb(int callbackId) | Future\ | This API is used to remove location updates of the specified callbackId. | +| onLocationData | Future\Location\> | This API is used to listen location updates that comes from requestLocationUpdates API method. | +| getNavigationContextState(int type) | Future\ | Obtains the requested navigation type. | + +#### Public Constructors + +##### FusedLocationProviderClient() + +Constructor for *FusedLocationProviderClient* object. + +#### Public Methods + +##### Future\ checkLocationSettings(LocationSettingsRequest locationSettingsRequest) *async* + +Checks location settings of the device. + +| Parameter | Description | +|-------------------------|-------------| +| locationSettingsRequest | Location setting request object. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | LocationSettingsStates object if the operation is successful; PlatformException otherwise. | + +##### Future\ getLastLocation() *async* + +Obtains the available location of the last request. Instead of proactively requesting a location, this method uses the location cached during the last request. + +The value null may be returned in the following scenarios: +- The location function has never been used. +- The location function is disabled. +- The device is restored to factory settings. + +If real-time location is required, you are advised to proactively call requestLocationUpdates instead of this method. To receive a location once only, you can set numUpdates in LocationRequest to 1. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Location object if the operation is successful; PlatformException otherwise. | + +##### Future\ getLastLocationWithAddress(LocationRequest locationRequest) *async* + +Obtains the available location of the last request, including the detailed address information. If no location is available, null is returned. + +| Parameter | Description | +|-------------------------|-------------| +| locationRequest | Location request object. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | HWLocation object if the operation is successful; PlatformException otherwise. | + +##### Future\ getLocationAvailability() *async* + +Checks whether the location data is available. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | LocationAvailability object if the operation is successful; PlatformException otherwise. | + +##### Future\ setMockMode(bool mockMode) *async* + +Sets whether the location provider uses the mock mode. If yes, the GPS or network location is not used and the location set through setMockLocation is directly returned. + +| Parameter | Description | +|-------------------------|-------------| +| mockMode | Indicates whether the location provider uses the mock mode. true: yes; false: no. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Future\ setMockLocation(Location location) *async* + +Sets whether the location provider uses the mock mode. If yes, the GPS or network location is not used and the location set through setMockLocation is directly returned. + +| Parameter | Description | +|-------------------------|-------------| +| location | Mock location. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Future\ requestLocationUpdates(LocationRequest locationRequest) *async* + +Continuously requests location updates. You can call the onLocationData API method to listen for location updates. + +| Parameter | Description | +|-------------------------|-------------| +| locationRequest | Location update request. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Request code if the operation is successful; PlatformException otherwise. The request code can be used to call the removeLocationUpdates method. | + +##### Future\ requestLocationUpdatesCb(LocationRequest locationRequest, LocationCallback locationCallback) *async* + +Requests location updates using the callback. + +| Parameter | Description | +|-------------------------|-------------| +| locationRequest | Location update request. | +| locationCallback | Location update callback.| + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Callback ID if the operation is successful; PlatformException otherwise. The callback ID can be used to call the removeLocationUpdatesCb method. | + +##### Future\ requestLocationUpdatesExCb(LocationRequest locationRequest, LocationCallback locationCallback) *async* + +Continuously requests location updates. This method is an extended location information service API. It supports high-precision location and is compatible with common location APIs. If the device does not support high-precision location or the app does not request the high-precision location, this method returns common location information similar to that returned by the requestLocationUpdatesCb method. + +| Parameter | Description | +|-------------------------|-------------| +| locationRequest | Location update request. | +| locationCallback | Location update callback.| + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Callback ID if the operation is successful; PlatformException otherwise. The callback ID can be used to call the removeLocationUpdatesCb method. | + +##### Future\ removeLocationUpdates(int requestCode) *async* + +Removes location updates based on the specified request code. + +| Parameter | Description | +|-------------------------|-------------| +| requestCode | Request code returned by the requestLocationUpdates method. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Future\ removeLocationUpdatesCb(int callbackId) *async* + +Removes location updates based on the specified callback ID. + +| Parameter | Description | +|-------------------------|-------------| +| callbackId | Callback ID returned by the requestLocationUpdatesCb or requestLocationUpdatesExCb method. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Stream\ onLocationData + +Listens for location updates that come from the requestLocationUpdates method. + +| Return Type | Description | +|----------------------------------|-------------| +| Stream\ | Stream of Location object. You can call the .listen() method to subscribe to this stream and listen for updates. | + +##### Future\ getNavigationContextState(int type) *async* + +Removes location updates based on the specified callback ID. + +| Parameter | Description | +|-------------------------|-------------| +| type | Requested navigation type. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | NavigationResult object if the operation is successful; PlatformException otherwise. | + +### Location + +#### Public Properties + +| Properties | Type | Description | +|------------------------------|--------|-------------------------------------------------------------------------------------------------------------------------| +| provider | String | Location provider, such as network location, GPS, Wi-Fi, and Bluetooth. | +| latitude | double | Latitude of a location. If no latitude is available, 0.0 is returned. | +| longitude | double | Longitude of a location. If no longitude is available, 0.0 is returned. | +| altitude | double | Altitude of a location. If no altitude is available, 0.0 is returned. | +| speed | double | Speed of a device at the current location, in meters per second. If no speed is available, 0.0 is returned. | +| bearing | double | Bearing of a device at the current location, in degrees. If no bearing is available, 0.0 is returned. | +| horizontalAccuracyMeters | double | Horizontal error of a location, in meters. If no horizontal error is available, 0.0 is returned. | +| verticalAccuracyMeters | double | Vertical error of a location, in meters. If no vertical error is available, 0.0 is returned. | +| speedAccuracyMetersPerSecond | double | Speed error of a device at the current location, in meters per second. If no speed error is available, 0.0 is returned. | +| bearingAccuracyDegrees | double | Bearing error of the current location, in degrees. If no bearing error is available, 0.0 is returned. | +| time | int | Current timestamp, in milliseconds. | +| elapsedRealtimeNanos | int | Time elapsed since the system was started, in nanoseconds | + +#### Public Constructor Summary + +| Constructor | Description | +|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| Location({String provider, double latitude, double longitude, double altitude, double speed, double bearing, double horizontalAccuracyMeters, double verticalAccuracyMeters, double speedAccuracyMetersPerSecond, double bearingAccuracyDegrees, int time, int elapsedRealtimeNanos}) | Default constructor. | +| Location.fromMap(Map\ map) | Creates a Location object from a map. | +| Location.fromJson(String source) | Creates a Location object from a JSON string. | + +#### Public Constructors + +##### Location({String provider, double latitude, double longitude, double altitude, double speed, double bearing, double horizontalAccuracyMeters, double verticalAccuracyMeters, double speedAccuracyMetersPerSecond, double bearingAccuracyDegrees, int time, int elapsedRealtimeNanos}) + +Constructor for Location object. + +| Parameter | Type | Description | +|------------------------------|--------|-------------------------------------------------------------------------------------------------------------------------| +| provider | String | Location provider, such as network location, GPS, Wi-Fi, and Bluetooth. | +| latitude | double | Latitude of a location. If no latitude is available, 0.0 is returned. | +| longitude | double | Longitude of a location. If no longitude is available, 0.0 is returned. | +| altitude | double | Altitude of a location. If no altitude is available, 0.0 is returned. | +| speed | double | Speed of a device at the current location, in meters per second. If no speed is available, 0.0 is returned. | +| bearing | double | Bearing of a device at the current location, in degrees. If no bearing is available, 0.0 is returned. | +| horizontalAccuracyMeters | double | Horizontal error of a location, in meters. If no horizontal error is available, 0.0 is returned. | +| verticalAccuracyMeters | double | Vertical error of a location, in meters. If no vertical error is available, 0.0 is returned. | +| speedAccuracyMetersPerSecond | double | Speed error of a device at the current location, in meters per second. If no speed error is available, 0.0 is returned. | +| bearingAccuracyDegrees | double | Bearing error of the current location, in degrees. If no bearing error is available, 0.0 is returned. | +| time | int | Current timestamp, in milliseconds. | +| elapsedRealtimeNanos | int | Time elapsed since the system was started, in nanoseconds | + +##### Location.fromMap(Map\ map) + +Creates a Location object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### Location.fromJson(String source) + +Creates a Location object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + + +### HWLocation + +#### Public Properties + +| Properties | Type | Description | +|------------------------------|------------------------|-------------------------------------------------------------------------------------------------------------------------| +| provider | String | Location provider, such as network location, GPS, Wi-Fi, and Bluetooth. | +| latitude | double | Latitude of a location. If no latitude is available, 0.0 is returned. | +| longitude | double | Longitude of a location. If no longitude is available, 0.0 is returned. | +| altitude | double | Altitude of a location. If no altitude is available, 0.0 is returned. | +| speed | double | Speed of a device at the current location, in meters per second. If no speed is available, 0.0 is returned. | +| bearing | double | Bearing of a device at the current location, in degrees. If no bearing is available, 0.0 is returned. | +| horizontalAccuracyMeters | double | Horizontal error of a location, in meters. If no horizontal error is available, 0.0 is returned. | +| verticalAccuracyMeters | double | Vertical error of a location, in meters. If no vertical error is available, 0.0 is returned. | +| speedAccuracyMetersPerSecond | double | Speed error of a device at the current location, in meters per second. If no speed error is available, 0.0 is returned. | +| bearingAccuracyDegrees | double | Bearing error of the current location, in degrees. If no bearing error is available, 0.0 is returned. | +| time | int | Current timestamp, in milliseconds. | +| elapsedRealtimeNanos | int | Time elapsed since the system was started, in nanoseconds | +| countryCode | String | Country code. The value is a two-letter code complying with the ISO 3166-1 standard. | +| countryName | String | Country name. | +| state | String | Administrative region. | +| city | String | City of the current location. | +| county | String | County of the current location. | +| street | String | Street of the current location. | +| featureName | String | Landmark building at the current location. | +| postalCode | String | Postal code of the current location. | +| phone | String | Phone number of the landmark building (such as a store or company) at the current location. | +| url | String | Website of the landmark building (such as a store or company) at the current location. | +| extraInfo | Map\ | Additional information, which is a key-value pair. | + + +#### Public Constructor Summary + +| Constructor | Description | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| HWLocation({String provider, double latitude, double longitude, double altitude, double speed, double bearing, double horizontalAccuracyMeters, double verticalAccuracyMeters, double speedAccuracyMetersPerSecond, double bearingAccuracyDegrees, int time, int elapsedRealtimeNanos, String countryCode, String countryName, String state, String city, String county, String street, String featureName, String postalCode, String phone, String url, Map extraInfo}) | Default constructor. | +| HWLocation.fromMap(Map\ map) | Creates a HWLocation object from a map. | +| HWLocation.fromJson(String source) | Creates a HWLocation object from a JSON string. | + +#### Public Constructors + +##### HwLocation({String provider, double latitude, double longitude, double altitude, double speed, double bearing, double horizontalAccuracyMeters, double verticalAccuracyMeters, double speedAccuracyMetersPerSecond, double bearingAccuracyDegrees, int time, int elapsedRealtimeNanos}) + +Constructor for Location object. + +| Parameter | Type | Description | +|------------------------------|------------------------|-------------------------------------------------------------------------------------------------------------------------| +| provider | String | Location provider, such as network location, GPS, Wi-Fi, and Bluetooth. | +| latitude | double | Latitude of a location. If no latitude is available, 0.0 is returned. | +| longitude | double | Longitude of a location. If no longitude is available, 0.0 is returned. | +| altitude | double | Altitude of a location. If no altitude is available, 0.0 is returned. | +| speed | double | Speed of a device at the current location, in meters per second. If no speed is available, 0.0 is returned. | +| bearing | double | Bearing of a device at the current location, in degrees. If no bearing is available, 0.0 is returned. | +| horizontalAccuracyMeters | double | Horizontal error of a location, in meters. If no horizontal error is available, 0.0 is returned. | +| verticalAccuracyMeters | double | Vertical error of a location, in meters. If no vertical error is available, 0.0 is returned. | +| speedAccuracyMetersPerSecond | double | Speed error of a device at the current location, in meters per second. If no speed error is available, 0.0 is returned. | +| bearingAccuracyDegrees | double | Bearing error of the current location, in degrees. If no bearing error is available, 0.0 is returned. | +| time | int | Current timestamp, in milliseconds. | +| elapsedRealtimeNanos | int | Time elapsed since the system was started, in nanoseconds | +| countryCode | String | Country code. The value is a two-letter code complying with the ISO 3166-1 standard. | +| countryName | String | Country name. | +| state | String | Administrative region. | +| city | String | City of the current location. | +| county | String | County of the current location. | +| street | String | Street of the current location. | +| featureName | String | Landmark building at the current location. | +| postalCode | String | Postal code of the current location. | +| phone | String | Phone number of the landmark building (such as a store or company) at the current location. | +| url | String | Website of the landmark building (such as a store or company) at the current location. | +| extraInfo | Map\ | Additional information, which is a key-value pair. | + +##### HWLocation.fromMap(Map map) + +Creates a HWLocation object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### HWLocation.fromJson(String source) + +Creates a HWLocation object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### LocationAvailability + +Device location availability. + +#### Public Properties + +| Properties | Type | Description | +|-------------------|------|-----------------------------------------------------------------------------------------------------------| +| cellStatus | int | Availability status code of cell location. Currently not provided. Value is always **0**. | +| wifiStatus | int | Availability status code of wifi location. Currently not provided. Value is always **0**. | +| elapsedRealtimeNs | int | Time elapsed since the system was started, in nanoseconds. Currently not provided. Value is always **0**. | +| locationStatus | int | Location status code. If the value of smaller than 1000, then device location is available. | + +**NOTE:** + +Currently, cellStatus, wifiStatus and elapsedRealtimeNs are not provided and have a fixed value of 0. + +#### Public Constructor Summary + +| Constructor | Description | +|---------------------------------------------------------------------------------------------------|----------------------| +| LocationAvailability({int cellStatus, int wifiStatus, int elapsedRealtimeNs, int locationStatus}) | Default constructor. | +| LocationAvailability.fromMap(Map\ map) | Creates a LocationAvailability object from a map. | +| LocationAvailability.fromJson(String source) | Creates a LocationAvailability object from a JSON string. | + +#### Public Method Summary + +| Methods | Return Type | Description | +|---------------------|-------------|------------------------------------------------| +| isLocationAvailable | bool | Indicates if the location is available or not. | + +#### Public Constructors + +##### LocationAvailability({int cellStatus, int wifiStatus, int elapsedRealtimeNs, int locationStatus}) + +Constructor for LocationAvailability object. + +| Parameter | Type | Description | +|-------------------|------|-----------------------------------------------------------------------------------------------------------| +| cellStatus | int | Availability status code of cell location. Currently not provided. Value is always **0**. | +| wifiStatus | int | Availability status code of wifi location. Currently not provided. Value is always **0**. | +| elapsedRealtimeNs | int | Time elapsed since the system was started, in nanoseconds. Currently not provided. Value is always **0**. | +| locationStatus | int | Location status code. If the value of smaller than 1000, then device location is available. | + +##### LocationAvailability.fromMap(Map\ map) + +Creates a LocationAvailability object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### LocationAvailability.fromJson(String source) + +Creates a LocationAvailability object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +#### Public Methods + +##### bool isLocationAvaible + +Checks whether the device location is available. If yes, the value true is returned. A number of causes may result in failure to determine the location, such as that the location function is disabled or sensor data cannot be obtained from the device. + +| Return Type | Description | +|------------------------------|----------------------| +| String | JSON string as a source. | + +### LocationCallback + +#### Public Properties + +| Properties | Type | Description | +|------------------------|----------------------------------------------------------|------------------------------------------------------------| +| onLocationAvailability | void Function(LocationAvailability locationAvailability) | Callback function to listen location availability changes. | +| onLocationResult | void Function(LocationResult locationResult) | Callback function to listen location updates. | + +#### Public Constructor Summary + +| Constructor | Description | +|------------------------------------------------------------------------------------------|----------------------| +| LocationCallback({void Function onLocationResult, void Function onLocationAvailability}) | Default constructor. | + +#### Public Constructors + +##### LocationCallback({void Function onLocationResult, void Function onLocationAvailability}) + +Constructor for LocationCallback object. + +| Parameter | Type | Description | +|------------------------------|---------------|-------------------------------------------------------------------------------------------------------------------------| +| onLocationResult | void Function | Callback function to trigger when the device location is available. | +| onLocationAvailability | void Function | Callback function to trigger when the device location availability changes. | + +### LocationRequest + +Location request class. + +#### Public Properties + +| Properties | Type | Description | +|----------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| priority | int | Request priority. The default value is 100. | +| interval | int | Request interval, in milliseconds. The default value is 3600000. | +| fastestInterval | int | Shortest request interval, in milliseconds. The default value is 600000. If another app initiates a location request, the location is also reported to the app at the interval specified in fastestInterval. | +| expirationTime | int | Request expiration time, in milliseconds. | +| numUpdates | int | Number of requested location updates. | +| smallestDisplacement | double | Minimum displacement between location updates, in meters. | +| maxWaitTime | int | Maximum waiting timeIndicates whether to return the address information. The default value is false. | +| needAddress | bool | Indicates whether to return the address information. The default value is false. | +| language | String | Language. The value consists of two letters and complies with the ISO 639-1 international standard. By default, the value is empty. | +| countryCode | String | Country code. The value consists of two letters and complies with the ISO 3166-1 international standard. By default, the value is empty. | + +#### Public Constants + +| Constants | Type | Value | Description | +|----------------------------------|------|-------|----------------------------------------------------------------------------------------------------------------------------------------------| +| PRIORITY_HIGH_ACCURACY | int | 100 | Used to request the most accurate location. | +| PRIORITY_BALANCED_POWER_ACCURACY | int | 102 | Used to request the block-level location. | +| PRIORITY_LOW_POWER | int | 104 | Used to request the city-level location. | +| PRIORITY_NO_POWER | int | 105 | Used to request the location with the optimal accuracy without additional power consumption. | +| PRIORITY_HD_ACCURACY | int | 200 | Used to request the high-precision location information. Currently, this parameter is available only for the requestLocationUpdatesExCb API. | + +#### Public Constructor Summary + +| Constructor | Description | +|-------------------------------------------------------|----------------------| +| LocationRequest() | Default constructor. | +| LocationRequest.fromMap(Map\ map) | Creates a Location object from a map. | +| LocationRequest.fromJson(String source) | Creates a Location object from a JSON string. | + +#### Public Method Summary + +| Return Type | Method | Description | +|-----------------|--------------------------------|-------------| +| bool | isFastestIntervalExplicitlySet | Indicates whether the fastest interval explicitly set or default value is being used. | + + +#### Public Constructors + +##### LocationRequest() + +Constructor for Location object. + +##### LocationRequest.fromMap(Map\ map) + +Creates a Location object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### LocationRequest.fromJson(String source) + +Creates a Location object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +#### Public Methods + +##### bool isFastestIntervalExplicitlySet + +Checks whether the shortest interval is used. + +| Methods | Return Type | Description | +|--------------------------------|-------------|---------------------------------------------------------------------------------------| +| isFastestIntervalExplicitlySet | bool | Indicates whether the fastest interval explicitly set or default value is being used. | + +### LocationResult + +Location data information class. + +#### Public Properties + +| Properties | Type | Description | +|----------------|--------------------|-------------------------------------------------------------------------------------------------------| +| locations | List\ | Available locations, which are ordered from oldest to newest. | +| hwLocations | List\ | List of available locations sorted from oldest to newest, including the detailed address information. | +| lastLocation | Location | Available location of the last request. | +| lastHWLocation | HWLocation | Available location of the last request, including the detailed address information. | + +#### Public Constructor Summary + +| Constructor | Description | +|----------------------------------------------------------------------------------------------------------------------------|----------------------| +| LocationResult({List locations, List hwLocations, Location lastLocation, HWLocation lastHWLocation}) | Default constructor. | +| LocationResult.fromMap(Map\ map) | Creates a Location object from a map. | +| LocationResult.fromJson(String source) | Creates a Location object from a JSON string. | + +#### Public Constructors + +##### LocationResult({List locations, List hwLocations, Location lastLocation, HWLocation lastHWLocation}) + +Constructor for LocationResult object. + +| Parameter | Type | Description | +|------------------------------|--------------------|-------------------------------------------------------------------------------------------------------------------------| +| locations | List\ | Available locations, which are sorted from oldest to newest. | +| hwLocations | List\ | List of available locations sorted from oldest to newest, including the detailed address. | +| lastLocation | Location | Available location of the last request. | +| lastHWLocation | HWLocation | Available location of the last request, including the detailed address. | + + +##### LocationResult.fromMap(Map\ map) + +Creates a LocationResult object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### LocationResult.fromJson(String source) + +Creates a LocationResult object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + + + + +### LocationSettingsRequest + +Class for specifying the location service types and checking the location settings to obtain optimal functionality of all requested services. + +#### Public Properties + +| Properties | Type | Description | +|------------|-------------------------|--------------------------------------------------------------------------------------------------------------| +| requests | List\ | Collection of LocationRequest object. | +| alwaysShow | bool | Indicates whether BLE scanning needs to be enabled. The options are true (yes) and false (no). | +| needBle | bool | Indicates whether a location is required for the app to continue. The options are true (yes) and false (no). | + +#### Public Constructor Summary + +| Constructor | Description | +|--------------------------------------------------------------------------------------------|----------------------| +| LocationSettingsRequest({List\ requests, bool alwaysShow, bool needBle}) | Default constructor. | +| LocationSettingsRequest.fromMap(Map\ map) | Creates a LocationSettingsRequest object from a map. | +| LocationSettingsRequest.fromJson(String source) | Creates a LocationSettingsRequest object from a JSON string. | + +#### Public Constructors + +##### LocationSettingsRequest({List\ requests, bool alwaysShow, bool needBle}) + +Constructor for LocationSettingsRequest object. + +| Parameter | Type | Description | +|-----------------------------|-------------------------|-------------------------------------------------------------------------------------------------------------------------| +| requests | List\ | Collection of LocationRequest objects. | +| alwaysShow | bool | Indicates whether BLE scanning needs to be enabled. The options are true (yes) and false (no). | +| needBle | bool | Indicates whether a location is required for the app to continue. The options are true (yes) and false (no). | + +##### LocationSettingsRequest.fromMap(Map\ map) + +Creates a LocationSettingsRequest object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### LocationSettingsRequest.fromJson(String source) + +Creates a LocationSettingsRequest object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### LocationSettingsStates + +Current location-related setting status. + +#### Public Properties + +| Properties | Type | Description | +|------------------------|------|----------------------------------------------------------------------------------------| +| blePresent | bool | Indicates whether the BLE exists on the device. | +| bleUsable | bool | Indicates whether the BLE is enabled and can be used by the app. | +| gpsPresent | bool | Indicates whether the GPS provider exists on the device. | +| gpsUsable | bool | Indicates whether the GPS provider is enabled and can be used by the app. | +| locationPresent | bool | Indicates whether the location provider exists on the device. | +| locationUsable | bool | Indicates whether the location provider is enabled and can be used by the app. | +| networkLocationPresent | bool | Indicates whether the network location provider exists on the device. | +| networkLocationUsable | bool | Indicates whether the network location provider is enabled and can be used by the app. | + +#### Public Constructor Summary + +| Constructor | Description | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| LocationSettingsStates({bool blePresent, bool bleUsable, bool gpsPresent, bool gpsUsable, bool locationPresent, bool locationUsable, bool networkLocationPresent, bool networkLocationUsable}) | Default constructor. | +| LocationSettingsStates.fromMap(Map\ map) | Creates a LocationSettingsStates object from a map. | +| LocationSettingsStates.fromJson(String source) | Creates a LocationSettingsStates object from a JSON string. | + +#### Public Constructors + +##### LocationSettingsStates({bool blePresent, bool bleUsable, bool gpsPresent, bool gpsUsable, bool locationPresent, bool locationUsable, bool networkLocationPresent, bool networkLocationUsable}) + +Constructor for LocationSettingsStates object. + +| Parameter | Type | Description | +|------------------------|------|----------------------------------------------------------------------------------------| +| blePresent | bool | Indicates whether the BLE exists on the device. | +| bleUsable | bool | Indicates whether the BLE is enabled and can be used by the app. | +| gpsPresent | bool | Indicates whether the GPS provider exists on the device. | +| gpsUsable | bool | Indicates whether the GPS provider is enabled and can be used by the app. | +| locationPresent | bool | Indicates whether the location provider exists on the device. | +| locationUsable | bool | Indicates whether the location provider is enabled and can be used by the app. | +| networkLocationPresent | bool | Indicates whether the network location provider exists on the device. | +| networkLocationUsable | bool | Indicates whether the network location provider is enabled and can be used by the app. | + +##### LocationSettingsStates.fromMap(Map\ map) + +Creates a Location object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### LocationSettingsStates.fromJson(String source) + +Creates a Location object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### NavigationResult + +#### Public Properties + +| Methods | Return Type | Description | +|-------------------|-------------|----------------------------------------------------------------| +| possibility | int | Obtains the confidence of the status information. The value ranges from 0 to 100. A larger value indicates more reliable result authenticity. | +| state | int | Status information.If the navigation type is IS_SUPPORT_EX, the return values are described as follows: 11: The user device supports high-precision location.12: The user device does not support high-precision location. | + +#### Public Constructor Summary + +| Constructor | Description | +|-------------------------------------------------------|----------------------| +| NavigationResult({state, possibility}) | Default constructor. | +| NavigationResult.fromMap(Map\ map) | Creates a NavigationResult object from a map. | +| NavigationResult.fromJson(String source) | Creates a NavigationResult object from a JSON string. | + + +#### Public Constructors + +##### NavigationResult({state, possibility}) + +Constructor for NavigationResult object. + +| Parameter | Return Type | Description | +|-------------------|-------------|----------------------------------------------------------------| +| possibility | int | Obtains the confidence of the status information. The value ranges from 0 to 100. A larger value indicates more reliable result authenticity. | +| state | int | Status information.If the navigation type is IS_SUPPORT_EX, the return values are described as follows: 11: The user device supports high-precision location.12: The user device does not support high-precision location. | + + +##### NavigationResult.fromMap(Map\ map) + +Creates a NavigationResult object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### NavigationResult.fromJson(String source) + +Creates a NavigationResult object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### NavigationRequest + +#### Public Properties + +| Properties | Type | Description | +|--------------------------|----------------|--------------------------------------------| +| type | int | Requested navigation type. | + +#### Public Constants + +| Constants | Type | Value | Description | +|---------------------------|------|-------|----------------------------------------------------- | +| IS_SUPPORT_EX | int | 2 | Used to check whether the device supports high-precision location. | + +### ActivityIdentificationService + +Interaction access point of activity identification. + +#### Public Constructor Summary + +| Constructor | Description | +|------------------------------------------------|----------------------| +| ActivityIdentificationService() | Default constructor. | + +#### Public Method Summary + +| Return Type | Method | Description | +|------------------------------------------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Future\ | createActivityIdentificationUpdates(int detectionIntervalMillis) | This API is used to register for activity identification updates. | +| Future\ | createActivityConversionUpdates(List\ activityConversions) | This API is used to activity conversions (entering and exit), for example, detecting user status change from walking to bicycling.The Conversion API supports the following activity parameters. | +| Future\ | deleteActivityIdentificationUpdates(int requestCode) | This API is used to remove all activity identification updates from the specified **requestCode**. | +| Future\ | deleteActivityConversionUpdates(int requestCode) | This API is used to remove all activity conversion updates from the specified **requestCode**. | +| Stream\ | onActivityIdentification | This API is used to listen activity identification updates that comes from createActivityIdentificationUpdates API method. | +| Stream\ | onActivityConversion | This API is used to listen activity conversion updates that comes from createActivityConversionUpdates API method. | + + +#### Public Constructors + +##### ActivityIdentificationService() + +Constructor for ActivityIdentificationService object. + +#### Public Methods + +##### Future\ createActivityIdentificationUpdates(int detectionIntervalMillis) *async* + +Registers for activity identification updates. + +| Parameter | Description | +|-------------------------|-------------| +| detectionIntervalMillis | Interval for activity identification updates, in milliseconds. A larger value will result in a lower activity identification frequency, while a smaller value will result in a higher activity identification frequency. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Request code if the operation is successful; PlatformException otherwise. The request code can be used to call the deleteActivityIdentificationUpdates method. | + +##### Future\ createActivityConversionUpdates(List\ activityConversions) *async* + +Registers for activity conversion updates (entering and exit), for example, detecting user status change from walking to bicycling. + +| Parameter | Description | +|---------------------|-------------| +| activityConversions | Activity conversion list. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Request code if the operation is successful; PlatformException otherwise. The request code can be used to call the deleteActivityConversionUpdates method. | + +##### Future\ deleteActivityIdentificationUpdates(int requestCode) *async* + +Removes activity identification updates based on the specified request code. + +| Parameter | Description | +|-------------|-------------| +| requestCode | Request code returned by the createActivityIdentificationUpdates method. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Future\ deleteActivityConversionUpdates(int requestCode) *async* + +Removes activity conversion updates based on the specified request code. + +| Parameter | Description | +|-------------|-------------| +| requestCode | Request code returned by the createActivityConversionUpdates method. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Stream\ onActivityIdentification + +Listens for activity identification updates that come from the createActivityIdentificationUpdates method. + +| Return Type | Description | +|------------------------------------------|-------------| +| Stream\ | Stream of ActivityIdentificationResponse object. You can call the .listen() method to subscribe to this stream and listen for updates. | + +##### Stream\ onActivityConversion + +Listens for activity conversion updates that come from the createActivityConversionUpdates method. + +| Return Type | Description | +|--------------------------------------|-------------| +| Stream\ | Stream of ActivityConversionResponse object. You can call the .listen() method to subscribe to this stream and listen for updates. | + +### ActivityConversionData + +Activity conversion event. + +#### Public Properties + +| Properties | Type | Description | +|-----------------------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| activityType | int | Activity type of the conversion. The value is one of the activity types defined in ActivityIdentificationData. | +| conversionType | int | Activity conversion information. The options are ActivityConversionInfo.ENTER_ACTIVITY_CONVERSION and ActivityConversionInfo.EXIT_ACTIVITY_CONVERSION. | +| elapsedTimeFromReboot | int | Elapsed real time (in milliseconds) of this conversion since boot. | + +#### Public Constructor Summary + +| Constructor | Description | +|-------------------------------------------------------------------------------------------|----------------------| +| ActivityConversionData({int activityType, int conversionType, int elapsedTimeFromReboot}) | Default constructor. | +| ActivityConversionData.fromMap(Map\ map) | Creates a ActivityConversionData object from a map. | +| ActivityConversionData.fromJson(String source) | Creates a ActivityConversionData object from a JSON string. | + +#### Public Constructors + +##### ActivityConversionData({int activityType, int conversionType, int elapsedTimeFromReboot}) + +Constructor for ActivityConversionData object. + +| Parameter | Type | Description | +|-----------------------|------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +| activityType | int | Activity type of the conversion. The value is one of the activity types defined in ActivityIdentificationData. | +| conversionType | int | Activity conversion information. The options are ActivityConversionInfo.ENTER_ACTIVITY_CONVERSION and ActivityConversionInfo.EXIT_ACTIVITY_CONVERSION. | +| elapsedTimeFromReboot | int | Elapsed real time (in milliseconds) of this conversion since boot. | + +##### ActivityConversionData.fromMap(Map\ map) + +Creates a ActivityConversionData object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### ActivityConversionData.fromJson(String source) + +Creates a ActivityConversionData object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### ActivityConversionInfo + +Activity conversion information. + +#### Public Properties + +| Properties | Type | Description | +|----------------|------|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| activityType | int | Activity type of the conversion. The value is one of the activity types defined in ActivityIdentificationData. | +| conversionType | int | Activity conversion information. The options are ActivityConversionInfo.ENTER_ACTIVITY_CONVERSION and ActivityConversionInfo.EXIT_ACTIVITY_CONVERSION. | + +#### Public Constants + +| Constants | Type | Value | Description | +|---------------------------|------|-------|-----------------------------------| +| ENTER_ACTIVITY_CONVERSION | int | 0 | A user enters the given activity. | +| EXIT_ACTIVITY_CONVERSION | int | 1 | A user exits the given activity. | + +#### Public Constructor Summary + +| Constructor | Description | +|----------------------------------------------------------------|----------------------| +| ActivityConversionInfo({int activityType, int conversionType}) | Default constructor. | +| ActivityConversionInfo.fromMap(Map\ map) | Creates a ActivityConversionInfo object from a map. | +| ActivityConversionInfo.fromJson(String source) | Creates a ActivityConversionInfo object from a JSON string. | + +#### Public Constructors + +##### ActivityConversionInfo({int activityType, int conversionType}) + +Constructor for ActivityConversionInfo object. + +| Properties | Type | Description | +|----------------|------|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| activityType | int | Activity type of the conversion. The value is one of the activity types defined in ActivityIdentificationData. | +| conversionType | int | Activity conversion information. The options are ActivityConversionInfo.ENTER_ACTIVITY_CONVERSION and ActivityConversionInfo.EXIT_ACTIVITY_CONVERSION. | + +##### ActivityConversionInfo.fromMap(Map\ map) + +Creates a ActivityConversionInfo object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### ActivityConversionInfo.fromJson(String source) + +Creates a ActivityConversionInfo object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### ActivityConversionResponse + +Activity conversion result. + +#### Public Properties + +| Properties | Type | Description | +|-------------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------| +| activityConversionDatas | List\ | All activity conversion events in the result. The obtained activity events are sorted by time in ascending order. | + +#### Public Constructor Summary + +| Constructor | Description | +|--------------------------------------------------------------------------------------|----------------------| +| ActivityConversionResponse({List\ activityConversionDatas}) | Default constructor. | +| ActivityConversionResponse.fromMap(Map\ map) | Creates a ActivityConversionResponse object from a map. | +| ActivityConversionResponse.fromJson(String source) | Creates a ActivityConversionResponse object from a JSON string. | + +#### Public Constructors + +##### ActivityConversionResponse({List\ activityConversionDatas}) + +Constructor for ActivityConversionResponse object. + +| Parameter | Type | Description | +|------------------------------|--------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| activityConversionDatas | List\ | All activity conversion events in the result. The obtained activity events are sorted by time in ascending order. | + + +##### ActivityConversionResponse.fromMap(Map\ map) + +Creates a ActivityConversionResponse object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### ActivityConversionResponse.fromJson(String source) + +Creates a ActivityConversionResponse object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### ActivityIdentificationData + +Class for detecting the activity. + +#### Public Properties + +| Properties | Type | Description | +|------------------------|------|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| identificationActivity | int | Type of the detected activity. | +| possibility | int | The confidence for the user to execute the activity. The confidence ranges from 0 to 100. A larger value indicates more reliable activity authenticity. | + +#### Public Constants + +| Constants | Type | Value | Description | +|-----------|------|-------|-----------------------------------------------------------------| +| VEHICLE | int | 100 | The device is in a vehicle, such as a car. | +| BIKE | int | 101 | The device is on a bicycle. | +| FOOT | int | 102 | The device user is walking or running. | +| STILL | int | 103 | The device is still. | +| OTHERS | int | 104 | The current activity cannot be detected. | +| TILTING | int | 105 | The device has an obvious tilt change. | +| WALKING | int | 107 | The user of the device is walking,it is a sub-activity of FOOT. | +| RUNNING | int | 108 | The user of the device is running,it is a sub-activity of FOOT. | + +#### Public Constructor Summary + +| Constructor | Description | +|-----------------------------------------------------------------|----------------------| +| ActivityIdentificationData({int identificationActivity, int possibility}) | Default constructor. | +| ActivityIdentificationData.fromMap(Map\ map) | Creates a ActivityIdentificationData object from a map. | +| ActivityIdentificationData.fromJson(String source) | Creates a ActivityIdentificationData object from a JSON string. | + +#### Public Method Summary + +| Methods | Return Type | Description | +|------------------------------|-------------|----------------------------------------------------------------| +| static isValidType(int type) | bool | Checks that given activity type is one of the valid constants. | + +#### Public Constructors + +##### ActivityIdentificationData({int identificationActivity, int possibility}) + +Constructor for ActivityIdentificationData object. + +| Parameter | Type | Description | +|------------------------|------|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| identificationActivity | int | Type of the detected activity. | +| possibility | int | The confidence for the user to execute the activity. The confidence ranges from 0 to 100. A larger value indicates more reliable activity authenticity. | + +##### ActivityIdentificationData.fromMap(Map\ map) + +Creates a ActivityIdentificationData object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### ActivityIdentificationData.fromJson(String source) + +Creates a ActivityIdentificationData object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +#### Public Methods + +##### bool isValidType + +Checks whether the specified activity type is a valid constant. + +| Return Type | Description | +|-----------------|-------------| +| bool | true if the specified activity type is a valid constant; false otherwise. | + +### ActivityIdentificationResponse + +Activity identification result. + +#### Public Properties + +| Properties | Type | Description | +|-----------------------------|------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| time | int | Time of this identification, which is in milliseconds since January 1, 1970. | +| elapsedTimeFromReboot | int | Elapsed real time (in milliseconds) of this identification since boot. | +| activityIdentificationDatas | List\ | List of activitiy identification list. The activity identifications are sorted by most probable activity first. | + +#### Public Constructor Summary + +| Constructor | Description | +|---------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| ActivityIdentificationResponse({int time, int elapsedTimeFromReboot, List\ activityIdentificationDatas}) | Default constructor. | +| ActivityIdentificationResponse.fromMap(Map\ map) | Creates a ActivityIdentificationResponse object from a map. | +| ActivityIdentificationResponse.fromJson(String source) | Creates a ActivityIdentificationResponse object from a JSON string. | + +#### Public Method Summary + +| Methods | Return Type | Description | +|------------------------------------------|----------------------------|-----------------------------------------------------------------------------------| +| mostActivityIdentification | ActivityIdentificationData | This API is used to obtain the most probable activity identification of the user. | +| getActivityPossibility(int activityType) | int | This API is used to obtain the confidence of an activity type. | + +#### Public Constructors + +##### ActivityIdentificationResponse({int time, int elapsedTimeFromReboot, List\ activityIdentificationDatas}) + +Constructor for ActivityIdentificationResponse object. + +| Parameter | Type | Description | +|-----------------------------|------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| time | int | Time of this identification, which is in milliseconds since January 1, 1970. | +| elapsedTimeFromReboot | int | Elapsed real time (in milliseconds) of this identification since boot. | +| activityIdentificationDatas | List\ | List of activitiy identification list. The activity identifications are sorted by most probable activity first. | + +##### ActivityIdentificationResponse.fromMap(Map\ map) + +Creates a ActivityIdentificationResponse object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### ActivityIdentificationResponse.fromJson(String source) + +Creates a Location object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +#### Public Methods + +##### ActivityIdentificationData mostActivityIdentification + +Obtains the most possible activity of a user. + +| Return Type | Description | +|----------------------------------|-------------| +| ActivityIdentificationData | Identified activity. | + +##### int getActivityPossibility + +Obtains the confidence of an activity type. + +| Parameter | Description | +|-------------------------|-------------| +| activityType | Activity type. | + +| Return Type | Description | +|----------------------------------|-------------| +| int | Confidence of the activity type. | + +### Geofence Service + +Interaction access point of geofence. + +#### Public Constructor Summary + +| Constructor | Description | +|----------------------------------------|----------------------| +| GeofenceService() | Default constructor. | + +#### Public Method Summary + +| Return Type | Method | Description | +|------------------------|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| Future\ | createGeofenceList(GeofenceRequest geofenceRequest) | This API is used to add geofences. When a geofence is triggered, a notification can be listened through **onGeofenceData** API method. | +| Future\ | deleteGeofenceList(int requestCode) | This API is used to remove geofences associated with a **requestCode**. | +| Future\ | deleteGeofenceListWithIds(List\ geofenceIds) | This API is used to remove geofences by their unique IDs. | +| Stream\ | onGeofenceData | This API is used to listen geofence updates that comes from createGeofenceList API method. | + +#### Public Constructors + +##### GeofenceService() + +Constructor for GeofenceService object. + +#### Public Methods + +##### Future\ createGeofenceList(GeofenceRequest geofenceRequest) *async* + +Adds geofences. When a geofence is triggered, the onGeofenceData method can listen for a notification. + +| Parameter | Description | +|-------------------------|-------------| +| geofenceRequest | Geofence request object. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Request code if the operation is successful; PlatformException otherwise. The request code can be used to call the deleteGeofenceList method. | + +##### Future\ deleteGeofenceList(int requestCode) *async* + +Removes geofences associated with a request code. + +| Parameter | Description | +|-------------------------|-------------| +| requestCode | Request code returned by the createGeofenceList method. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Future\ deleteGeofenceListWithIds(List\ geofenceIds) *async* + +Removes geofences by their unique IDs. + +| Parameter | Description | +|-------------------------|-------------| +| geofenceIds | Unique IDs of geofences to be removed. | + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | No return value if the operation is successful; PlatformException otherwise. | + +##### Stream\ onGeofenceData + +Listens for geofence updates that come from the createGeofenceList method. + +| Parameter | Description | +|-------------------------|-------------| +| geofenceIds | Unique IDs of geofences to be removed. | + +| Return Type | Description | +|----------------------------------|-------------| +| Stream\ | Stream of GeofenceData object. You can call the .listen() method to subscribe to this stream and listen for updates. | + +### Geofence + +Class that contains information about geofences. + +#### Public Properties + +| Properties | Type | Description | +|----------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| uniqueId | String | Unique ID. If the unique ID already exists, the new geofence will overwrite the old one. | +| conversions | int | Geofence conversions. The bitwise-OR operation is supported. | +| validDuration | int | Geofence timeout interval, in milliseconds. The geofence will be automatically deleted after this amount of time. | +| latitude | double | Latitude. The value range is [-90,90]. | +| longitude | double | Longitude. The value range is [-180,180]. | +| radius | double | Radius, in meters. | +| notificationInterval | int | Notification response capability. The default value is **0**. Setting it to a larger value can reduce power consumption accordingly. However, reporting of geofence events may be delayed. | +| dwellDelayTime | int | Lingering duration for converting a geofence event, in milliseconds. A geofence event is converted when a user lingers in a geofence for this amount of time. | + +#### Public Constants + +| Constants | Type | Value | Description | +|---------------------------|------|-------|-----------------------------------------------------| +| ENTER_GEOFENCE_CONVERSION | int | 1 | A user enters the geofence. | +| EXIT_GEOFENCE_CONVERSION | int | 2 | A user exits the geofence. | +| DWELL_GEOFENCE_CONVERSION | int | 4 | A user lingers in the geofence. | +| GEOFENCE_NEVER_EXPIRE | int | -1 | No timeout interval is configured for the geofence. | + +#### Public Constructor Summary + +| Constructor | Description | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| +| Geofence({String uniqueId, int conversions, int validDuration, double latitude, double longitude, double radius, int notificationInterval, int dwellDelayTime}) | Default constructor. | +| Geofence.fromMap(Map\ map) | Creates a Geofence object from a map. | +| Geofence.fromJson(String source) | Creates a Geofence object from a JSON string. | + +### Public Constructors + +##### Geofence({String uniqueId, int conversions, int validDuration, double latitude, double longitude, double radius, int notificationInterval, int dwellDelayTime}) + +Constructor for Geofence object. + +| Parameter | Type | Description | +|----------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| uniqueId | String | Unique ID. If the unique ID already exists, the new geofence will overwrite the old one. | +| conversions | int | Geofence conversions. The bitwise-OR operation is supported. | +| validDuration | int | Geofence timeout interval, in milliseconds. The geofence will be automatically deleted after this amount of time. | +| latitude | double | Latitude. The value range is [-90,90]. | +| longitude | double | Longitude. The value range is [-180,180]. | +| radius | double | Radius, in meters. | +| notificationInterval | int | Notification response capability. The default value is **0**. Setting it to a larger value can reduce power consumption accordingly. However, reporting of geofence events may be delayed. | +| dwellDelayTime | int | Lingering duration for converting a geofence event, in milliseconds. A geofence event is converted when a user lingers in a geofence for this amount of time. | + +##### Geofence.fromMap(Map\ map) + +Creates a Geofence object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### Geofence.fromJson(String source) + +Creates a Geofence object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### GeofenceData + +Geofence event. + +#### Public Properties + +| Properties | Type | Description | +|--------------------------|----------------|--------------------------------------------| +| errorCode | int | Error code. | +| conversion | int | Geofence convert type. | +| convertingGeofenceIdList | List\ | List of converted geofence unique IDs. | +| convertingLocation | Location | The location when a geofence is converted. | + +#### Public Constructor Summary + +| Constructor | Description | +|-------------------------------------------------------------------------------------------------------------------|----------------------| +| GeofenceData({int errorCode, int conversion, List convertingGeofenceIdList, Location convertingLocation}) | Default constructor. | +| GeofenceData.fromMap(Map\ map) | Creates a GeofenceData object from a map. | +| GeofenceData.fromJson(String source) | Creates a GeofenceData object from a JSON string. | + +#### Public Constructors + +##### GeofenceData({int errorCode, int conversion, List convertingGeofenceIdList, Location convertingLocation}) + +Constructor for GeofenceData object. + +| Properties | Type | Description | +|--------------------------|----------------|--------------------------------------------| +| errorCode | int | Error code. | +| conversion | int | Geofence convert type. | +| convertingGeofenceIdList | List\ | List of converted geofence unique IDs. | +| convertingLocation | Location | The location when a geofence is converted. | + +##### GeofenceData.fromMap(Map\ map) + +Creates a GeofenceData object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### GeofenceData.fromJson(String source) + +Creates a GeofenceData object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### GeofenceRequest + +Geofence request class. + +#### Public Properties + +| Properties | Type | Description | +|-----------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| geofenceList | List\ | List of geofences to be monitored. | +| initConversions | int | Initial conversion type. This parameter is invalid if it is set to 0. The default value is GeofenceRequest.ENTER_INIT_CONVERSION | GeofenceRequest.DWELL_INIT_CONVERSION. | +| coordinateType | int | Coordinate type of geofences. Defaults to GeofenceRequest.COORDINATE_TYPE_WGS_84. | + +#### Public Constants + +| Constants | Type | Value | Description | +|------------------------|------|-------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ENTER_INIT_CONVERSION | int | 1 | ENTER_INIT_CONVERSION is converted immediately when a request is initiated to add the geofence where a user device has already entered. | +| EXIT_INIT_CONVERSION | int | 2 | EXIT_INIT_CONVERSION is converted immediately when a request is initiated to add the geofence where a user device has already exit. | +| DWELL_INIT_CONVERSION | int | 4 | DWELL_INIT_CONVERSION is converted immediately when a request is initiated to add the geofence where a user device has already entered and stayed for the specified duration. | +| COORDINATE_TYPE_WGS_84 | int | 1 | WGS_84 coordinate system. | +| COORDINATE_TYPE_GCJ_02 | int | 0 | GCJ-02 coordinate system. | + +#### Public Constructor Summary + +| Constructor | Description | +|-------------------------------------------------------------------------------------------|----------------------| +| GeofenceRequest({List\ geofenceList, int initConversions, int coordinateType}) | Default constructor. | +| GeofenceRequest.fromMap(Map\ map) | Creates a GeofenceRequest object from a map. | +| GeofenceRequest.fromJson(String source) | Creates a GeofenceRequest object from a JSON string. | + +#### Public Constructors + +##### GeofenceRequest({List\ geofenceList, int initConversions, int coordinateType}) + +Constructor for GeofenceRequest object. + +| Properties | Type | Description | +|-----------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| geofenceList | List\ | List of geofences to be monitored. | +| initConversions | int | Initial conversion type. This parameter is invalid if it is set to 0. The default value is GeofenceRequest.ENTER_INIT_CONVERSION | GeofenceRequest.DWELL_INIT_CONVERSION. | +| coordinateType | int | Coordinate type of geofences. Defaults to GeofenceRequest.COORDINATE_TYPE_WGS_84. | + +##### GeofenceRequest.fromMap(Map\ map) + +Creates a GeofenceRequest object from a map. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| map | Map\| Map as a source. | + +##### GeofenceRequest.fromJson(String source) + +Creates a GeofenceRequest object from a JSON string. + +| Parameter | Type | Description | +|------------------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------| +| source | String | JSON string as a source. | + +### PermissionHandler + +Checks or requests required permissions. + +#### Public Constructor Summary + +| Constructor | Description | +|------------------------------------------------|----------------------| +| PermissionHandler() | Default constructor. | + +#### Public Method Summary + +| Return Type | Method | Description | +|----------------|----------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| Future\ | hasLocationPermission() | This API is used to check location permission is available or not. | +| Future\ | hasBackgroundLocationPermission() | This API is used to check background location permission is available or not. | +| Future\ | hasActivityRecognitionPermission() | This API is used to check activity permission is available or not. | +| Future\ | requestLocationPermission() | This API is used to request location permission. Returns true if permission is granted, else returns false. | +| Future\ | requestBackgroundLocationPermission() | This API is used to request background location permission. Returns true if permission is granted, else returns false. | +| Future\ | requestActivityRecognitionPermission() | This API is used to request activity recognition permission. Returns true if permission is granted, else returns false. | + +#### Public Constructors + +##### PermissionHandler() + +Constructor for PermissionHandler object. + +#### Public Methods + +##### Future\ hasLocationPermission() *async* + +Checks whether the location permission is available. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | true or false if the operation is successful; PlatformException otherwise. | + +##### Future\ hasBackgroundLocationPermission() *async* + +Checks whether the background location permission is available. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | true or false if the operation is successful; PlatformException otherwise. | + +##### Future\ hasActivityRecognitionPermission() *async* + +Checks whether the activity permission is available. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | true or false if the operation is successful; PlatformException otherwise. | + +##### Future\ requestLocationPermission() *async* + +Requests the location permission. The value true is returned if the permission is granted. Otherwise, false is returned. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | true or false if the operation is successful; PlatformException otherwise. | + +##### Future\ requestBackgroundLocationPermission() *async* + +Requests the background location permission. The value true is returned if the permission is granted. Otherwise, false is returned + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | true or false if the operation is successful; PlatformException otherwise. | + +##### Future\ requestActivityRecognitionPermission() *async* + +Requests the activity permission. The value true is returned if the permission is granted. Otherwise, false is returned. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | true or false if the operation is successful; PlatformException otherwise. | + +### HMSLogger + +#### Public Method Summary + +| Return Type | Method | Description | +|----------------|----------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| Future\ | enableLogger() | This method enables the HMSLogger capability which is used for sending usage analytics of Location SDK's methods to improve the service quality.. | +| Future\ | disableLogger() | This method disables the HMSLogger capability which is used for sending usage analytics of Location SDK's methods to improve the service quality. | + +#### Public Methods + +##### Future\ enableLogger() *async* + +This method enables the HMSLogger capability which is used for sending usage analytics of Location SDK's methods to improve the service quality. HMSLogger is enabled by default, it can be disabled with the disableLogger method. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Future result of an execution that returns no value. | + +##### Future\ disableLogger() *async* + +This method disables the HMSLogger capability which is used for sending usage analytics of Location SDK's methods to improve the service quality. + +| Return Type | Description | +|----------------------------------|-------------| +| Future\ | Future result of an execution that returns no value. | + + +## Configuration Description + + +### Preparing for Release + +Before building a release version of your app you may need to customize the **proguard-rules.pro** obfuscation configuration file to prevent the HMS Core SDK from being obfuscated. Add the configurations below to exclude the HMS Core SDK from obfuscation. For more information on this topic refer to [this Android developer guide](https://developer.android.com/studio/build/shrink-code). + +**/android/app/proguard-rules. pro** + +``` +-ignorewarnings +-keepattributes *Annotation* +-keepattributes Exceptions +-keepattributes InnerClasses +-keepattributes Signature +-keepattributes SourceFile,LineNumberTable +-keep class com.hianalytics.android.**{*;} +-keep class com.huawei.updatesdk.**{*;} +-keep class com.huawei.hms.**{*;} + +``` + +**/android/app/build.gradle** + +```gradle +buildTypes { + debug { + signingConfig signingConfigs.config + } + release { + + // Enables code shrinking, obfuscation and optimization for release builds + minifyEnabled true + // Unused resources will be removed, resources defined in the res/raw/keep.xml will be kept. + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + + signingConfig signingConfigs.config + } +} +``` + + +## Sample Project +This plugin includes a demo project in the **example** folder, there you can find more usage examples. + + + +## Questions or Issues + +If you have questions about how to use HMS samples, try the following options: + +- [Stack Overflow](https://stackoverflow.com/questions/tagged/huawei-mobile-services) is the best place for any programming questions. Be sure to tag your question with **huawei-mobile-services**. +- [Github](https://github.com/HMS-Core/hms-flutter-plugin) is the official repository for these plugins, You can open an issue or submit your ideas. +- [Huawei Developer Forum](https://forums.developer.huawei.com/forumPortal/en/home?fid=0101187876626530001) HMS Core Module is great for general questions, or seeking recommendations and opinions. +- [Huawei Developer Docs](https://developer.huawei.com/consumer/en/doc/overview/HMS-Core-Plugin) is place to official documentation for all HMS Core Kits, you can find detailed documentations in there. + +If you run into a bug in our samples, please submit an issue to the Github Repository. + + +## Licensing and Terms + +Huawei Location Kit Flutter Plugin uses the Apache 2.0 license. \ No newline at end of file diff --git a/hms-plugins/flutter-hms-location/android/build.gradle b/hms-plugins/flutter-hms-location/android/build.gradle new file mode 100644 index 00000000..228af211 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/build.gradle @@ -0,0 +1,45 @@ +group 'com.huawei.hms.flutter.location' +version '1.0' + +buildscript { + repositories { + google() + jcenter() + maven { url 'https://developer.huawei.com/repo/' } + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.6.0' + } +} + +rootProject.allprojects { + repositories { + google() + jcenter() + maven { url 'https://developer.huawei.com/repo/' } + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + lintOptions { + disable 'InvalidPackage' + } +} + +dependencies { + implementation 'com.huawei.hms:location:5.0.0.301' +} diff --git a/hms-plugins/flutter-hms-location/android/gradle.properties b/hms-plugins/flutter-hms-location/android/gradle.properties new file mode 100644 index 00000000..38c8d454 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/hms-plugins/flutter-hms-location/android/gradle/wrapper/gradle-wrapper.properties b/hms-plugins/flutter-hms-location/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..a4b44297 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/hms-plugins/flutter-hms-location/android/gradlew b/hms-plugins/flutter-hms-location/android/gradlew new file mode 100644 index 00000000..2fe81a7d --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/hms-plugins/flutter-hms-location/android/gradlew.bat b/hms-plugins/flutter-hms-location/android/gradlew.bat new file mode 100644 index 00000000..62bd9b9c --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/hms-plugins/flutter-hms-location/android/settings.gradle b/hms-plugins/flutter-hms-location/android/settings.gradle new file mode 100644 index 00000000..2a0347f0 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'huawei_location' diff --git a/hms-plugins/flutter-hms-location/android/src/main/AndroidManifest.xml b/hms-plugins/flutter-hms-location/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..d54dab86 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/LocationPlugin.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/LocationPlugin.java new file mode 100644 index 00000000..4e98a26d --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/LocationPlugin.java @@ -0,0 +1,201 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location; + +import android.app.Activity; +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.huawei.hms.flutter.location.constants.Channel; +import com.huawei.hms.flutter.location.handlers.ActivityConversionStreamHandler; +import com.huawei.hms.flutter.location.handlers.ActivityIdentificationMethodHandler; +import com.huawei.hms.flutter.location.handlers.ActivityIdentificationStreamHandler; +import com.huawei.hms.flutter.location.handlers.FusedLocationMethodHandler; +import com.huawei.hms.flutter.location.handlers.FusedLocationStreamHandler; +import com.huawei.hms.flutter.location.handlers.GeofenceMethodHandler; +import com.huawei.hms.flutter.location.handlers.GeofenceStreamHandler; +import com.huawei.hms.flutter.location.handlers.HMSLoggerMethodHandler; +import com.huawei.hms.flutter.location.handlers.PermissionHandler; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.EventChannel; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.PluginRegistry.Registrar; + +public class LocationPlugin implements FlutterPlugin, ActivityAware { + private MethodChannel permissionMethodChannel; + private MethodChannel fusedLocationMethodChannel; + private MethodChannel geofenceMethodChannel; + private MethodChannel activityIdentificationMethodChannel; + private MethodChannel locationEnhanceMethodChannel; + private MethodChannel hmsLoggerMethodChannel; + private EventChannel mFusedLocationEventChannel; + private EventChannel mGeofenceEventChannel; + private EventChannel mActivityIdentificationEventChannel; + private EventChannel mActivityConversionEventChannel; + private PermissionHandler permissionHandler; + private EventChannel.StreamHandler mFusedLocationStreamHandler; + private EventChannel.StreamHandler mGeofenceStreamHandler; + private EventChannel.StreamHandler mActivityIdentificationStreamHandler; + private EventChannel.StreamHandler mActivityConversionStreamHandler; + private FusedLocationMethodHandler fusedLocationMethodHandler; + private GeofenceMethodHandler geofenceMethodHandler; + private ActivityIdentificationMethodHandler activityIdentificationMethodHandler; + private HMSLoggerMethodHandler hmsLoggerMethodHandler; + + public static void registerWith(final Registrar registrar) { + final LocationPlugin instance = new LocationPlugin(); + final Activity activity = registrar.activity(); + final Context context = activity.getApplicationContext(); + final BinaryMessenger messenger = registrar.messenger(); + + instance.onAttachedToEngine(context, messenger); + instance.permissionHandler = new PermissionHandler(activity); + instance.permissionMethodChannel.setMethodCallHandler(instance.permissionHandler); + instance.fusedLocationMethodHandler = new FusedLocationMethodHandler(activity, + instance.fusedLocationMethodChannel); + instance.fusedLocationMethodChannel.setMethodCallHandler(instance.fusedLocationMethodHandler); + instance.geofenceMethodHandler = new GeofenceMethodHandler(activity); + instance.geofenceMethodChannel.setMethodCallHandler(instance.geofenceMethodHandler); + instance.activityIdentificationMethodHandler = new ActivityIdentificationMethodHandler(activity); + instance.activityIdentificationMethodChannel.setMethodCallHandler(instance.activityIdentificationMethodHandler); + instance.hmsLoggerMethodHandler = new HMSLoggerMethodHandler(context); + instance.hmsLoggerMethodChannel.setMethodCallHandler(instance.hmsLoggerMethodHandler); + registrar.addRequestPermissionsResultListener(instance.permissionHandler); + registrar.addActivityResultListener(instance.fusedLocationMethodHandler); + } + + private void initChannels(final BinaryMessenger messenger) { + permissionMethodChannel = new MethodChannel(messenger, Channel.PERMISSON_METHOD.id()); + fusedLocationMethodChannel = new MethodChannel(messenger, Channel.FUSED_LOCATION_METHOD.id()); + geofenceMethodChannel = new MethodChannel(messenger, Channel.GEOFENCE_METHOD.id()); + activityIdentificationMethodChannel = new MethodChannel(messenger, Channel.ACTIVITY_IDENTIFICATION_METHOD.id()); + locationEnhanceMethodChannel = new MethodChannel(messenger, Channel.LOCATION_ENHANCE_METHOD.id()); + hmsLoggerMethodChannel = new MethodChannel(messenger, Channel.HMSLOGGER_METHOD.id()); + mFusedLocationEventChannel = new EventChannel(messenger, Channel.FUSED_LOCATION_EVENT.id()); + mGeofenceEventChannel = new EventChannel(messenger, Channel.GEOFENCE_EVENT.id()); + mActivityIdentificationEventChannel = new EventChannel(messenger, Channel.ACTIVITY_IDENTIFICATION_EVENT.id()); + mActivityConversionEventChannel = new EventChannel(messenger, Channel.ACTIVITY_CONVERSION_EVENT.id()); + } + + private void initStreamHandlers(final Context context) { + mFusedLocationStreamHandler = new FusedLocationStreamHandler(context); + mGeofenceStreamHandler = new GeofenceStreamHandler(context); + mActivityIdentificationStreamHandler = new ActivityIdentificationStreamHandler(context); + mActivityConversionStreamHandler = new ActivityConversionStreamHandler(context); + } + + private void setStreamHandlers() { + mFusedLocationEventChannel.setStreamHandler(mFusedLocationStreamHandler); + mGeofenceEventChannel.setStreamHandler(mGeofenceStreamHandler); + mActivityIdentificationEventChannel.setStreamHandler(mActivityIdentificationStreamHandler); + mActivityConversionEventChannel.setStreamHandler(mActivityConversionStreamHandler); + } + + private void resetStreamHandlers() { + mFusedLocationEventChannel.setStreamHandler(null); + mGeofenceEventChannel.setStreamHandler(null); + mActivityIdentificationEventChannel.setStreamHandler(null); + mActivityConversionEventChannel.setStreamHandler(null); + } + + private void removeStreamHandlers() { + mFusedLocationStreamHandler = null; + mGeofenceStreamHandler = null; + mActivityIdentificationStreamHandler = null; + mActivityConversionStreamHandler = null; + } + + private void removeChannels() { + permissionMethodChannel = null; + fusedLocationMethodChannel = null; + geofenceMethodChannel = null; + activityIdentificationMethodChannel = null; + locationEnhanceMethodChannel = null; + hmsLoggerMethodChannel = null; + mFusedLocationEventChannel = null; + mGeofenceEventChannel = null; + mActivityIdentificationEventChannel = null; + mActivityConversionEventChannel = null; + } + + private void onAttachedToEngine(final Context context, final BinaryMessenger messenger) { + initChannels(messenger); + initStreamHandlers(context); + setStreamHandlers(); + } + + @Override + public void onAttachedToEngine(@NonNull final FlutterPluginBinding binding) { + onAttachedToEngine(binding.getApplicationContext(), binding.getBinaryMessenger()); + } + + @Override + public void onDetachedFromEngine(@NonNull final FlutterPluginBinding binding) { + resetStreamHandlers(); + removeStreamHandlers(); + removeChannels(); + } + + @Override + public void onAttachedToActivity(@NonNull final ActivityPluginBinding binding) { + final Activity activity = binding.getActivity(); + + permissionHandler = new PermissionHandler(activity); + fusedLocationMethodHandler = new FusedLocationMethodHandler(activity, fusedLocationMethodChannel); + geofenceMethodHandler = new GeofenceMethodHandler(activity); + activityIdentificationMethodHandler = new ActivityIdentificationMethodHandler(activity); + hmsLoggerMethodHandler = new HMSLoggerMethodHandler(activity.getApplicationContext()); + + binding.addRequestPermissionsResultListener(permissionHandler); + binding.addActivityResultListener(fusedLocationMethodHandler); + + permissionMethodChannel.setMethodCallHandler(permissionHandler); + fusedLocationMethodChannel.setMethodCallHandler(fusedLocationMethodHandler); + geofenceMethodChannel.setMethodCallHandler(geofenceMethodHandler); + activityIdentificationMethodChannel.setMethodCallHandler(activityIdentificationMethodHandler); + hmsLoggerMethodChannel.setMethodCallHandler(hmsLoggerMethodHandler); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + onDetachedFromActivity(); + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull final ActivityPluginBinding binding) { + onAttachedToActivity(binding); + } + + @Override + public void onDetachedFromActivity() { + activityIdentificationMethodChannel.setMethodCallHandler(null); + geofenceMethodChannel.setMethodCallHandler(null); + fusedLocationMethodChannel.setMethodCallHandler(null); + locationEnhanceMethodChannel.setMethodCallHandler(null); + hmsLoggerMethodChannel.setMethodCallHandler(null); + activityIdentificationMethodHandler = null; + geofenceMethodHandler = null; + fusedLocationMethodHandler = null; + permissionHandler = null; + hmsLoggerMethodHandler = null; + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Action.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Action.java new file mode 100644 index 00000000..9a83e743 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Action.java @@ -0,0 +1,24 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.constants; + +public interface Action { + String PROCESS_LOCATION = "com.huawei.hms.flutter.location.ACTION_PROCESS_LOCATION"; + String PROCESS_GEOFENCE = "com.huawei.hms.flutter.location.ACTION_PROCESS_GEOFENCE"; + String PROCESS_CONVERSION = "com.huawei.hms.flutter.location.ACTION_PROCESS_CONVERSION"; + String PROCESS_IDENTIFICATION = "com.huawei.hms.flutter.location.ACTION_PROCESS_IDENTIFICATION"; +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Channel.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Channel.java new file mode 100644 index 00000000..3e21a60f --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Channel.java @@ -0,0 +1,40 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.constants; + +public enum Channel { + PERMISSON_METHOD("com.huawei.flutter.location/permission_methodchannel"), + FUSED_LOCATION_METHOD("com.huawei.flutter.location/fusedlocation_methodchannel"), + FUSED_LOCATION_EVENT("com.huawei.flutter.location/fusedlocation_eventchannel"), + GEOFENCE_METHOD("com.huawei.flutter.location/geofence_methodchannel"), + GEOFENCE_EVENT("com.huawei.flutter.location/geofence_eventchannel"), + ACTIVITY_IDENTIFICATION_METHOD("com.huawei.flutter.location/activityidentification_methodchannel"), + ACTIVITY_IDENTIFICATION_EVENT("com.huawei.flutter.location/activityidentification_eventchannel"), + ACTIVITY_CONVERSION_EVENT("com.huawei.flutter.location/activityconversion_eventchannel"), + LOCATION_ENHANCE_METHOD("com.huawei.flutter.location/locationenhance_methodchannel"), + HMSLOGGER_METHOD("com.huawei.flutter.location/hmslogger_methodchannel"); + + private final String id; + + Channel(final String id) { + this.id = id; + } + + public String id() { + return id; + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Error.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Error.java new file mode 100644 index 00000000..35f42ea8 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/constants/Error.java @@ -0,0 +1,33 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.constants; + +public enum Error { + LOCATION_SETTINGS_NOT_AVAILABLE("Unable to get location settings"), + NON_EXISTING_REQUEST_ID("Request ID does not exists"), + SEND_INTENT_EXCEPTION("Unable to send intent"); + + private final String message; + + Error(final String message) { + this.message = message; + } + + public String message() { + return message; + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityConversionStreamHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityConversionStreamHandler.java new file mode 100644 index 00000000..f761e0ca --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityConversionStreamHandler.java @@ -0,0 +1,53 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.receivers.ActivityConversionBroadcastReceiver; + +import io.flutter.plugin.common.EventChannel.EventSink; +import io.flutter.plugin.common.EventChannel.StreamHandler; + +public class ActivityConversionStreamHandler implements StreamHandler { + private final Context context; + private BroadcastReceiver broadcastReceiver; + + public ActivityConversionStreamHandler(final Context context) { + this.context = context; + } + + @Override + public void onListen(final Object arguments, final EventSink events) { + broadcastReceiver = new ActivityConversionBroadcastReceiver(events); + context.registerReceiver(broadcastReceiver, new IntentFilter(Action.PROCESS_CONVERSION)); + + HMSLogger.getInstance(context).sendSingleEvent("ActivityConversionStreamHandler.onListen"); + } + + @Override + public void onCancel(final Object arguments) { + context.unregisterReceiver(broadcastReceiver); + broadcastReceiver = null; + + HMSLogger.getInstance(context).sendSingleEvent("ActivityConversionStreamHandler.onCancel"); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationMethodHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationMethodHandler.java new file mode 100644 index 00000000..ab502248 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationMethodHandler.java @@ -0,0 +1,141 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; + +import androidx.annotation.NonNull; +import androidx.core.util.Pair; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.constants.Error; +import com.huawei.hms.flutter.location.listeners.DefaultFailureListener; +import com.huawei.hms.flutter.location.listeners.RemoveUpdatesSuccessListener; +import com.huawei.hms.flutter.location.listeners.RequestUpdatesFailureListener; +import com.huawei.hms.flutter.location.listeners.RequestUpdatesSuccessListener; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.ActivityUtils; +import com.huawei.hms.location.ActivityConversionRequest; +import com.huawei.hms.location.ActivityIdentification; +import com.huawei.hms.location.ActivityIdentificationService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +public class ActivityIdentificationMethodHandler implements MethodCallHandler { + private final Activity activity; + private final Map requests; + private final ActivityIdentificationService service; + + private int requestCode = 0; + + public ActivityIdentificationMethodHandler(final Activity activity) { + this.activity = activity; + service = ActivityIdentification.getService(activity); + requests = new HashMap<>(); + } + + private void createActivityIdentificationUpdates(final MethodCall call, final Result result) { + final Pair intentData = buildPendingIntent(Action.PROCESS_IDENTIFICATION); + + service.createActivityIdentificationUpdates(call.arguments(), intentData.second) + .addOnSuccessListener(new RequestUpdatesSuccessListener(call.method, activity, result, intentData.first)) + .addOnFailureListener( + new RequestUpdatesFailureListener<>(call.method, activity, result, intentData.first, requests)); + } + + private void createActivityConversionUpdates(final MethodCall call, final Result result) { + final List> args = call.arguments(); + final Pair intentData = buildPendingIntent(Action.PROCESS_CONVERSION); + final ActivityConversionRequest request + = ActivityUtils.fromActivityConversionInfoListToActivityConversionRequest(args); + + service.createActivityConversionUpdates(request, intentData.second) + .addOnSuccessListener(new RequestUpdatesSuccessListener(call.method, activity, result, intentData.first)) + .addOnFailureListener( + new RequestUpdatesFailureListener<>(call.method, activity, result, intentData.first, requests)); + } + + private void deleteActivityIdentificationUpdates(final MethodCall call, final Result result) { + final int incomingRequestCode = call.arguments(); + + if (!requests.containsKey(incomingRequestCode)) { + result.error(Error.NON_EXISTING_REQUEST_ID.name(), Error.NON_EXISTING_REQUEST_ID.message(), null); + } else { + service.deleteActivityIdentificationUpdates(requests.get(incomingRequestCode)) + .addOnSuccessListener( + new RemoveUpdatesSuccessListener<>(call.method, activity, result, incomingRequestCode, requests)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + } + + private void deleteActivityConversionUpdates(final MethodCall call, final Result result) { + final int incomingRequestCode = call.arguments(); + + if (!requests.containsKey(incomingRequestCode)) { + result.error(Error.NON_EXISTING_REQUEST_ID.name(), Error.NON_EXISTING_REQUEST_ID.message(), null); + } else { + service.deleteActivityConversionUpdates(requests.get(incomingRequestCode)) + .addOnSuccessListener( + new RemoveUpdatesSuccessListener<>(call.method, activity, result, incomingRequestCode, requests)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + } + + private Pair buildPendingIntent(final String action) { + final Intent intent = new Intent(); + + intent.setPackage(activity.getPackageName()); + intent.setAction(action); + + final PendingIntent pendingIntent = PendingIntent.getBroadcast(activity.getApplicationContext(), ++requestCode, + intent, PendingIntent.FLAG_UPDATE_CURRENT); + + requests.put(requestCode, pendingIntent); + return Pair.create(requestCode, pendingIntent); + } + + @Override + public void onMethodCall(@NonNull final MethodCall call, @NonNull final Result result) { + HMSLogger.getInstance(activity).startMethodExecutionTimer(call.method); + + switch (call.method) { + case "createActivityIdentificationUpdates": + createActivityIdentificationUpdates(call, result); + break; + case "createActivityConversionUpdates": + createActivityConversionUpdates(call, result); + break; + case "deleteActivityIdentificationUpdates": + deleteActivityIdentificationUpdates(call, result); + break; + case "deleteActivityConversionUpdates": + deleteActivityConversionUpdates(call, result); + break; + default: + result.notImplemented(); + break; + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationStreamHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationStreamHandler.java new file mode 100644 index 00000000..8a2303d4 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/ActivityIdentificationStreamHandler.java @@ -0,0 +1,53 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.receivers.ActivityIdentificationBroadcastReceiver; + +import io.flutter.plugin.common.EventChannel.EventSink; +import io.flutter.plugin.common.EventChannel.StreamHandler; + +public class ActivityIdentificationStreamHandler implements StreamHandler { + private final Context context; + private BroadcastReceiver broadcastReceiver; + + public ActivityIdentificationStreamHandler(final Context context) { + this.context = context; + } + + @Override + public void onListen(final Object arguments, final EventSink events) { + broadcastReceiver = new ActivityIdentificationBroadcastReceiver(events); + context.registerReceiver(broadcastReceiver, new IntentFilter(Action.PROCESS_IDENTIFICATION)); + + HMSLogger.getInstance(context).sendSingleEvent("ActivityIdentificationStreamHandler.onListen"); + } + + @Override + public void onCancel(final Object arguments) { + context.unregisterReceiver(broadcastReceiver); + broadcastReceiver = null; + + HMSLogger.getInstance(context).sendSingleEvent("ActivityIdentificationStreamHandler.onCancel"); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationMethodHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationMethodHandler.java new file mode 100644 index 00000000..d4ec83a0 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationMethodHandler.java @@ -0,0 +1,265 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Looper; +import android.util.Pair; + +import androidx.annotation.NonNull; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.constants.Error; +import com.huawei.hms.flutter.location.listeners.DefaultFailureListener; +import com.huawei.hms.flutter.location.listeners.DefaultSuccessListener; +import com.huawei.hms.flutter.location.listeners.LocationSettingsFailureListener; +import com.huawei.hms.flutter.location.listeners.RemoveUpdatesSuccessListener; +import com.huawei.hms.flutter.location.listeners.RequestUpdatesFailureListener; +import com.huawei.hms.flutter.location.listeners.RequestUpdatesSuccessListener; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.LocationUtils; +import com.huawei.hms.location.FusedLocationProviderClient; +import com.huawei.hms.location.LocationEnhanceService; +import com.huawei.hms.location.LocationRequest; +import com.huawei.hms.location.LocationServices; +import com.huawei.hms.location.LocationSettingsRequest; +import com.huawei.hms.location.LocationSettingsStates; +import com.huawei.hms.location.NavigationRequest; +import com.huawei.hms.location.SettingsClient; + +import java.util.HashMap; +import java.util.Map; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.PluginRegistry.ActivityResultListener; + +public class FusedLocationMethodHandler implements MethodChannel.MethodCallHandler, ActivityResultListener { + private final Activity activity; + private final MethodChannel channel; + private final Map callbacks; + private final Map requests; + private final SettingsClient settingsClient; + private final FusedLocationProviderClient service; + private final LocationEnhanceService enhanceService; + + private int requestCode = 0; + private MethodChannel.Result result; + + public FusedLocationMethodHandler(final Activity activity, final MethodChannel channel) { + this.activity = activity; + this.channel = channel; + callbacks = new HashMap<>(); + requests = new HashMap<>(); + settingsClient = LocationServices.getSettingsClient(activity); + service = LocationServices.getFusedLocationProviderClient(activity); + enhanceService = LocationServices.getLocationEnhanceService(activity); + } + + private void checkLocationSettings(final MethodCall call, final MethodChannel.Result result) { + final LocationSettingsRequest request = LocationUtils.fromMapToLocationSettingsRequest(call.arguments()); + this.result = result; + + settingsClient.checkLocationSettings(request) + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new LocationSettingsFailureListener(result, activity)); + } + + private void getLastLocation(final MethodCall call, final MethodChannel.Result result) { + service.getLastLocation() + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private void getLastLocationWithAddress(final MethodCall call, final MethodChannel.Result result) { + service.getLastLocationWithAddress( + LocationUtils.fromMapToLocationRequest(call.>arguments())) + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private void getLocationAvailability(final MethodCall call, final MethodChannel.Result result) { + service.getLocationAvailability() + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private void setMockMode(final MethodCall call, final MethodChannel.Result result) { + service.setMockMode(call.arguments()) + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private void setMockLocation(final MethodCall call, final MethodChannel.Result result) { + service.setMockLocation(LocationUtils.fromMapToLocation(call.arguments())) + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private void requestLocationUpdates(final MethodCall call, final MethodChannel.Result result) { + final Pair intentData = buildLocationIntent(); + final LocationRequest request = LocationUtils.fromMapToLocationRequest(call.>arguments()); + + service.requestLocationUpdates(request, intentData.second) + .addOnSuccessListener(new RequestUpdatesSuccessListener(call.method, activity, result, intentData.first)) + .addOnFailureListener( + new RequestUpdatesFailureListener<>(call.method, activity, result, intentData.first, requests)); + } + + private void requestLocationUpdatesCb(final MethodCall call, final MethodChannel.Result result) { + final LocationRequest request = LocationUtils.fromMapToLocationRequest(call.>arguments()); + final Pair callbackData = buildCallback(call.method); + + service.requestLocationUpdates(request, callbackData.second, Looper.getMainLooper()) + .addOnSuccessListener(new RequestUpdatesSuccessListener(call.method, activity, result, callbackData.first)) + .addOnFailureListener( + new RequestUpdatesFailureListener<>(call.method, activity, result, callbackData.first, callbacks)); + } + + private void requestLocationUpdatesExCb(final MethodCall call, final MethodChannel.Result result) { + final LocationRequest request = LocationUtils.fromMapToLocationRequest(call.>arguments()); + final Pair callbackData = buildCallback(call.method); + + service.requestLocationUpdatesEx(request, callbackData.second, Looper.getMainLooper()) + .addOnSuccessListener(new RequestUpdatesSuccessListener(call.method, activity, result, callbackData.first)) + .addOnFailureListener( + new RequestUpdatesFailureListener<>(call.method, activity, result, callbackData.first, callbacks)); + } + + private void removeLocationUpdates(final MethodCall call, final MethodChannel.Result result) { + final int incomingRequestCode = call.arguments(); + + if (!requests.containsKey(incomingRequestCode)) { + result.error(Error.NON_EXISTING_REQUEST_ID.name(), Error.NON_EXISTING_REQUEST_ID.message(), null); + } else { + service.removeLocationUpdates(requests.get(incomingRequestCode)) + .addOnSuccessListener( + new RemoveUpdatesSuccessListener<>(call.method, activity, result, incomingRequestCode, requests)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + } + + private void removeLocationUpdatesCb(final MethodCall call, final MethodChannel.Result result) { + final int callbackId = call.arguments(); + + if (!callbacks.containsKey(callbackId)) { + result.error(Error.NON_EXISTING_REQUEST_ID.name(), Error.NON_EXISTING_REQUEST_ID.message(), null); + } else { + service.removeLocationUpdates(callbacks.get(callbackId)) + .addOnSuccessListener( + new RemoveUpdatesSuccessListener<>(call.method, activity, result, callbackId, callbacks)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + } + + private void getNavigationContextState(final MethodCall call, final MethodChannel.Result result) { + final NavigationRequest request = LocationUtils.fromMapToNavigationRequest(call.arguments()); + + enhanceService.getNavigationState(request) + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private Pair buildCallback(final String methodName) { + final LocationCallbackHandler callBack = new LocationCallbackHandler(activity.getApplicationContext(), + methodName, ++requestCode, channel); + callbacks.put(requestCode, callBack); + return Pair.create(requestCode, callBack); + } + + private Pair buildLocationIntent() { + final Intent intent = new Intent(); + intent.setPackage(activity.getPackageName()); + intent.setAction(Action.PROCESS_LOCATION); + + final PendingIntent pendingIntent = PendingIntent.getBroadcast(activity.getApplicationContext(), ++requestCode, + intent, PendingIntent.FLAG_UPDATE_CURRENT); + requests.put(requestCode, pendingIntent); + return Pair.create(requestCode, pendingIntent); + } + + @Override + public void onMethodCall(@NonNull final MethodCall call, @NonNull final MethodChannel.Result result) { + HMSLogger.getInstance(activity.getApplicationContext()).startMethodExecutionTimer(call.method); + + switch (call.method) { + case "checkLocationSettings": + checkLocationSettings(call, result); + break; + case "getLastLocation": + getLastLocation(call, result); + break; + case "getLastLocationWithAddress": + getLastLocationWithAddress(call, result); + break; + case "getLocationAvailability": + getLocationAvailability(call, result); + break; + case "setMockMode": + setMockMode(call, result); + break; + case "setMockLocation": + setMockLocation(call, result); + break; + case "requestLocationUpdates": + requestLocationUpdates(call, result); + break; + case "requestLocationUpdatesCb": + requestLocationUpdatesCb(call, result); + break; + case "requestLocationUpdatesExCb": + requestLocationUpdatesExCb(call, result); + break; + case "removeLocationUpdates": + removeLocationUpdates(call, result); + break; + case "removeLocationUpdatesCb": + removeLocationUpdatesCb(call, result); + break; + case "getNavigationContextState": + getNavigationContextState(call, result); + break; + default: + result.notImplemented(); + break; + } + } + + @Override + public boolean onActivityResult(final int requestCode, final int resultCode, final Intent intent) { + final MethodChannel.Result incomingResult = result; + result = null; + + if (incomingResult != null && requestCode == 0) { + if (resultCode == Activity.RESULT_OK) { + final LocationSettingsStates states = LocationSettingsStates.fromIntent(intent); + HMSLogger.getInstance(activity.getApplicationContext()) + .sendSingleEvent("checkLocationSettings.onActivityResult"); + incomingResult.success(LocationUtils.fromLocationSettingsStatesToMap(states)); + } else { + HMSLogger.getInstance(activity.getApplicationContext()) + .sendSingleEvent("checkLocationSettings" + ".onActivityResult", "-1"); + incomingResult.error(Error.LOCATION_SETTINGS_NOT_AVAILABLE.name(), + Error.LOCATION_SETTINGS_NOT_AVAILABLE.message(), null); + } + } + + return true; + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationStreamHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationStreamHandler.java new file mode 100644 index 00000000..03d732b0 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/FusedLocationStreamHandler.java @@ -0,0 +1,51 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.receivers.FusedLocationBroadcastReceiver; + +import io.flutter.plugin.common.EventChannel.EventSink; +import io.flutter.plugin.common.EventChannel.StreamHandler; + +public class FusedLocationStreamHandler implements StreamHandler { + private final Context context; + private BroadcastReceiver broadcastReceiver; + + public FusedLocationStreamHandler(final Context context) { + this.context = context; + } + + @Override + public void onListen(final Object arguments, final EventSink events) { + broadcastReceiver = new FusedLocationBroadcastReceiver(events); + context.registerReceiver(broadcastReceiver, new IntentFilter(Action.PROCESS_LOCATION)); + HMSLogger.getInstance(context).sendSingleEvent("FusedLocationStreamHandler.onListen"); + } + + @Override + public void onCancel(final Object arguments) { + context.unregisterReceiver(broadcastReceiver); + broadcastReceiver = null; + HMSLogger.getInstance(context).sendSingleEvent("FusedLocationStreamHandler.onCancel"); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceMethodHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceMethodHandler.java new file mode 100644 index 00000000..6c479b35 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceMethodHandler.java @@ -0,0 +1,119 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.util.Pair; + +import androidx.annotation.NonNull; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.constants.Error; +import com.huawei.hms.flutter.location.listeners.DefaultFailureListener; +import com.huawei.hms.flutter.location.listeners.DefaultSuccessListener; +import com.huawei.hms.flutter.location.listeners.RemoveUpdatesSuccessListener; +import com.huawei.hms.flutter.location.listeners.RequestUpdatesFailureListener; +import com.huawei.hms.flutter.location.listeners.RequestUpdatesSuccessListener; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.GeofenceUtils; +import com.huawei.hms.location.GeofenceRequest; +import com.huawei.hms.location.GeofenceService; +import com.huawei.hms.location.LocationServices; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +public class GeofenceMethodHandler implements MethodCallHandler { + private final Activity activity; + private final GeofenceService service; + private final Map requests; + + private int requestCode = 0; + + public GeofenceMethodHandler(final Activity activity) { + this.activity = activity; + service = LocationServices.getGeofenceService(activity); + requests = new HashMap<>(); + } + + private void createGeofenceList(@NonNull final MethodCall call, @NonNull final Result result) { + final GeofenceRequest geofenceRequest = GeofenceUtils.fromMapToGeofenceRequest(call.arguments()); + final Pair intentData = buildGeofenceIntent(); + + service.createGeofenceList(geofenceRequest, intentData.second) + .addOnSuccessListener(new RequestUpdatesSuccessListener(call.method, activity, result, intentData.first)) + .addOnFailureListener( + new RequestUpdatesFailureListener<>(call.method, activity, result, intentData.first, requests)); + } + + private void deleteGeofenceList(@NonNull final MethodCall call, @NonNull final Result result) { + final int incomingRequestCode = call.arguments(); + + if (!requests.containsKey(incomingRequestCode)) { + result.error(Error.NON_EXISTING_REQUEST_ID.name(), Error.NON_EXISTING_REQUEST_ID.message(), null); + } else { + service.deleteGeofenceList(requests.get(incomingRequestCode)) + .addOnSuccessListener( + new RemoveUpdatesSuccessListener<>(call.method, activity, result, incomingRequestCode, requests)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + } + + private void deleteGeofenceListWithIds(@NonNull final MethodCall call, @NonNull final Result result) { + service.deleteGeofenceList(call.>arguments()) + .addOnSuccessListener(new DefaultSuccessListener<>(call.method, activity, result)) + .addOnFailureListener(new DefaultFailureListener(call.method, activity, result)); + } + + private Pair buildGeofenceIntent() { + final Intent intent = new Intent(); + intent.setPackage(activity.getPackageName()); + intent.setAction(Action.PROCESS_GEOFENCE); + + final PendingIntent pendingIntent = PendingIntent.getBroadcast(activity.getApplicationContext(), ++requestCode, + intent, PendingIntent.FLAG_UPDATE_CURRENT); + requests.put(requestCode, pendingIntent); + return Pair.create(requestCode, pendingIntent); + } + + @Override + public void onMethodCall(@NonNull final MethodCall call, @NonNull final Result result) { + HMSLogger.getInstance(activity.getApplicationContext()).startMethodExecutionTimer(call.method); + + switch (call.method) { + case "createGeofenceList": + createGeofenceList(call, result); + break; + case "deleteGeofenceList": + deleteGeofenceList(call, result); + break; + case "deleteGeofenceListWithIds": + deleteGeofenceListWithIds(call, result); + break; + default: + result.notImplemented(); + break; + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceStreamHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceStreamHandler.java new file mode 100644 index 00000000..d2c24d69 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/GeofenceStreamHandler.java @@ -0,0 +1,53 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; + +import com.huawei.hms.flutter.location.constants.Action; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.receivers.GeofenceBroadcastReceiver; + +import io.flutter.plugin.common.EventChannel.EventSink; +import io.flutter.plugin.common.EventChannel.StreamHandler; + +public class GeofenceStreamHandler implements StreamHandler { + private final Context context; + private BroadcastReceiver broadcastReceiver; + + public GeofenceStreamHandler(final Context context) { + this.context = context; + } + + @Override + public void onListen(final Object arguments, final EventSink events) { + broadcastReceiver = new GeofenceBroadcastReceiver(events); + context.registerReceiver(broadcastReceiver, new IntentFilter(Action.PROCESS_GEOFENCE)); + + HMSLogger.getInstance(context).sendSingleEvent("GeofenceStreamHandler.onListen"); + } + + @Override + public void onCancel(final Object arguments) { + context.unregisterReceiver(broadcastReceiver); + broadcastReceiver = null; + + HMSLogger.getInstance(context).sendSingleEvent("GeofenceStreamHandler.onCancel"); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/HMSLoggerMethodHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/HMSLoggerMethodHandler.java new file mode 100644 index 00000000..57d4a65e --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/HMSLoggerMethodHandler.java @@ -0,0 +1,51 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.huawei.hms.flutter.location.logger.HMSLogger; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +public class HMSLoggerMethodHandler implements MethodCallHandler { + private final Context context; + + public HMSLoggerMethodHandler(final Context context) { + this.context = context; + } + + @Override + public void onMethodCall(final MethodCall call, @NonNull final Result result) { + switch (call.method) { + case "enableLogger": + HMSLogger.getInstance(context).enableLogger(); + result.success(null); + break; + case "disableLogger": + HMSLogger.getInstance(context).disableLogger(); + result.success(null); + break; + default: + break; + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/LocationCallbackHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/LocationCallbackHandler.java new file mode 100644 index 00000000..d932988e --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/LocationCallbackHandler.java @@ -0,0 +1,71 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import android.content.Context; + +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.LocationUtils; +import com.huawei.hms.location.LocationAvailability; +import com.huawei.hms.location.LocationCallback; +import com.huawei.hms.location.LocationResult; + +import java.util.HashMap; +import java.util.Map; + +import io.flutter.plugin.common.MethodChannel; + +public class LocationCallbackHandler extends LocationCallback { + private final Context context; + private final String methodName; + private final int callbackId; + private final MethodChannel channel; + + LocationCallbackHandler(final Context context, final String methodName, final int callbackId, + final MethodChannel channel) { + this.context = context; + this.methodName = methodName; + this.callbackId = callbackId; + this.channel = channel; + } + + @Override + public void onLocationResult(final LocationResult locationResult) { + if (locationResult != null) { + final Map map = new HashMap<>(); + + map.put("callbackId", callbackId); + map.put("locationResult", LocationUtils.fromLocationResultToMap(locationResult)); + + HMSLogger.getInstance(context).sendPeriodicEvent(methodName + ".onLocationResult"); + channel.invokeMethod("onLocationResult", map); + } + } + + @Override + public void onLocationAvailability(final LocationAvailability locationAvailability) { + if (locationAvailability != null) { + final Map map = new HashMap<>(); + + map.put("callbackId", callbackId); + map.put("locationAvailability", LocationUtils.fromLocationAvailabilityToMap(locationAvailability)); + + HMSLogger.getInstance(context).sendPeriodicEvent(methodName + ".onLocationAvailability"); + channel.invokeMethod("onLocationAvailability", map); + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/PermissionHandler.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/PermissionHandler.java new file mode 100644 index 00000000..7db5a267 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/handlers/PermissionHandler.java @@ -0,0 +1,192 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.handlers; + +import static androidx.core.content.PermissionChecker.PERMISSION_GRANTED; +import static androidx.core.content.PermissionChecker.checkSelfPermission; + +import android.app.Activity; +import android.os.Build; + +import androidx.annotation.NonNull; + +import com.huawei.hms.flutter.location.logger.HMSLogger; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; +import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener; + +public class PermissionHandler implements MethodCallHandler, RequestPermissionsResultListener { + private static final String HUAWEI_ACTIVITY_RECOGNITION_PERMISSION = "android.permission.ACTIVITY_RECOGNITION"; + private static final String[] BG_LOC_PERMISSION_OLD = { + android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION + }; + private final Activity activity; + private final HMSLogger hmsLogger; + + private Result result; + + public PermissionHandler(final Activity activity) { + this.activity = activity; + hmsLogger = HMSLogger.getInstance(activity.getApplicationContext()); + } + + private boolean hasLocationPermission() { + hmsLogger.sendSingleEvent("hasLocationPermission"); + return isCoarseLocGranted() && isFineLocGranted(); + } + + private boolean hasBackgroundLocationPermission() { + hmsLogger.sendSingleEvent("hasBackgroundLocationPermission"); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + return isFineLocGranted() && isBackgroundLocGranted(); + } + return isCoarseLocGranted() || isFineLocGranted(); + } + + private boolean hasActivityRecognitionPermission() { + hmsLogger.sendSingleEvent("hasActivityRecognitionPermission"); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + return PERMISSION_GRANTED == checkSelfPermission(activity.getApplicationContext(), + android.Manifest.permission.ACTIVITY_RECOGNITION); + } else { + return PERMISSION_GRANTED == checkSelfPermission(activity.getApplicationContext(), + HUAWEI_ACTIVITY_RECOGNITION_PERMISSION); + } + } + + private void requestLocationPermission() { + hmsLogger.sendSingleEvent("requestLocationPermission"); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + final String[] permissions = { + android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION + }; + activity.requestPermissions(permissions, 1); + } else { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + activity.requestPermissions(BG_LOC_PERMISSION_OLD, 2); + } + } + } + + private void requestBackgroundLocationPermission() { + hmsLogger.sendSingleEvent("requestBackgroundLocationPermission"); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + final String[] permissions = { + android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION + }; + activity.requestPermissions(permissions, 3); + } else { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + activity.requestPermissions(BG_LOC_PERMISSION_OLD, 4); + } + } + } + + private void requestActivityRecognitionPermission() { + hmsLogger.sendSingleEvent("requestActivityRecognitionPermission"); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + final String[] permissions = {android.Manifest.permission.ACTIVITY_RECOGNITION}; + activity.requestPermissions(permissions, 5); + } else { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + final String[] permissions = {HUAWEI_ACTIVITY_RECOGNITION_PERMISSION}; + activity.requestPermissions(permissions, 6); + } + } + } + + private boolean isCoarseLocGranted() { + final int coarseLoc = checkSelfPermission(activity, android.Manifest.permission.ACCESS_COARSE_LOCATION); + return coarseLoc == PERMISSION_GRANTED; + } + + private boolean isFineLocGranted() { + final int fineLoc = checkSelfPermission(activity, android.Manifest.permission.ACCESS_FINE_LOCATION); + return fineLoc == PERMISSION_GRANTED; + } + + private boolean isBackgroundLocGranted() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + final int backgroundLoc = checkSelfPermission(activity, + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION); + return backgroundLoc == PERMISSION_GRANTED; + } + return true; + } + + private boolean checkGrantStatus(final int[] grantResults) { + for (final int i : grantResults) { + if (i != 0) { + return false; + } + } + return true; + } + + @Override + public void onMethodCall(final MethodCall call, @NonNull final Result result) { + this.result = result; + hmsLogger.startMethodExecutionTimer(call.method); + + switch (call.method) { + case "hasLocationPermission": + result.success(hasLocationPermission()); + break; + case "hasBackgroundLocationPermission": + result.success(hasBackgroundLocationPermission()); + break; + case "hasActivityRecognitionPermission": + result.success(hasActivityRecognitionPermission()); + break; + case "requestLocationPermission": + requestLocationPermission(); + break; + case "requestBackgroundLocationPermission": + requestBackgroundLocationPermission(); + break; + case "requestActivityRecognitionPermission": + requestActivityRecognitionPermission(); + break; + default: + break; + } + } + + @Override + public boolean onRequestPermissionsResult(final int requestCode, final String[] permissions, + final int[] grantResults) { + final Result incomingResult = result; + result = null; + + if (incomingResult != null) { + if (requestCode == 1) { + incomingResult.success(grantResults[0] == 0 || grantResults[1] == 0); + } else if (requestCode == 3) { + incomingResult.success((grantResults[0] == 0 || grantResults[1] == 0) && grantResults[2] == 0); + } else { + incomingResult.success(checkGrantStatus(grantResults)); + } + } + return true; + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultFailureListener.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultFailureListener.java new file mode 100644 index 00000000..c0a45044 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultFailureListener.java @@ -0,0 +1,47 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.listeners; + +import android.content.Context; + +import com.huawei.hmf.tasks.OnFailureListener; +import com.huawei.hms.common.ApiException; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.ObjectUtils; + +import io.flutter.plugin.common.MethodChannel.Result; + +public class DefaultFailureListener implements OnFailureListener { + private final String methodName; + private final Context context; + private final Result result; + + public DefaultFailureListener(final String methodName, final Context context, final Result result) { + this.methodName = methodName; + this.context = context; + this.result = result; + } + + @Override + public void onFailure(final Exception e) { + final ApiException ex = ObjectUtils.cast(e, ApiException.class); + final String statusCodeString = Integer.toString(ex.getStatusCode()); + + HMSLogger.getInstance(context).sendSingleEvent(methodName, statusCodeString); + result.error(statusCodeString, ex.getMessage(), null); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultSuccessListener.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultSuccessListener.java new file mode 100644 index 00000000..3576ad59 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/DefaultSuccessListener.java @@ -0,0 +1,79 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.listeners; + +import android.content.Context; +import android.location.Location; + +import com.huawei.hmf.tasks.OnSuccessListener; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.LocationUtils; +import com.huawei.hms.flutter.location.utils.ObjectUtils; +import com.huawei.hms.location.HWLocation; +import com.huawei.hms.location.LocationAvailability; +import com.huawei.hms.location.LocationSettingsResponse; +import com.huawei.hms.location.LocationSettingsStates; +import com.huawei.hms.location.NavigationResult; + +import io.flutter.plugin.common.MethodChannel.Result; + +public class DefaultSuccessListener implements OnSuccessListener { + private final String methodName; + private final Context context; + private final Result result; + + public DefaultSuccessListener(final String methodName, final Context context, final Result result) { + this.methodName = methodName; + this.context = context; + this.result = result; + } + + @Override + public void onSuccess(final T o) { + HMSLogger.getInstance(context).sendSingleEvent(methodName); + + if (o instanceof Void || o == null) { + result.success(null); + } + + if (o instanceof Location) { + final Location location = ObjectUtils.cast(o, Location.class); + result.success(LocationUtils.fromLocationToMap(location)); + } + + if (o instanceof HWLocation) { + final HWLocation hwLocation = ObjectUtils.cast(o, HWLocation.class); + result.success(LocationUtils.fromHWLocationToMap(hwLocation)); + } + + if (o instanceof LocationAvailability) { + final LocationAvailability locationAvailability = ObjectUtils.cast(o, LocationAvailability.class); + result.success(LocationUtils.fromLocationAvailabilityToMap(locationAvailability)); + } + + if (o instanceof LocationSettingsResponse) { + final LocationSettingsResponse response = ObjectUtils.cast(o, LocationSettingsResponse.class); + final LocationSettingsStates locationSettingsStates = response.getLocationSettingsStates(); + result.success(LocationUtils.fromLocationSettingsStatesToMap(locationSettingsStates)); + } + + if (o instanceof NavigationResult) { + final NavigationResult navigationResult = ObjectUtils.cast(o, NavigationResult.class); + result.success(LocationUtils.fromNavigationResultToMap(navigationResult)); + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/LocationSettingsFailureListener.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/LocationSettingsFailureListener.java new file mode 100644 index 00000000..361e5c8e --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/LocationSettingsFailureListener.java @@ -0,0 +1,62 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.listeners; + +import static com.huawei.hms.location.LocationSettingsStatusCodes.RESOLUTION_REQUIRED; + +import android.app.Activity; +import android.content.IntentSender.SendIntentException; + +import com.huawei.hmf.tasks.OnFailureListener; +import com.huawei.hms.common.ApiException; +import com.huawei.hms.common.ResolvableApiException; +import com.huawei.hms.flutter.location.constants.Error; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.ObjectUtils; + +import io.flutter.plugin.common.MethodChannel.Result; + +public class LocationSettingsFailureListener implements OnFailureListener { + private final Result result; + private final Activity activity; + + public LocationSettingsFailureListener(final Result result, final Activity activity) { + this.result = result; + this.activity = activity; + } + + @Override + public void onFailure(final Exception e) { + final ApiException apiException = ObjectUtils.cast(e, ApiException.class); + final int statusCode = apiException.getStatusCode(); + final String statusCodeString = Integer.toString(statusCode); + + if (statusCode == RESOLUTION_REQUIRED) { + try { + final ResolvableApiException resolvableApiException = ObjectUtils.cast(e, ResolvableApiException.class); + resolvableApiException.startResolutionForResult(activity, 0); + } catch (final SendIntentException ex) { + HMSLogger.getInstance(activity.getApplicationContext()).sendSingleEvent("checkLocationSettings", "-1"); + result.error(Error.SEND_INTENT_EXCEPTION.name(), ex.getMessage(), null); + } + } else { + HMSLogger.getInstance(activity.getApplicationContext()) + .sendSingleEvent("checkLocationSettings", statusCodeString); + result.error(statusCodeString, apiException.getMessage(), null); + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RemoveUpdatesSuccessListener.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RemoveUpdatesSuccessListener.java new file mode 100644 index 00000000..ff83dca9 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RemoveUpdatesSuccessListener.java @@ -0,0 +1,50 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.listeners; + +import android.content.Context; + +import com.huawei.hmf.tasks.OnSuccessListener; +import com.huawei.hms.flutter.location.logger.HMSLogger; + +import java.util.Map; + +import io.flutter.plugin.common.MethodChannel.Result; + +public class RemoveUpdatesSuccessListener implements OnSuccessListener { + private final String methodName; + private final Context context; + private final Result result; + private final Integer id; + private final Map map; + + public RemoveUpdatesSuccessListener(final String methodName, final Context context, final Result result, + final Integer id, final Map map) { + this.methodName = methodName; + this.context = context; + this.result = result; + this.id = id; + this.map = map; + } + + @Override + public void onSuccess(final Void avoid) { + map.remove(id); + HMSLogger.getInstance(context).sendSingleEvent(methodName); + result.success(null); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesFailureListener.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesFailureListener.java new file mode 100644 index 00000000..6bc0131f --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesFailureListener.java @@ -0,0 +1,55 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.listeners; + +import android.content.Context; + +import com.huawei.hmf.tasks.OnFailureListener; +import com.huawei.hms.common.ApiException; +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.ObjectUtils; + +import java.util.Map; + +import io.flutter.plugin.common.MethodChannel.Result; + +public class RequestUpdatesFailureListener implements OnFailureListener { + private final String methodName; + private final Context context; + private final Result result; + private final Integer id; + private final Map map; + + public RequestUpdatesFailureListener(final String methodName, final Context context, final Result result, + final Integer id, final Map map) { + this.methodName = methodName; + this.context = context; + this.result = result; + this.id = id; + this.map = map; + } + + @Override + public void onFailure(final Exception e) { + final ApiException ex = ObjectUtils.cast(e, ApiException.class); + final String statusCodeString = Integer.toString(ex.getStatusCode()); + + map.remove(id); + HMSLogger.getInstance(context).sendSingleEvent(methodName, statusCodeString); + result.error(statusCodeString, ex.getMessage(), null); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesSuccessListener.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesSuccessListener.java new file mode 100644 index 00000000..b90fbd2d --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/listeners/RequestUpdatesSuccessListener.java @@ -0,0 +1,45 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.listeners; + +import android.content.Context; + +import com.huawei.hmf.tasks.OnSuccessListener; +import com.huawei.hms.flutter.location.logger.HMSLogger; + +import io.flutter.plugin.common.MethodChannel.Result; + +public class RequestUpdatesSuccessListener implements OnSuccessListener { + private final Result result; + private final Integer requestCode; + private final Context context; + private final String methodName; + + public RequestUpdatesSuccessListener(final String methodName, final Context context, final Result result, + final Integer requestCode) { + this.methodName = methodName; + this.context = context; + this.result = result; + this.requestCode = requestCode; + } + + @Override + public void onSuccess(final Void avoid) { + HMSLogger.getInstance(context).sendSingleEvent(methodName); + result.success(requestCode); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/logger/HMSLogger.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/logger/HMSLogger.java new file mode 100644 index 00000000..80af0462 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/logger/HMSLogger.java @@ -0,0 +1,449 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.logger; + +import static android.os.Build.DEVICE; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; +import android.util.Log; + +import com.huawei.agconnect.config.AGConnectServicesConfig; +import com.huawei.hms.support.hianalytics.HiAnalyticsUtils; +import com.huawei.hms.utils.HMSBIInitializer; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public final class HMSLogger { + private static final String TAG = "HMSLogger"; + + private static final String VERSION = "5.0.0.301"; + private static final String SERVICE = "FlutterLocation"; + + private static final String SUCCESS = "0"; + private static final String UNKNOWN = "UNKNOWN"; + private static final String NOT_AVAILABLE = "NOT_AVAILABLE"; + + private static final String SINGLE_EVENT_ID = "60000"; + private static final String PERIODIC_EVENT_ID = "60001"; + + private static final String NETWORK_TYPE_WIFI = "WIFI"; + + private static volatile HMSLogger instance; + + private final WeakReference weakContext; + private final HiAnalyticsUtils hiAnalyticsUtils; + + private final Map singleEventMap = new HashMap<>(); + private final Map periodicEventMap = new HashMap<>(); + private final Map allCountMap = new HashMap<>(); + private final Map failCountMap = new HashMap<>(); + private final Map startTimeMap = new HashMap<>(); + private final Map firstReceiveTimeMap = new HashMap<>(); + private final Map lastReceiveTimeMap = new HashMap<>(); + private final Map> resultCodeCountMap = new HashMap<>(); + private final Map networkTypeMap = createNetworkTypeMap(); + + private boolean isEnabled = true; + + /** + * Private constructor of this class + * + * @param context Application's context + */ + private HMSLogger(final Context context) { + weakContext = new WeakReference<>(context); + hiAnalyticsUtils = HiAnalyticsUtils.getInstance(); + + setupEventMap(singleEventMap); + setupEventMap(periodicEventMap); + initHMSBI(HMSBIInitializer.getInstance(context)); + + Log.d(TAG, "HMS Plugin Dotting is Enabled!"); + } + + /** + * Returns the instance of this class + * + * @param context Application's context + * @return HMSLogger instance + */ + public static synchronized HMSLogger getInstance(final Context context) { + if (instance == null) { + synchronized (HMSLogger.class) { + if (instance == null) { + instance = new HMSLogger(context); + } + } + } + return instance; + } + + /** + * Returns actual context reference + * + * @return Actual context reference + */ + private Context getContext() { + return weakContext.get(); + } + + /** + * Enables HMSLogger + */ + public synchronized void enableLogger() { + isEnabled = true; + Log.d(TAG, "HMS Plugin Dotting is Enabled!"); + } + + /** + * Disables HMSLogger + */ + public synchronized void disableLogger() { + isEnabled = false; + Log.d(TAG, "HMS Plugin Dotting is Disabled!"); + } + + /** + * Sets method start time for given method name + * + * @param methodName Name of the method that will be logged + */ + public synchronized void startMethodExecutionTimer(final String methodName) { + startTimeMap.put(methodName, System.currentTimeMillis()); + } + + /** + * Sends successful single event + * + * @param methodName The name of the method called + */ + public synchronized void sendSingleEvent(final String methodName) { + sendEvent(SINGLE_EVENT_ID, methodName, SUCCESS); + } + + /** + * Sends unsuccessful single event + * + * @param methodName The name of the method called + * @param errorCode API error code + */ + public synchronized void sendSingleEvent(final String methodName, final String errorCode) { + sendEvent(SINGLE_EVENT_ID, methodName, errorCode); + } + + /** + * Sends successful periodic event + * + * @param methodName The name of the method called + */ + public synchronized void sendPeriodicEvent(final String methodName) { + sendEvent(PERIODIC_EVENT_ID, methodName, SUCCESS); + } + + /** + * Sends unsuccessful periodic event + * + * @param methodName The name of the method called + * @param errorCode API error code + */ + public synchronized void sendPeriodicEvent(final String methodName, final String errorCode) { + sendEvent(PERIODIC_EVENT_ID, methodName, errorCode); + } + + /** + * Calls initBI() method from HMSBIInitializer + * + * @param initializer HMSBIInitializer object + */ + private void initHMSBI(final HMSBIInitializer initializer) { + if (!initializer.isInit()) { + initializer.initBI(); + } + } + + /** + * Sends the event based on eventId, methodName, and resultCode + * + * @param eventId Constant id of the event + * @param methodName The name of the method called + * @param resultCode Code of the method's result. "0" for success, others for error + */ + private synchronized void sendEvent(final String eventId, final String methodName, final String resultCode) { + if (isEnabled) { + final long currentTime = System.currentTimeMillis(); + + if (eventId.equals(SINGLE_EVENT_ID)) { + putToSingleEventMap(methodName, resultCode, currentTime); + hiAnalyticsUtils.onNewEvent(getContext(), SINGLE_EVENT_ID, singleEventMap); + + Log.d(TAG, "singleEventMap -> " + singleEventMap); + } else { + putToPeriodicEventMap(methodName, resultCode, currentTime); + hiAnalyticsUtils.onNewEvent(getContext(), PERIODIC_EVENT_ID, periodicEventMap); + + Log.d(TAG, "periodicEventMap -> " + periodicEventMap); + } + } + } + + /** + * Gets "client/app_id" value from agconnect-services.json file + * + * @return app_id value or NOT_AVAILABLE if not found + */ + private String getAppId() { + try { + return AGConnectServicesConfig.fromContext(getContext()).getString("client/app_id"); + } catch (final NullPointerException e) { + Log.d(TAG, "AgConnect is not found. Setting appId value to " + NOT_AVAILABLE); + } + return NOT_AVAILABLE; + } + + /** + * Gets app version name + * + * @param packageName Package name of the app + * @return App version name in String type + */ + private String getAppVersionName(final String packageName) { + try { + return getContext().getPackageManager().getPackageInfo(packageName, 0).versionName; + } catch (final PackageManager.NameNotFoundException e) { + Log.e(TAG, "getAppVersionName -> Could not get appVersionName!"); + return NOT_AVAILABLE; + } + } + + /** + * Detects current network type + * + * @return Human readable network type; such as WIFI, LTE + */ + private String getNetworkType() { + final ConnectivityManager cm = objectCast(getContext().getSystemService(Context.CONNECTIVITY_SERVICE), + ConnectivityManager.class); + if (cm != null) { + if (cm.getActiveNetworkInfo() == null || !cm.getActiveNetworkInfo().isConnected()) { + return NOT_AVAILABLE; + } + + final int networkSubtype = Objects.requireNonNull(cm.getActiveNetworkInfo()).getSubtype(); + final String networkSubtypeString = getOrDefault(networkTypeMap, networkSubtype, UNKNOWN); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + final NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork()); + if (capabilities != null) { + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return networkSubtypeString; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + return NETWORK_TYPE_WIFI; + } else { + return UNKNOWN; + } + } else { + return NOT_AVAILABLE; + } + } else { + final int networkType = Objects.requireNonNull(cm.getActiveNetworkInfo()).getType(); + switch (networkType) { + case ConnectivityManager.TYPE_WIFI: + return NETWORK_TYPE_WIFI; + case ConnectivityManager.TYPE_MOBILE: + return networkSubtypeString; + default: + return UNKNOWN; + } + } + } + return NOT_AVAILABLE; + } + + /** + * Sets default values to given map + * + * @param map HashMap to put default values + */ + private void setupEventMap(final Map map) { + map.put("version", VERSION); + map.put("service", SERVICE); + map.put("appid", getAppId()); + map.put("package", getContext().getPackageName()); + map.put("cpAppVersion", getAppVersionName(getContext().getPackageName())); + map.put("model", DEVICE); + } + + /** + * Prepares sing-event map according to input parameters + * + * @param methodName The name of the method called + * @param resultCode Code of the method's result. "0" for success, others for error + * @param currentTime Current timestamp in millisecond + */ + private void putToSingleEventMap(final String methodName, final String resultCode, final long currentTime) { + final long startTime = getOrDefault(startTimeMap, methodName, currentTime); + final int costTime = (int) (currentTime - startTime); + singleEventMap.put("apiName", methodName); + singleEventMap.put("result", resultCode); + singleEventMap.put("callTime", currentTime); + singleEventMap.put("costTime", costTime); + singleEventMap.put("networkType", getNetworkType()); + } + + /** + * Prepares periodic-event map according to input parameters + * + * @param methodName The name of the method called + * @param resultCode Code of the method's result. "0" for success, others for error + * @param currentTime Current timestamp in millisecond + */ + private void putToPeriodicEventMap(final String methodName, final String resultCode, final long currentTime) { + increaseResultCodeCount(methodName, resultCode); + increaseMapValue(methodName, allCountMap); + + if (!resultCode.equals(SUCCESS)) { + increaseMapValue(methodName, failCountMap); + } + + final long firstReceiveTime = getOrDefault(firstReceiveTimeMap, methodName, currentTime); + periodicEventMap.put("callTime", firstReceiveTime); + + final long lastReceiveTime = getOrDefault(lastReceiveTimeMap, methodName, currentTime); + final int costTime = (int) (currentTime - lastReceiveTime); + periodicEventMap.put("costTime", costTime); + + periodicEventMap.put("apiName", methodName); + periodicEventMap.put("result", resultCodeCountMap.get(methodName)); + + final long allCount = getOrDefault(allCountMap, methodName, 0L); + periodicEventMap.put("allCnt", allCount); + + final long failCount = getOrDefault(failCountMap, methodName, 0L); + periodicEventMap.put("failCnt", failCount); + + periodicEventMap.put("lastCallTime", currentTime); + periodicEventMap.put("networkType", getNetworkType()); + + putIfAbsent(firstReceiveTimeMap, methodName, currentTime); + lastReceiveTimeMap.put(methodName, currentTime); + } + + /** + * Prepares HashMap of network type id and its human-readable string pairs + * + * @return HashMap of human readable network type names + */ + private Map createNetworkTypeMap() { + final Map map = new HashMap<>(); + + map.put(0, UNKNOWN); + map.put(1, "2.5G"); + map.put(2, "2.75G"); + map.put(3, "3G"); + map.put(4, "2G"); + map.put(5, "3G"); + map.put(6, "3G"); + map.put(7, "2G"); + map.put(8, "3G"); + map.put(9, "3G"); + map.put(10, "3G"); + map.put(11, "2G"); + map.put(12, "3G"); + map.put(13, "4G"); + map.put(14, "3G"); + map.put(15, "3G"); + map.put(16, "2G"); + map.put(17, "3G"); + map.put(18, "4G"); + map.put(19, "4G"); + map.put(20, "5G"); + + return Collections.unmodifiableMap(map); + } + + /** + * Increases count of the given result code + * + * @param methodName Name of the calling method + * @param resultCode Code of the method's result. "0" for success, others for error + */ + private void increaseResultCodeCount(final String methodName, final String resultCode) { + final Map map = getOrDefault(resultCodeCountMap, methodName, new HashMap<>()); + + increaseMapValue(resultCode, map); + resultCodeCountMap.put(methodName, map); + } + + /** + * Increases the value of the corresponding key which in the map + * + * @param key Key for map lookup + * @param map The Map that contains the key and its corresponding value + */ + private void increaseMapValue(final String key, final Map map) { + map.put(key, getOrDefault(map, key, 0L) + 1); + } + + /** + * Get the corresponding value of the key. If the key does not exist in the map then the default value is returned. + * + * @param map The Map + * @param key Lookup key + * @param defaultValue The default value will be returned if the key is absent + * @param Generic type of the key + * @param Generic type of the value + * @return Corresponding value or default value + */ + private V getOrDefault(final Map map, final K key, final V defaultValue) { + return map.containsKey(key) ? map.get(key) : defaultValue; + } + + /** + * Put key-value pair to map if the key is absent + * + * @param map The Map + * @param key Lookup key + * @param value The value will be put to the map if the key is absent + * @param Generic type of the key + * @param Generic type of the value + */ + private void putIfAbsent(final Map map, final K key, final V value) { + if (!map.containsKey(key)) { + map.put(key, value); + } + } + + /** + * Utility method that castes given object to given class type + * + * @param source Source object to be casted + * @param clazz Class that object will be casted to its type + * @param Source object's type + * @param Destination type + * @return Object that casted to D type + */ + private D objectCast(final S source, final Class clazz) { + return clazz.cast(source); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityConversionBroadcastReceiver.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityConversionBroadcastReceiver.java new file mode 100644 index 00000000..06d8e231 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityConversionBroadcastReceiver.java @@ -0,0 +1,45 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.ActivityUtils; +import com.huawei.hms.location.ActivityConversionResponse; + +import io.flutter.plugin.common.EventChannel.EventSink; + +public class ActivityConversionBroadcastReceiver extends BroadcastReceiver { + private final EventSink eventSink; + + public ActivityConversionBroadcastReceiver(final EventSink eventSink) { + this.eventSink = eventSink; + } + + @Override + public void onReceive(final Context context, final Intent intent) { + if (ActivityConversionResponse.containDataFromIntent(intent)) { + final ActivityConversionResponse response = ActivityConversionResponse.getDataFromIntent(intent); + + HMSLogger.getInstance(context).sendPeriodicEvent("ActivityConversionUpdates"); + eventSink.success(ActivityUtils.activityConversionResponseToMap(response)); + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityIdentificationBroadcastReceiver.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityIdentificationBroadcastReceiver.java new file mode 100644 index 00000000..bb2c9666 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/ActivityIdentificationBroadcastReceiver.java @@ -0,0 +1,45 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.ActivityUtils; +import com.huawei.hms.location.ActivityIdentificationResponse; + +import io.flutter.plugin.common.EventChannel.EventSink; + +public class ActivityIdentificationBroadcastReceiver extends BroadcastReceiver { + private final EventSink eventSink; + + public ActivityIdentificationBroadcastReceiver(final EventSink eventSink) { + this.eventSink = eventSink; + } + + @Override + public void onReceive(final Context context, final Intent intent) { + if (ActivityIdentificationResponse.containDataFromIntent(intent)) { + final ActivityIdentificationResponse response = ActivityIdentificationResponse.getDataFromIntent(intent); + + HMSLogger.getInstance(context).sendPeriodicEvent("ActivityIdentificationUpdates"); + eventSink.success(ActivityUtils.activityIdentificationResponseToMap(response)); + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/FusedLocationBroadcastReceiver.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/FusedLocationBroadcastReceiver.java new file mode 100644 index 00000000..dcdfbd49 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/FusedLocationBroadcastReceiver.java @@ -0,0 +1,46 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.location.Location; + +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.LocationUtils; +import com.huawei.hms.location.LocationResult; + +import io.flutter.plugin.common.EventChannel.EventSink; + +public class FusedLocationBroadcastReceiver extends BroadcastReceiver { + private final EventSink eventSink; + + public FusedLocationBroadcastReceiver(final EventSink eventSink) { + this.eventSink = eventSink; + } + + @Override + public void onReceive(final Context context, final Intent intent) { + if (LocationResult.hasResult(intent)) { + HMSLogger.getInstance(context).sendPeriodicEvent("LocationUpdates"); + for (final Location location : LocationResult.extractResult(intent).getLocations()) { + eventSink.success(LocationUtils.fromLocationToMap(location)); + } + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/GeofenceBroadcastReceiver.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/GeofenceBroadcastReceiver.java new file mode 100644 index 00000000..ae9f5451 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/receivers/GeofenceBroadcastReceiver.java @@ -0,0 +1,44 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.huawei.hms.flutter.location.logger.HMSLogger; +import com.huawei.hms.flutter.location.utils.GeofenceUtils; +import com.huawei.hms.location.GeofenceData; + +import io.flutter.plugin.common.EventChannel.EventSink; + +public class GeofenceBroadcastReceiver extends BroadcastReceiver { + private final EventSink eventSink; + + public GeofenceBroadcastReceiver(final EventSink eventSink) { + this.eventSink = eventSink; + } + + @Override + public void onReceive(final Context context, final Intent intent) { + if (intent != null) { + final GeofenceData geofenceData = GeofenceData.getDataFromIntent(intent); + HMSLogger.getInstance(context).sendPeriodicEvent("GeofenceUpdates"); + eventSink.success(GeofenceUtils.fromGeofenceDataToMap(geofenceData)); + } + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ActivityUtils.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ActivityUtils.java new file mode 100644 index 00000000..6600697a --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ActivityUtils.java @@ -0,0 +1,140 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.utils; + +import com.huawei.hms.location.ActivityConversionData; +import com.huawei.hms.location.ActivityConversionInfo; +import com.huawei.hms.location.ActivityConversionRequest; +import com.huawei.hms.location.ActivityConversionResponse; +import com.huawei.hms.location.ActivityIdentificationData; +import com.huawei.hms.location.ActivityIdentificationResponse; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public interface ActivityUtils { + /** + * Utility method + * @param list List of the ActivityConversionInfo + * @return ActivityConversionRequest object + */ + static ActivityConversionRequest fromActivityConversionInfoListToActivityConversionRequest( + final List> list) { + final List activityConversionInfos = new ArrayList<>(); + + for (final Map map : list) { + activityConversionInfos.add(fromMapToActivityConversionInfo(map)); + } + + return new ActivityConversionRequest(activityConversionInfos); + } + + /** + * Utility method + * @param map HashMap representation of the ActivityConversionInfo object + * @return ActivityConversionInfo object + */ + static ActivityConversionInfo fromMapToActivityConversionInfo(final Map map) { + return new ActivityConversionInfo(ValueGetter.getInt("activityType", map), + ValueGetter.getInt("conversionType", map)); + } + + /** + * Utility method + * @param data ActivityIdentificationData object + * @return HashMap representation of ActivityIdentificationData object + */ + static Map activityIdentificationDataToMap(final ActivityIdentificationData data) { + final Map map = new HashMap<>(); + + if (data == null) { + return map; + } + + map.put("identificationActivity", data.getIdentificationActivity()); + map.put("possibility", data.getPossibility()); + + return map; + } + + /** + * Utility method + * @param response ActivityIdentificationResponse object + * @return HashMap representation of ActivityIdentificationResponse object + */ + static Map activityIdentificationResponseToMap(final ActivityIdentificationResponse response) { + final Map map = new HashMap<>(); + + if (response == null) { + return map; + } + + map.put("time", response.getTime()); + map.put("elapsedTimeFromReboot", response.getElapsedTimeFromReboot()); + + final List> activityIdentificationDatas = new ArrayList<>(); + for (final ActivityIdentificationData data : response.getActivityIdentificationDatas()) { + activityIdentificationDatas.add(activityIdentificationDataToMap(data)); + } + map.put("activityIdentificationDatas", activityIdentificationDatas); + + return map; + } + + /** + * Utility method + * @param data ActivityConversionData object + * @return HashMap representation of the ActivityConversionData object + */ + static Map activityConversionDataToMap(final ActivityConversionData data) { + final Map map = new HashMap<>(); + + if (data == null) { + return map; + } + + map.put("activityType", data.getActivityType()); + map.put("conversionType", data.getConversionType()); + map.put("elapsedTimeFromReboot", data.getElapsedTimeFromReboot()); + + return map; + } + + /** + * Utility method + * @param response ActivityConversionResponse object + * @return HashMap representation of the ActivityConversionResponse object + */ + static Map activityConversionResponseToMap(final ActivityConversionResponse response) { + final Map map = new HashMap<>(); + + if (response == null) { + return map; + } + + final List> activityConversionDatas = new ArrayList<>(); + + for (final ActivityConversionData data : response.getActivityConversionDatas()) { + activityConversionDatas.add(ActivityUtils.activityConversionDataToMap(data)); + } + + map.put("activityConversionDatas", activityConversionDatas); + return map; + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/GeofenceUtils.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/GeofenceUtils.java new file mode 100644 index 00000000..d79cf75d --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/GeofenceUtils.java @@ -0,0 +1,98 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.utils; + +import com.huawei.hms.location.Geofence; +import com.huawei.hms.location.GeofenceData; +import com.huawei.hms.location.GeofenceRequest; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public interface GeofenceUtils { + /** + * Utility method + * @param geofenceData GeofenceData object + * @return HashMap representation of the GeofenceData object + */ + static Map fromGeofenceDataToMap(final GeofenceData geofenceData) { + if (geofenceData == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + final List convertingGeofenceIdList = new ArrayList<>(); + + for (final Geofence geofence : geofenceData.getConvertingGeofenceList()) { + convertingGeofenceIdList.add(geofence.getUniqueId()); + } + + map.put("errorCode", geofenceData.getErrorCode()); + map.put("conversion", geofenceData.getConversion()); + map.put("convertingGeofenceIdList", convertingGeofenceIdList); + map.put("convertingLocation", LocationUtils.fromLocationToMap(geofenceData.getConvertingLocation())); + + return map; + } + + /** + * Utility method + * @param map HashMap representation of the Geofence object + * @return Geofence object + */ + static Geofence fromMapToGeofence(final Map map) { + final Geofence.Builder builder = new Geofence.Builder(); + + final double lat = ValueGetter.getDouble("latitude", map); + final double lng = ValueGetter.getDouble("longitude", map); + final float rad = ValueGetter.getFloat("radius", map); + + builder.setRoundArea(lat, lng, rad); + builder.setUniqueId(ValueGetter.getString("uniqueId", map)); + builder.setConversions(ValueGetter.getInt("conversions", map)); + builder.setDwellDelayTime(ValueGetter.getInt("dwellDelayTime", map)); + builder.setValidContinueTime(ValueGetter.getLong("validDuration", map)); + builder.setNotificationInterval(ValueGetter.getInt("notificationInterval", map)); + + return builder.build(); + } + + /** + * Utility method + * @param map HashMap representation of the GeofenceRequest object + * @return GeofenceRequest object + */ + static GeofenceRequest fromMapToGeofenceRequest(final Map map) { + final GeofenceRequest.Builder builder = new GeofenceRequest.Builder(); + final List geofenceMapList = ObjectUtils.cast(map.get("geofenceList"), List.class); + + if (geofenceMapList != null) { + for (final Object geofence : geofenceMapList) { + final Map geofenceMap = ObjectUtils.cast(geofence, Map.class); + builder.createGeofence(fromMapToGeofence(geofenceMap)); + } + } + + builder.setCoordinateType(ValueGetter.getInt("coordinateType", map)); + builder.setInitConversions(ValueGetter.getInt("initConversions", map)); + + return builder.build(); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/LocationUtils.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/LocationUtils.java new file mode 100644 index 00000000..36931b67 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/LocationUtils.java @@ -0,0 +1,309 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.utils; + +import android.location.Location; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; + +import com.huawei.hms.location.HWLocation; +import com.huawei.hms.location.LocationAvailability; +import com.huawei.hms.location.LocationRequest; +import com.huawei.hms.location.LocationResult; +import com.huawei.hms.location.LocationSettingsRequest; +import com.huawei.hms.location.LocationSettingsStates; +import com.huawei.hms.location.NavigationRequest; +import com.huawei.hms.location.NavigationResult; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface LocationUtils { + /** + * Utility method + * @param location ActivityIdentificationData object + * @return HashMap representation of Location object + */ + static Map fromLocationToMap(final Location location) { + if (location == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + + map.put("provider", location.getProvider()); + map.put("latitude", location.getLatitude()); + map.put("longitude", location.getLongitude()); + map.put("altitude", location.getAltitude()); + map.put("speed", location.getSpeed()); + map.put("bearing", location.getBearing()); + map.put("horizontalAccuracyMeters", location.getAccuracy()); + map.put("time", location.getTime()); + map.put("elapsedRealtimeNanos", location.getElapsedRealtimeNanos()); + map.put("isFromMockProvider", location.isFromMockProvider()); + + if (VERSION.SDK_INT >= VERSION_CODES.O) { + map.put("verticalAccuracyMeters", location.getVerticalAccuracyMeters()); + map.put("speedAccuracyMetersPerSecond", location.getSpeedAccuracyMetersPerSecond()); + map.put("bearingAccuracyDegrees", location.getBearingAccuracyDegrees()); + } else { + map.put("verticalAccuracyMeters", 0.0); + map.put("speedAccuracyMetersPerSecond", 0.0); + map.put("bearingAccuracyDegrees", 0.0); + } + + return map; + } + + /** + * Utility method + * @param hwLocation HWLocation object + * @return HashMap representation of HWLocation object + */ + static Map fromHWLocationToMap(final HWLocation hwLocation) { + if (hwLocation == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + + map.put("provider", hwLocation.getProvider()); + map.put("latitude", hwLocation.getLatitude()); + map.put("longitude", hwLocation.getLongitude()); + map.put("altitude", hwLocation.getAltitude()); + map.put("speed", hwLocation.getSpeed()); + map.put("bearing", hwLocation.getBearing()); + map.put("horizontalAccuracyMeters", hwLocation.getAccuracy()); + map.put("verticalAccuracyMeters", hwLocation.getVerticalAccuracyMeters()); + map.put("speedAccuracyMetersPerSecond", hwLocation.getSpeedAccuracyMetersPerSecond()); + map.put("bearingAccuracyDegrees", hwLocation.getBearingAccuracyDegrees()); + map.put("time", hwLocation.getTime()); + map.put("elapsedRealtimeNanos", hwLocation.getElapsedRealtimeNanos()); + map.put("countryCode", hwLocation.getCountryCode()); + map.put("countryName", hwLocation.getCountryName()); + map.put("state", hwLocation.getState()); + map.put("city", hwLocation.getCity()); + map.put("county", hwLocation.getCounty()); + map.put("street", hwLocation.getStreet()); + map.put("featureName", hwLocation.getFeatureName()); + map.put("postalCode", hwLocation.getPostalCode()); + map.put("phone", hwLocation.getPhone()); + map.put("url", hwLocation.getUrl()); + map.put("extraInfo", hwLocation.getExtraInfo()); + + return map; + } + + /** + * Utility method + * @param locationAvailability LocationAvailability object + * @return HashMap representation of LocationAvailability object + */ + static Map fromLocationAvailabilityToMap(final LocationAvailability locationAvailability) { + if (locationAvailability == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + + map.put("cellStatus", locationAvailability.getCellStatus()); + map.put("wifiStatus", locationAvailability.getWifiStatus()); + map.put("elapsedRealtimeNs", locationAvailability.getElapsedRealtimeNs()); + map.put("locationStatus", locationAvailability.getLocationStatus()); + + return map; + } + + /** + * Utility method + * @param locationResult LocationResult object + * @return HashMap representation of LocationResult object + */ + static Map fromLocationResultToMap(final LocationResult locationResult) { + if (locationResult == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + final List> locationMaps = new ArrayList<>(); + final List> hwLocationMaps = new ArrayList<>(); + + for (final Location location : locationResult.getLocations()) { + locationMaps.add(fromLocationToMap(location)); + } + + for (final HWLocation hwLocation : locationResult.getHWLocationList()) { + hwLocationMaps.add(fromHWLocationToMap(hwLocation)); + } + + map.put("locations", locationMaps); + map.put("hwLocations", hwLocationMaps); + map.put("lastLocation", fromLocationToMap(locationResult.getLastLocation())); + map.put("lastHWLocation", fromHWLocationToMap(locationResult.getLastHWLocation())); + + return map; + } + + /** + * Utility method + * @param states LocationSettingsStates object + * @return HashMap representation of LocationSettingsStates object + */ + static Map fromLocationSettingsStatesToMap(final LocationSettingsStates states) { + if (states == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + + map.put("blePresent", states.isBlePresent()); + map.put("bleUsable", states.isBleUsable()); + map.put("gpsPresent", states.isGpsPresent()); + map.put("gpsUsable", states.isGpsUsable()); + map.put("locationPresent", states.isLocationPresent()); + map.put("locationUsable", states.isLocationUsable()); + map.put("networkLocationPresent", states.isNetworkLocationPresent()); + map.put("networkLocationUsable", states.isNetworkLocationUsable()); + + return map; + } + + /** + * Utility method + * @param result NavigationResult object + * @return HashMap representation of NavigationResult object + */ + static Map fromNavigationResultToMap(final NavigationResult result) { + if (result == null) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + + map.put("state", result.getState()); + map.put("possibility", result.getPossibility()); + + return map; + } + + /** + * Utility method + * @param map HashMap representation of the LocationRequest object + * @return LocationRequest object + */ + static LocationRequest fromMapToLocationRequest(final Map map) { + final boolean isFastestIntervalExplicitlySet = ValueGetter.getBoolean("isFastestIntervalExplicitlySet", map); + + final LocationRequest result = LocationRequest.create(); + + if (isFastestIntervalExplicitlySet) { + result.setFastestInterval(ValueGetter.getLong("fastestInterval", map)); + } + + result.setPriority(ValueGetter.getInt("priority", map)); + result.setInterval(ValueGetter.getLong("interval", map)); + result.setExpirationTime(ValueGetter.getLong("expirationTime", map)); + result.setNumUpdates(ValueGetter.getInt("numUpdates", map)); + result.setSmallestDisplacement(ValueGetter.getFloat("smallestDisplacement", map)); + result.setMaxWaitTime(ValueGetter.getLong("maxWaitTime", map)); + result.setNeedAddress(ValueGetter.getBoolean("needAddress", map)); + result.setLanguage(ValueGetter.getString("language", map)); + result.setCountryCode(ValueGetter.getString("countryCode", map)); + + final Map extras = ObjectUtils.cast(map.get("extras"), Map.class); + + if (extras != null) { + final Set entries = extras.entrySet(); + for (final Object entry : entries) { + final Map.Entry mapEntry = ObjectUtils.cast(entry, Map.Entry.class); + final String key = ObjectUtils.cast(mapEntry.getKey(), String.class); + final String value = ObjectUtils.cast(mapEntry.getValue(), String.class); + result.putExtras(key, value); + } + } + + return result; + } + + /** + * Utility method + * @param map HashMap representation of the NavigationRequest object + * @return NavigationRequest object + */ + static NavigationRequest fromMapToNavigationRequest(final Map map) { + final int type = ValueGetter.getInt("type", map); + final Map extras = ObjectUtils.cast(map.get("extras"), Map.class); + + final NavigationRequest request = new NavigationRequest(type); + request.setExtras(extras); + + return request; + } + + /** + * Utility method + * @param map HashMap representation of the Location object + * @return Location object + */ + static Location fromMapToLocation(final Map map) { + final String provider = ValueGetter.getString("provider", map); + final Location location = new Location(provider); + + location.setLatitude(ValueGetter.getDouble("latitude", map)); + location.setLongitude(ValueGetter.getDouble("longitude", map)); + location.setAltitude(ValueGetter.getDouble("altitude", map)); + location.setSpeed(ValueGetter.getFloat("speed", map)); + location.setBearing(ValueGetter.getFloat("bearing", map)); + location.setAccuracy(ValueGetter.getFloat("horizontalAccuracyMeters", map)); + location.setTime(ValueGetter.getLong("time", map)); + location.setElapsedRealtimeNanos(ValueGetter.getLong("elapsedRealtimeNanos", map)); + + if (VERSION.SDK_INT >= VERSION_CODES.O) { + location.setVerticalAccuracyMeters(ValueGetter.getFloat("verticalAccuracyMeters", map)); + location.setSpeedAccuracyMetersPerSecond(ValueGetter.getFloat("speedAccuracyMetersPerSecond", map)); + location.setBearingAccuracyDegrees(ValueGetter.getFloat("bearingAccuracyDegrees", map)); + } + + return location; + } + + /** + * Utility method + * @param map HashMap representation of the LocationSettingsRequest object + * @return LocationSettingsRequest object + */ + static LocationSettingsRequest fromMapToLocationSettingsRequest(final Map map) { + final LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); + final List requests = ObjectUtils.cast(map.get("requests"), List.class); + + if (requests != null) { + for (final Object request : requests) { + final Map requestMap = ObjectUtils.cast(request, Map.class); + builder.addLocationRequest(fromMapToLocationRequest(requestMap)); + } + } + + builder.setAlwaysShow(ValueGetter.getBoolean("alwaysShow", map)); + builder.setNeedBle(ValueGetter.getBoolean("needBle", map)); + + return builder.build(); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ObjectUtils.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ObjectUtils.java new file mode 100644 index 00000000..395d14a8 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ObjectUtils.java @@ -0,0 +1,31 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.utils; + +public interface ObjectUtils { + /** + * Utility method that castes given object to given class type + * @param source Source object to be casted + * @param clazz Class that object will be casted to its type + * @param Source object's type + * @param Destination type + * @return Object that casted to D type + */ + static D cast(final S source, final Class clazz) { + return clazz.cast(source); + } +} diff --git a/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ValueGetter.java b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ValueGetter.java new file mode 100644 index 00000000..f5d2ba38 --- /dev/null +++ b/hms-plugins/flutter-hms-location/android/src/main/java/com/huawei/hms/flutter/location/utils/ValueGetter.java @@ -0,0 +1,112 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package com.huawei.hms.flutter.location.utils; + +import java.util.Map; +import java.util.Objects; + +public interface ValueGetter { + /** + * Utility method + * @param key Lookup key for the map + * @param map Map that contains argument keys and values + * @return The "int" value for the corresponding key. + */ + static int getInt(final String key, final Map map) { + final Object value = Objects.requireNonNull(map.get(key)); + if (value instanceof Number) { + return ((Number) value).intValue(); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Utility method + * @param key Lookup key for the map + * @param map Map that contains argument keys and values + * @return The "long" value for the corresponding key. + */ + static long getLong(final String key, final Map map) { + final Object value = Objects.requireNonNull(map.get(key)); + if (value instanceof Number) { + return ((Number) value).longValue(); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Utility method + * @param key Lookup key for the map + * @param map Map that contains argument keys and values + * @return The "float" value for the corresponding key. + */ + static float getFloat(final String key, final Map map) { + final Object value = Objects.requireNonNull(map.get(key)); + if (value instanceof Number) { + return ((Number) value).floatValue(); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Utility method + * @param key Lookup key for the map + * @param map Map that contains argument keys and values + * @return The "double" value for the corresponding key. + */ + static double getDouble(final String key, final Map map) { + final Object value = Objects.requireNonNull(map.get(key)); + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Utility method + * @param key Lookup key for the map + * @param map Map that contains argument keys and values + * @return The "boolean" value for the corresponding key. + */ + static boolean getBoolean(final String key, final Map map) { + final Object value = Objects.requireNonNull(map.get(key)); + if (value instanceof Boolean) { + return (boolean) value; + } else { + throw new IllegalArgumentException(); + } + } + + /** + * Utility method + * @param key Lookup key for the map + * @param map Map that contains argument keys and values + * @return The "String" value for the corresponding key. + */ + static String getString(final String key, final Map map) { + final Object value = Objects.requireNonNull(map.get(key)); + if (value instanceof String) { + return (String) value; + } else { + throw new IllegalArgumentException(); + } + } +} diff --git a/hms-plugins/flutter-hms-location/ios/Assets/.gitkeep b/hms-plugins/flutter-hms-location/ios/Assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.h b/hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.h new file mode 100644 index 00000000..0676dadb --- /dev/null +++ b/hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface LocationPlugin : NSObject +@end diff --git a/hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.m b/hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.m new file mode 100644 index 00000000..e71b90d2 --- /dev/null +++ b/hms-plugins/flutter-hms-location/ios/Classes/LocationPlugin.m @@ -0,0 +1,20 @@ +#import "LocationPlugin.h" + +@implementation LocationPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:@"location" + binaryMessenger:[registrar messenger]]; + LocationPlugin* instance = [[LocationPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + if ([@"getPlatformVersion" isEqualToString:call.method]) { + result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); + } else { + result(FlutterMethodNotImplemented); + } +} + +@end diff --git a/hms-plugins/flutter-hms-location/ios/huawei_location.podspec b/hms-plugins/flutter-hms-location/ios/huawei_location.podspec new file mode 100644 index 00000000..cd8d7142 --- /dev/null +++ b/hms-plugins/flutter-hms-location/ios/huawei_location.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint location.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'huawei_location' + s.version = '5.0.0+301' + s.summary = 'HUAWEI Flutter Location Kit plugin.' + s.description = <<-DESC + HUAWEI Flutter Location Kit plugin combines the GPS, Wi-Fi and base station locations to help you quickly obtain precise user locations & build up global positioning capabilities. + DESC + s.homepage = 'https://www.huawei.com' + s.license = { :type => 'Apache 2.0', :file => '../LICENSE' } + s.author = { 'Huawei Technologies' => 'huaweideveloper1@gmail.com' } + s.source = { :git => 'https://github.com/HMS-Core/hms-flutter-plugin/tree/master/flutter-hms-location' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } +end diff --git a/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_data.dart b/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_data.dart new file mode 100644 index 00000000..8b5c6444 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_data.dart @@ -0,0 +1,76 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +class ActivityConversionData { + int activityType; + int conversionType; + int elapsedTimeFromReboot; + + ActivityConversionData({ + this.activityType, + this.conversionType, + this.elapsedTimeFromReboot, + }); + + Map toMap() { + return { + 'activityType': activityType, + 'conversionType': conversionType, + 'elapsedTimeFromReboot': elapsedTimeFromReboot, + }; + } + + factory ActivityConversionData.fromMap(Map map) { + if (map == null) return null; + + return ActivityConversionData( + activityType: map['activityType'], + conversionType: map['conversionType'], + elapsedTimeFromReboot: map['elapsedTimeFromReboot'], + ); + } + + String toJson() => json.encode(toMap()); + + factory ActivityConversionData.fromJson(String source) => + ActivityConversionData.fromMap(json.decode(source)); + + @override + String toString() => + 'ActivityConversionData(activityType: $activityType, conversionType: $conversionType, elapsedTimeFromReboot: $elapsedTimeFromReboot)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is ActivityConversionData && + o.activityType == activityType && + o.conversionType == conversionType && + o.elapsedTimeFromReboot == elapsedTimeFromReboot; + } + + @override + int get hashCode { + return hashList([ + activityType, + conversionType, + elapsedTimeFromReboot, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_info.dart b/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_info.dart new file mode 100644 index 00000000..8eae9a55 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_info.dart @@ -0,0 +1,73 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +class ActivityConversionInfo { + static const int ENTER_ACTIVITY_CONVERSION = 0; + static const int EXIT_ACTIVITY_CONVERSION = 1; + + int activityType; + int conversionType; + + ActivityConversionInfo({ + this.activityType, + this.conversionType, + }); + + Map toMap() { + return { + 'activityType': activityType, + 'conversionType': conversionType, + }; + } + + factory ActivityConversionInfo.fromMap(Map map) { + if (map == null) return null; + + return ActivityConversionInfo( + activityType: map['activityType'], + conversionType: map['conversionType'], + ); + } + + String toJson() => json.encode(toMap()); + + factory ActivityConversionInfo.fromJson(String source) => + ActivityConversionInfo.fromMap(json.decode(source)); + + @override + String toString() => + 'ActivityConversionInfo(activityType: $activityType, conversionType: $conversionType)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is ActivityConversionInfo && + o.activityType == activityType && + o.conversionType == conversionType; + } + + @override + int get hashCode { + return hashList([ + activityType, + conversionType, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_response.dart b/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_response.dart new file mode 100644 index 00000000..28c44158 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/activity/activity_conversion_response.dart @@ -0,0 +1,71 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import 'activity_conversion_data.dart'; + +class ActivityConversionResponse { + List activityConversionDatas; + + ActivityConversionResponse({ + this.activityConversionDatas, + }); + + Map toMap() { + return { + 'activityConversionDatas': + activityConversionDatas?.map((x) => x?.toMap())?.toList(), + }; + } + + factory ActivityConversionResponse.fromMap(Map map) { + if (map == null) return null; + + return ActivityConversionResponse( + activityConversionDatas: List.from( + map['activityConversionDatas'] + ?.map((x) => ActivityConversionData.fromMap(x))), + ); + } + + String toJson() => json.encode(toMap()); + + factory ActivityConversionResponse.fromJson(String source) => + ActivityConversionResponse.fromMap(json.decode(source)); + + @override + String toString() => + 'ActivityConversionResponse(activityConversionDatas: $activityConversionDatas)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is ActivityConversionResponse && + listEquals(o.activityConversionDatas, activityConversionDatas); + } + + @override + int get hashCode { + return hashList([ + activityConversionDatas, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/activity/activity_identification_data.dart b/hms-plugins/flutter-hms-location/lib/activity/activity_identification_data.dart new file mode 100644 index 00000000..ed171175 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/activity/activity_identification_data.dart @@ -0,0 +1,94 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +class ActivityIdentificationData { + static const int VEHICLE = 100; + static const int BIKE = 101; + static const int FOOT = 102; + static const int STILL = 103; + static const int OTHERS = 104; + static const int TILTING = 105; + static const int WALKING = 107; + static const int RUNNING = 108; + + static const List _validTypes = [ + VEHICLE, + BIKE, + FOOT, + STILL, + OTHERS, + TILTING, + WALKING, + RUNNING + ]; + + int identificationActivity; + int possibility; + + ActivityIdentificationData({ + this.identificationActivity, + this.possibility, + }); + + static bool isValidType(int type) { + return _validTypes.contains(type) ? true : false; + } + + Map toMap() { + return { + 'identificationActivity': identificationActivity, + 'possibility': possibility, + }; + } + + factory ActivityIdentificationData.fromMap(Map map) { + if (map == null) return null; + + return ActivityIdentificationData( + identificationActivity: map['identificationActivity'], + possibility: map['possibility'], + ); + } + + String toJson() => json.encode(toMap()); + + factory ActivityIdentificationData.fromJson(String source) => + ActivityIdentificationData.fromMap(json.decode(source)); + + @override + String toString() => + 'ActivityIdentificationData(identificationActivity: $identificationActivity, possibility: $possibility)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is ActivityIdentificationData && + o.identificationActivity == identificationActivity && + o.possibility == possibility; + } + + @override + int get hashCode { + return hashList([ + identificationActivity, + possibility, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/activity/activity_identification_response.dart b/hms-plugins/flutter-hms-location/lib/activity/activity_identification_response.dart new file mode 100644 index 00000000..c059ca50 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/activity/activity_identification_response.dart @@ -0,0 +1,106 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import 'activity_identification_data.dart'; + +class ActivityIdentificationResponse { + int time; + int elapsedTimeFromReboot; + List activityIdentificationDatas; + + ActivityIdentificationResponse({ + this.activityIdentificationDatas, + this.time, + this.elapsedTimeFromReboot, + }); + + ActivityIdentificationData get mostActivityIdentification { + return activityIdentificationDatas != null && + activityIdentificationDatas.length > 0 + ? activityIdentificationDatas[0] + : null; + } + + int getActivityPossibility(int activityType) { + if (activityIdentificationDatas != null && + activityIdentificationDatas.length > 0) { + Iterator iterator = activityIdentificationDatas.iterator; + + while (iterator.moveNext()) { + ActivityIdentificationData currentData = iterator.current; + if (currentData.identificationActivity == activityType) { + return currentData.possibility; + } + } + } + + return 0; + } + + Map toMap() { + return { + 'time': time, + 'elapsedTimeFromReboot': elapsedTimeFromReboot, + 'activityIdentificationDatas': + activityIdentificationDatas?.map((x) => x?.toMap())?.toList(), + }; + } + + factory ActivityIdentificationResponse.fromMap(Map map) { + if (map == null) return null; + + return ActivityIdentificationResponse( + time: map['time'], + elapsedTimeFromReboot: map['elapsedTimeFromReboot'], + activityIdentificationDatas: List.from( + map['activityIdentificationDatas'] + ?.map((x) => ActivityIdentificationData.fromMap(x))), + ); + } + + String toJson() => json.encode(toMap()); + + factory ActivityIdentificationResponse.fromJson(String source) => + ActivityIdentificationResponse.fromMap(json.decode(source)); + + @override + String toString() => + 'ActivityIdentificationResponse(time: $time, elapsedTimeFromReboot: $elapsedTimeFromReboot, activityIdentificationDatas: $activityIdentificationDatas)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is ActivityIdentificationResponse && + o.time == time && + o.elapsedTimeFromReboot == elapsedTimeFromReboot && + listEquals(o.activityIdentificationDatas, activityIdentificationDatas); + } + + @override + int get hashCode { + return hashList([ + time, + elapsedTimeFromReboot, + activityIdentificationDatas, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/activity/activity_identification_service.dart b/hms-plugins/flutter-hms-location/lib/activity/activity_identification_service.dart new file mode 100644 index 00000000..c8598871 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/activity/activity_identification_service.dart @@ -0,0 +1,98 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'package:flutter/services.dart'; + +import 'activity_conversion_info.dart'; +import 'activity_conversion_response.dart'; +import 'activity_identification_response.dart'; + +class ActivityIdentificationService { + static ActivityIdentificationService _instance; + + final MethodChannel _methodChannel; + final EventChannel _activityIdentificationEventChannel; + final EventChannel _activityConversionEventChannel; + + Stream _onActivityIdentification; + Stream _onActivityConversion; + + ActivityIdentificationService._create( + this._methodChannel, + this._activityIdentificationEventChannel, + this._activityConversionEventChannel, + ); + + factory ActivityIdentificationService() { + if (_instance == null) { + final MethodChannel methodChannel = const MethodChannel( + 'com.huawei.flutter.location/activityidentification_methodchannel'); + + final EventChannel activityIdentificationEventChannel = const EventChannel( + 'com.huawei.flutter.location/activityidentification_eventchannel'); + + final EventChannel activityConversionEventChannel = const EventChannel( + 'com.huawei.flutter.location/activityconversion_eventchannel'); + + _instance = ActivityIdentificationService._create( + methodChannel, + activityIdentificationEventChannel, + activityConversionEventChannel, + ); + } + return _instance; + } + + Future createActivityIdentificationUpdates( + int detectionIntervalMillis) async { + return _methodChannel.invokeMethod( + 'createActivityIdentificationUpdates', detectionIntervalMillis); + } + + Future createActivityConversionUpdates( + List activityConversions) async { + return _methodChannel.invokeMethod('createActivityConversionUpdates', + activityConversions?.map((x) => x?.toMap())?.toList()); + } + + Future deleteActivityIdentificationUpdates(int requestCode) async { + return _methodChannel.invokeMethod( + 'deleteActivityIdentificationUpdates', requestCode); + } + + Future deleteActivityConversionUpdates(int requestCode) async { + return _methodChannel.invokeMethod( + 'deleteActivityConversionUpdates', requestCode); + } + + Stream get onActivityIdentification { + if (_onActivityIdentification == null) { + _onActivityIdentification = _activityIdentificationEventChannel + .receiveBroadcastStream() + .map((event) => ActivityIdentificationResponse.fromMap(event)); + } + return _onActivityIdentification; + } + + Stream get onActivityConversion { + if (_onActivityConversion == null) { + _onActivityConversion = _activityConversionEventChannel + .receiveBroadcastStream() + .map((event) => ActivityConversionResponse.fromMap(event)); + } + return _onActivityConversion; + } +} diff --git a/hms-plugins/flutter-hms-location/lib/geofence/geofence.dart b/hms-plugins/flutter-hms-location/lib/geofence/geofence.dart new file mode 100644 index 00000000..fc93cb7b --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/geofence/geofence.dart @@ -0,0 +1,113 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:core'; +import 'dart:ui'; + +class Geofence { + static const int ENTER_GEOFENCE_CONVERSION = 1; + static const int EXIT_GEOFENCE_CONVERSION = 2; + static const int DWELL_GEOFENCE_CONVERSION = 4; + static const int GEOFENCE_NEVER_EXPIRE = -1; + + String uniqueId; + int conversions; + int validDuration; + double latitude; + double longitude; + double radius; + int notificationInterval; + int dwellDelayTime; + + Geofence({ + this.uniqueId, + this.conversions, + this.validDuration, + this.latitude, + this.longitude, + this.radius, + this.notificationInterval = 0, + this.dwellDelayTime = 0, + }); + + Map toMap() { + return { + 'uniqueId': uniqueId, + 'conversions': conversions, + 'validDuration': validDuration, + 'latitude': latitude, + 'longitude': longitude, + 'radius': radius, + 'notificationInterval': notificationInterval, + 'dwellDelayTime': dwellDelayTime, + }; + } + + factory Geofence.fromMap(Map map) { + if (map == null) return null; + + return Geofence( + uniqueId: map['uniqueId'], + conversions: map['conversions'], + validDuration: map['validDuration'], + latitude: map['latitude'], + longitude: map['longitude'], + radius: map['radius'], + notificationInterval: map['notificationInterval'], + dwellDelayTime: map['dwellDelayTime'], + ); + } + + String toJson() => json.encode(toMap()); + + factory Geofence.fromJson(String source) => + Geofence.fromMap(json.decode(source)); + + @override + String toString() { + return 'Geofence(uniqueId: $uniqueId, conversions: $conversions, validDuration: $validDuration, latitude: $latitude, longitude: $longitude, radius: $radius, notificationInterval: $notificationInterval, dwellDelayTime: $dwellDelayTime)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is Geofence && + o.uniqueId == uniqueId && + o.conversions == conversions && + o.validDuration == validDuration && + o.latitude == latitude && + o.longitude == longitude && + o.radius == radius && + o.notificationInterval == notificationInterval && + o.dwellDelayTime == dwellDelayTime; + } + + @override + int get hashCode { + return hashList([ + uniqueId, + conversions, + validDuration, + latitude, + longitude, + radius, + notificationInterval, + dwellDelayTime, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/geofence/geofence_data.dart b/hms-plugins/flutter-hms-location/lib/geofence/geofence_data.dart new file mode 100644 index 00000000..c2aca69e --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/geofence/geofence_data.dart @@ -0,0 +1,88 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import '../location/location.dart'; + +class GeofenceData { + int errorCode; + int conversion; + List convertingGeofenceIdList; + Location convertingLocation; + + GeofenceData({ + this.errorCode, + this.conversion, + this.convertingGeofenceIdList, + this.convertingLocation, + }); + + Map toMap() { + return { + 'errorCode': errorCode, + 'conversion': conversion, + 'convertingGeofenceIdList': convertingGeofenceIdList, + 'convertingLocation': convertingLocation?.toMap(), + }; + } + + factory GeofenceData.fromMap(Map map) { + if (map == null) return null; + + return GeofenceData( + errorCode: map['errorCode'], + conversion: map['conversion'], + convertingGeofenceIdList: + List.from(map['convertingGeofenceIdList']), + convertingLocation: Location.fromMap(map['convertingLocation']), + ); + } + + String toJson() => json.encode(toMap()); + + factory GeofenceData.fromJson(String source) => + GeofenceData.fromMap(json.decode(source)); + + @override + String toString() { + return 'GeofenceData(errorCode: $errorCode, conversion: $conversion, convertingGeofenceIdList: $convertingGeofenceIdList, convertingLocation: $convertingLocation)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is GeofenceData && + o.errorCode == errorCode && + o.conversion == conversion && + listEquals(o.convertingGeofenceIdList, convertingGeofenceIdList) && + o.convertingLocation == convertingLocation; + } + + @override + int get hashCode { + return hashList([ + errorCode, + conversion, + convertingGeofenceIdList, + convertingLocation, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/geofence/geofence_request.dart b/hms-plugins/flutter-hms-location/lib/geofence/geofence_request.dart new file mode 100644 index 00000000..bbefb528 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/geofence/geofence_request.dart @@ -0,0 +1,91 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import 'geofence.dart'; + +class GeofenceRequest { + static const int ENTER_INIT_CONVERSION = 1; + static const int EXIT_INIT_CONVERSION = 2; + static const int DWELL_INIT_CONVERSION = 4; + static const int COORDINATE_TYPE_WGS_84 = 1; + static const int COORDINATE_TYPE_GCJ_02 = 0; + + List geofenceList; + int initConversions; + int coordinateType; + + GeofenceRequest({ + this.geofenceList, + this.initConversions = ENTER_INIT_CONVERSION | DWELL_INIT_CONVERSION, + this.coordinateType = COORDINATE_TYPE_WGS_84, + }) { + if (this.geofenceList == null) { + this.geofenceList = []; + } + } + + Map toMap() { + return { + 'geofenceList': geofenceList?.map((x) => x?.toMap())?.toList(), + 'initConversions': initConversions, + 'coordinateType': coordinateType, + }; + } + + factory GeofenceRequest.fromMap(Map map) { + if (map == null) return null; + + return GeofenceRequest( + geofenceList: List.from( + map['geofenceList']?.map((x) => Geofence.fromMap(x))), + initConversions: map['initConversions'], + coordinateType: map['coordinateType'], + ); + } + + String toJson() => json.encode(toMap()); + + factory GeofenceRequest.fromJson(String source) => + GeofenceRequest.fromMap(json.decode(source)); + + @override + String toString() => + 'GeofenceRequest(geofenceList: $geofenceList, initConversions: $initConversions, coordinateType: $coordinateType)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is GeofenceRequest && + listEquals(o.geofenceList, geofenceList) && + o.initConversions == initConversions && + o.coordinateType == coordinateType; + } + + @override + int get hashCode { + return hashList([ + geofenceList, + initConversions, + coordinateType, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/geofence/geofence_service.dart b/hms-plugins/flutter-hms-location/lib/geofence/geofence_service.dart new file mode 100644 index 00000000..87e9d0a8 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/geofence/geofence_service.dart @@ -0,0 +1,72 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'package:flutter/services.dart'; + +import 'geofence_data.dart' show GeofenceData; +import 'geofence_request.dart' show GeofenceRequest; + +class GeofenceService { + static GeofenceService _instance; + + final MethodChannel _methodChannel; + final EventChannel _eventChannel; + + Stream _onGeofenceData; + + GeofenceService._create( + this._methodChannel, + this._eventChannel, + ); + + factory GeofenceService() { + if (_instance == null) { + final MethodChannel methodChannel = const MethodChannel( + 'com.huawei.flutter.location/geofence_methodchannel'); + final EventChannel eventChannel = const EventChannel( + 'com.huawei.flutter.location/geofence_eventchannel'); + + _instance = GeofenceService._create( + methodChannel, + eventChannel, + ); + } + return _instance; + } + + Future createGeofenceList(GeofenceRequest geofenceRequest) async { + return _methodChannel.invokeMethod( + 'createGeofenceList', geofenceRequest.toMap()); + } + + Future deleteGeofenceList(int requestCode) async { + return _methodChannel.invokeMethod('deleteGeofenceList', requestCode); + } + + Future deleteGeofenceListWithIds(List geofenceIds) async { + return _methodChannel.invokeMethod( + 'deleteGeofenceListWithIds', geofenceIds); + } + + Stream get onGeofenceData { + if (_onGeofenceData == null) { + _onGeofenceData = _eventChannel + .receiveBroadcastStream() + .map((event) => GeofenceData.fromMap(event)); + } + return _onGeofenceData; + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/fused_location_provider_client.dart b/hms-plugins/flutter-hms-location/lib/location/fused_location_provider_client.dart new file mode 100644 index 00000000..b67c49af --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/fused_location_provider_client.dart @@ -0,0 +1,158 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'package:flutter/services.dart'; + +import 'hwlocation.dart'; +import 'location.dart'; +import 'location_availability.dart'; +import 'location_callback.dart'; +import 'location_request.dart'; +import 'location_result.dart'; +import 'location_settings_request.dart'; +import 'location_settings_states.dart'; +import 'navigation_request.dart'; +import 'navigation_result.dart'; + +class FusedLocationProviderClient { + static FusedLocationProviderClient _instance; + + final MethodChannel _methodChannel; + final EventChannel _eventChannel; + final Map _callbacks; + + Stream _onLocationData; + + FusedLocationProviderClient._create( + this._methodChannel, + this._eventChannel, + this._callbacks, + ) { + _methodChannel.setMethodCallHandler(_methodCallHandler); + } + + factory FusedLocationProviderClient() { + if (_instance == null) { + _instance = FusedLocationProviderClient._create( + const MethodChannel( + 'com.huawei.flutter.location/fusedlocation_methodchannel'), + const EventChannel( + 'com.huawei.flutter.location/fusedlocation_eventchannel'), + {}, + ); + } + return _instance; + } + + Future _methodCallHandler(MethodCall methodCall) async { + switch (methodCall.method) { + case 'onLocationResult': + _callbacks[methodCall.arguments['callbackId']].onLocationResult( + LocationResult.fromMap(methodCall.arguments['locationResult'])); + break; + case 'onLocationAvailability': + _callbacks[methodCall.arguments['callbackId']].onLocationAvailability( + LocationAvailability.fromMap( + methodCall.arguments['locationAvailability'])); + break; + default: + break; + } + } + + Future checkLocationSettings( + LocationSettingsRequest locationSettingsRequest) async { + return LocationSettingsStates.fromMap( + await _methodChannel.invokeMapMethod( + 'checkLocationSettings', locationSettingsRequest.toMap())); + } + + Future getLastLocation() async { + return Location.fromMap(await _methodChannel + .invokeMapMethod('getLastLocation')); + } + + Future getLastLocationWithAddress( + LocationRequest locationRequest) async { + return HWLocation.fromMap( + await _methodChannel.invokeMapMethod( + 'getLastLocationWithAddress', locationRequest.toMap())); + } + + Future getLocationAvailability() async { + return LocationAvailability.fromMap(await _methodChannel + .invokeMapMethod('getLocationAvailability')); + } + + Future setMockMode(bool mockMode) async { + return _methodChannel.invokeMethod('setMockMode', mockMode); + } + + Future setMockLocation(Location location) async { + return _methodChannel.invokeMethod( + 'setMockLocation', location.toMap()); + } + + Future requestLocationUpdates(LocationRequest locationRequest) async { + return _methodChannel.invokeMethod( + 'requestLocationUpdates', locationRequest.toMap()); + } + + Future requestLocationUpdatesCb(LocationRequest locationRequest, + LocationCallback locationCallback) async { + int callbackId = await _methodChannel.invokeMethod( + 'requestLocationUpdatesCb', locationRequest.toMap()); + _callbacks.putIfAbsent(callbackId, () => locationCallback); + return callbackId; + } + + Future requestLocationUpdatesExCb(LocationRequest locationRequest, + LocationCallback locationCallback) async { + int callbackId = await _methodChannel.invokeMethod( + 'requestLocationUpdatesExCb', + (locationRequest..priority = LocationRequest.PRIORITY_HD_ACCURACY) + .toMap()); + _callbacks.putIfAbsent(callbackId, () => locationCallback); + return callbackId; + } + + Future removeLocationUpdates(int requestCode) async { + return _methodChannel.invokeMethod( + 'removeLocationUpdates', requestCode); + } + + Future removeLocationUpdatesCb(int callbackId) async { + await _methodChannel.invokeMethod( + 'removeLocationUpdatesCb', callbackId); + _callbacks.remove(callbackId); + } + + Future getNavigationContextState( + NavigationRequest navigationRequest) async { + return NavigationResult.fromMap( + await _methodChannel.invokeMapMethod( + 'getNavigationContextState', navigationRequest.toMap())); + } + + Stream get onLocationData { + if (_onLocationData == null) { + _onLocationData = _eventChannel + .receiveBroadcastStream() + .map((event) => Location.fromMap(event)); + } + return _onLocationData; + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/hwlocation.dart b/hms-plugins/flutter-hms-location/lib/location/hwlocation.dart new file mode 100644 index 00000000..97c92db5 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/hwlocation.dart @@ -0,0 +1,207 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +class HWLocation { + String provider; + double latitude; + double longitude; + double altitude; + double speed; + double bearing; + double horizontalAccuracyMeters; + double verticalAccuracyMeters; + double speedAccuracyMetersPerSecond; + double bearingAccuracyDegrees; + int time; + int elapsedRealtimeNanos; + String countryCode; + String countryName; + String state; + String city; + String county; + String street; + String featureName; + String postalCode; + String phone; + String url; + Map extraInfo; + + HWLocation({ + this.provider, + this.latitude, + this.longitude, + this.altitude, + this.speed, + this.bearing, + this.horizontalAccuracyMeters, + this.verticalAccuracyMeters, + this.speedAccuracyMetersPerSecond, + this.bearingAccuracyDegrees, + this.time, + this.elapsedRealtimeNanos, + this.countryCode, + this.countryName, + this.state, + this.city, + this.county, + this.street, + this.featureName, + this.postalCode, + this.phone, + this.url, + this.extraInfo, + }); + + Map toMap() { + return { + 'provider': provider, + 'latitude': latitude, + 'longitude': longitude, + 'altitude': altitude, + 'speed': speed, + 'bearing': bearing, + 'horizontalAccuracyMeters': horizontalAccuracyMeters, + 'verticalAccuracyMeters': verticalAccuracyMeters, + 'speedAccuracyMetersPerSecond': speedAccuracyMetersPerSecond, + 'bearingAccuracyDegrees': bearingAccuracyDegrees, + 'time': time, + 'elapsedRealtimeNanos': elapsedRealtimeNanos, + 'countryCode': countryCode, + 'countryName': countryName, + 'state': state, + 'city': city, + 'county': county, + 'street': street, + 'featureName': featureName, + 'postalCode': postalCode, + 'phone': phone, + 'url': url, + 'extraInfo': extraInfo, + }; + } + + factory HWLocation.fromMap(Map map) { + if (map == null) return null; + + return HWLocation( + provider: map["provider"] == null ? null : map["provider"], + latitude: map["latitude"] == null ? null : map["latitude"], + longitude: map["longitude"] == null ? null : map["longitude"], + altitude: map["altitude"] == null ? null : map["altitude"], + speed: map["speed"] == null ? null : map["speed"], + bearing: map["bearing"] == null ? null : map["bearing"], + horizontalAccuracyMeters: map["horizontalAccuracyMeters"], + verticalAccuracyMeters: map["verticalAccuracyMeters"] == null + ? null + : map["verticalAccuracyMeters"], + speedAccuracyMetersPerSecond: + map["speedAccuracyMetersPerSecond"] == null + ? null + : map["speedAccuracyMetersPerSecond"], + bearingAccuracyDegrees: map["bearingAccuracyDegrees"] == null + ? null + : map["bearingAccuracyDegrees"], + time: map["time"] == null ? null : map["time"], + elapsedRealtimeNanos: map["elapsedRealtimeNanos"] == null + ? null + : map["elapsedRealtimeNanos"], + countryCode: map["countryCode"] == null ? null : map["countryCode"], + countryName: map["countryName"] == null ? null : map["countryName"], + state: map["state"] == null ? null : map["state"], + city: map["city"] == null ? null : map["city"], + county: map["county"] == null ? null : map["county"], + street: map["street"] == null ? null : map["street"], + featureName: map["featureName"] == null ? null : map["featureName"], + postalCode: map["postalCode"] == null ? null : map["postalCode"], + phone: map["phone"] == null ? null : map["phone"], + url: map["url"] == null ? null : map["url"], + extraInfo: + map["extraInfo"] == null ? null : Map.from(map["extraInfo"])); + } + + String toJson() => json.encode(toMap()); + + factory HWLocation.fromJson(String source) => + HWLocation.fromMap(json.decode(source)); + + @override + String toString() { + return 'HWLocation(provider: $provider, latitude: $latitude, longitude: $longitude, altitude: $altitude, speed: $speed, bearing: $bearing, horizontalAccuracyMeters: $horizontalAccuracyMeters, verticalAccuracyMeters: $verticalAccuracyMeters, speedAccuracyMetersPerSecond: $speedAccuracyMetersPerSecond, bearingAccuracyDegrees: $bearingAccuracyDegrees, time: $time, elapsedRealtimeNanos: $elapsedRealtimeNanos, countryCode: $countryCode, countryName: $countryName, state: $state, city: $city, county: $county, street: $street, featureName: $featureName, postalCode: $postalCode, phone: $phone, url: $url, extraInfo: $extraInfo)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + return o is HWLocation && + o.provider == provider && + o.latitude == latitude && + o.longitude == longitude && + o.altitude == altitude && + o.speed == speed && + o.bearing == bearing && + o.horizontalAccuracyMeters == horizontalAccuracyMeters && + o.verticalAccuracyMeters == verticalAccuracyMeters && + o.speedAccuracyMetersPerSecond == speedAccuracyMetersPerSecond && + o.bearingAccuracyDegrees == bearingAccuracyDegrees && + o.time == time && + o.elapsedRealtimeNanos == elapsedRealtimeNanos && + o.countryCode == countryCode && + o.countryName == countryName && + o.state == state && + o.city == city && + o.county == county && + o.street == street && + o.featureName == featureName && + o.postalCode == postalCode && + o.phone == phone && + o.url == url && + mapEquals(o.extraInfo, extraInfo); + } + + @override + int get hashCode { + return hashList([ + provider, + latitude, + longitude, + altitude, + speed, + bearing, + horizontalAccuracyMeters, + verticalAccuracyMeters, + speedAccuracyMetersPerSecond, + bearingAccuracyDegrees, + time, + elapsedRealtimeNanos, + countryCode, + countryName, + state, + city, + county, + street, + featureName, + postalCode, + phone, + url, + extraInfo, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location.dart b/hms-plugins/flutter-hms-location/lib/location/location.dart new file mode 100644 index 00000000..5fa17b6a --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location.dart @@ -0,0 +1,141 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +class Location { + String provider; + double latitude; + double longitude; + double altitude; + double speed; + double bearing; + double horizontalAccuracyMeters; + double verticalAccuracyMeters; + double speedAccuracyMetersPerSecond; + double bearingAccuracyDegrees; + int time; + int elapsedRealtimeNanos; + + Location({ + this.provider = 'HMS Mock Location', + this.latitude = 0.0, + this.longitude = 0.0, + this.altitude = 0.0, + this.speed = 0.0, + this.bearing = 0.0, + this.horizontalAccuracyMeters = 0.0, + this.verticalAccuracyMeters = 0.0, + this.speedAccuracyMetersPerSecond = 0.0, + this.bearingAccuracyDegrees = 0.0, + this.time = 0, + this.elapsedRealtimeNanos = 0, + }); + + Map toMap() { + return { + 'provider': provider, + 'latitude': latitude, + 'longitude': longitude, + 'altitude': altitude, + 'speed': speed, + 'bearing': bearing, + 'horizontalAccuracyMeters': horizontalAccuracyMeters, + 'verticalAccuracyMeters': verticalAccuracyMeters, + 'speedAccuracyMetersPerSecond': speedAccuracyMetersPerSecond, + 'bearingAccuracyDegrees': bearingAccuracyDegrees, + 'time': time, + 'elapsedRealtimeNanos': elapsedRealtimeNanos, + }; + } + + factory Location.fromMap(Map map) { + if (map == null) return null; + + return Location( + provider: map["provider"] == null ? null : map["provider"], + latitude: map["latitude"] == null ? null : map["latitude"], + longitude: map["longitude"] == null ? null : map["longitude"], + altitude: map["altitude"] == null ? null : map["altitude"], + speed: map["speed"] == null ? null : map["speed"], + bearing: map["bearing"] == null ? null : map["bearing"], + horizontalAccuracyMeters: map["horizontalAccuracyMeters"] == null + ? null + : map["horizontalAccuracyMeters"], + verticalAccuracyMeters: map["verticalAccuracyMeters"] == null + ? null + : map["verticalAccuracyMeters"], + speedAccuracyMetersPerSecond: map["speedAccuracyMetersPerSecond"] == null + ? null + : map["speedAccuracyMetersPerSecond"], + bearingAccuracyDegrees: map["bearingAccuracyDegrees"] == null + ? null + : map["bearingAccuracyDegrees"], + time: map["time"] == null ? null : map["time"], + elapsedRealtimeNanos: map["elapsedRealtimeNanos"] == null + ? null + : map["elapsedRealtimeNanos"], + ); + } + + String toJson() => json.encode(toMap()); + + factory Location.fromJson(String source) => + Location.fromMap(json.decode(source)); + + @override + String toString() { + return 'Location(provider: $provider, latitude: $latitude, longitude: $longitude, altitude: $altitude, speed: $speed, bearing: $bearing, horizontalAccuracyMeters: $horizontalAccuracyMeters, verticalAccuracyMeters: $verticalAccuracyMeters, speedAccuracyMetersPerSecond: $speedAccuracyMetersPerSecond, bearingAccuracyDegrees: $bearingAccuracyDegrees, time: $time, elapsedRealtimeNanos: $elapsedRealtimeNanos)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is Location && + o.provider == provider && + o.latitude == latitude && + o.longitude == longitude && + o.altitude == altitude && + o.speed == speed && + o.bearing == bearing && + o.horizontalAccuracyMeters == horizontalAccuracyMeters && + o.verticalAccuracyMeters == verticalAccuracyMeters && + o.speedAccuracyMetersPerSecond == speedAccuracyMetersPerSecond && + o.bearingAccuracyDegrees == bearingAccuracyDegrees && + o.time == time && + o.elapsedRealtimeNanos == elapsedRealtimeNanos; + } + + @override + int get hashCode { + return hashList([ + provider, + latitude, + longitude, + altitude, + speed, + bearing, + horizontalAccuracyMeters, + verticalAccuracyMeters, + speedAccuracyMetersPerSecond, + bearingAccuracyDegrees, + time, + elapsedRealtimeNanos, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location_availability.dart b/hms-plugins/flutter-hms-location/lib/location/location_availability.dart new file mode 100644 index 00000000..a93a1061 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location_availability.dart @@ -0,0 +1,85 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +class LocationAvailability { + int cellStatus; + int wifiStatus; + int elapsedRealtimeNs; + int locationStatus; + + LocationAvailability({ + this.cellStatus, + this.wifiStatus, + this.elapsedRealtimeNs, + this.locationStatus, + }); + + bool get isLocationAvailable => locationStatus < 1000; + + Map toMap() { + return { + 'cellStatus': cellStatus, + 'wifiStatus': wifiStatus, + 'elapsedRealtimeNs': elapsedRealtimeNs, + 'locationStatus': locationStatus, + }; + } + + factory LocationAvailability.fromMap(Map map) { + if (map == null) return null; + + return LocationAvailability( + cellStatus: map['cellStatus'], + wifiStatus: map['wifiStatus'], + elapsedRealtimeNs: map['elapsedRealtimeNs'], + locationStatus: map['locationStatus'], + ); + } + + String toJson() => json.encode(toMap()); + + factory LocationAvailability.fromJson(String source) => + LocationAvailability.fromMap(json.decode(source)); + + @override + String toString() { + return 'LocationAvailability(cellStatus: $cellStatus, wifiStatus: $wifiStatus, elapsedRealtimeNs: $elapsedRealtimeNs, locationStatus: $locationStatus)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is LocationAvailability && + o.cellStatus == cellStatus && + o.wifiStatus == wifiStatus && + o.elapsedRealtimeNs == elapsedRealtimeNs && + o.locationStatus == locationStatus; + } + + @override + int get hashCode { + return hashList([ + cellStatus, + wifiStatus, + elapsedRealtimeNs, + locationStatus, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location_callback.dart b/hms-plugins/flutter-hms-location/lib/location/location_callback.dart new file mode 100644 index 00000000..b67068ae --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location_callback.dart @@ -0,0 +1,31 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'location_availability.dart'; +import 'location_result.dart'; + +typedef void OnLocationResult(LocationResult locationResult); +typedef void OnLocationAvailability(LocationAvailability locationAvailability); + +class LocationCallback { + OnLocationResult onLocationResult; + OnLocationAvailability onLocationAvailability; + + LocationCallback({ + this.onLocationResult, + this.onLocationAvailability, + }); +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location_request.dart b/hms-plugins/flutter-hms-location/lib/location/location_request.dart new file mode 100644 index 00000000..af4ee2ea --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location_request.dart @@ -0,0 +1,231 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +class LocationRequest { + static const int PRIORITY_HIGH_ACCURACY = 100; + static const int PRIORITY_BALANCED_POWER_ACCURACY = 102; + static const int PRIORITY_LOW_POWER = 104; + static const int PRIORITY_NO_POWER = 105; + static const int PRIORITY_HD_ACCURACY = 200; + static const double _FASTEST_INTERVAL_FACTOR = 6.0; + + int _priority; + int _interval; + int _fastestInterval; + bool _isFastestIntervalExplicitlySet; + int _expirationTime; + int _numUpdates; + double _smallestDisplacement; + int _maxWaitTime; + bool needAddress; + String language; + String countryCode; + Map extras; + + LocationRequest._create( + this._priority, + this._interval, + this._fastestInterval, + this._isFastestIntervalExplicitlySet, + this._expirationTime, + this._numUpdates, + this._smallestDisplacement, + this._maxWaitTime, + this.needAddress, + this.language, + this.countryCode, + this.extras, + ); + + LocationRequest() { + _priority = PRIORITY_BALANCED_POWER_ACCURACY; + _interval = 3600000; + _fastestInterval = (_interval ~/ _FASTEST_INTERVAL_FACTOR); + _isFastestIntervalExplicitlySet = false; + _expirationTime = 9223372036854775807; + _numUpdates = 2147483647; + _smallestDisplacement = 0.0; + _maxWaitTime = 0; + needAddress = false; + language = ''; + countryCode = ''; + } + + int get priority => _priority; + + set priority(int value) { + if (value == PRIORITY_HIGH_ACCURACY || + value == PRIORITY_BALANCED_POWER_ACCURACY || + value == PRIORITY_LOW_POWER || + value == PRIORITY_NO_POWER || + value == PRIORITY_HD_ACCURACY) { + _priority = value; + } else { + throw ArgumentError('Priority is not a known constant'); + } + } + + int get interval => _interval; + + set interval(int value) { + if (value.isNegative) { + throw ArgumentError('Interval is invalid'); + } else { + _interval = value; + _fastestInterval = _isFastestIntervalExplicitlySet + ? _fastestInterval + : (_interval ~/ _FASTEST_INTERVAL_FACTOR); + } + } + + int get fastestInterval => _fastestInterval; + + set fastestInterval(int value) { + if (value.isNegative) { + throw ArgumentError('FastestInterval is invalid'); + } else { + _isFastestIntervalExplicitlySet = true; + _fastestInterval = value; + } + } + + bool get isFastestIntervalExplicitlySet => _isFastestIntervalExplicitlySet; + + int get expirationTime => _expirationTime; + + set expirationTime(int value) { + _expirationTime = value.isNegative ? 0 : value; + } + + int get numUpdates => _numUpdates; + + set numUpdates(int value) { + if (value <= 0) { + throw ArgumentError('numUpdates is invalid'); + } else { + _numUpdates = value; + } + } + + double get smallestDisplacement => _smallestDisplacement; + + set smallestDisplacement(double value) { + if (value.isNegative) { + throw ArgumentError('smallestDisplacement param invalid'); + } else { + _smallestDisplacement = value; + } + } + + int get maxWaitTime => _maxWaitTime < _interval ? _interval : _maxWaitTime; + + set maxWaitTime(int value) => _maxWaitTime = value; + + void putExtras(String key, String value) { + if (extras == null) { + extras = Map(); + } + extras.putIfAbsent(key, () => value); + } + + Map toMap() { + return { + 'priority': _priority, + 'interval': _interval, + 'fastestInterval': _fastestInterval, + 'isFastestIntervalExplicitlySet': _isFastestIntervalExplicitlySet, + 'expirationTime': _expirationTime, + 'numUpdates': _numUpdates, + 'smallestDisplacement': _smallestDisplacement, + 'maxWaitTime': _maxWaitTime, + 'needAddress': needAddress, + 'language': language, + 'countryCode': countryCode, + 'extras': extras, + }; + } + + factory LocationRequest.fromMap(Map map) { + if (map == null) return null; + + return LocationRequest._create( + map['priority'], + map['interval'], + map['fastestInterval'], + map['isFastestIntervalExplicitlySet'], + map['expirationTime'], + map['numUpdates'], + map['smallestDisplacement'], + map['maxWaitTime'], + map['needAddress'], + map['language'], + map['countryCode'], + Map.from(map['extras']), + ); + } + + String toJson() => json.encode(toMap()); + + factory LocationRequest.fromJson(String source) => + LocationRequest.fromMap(json.decode(source)); + + @override + String toString() { + return 'LocationRequest(_priority: $_priority, _interval: $_interval, _fastestInterval: $_fastestInterval, _isFastestIntervalExplicitlySet: $_isFastestIntervalExplicitlySet, _expirationTime: $_expirationTime, _numUpdates: $_numUpdates, _smallestDisplacement: $_smallestDisplacement, _maxWaitTime: $_maxWaitTime, _needAddress: $needAddress, _language: $language, _countryCode: $countryCode, _extras: $extras)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is LocationRequest && + o._priority == _priority && + o._interval == _interval && + o._fastestInterval == _fastestInterval && + o._isFastestIntervalExplicitlySet == _isFastestIntervalExplicitlySet && + o._expirationTime == _expirationTime && + o._numUpdates == _numUpdates && + o._smallestDisplacement == _smallestDisplacement && + o._maxWaitTime == _maxWaitTime && + o.needAddress == needAddress && + o.language == language && + o.countryCode == countryCode && + mapEquals(o.extras, extras); + } + + @override + int get hashCode { + return hashList([ + _priority, + _interval, + _fastestInterval, + _isFastestIntervalExplicitlySet, + _expirationTime, + _numUpdates, + _smallestDisplacement, + _maxWaitTime, + needAddress, + language, + countryCode, + extras, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location_result.dart b/hms-plugins/flutter-hms-location/lib/location/location_result.dart new file mode 100644 index 00000000..04cac6d5 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location_result.dart @@ -0,0 +1,98 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import 'hwlocation.dart' show HWLocation; +import 'location.dart' show Location; + +class LocationResult { + List locations; + List hwLocations; + Location lastLocation; + HWLocation lastHWLocation; + + LocationResult({ + this.locations, + this.hwLocations, + this.lastLocation, + this.lastHWLocation, + }); + + Map toMap() { + return { + 'locations': locations?.map((x) => x?.toMap())?.toList(), + 'hwLocations': hwLocations?.map((x) => x?.toMap())?.toList(), + 'lastLocation': lastLocation?.toMap(), + 'lastHWLocation': lastHWLocation?.toMap(), + }; + } + + factory LocationResult.fromMap(Map map) { + if (map == null) return null; + + return LocationResult( + locations: map["locations"] == null + ? null + : List.from( + map["locations"].map((x) => Location.fromMap(x))), + hwLocations: map["hwLocations"] == null + ? null + : List.from( + map["hwLocations"].map((x) => HWLocation.fromMap(x))), + lastLocation: map["lastLocation"] == null + ? null + : Location.fromMap(map["lastLocation"]), + lastHWLocation: map["lastHWLocation"] == null + ? null + : HWLocation.fromMap(map["lastHWLocation"]), + ); + } + + String toJson() => json.encode(toMap()); + + factory LocationResult.fromJson(String source) => + LocationResult.fromMap(json.decode(source)); + + @override + String toString() { + return 'LocationResult(locations: $locations, hwLocations: $hwLocations, lastLocation: $lastLocation, lastHWLocation: $lastHWLocation)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is LocationResult && + listEquals(o.locations, locations) && + listEquals(o.hwLocations, hwLocations) && + o.lastLocation == lastLocation && + o.lastHWLocation == lastHWLocation; + } + + @override + int get hashCode { + return hashList([ + locations, + hwLocations, + lastLocation, + lastHWLocation, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location_settings_request.dart b/hms-plugins/flutter-hms-location/lib/location/location_settings_request.dart new file mode 100644 index 00000000..f03d7224 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location_settings_request.dart @@ -0,0 +1,81 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +import 'location_request.dart'; + +class LocationSettingsRequest { + List requests; + bool alwaysShow; + bool needBle; + + LocationSettingsRequest({ + this.requests = const [], + this.alwaysShow = false, + this.needBle = false, + }); + + Map toMap() { + return { + 'requests': requests?.map((x) => x?.toMap())?.toList(), + 'alwaysShow': alwaysShow, + 'needBle': needBle, + }; + } + + factory LocationSettingsRequest.fromMap(Map map) { + if (map == null) return null; + + return LocationSettingsRequest( + requests: List.from( + map['requests']?.map((x) => LocationRequest.fromMap(x))), + alwaysShow: map['alwaysShow'], + needBle: map['needBle'], + ); + } + + String toJson() => json.encode(toMap()); + + factory LocationSettingsRequest.fromJson(String source) => + LocationSettingsRequest.fromMap(json.decode(source)); + + @override + String toString() => + 'LocationSettingsRequest(requests: $requests, alwaysShow: $alwaysShow, needBle: $needBle)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is LocationSettingsRequest && + listEquals(o.requests, requests) && + o.alwaysShow == alwaysShow && + o.needBle == needBle; + } + + @override + int get hashCode { + return hashList([ + requests, + alwaysShow, + needBle, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/location_settings_states.dart b/hms-plugins/flutter-hms-location/lib/location/location_settings_states.dart new file mode 100644 index 00000000..096374c6 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/location_settings_states.dart @@ -0,0 +1,107 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +class LocationSettingsStates { + bool blePresent; + bool bleUsable; + bool gpsPresent; + bool gpsUsable; + bool locationPresent; + bool locationUsable; + bool networkLocationPresent; + bool networkLocationUsable; + + LocationSettingsStates({ + this.blePresent, + this.bleUsable, + this.gpsPresent, + this.gpsUsable, + this.locationPresent, + this.locationUsable, + this.networkLocationPresent, + this.networkLocationUsable, + }); + + Map toMap() { + return { + 'blePresent': blePresent, + 'bleUsable': bleUsable, + 'gpsPresent': gpsPresent, + 'gpsUsable': gpsUsable, + 'locationPresent': locationPresent, + 'locationUsable': locationUsable, + 'networkLocationPresent': networkLocationPresent, + 'networkLocationUsable': networkLocationUsable, + }; + } + + factory LocationSettingsStates.fromMap(Map map) { + if (map == null) return null; + + return LocationSettingsStates( + blePresent: map['blePresent'], + bleUsable: map['bleUsable'], + gpsPresent: map['gpsPresent'], + gpsUsable: map['gpsUsable'], + locationPresent: map['locationPresent'], + locationUsable: map['locationUsable'], + networkLocationPresent: map['networkLocationPresent'], + networkLocationUsable: map['networkLocationUsable'], + ); + } + + String toJson() => json.encode(toMap()); + + factory LocationSettingsStates.fromJson(String source) => + LocationSettingsStates.fromMap(json.decode(source)); + + @override + String toString() { + return 'LocationSettingsStates(blePresent: $blePresent, bleUsable: $bleUsable, gpsPresent: $gpsPresent, gpsUsable: $gpsUsable, locationPresent: $locationPresent, locationUsable: $locationUsable, networkLocationPresent: $networkLocationPresent, networkLocationUsable: $networkLocationUsable)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is LocationSettingsStates && + o.blePresent == blePresent && + o.bleUsable == bleUsable && + o.gpsPresent == gpsPresent && + o.gpsUsable == gpsUsable && + o.locationPresent == locationPresent && + o.locationUsable == locationUsable && + o.networkLocationPresent == networkLocationPresent && + o.networkLocationUsable == networkLocationUsable; + } + + @override + int get hashCode { + return hashList([ + blePresent, + bleUsable, + gpsPresent, + gpsUsable, + locationPresent, + locationUsable, + networkLocationPresent, + networkLocationUsable, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/navigation_request.dart b/hms-plugins/flutter-hms-location/lib/location/navigation_request.dart new file mode 100644 index 00000000..d929ec2b --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/navigation_request.dart @@ -0,0 +1,82 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; + +class NavigationRequest { + static const int OVERPASS = 1; + static const int IS_SUPPORT_EX = 2; + + int type; + Map extras; + + NavigationRequest({ + this.type, + this.extras, + }); + + void putExtras(String key, String value) { + if (extras == null) { + extras = Map(); + } + extras.putIfAbsent(key, () => value); + } + + Map toMap() { + return { + 'type': type, + 'extras': extras, + }; + } + + factory NavigationRequest.fromMap(Map map) { + if (map == null) return null; + + return NavigationRequest( + type: map['type'], + extras: Map.from(map['extras']), + ); + } + + String toJson() => json.encode(toMap()); + + factory NavigationRequest.fromJson(String source) => + NavigationRequest.fromMap(json.decode(source)); + + @override + String toString() { + return 'NavigationRequest(type: $type, extras: $extras)'; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + return o is NavigationRequest && + o.type == type && + mapEquals(o.extras, extras); + } + + @override + int get hashCode { + return hashList([ + type, + extras, + ]); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/location/navigation_result.dart b/hms-plugins/flutter-hms-location/lib/location/navigation_result.dart new file mode 100644 index 00000000..71d3a177 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/location/navigation_result.dart @@ -0,0 +1,53 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:convert'; + +class NavigationResult { + int possibility; + int state; + + NavigationResult({ + this.possibility, + this.state, + }); + + Map toMap() { + return { + 'state': state, + 'possibility': possibility, + }; + } + + factory NavigationResult.fromMap(Map map) { + if (map == null) return null; + + return NavigationResult( + state: map["state"] == null ? null : map["state"], + possibility: map["possibility"] == null ? null : map["possibility"], + ); + } + + String toJson() => json.encode(toMap()); + + factory NavigationResult.fromJson(String source) => + NavigationResult.fromMap(json.decode(source)); + + @override + String toString() { + return 'State: $state, Possibility: $possibility'; + } +} diff --git a/hms-plugins/flutter-hms-location/lib/logger/hmslogger.dart b/hms-plugins/flutter-hms-location/lib/logger/hmslogger.dart new file mode 100644 index 00000000..f35113e6 --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/logger/hmslogger.dart @@ -0,0 +1,30 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'package:flutter/services.dart'; + +class HMSLogger { + static const MethodChannel _methodChannel = + MethodChannel("com.huawei.flutter.location/hmslogger_methodchannel"); + + static Future enableLogger() async { + return _methodChannel.invokeMethod('enableLogger'); + } + + static Future disableLogger() async { + return _methodChannel.invokeMethod('disableLogger'); + } +} diff --git a/hms-plugins/flutter-hms-location/lib/permission/permission_handler.dart b/hms-plugins/flutter-hms-location/lib/permission/permission_handler.dart new file mode 100644 index 00000000..865234fb --- /dev/null +++ b/hms-plugins/flutter-hms-location/lib/permission/permission_handler.dart @@ -0,0 +1,63 @@ +/* + Copyright 2020. Huawei Technologies Co., Ltd. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'package:flutter/services.dart'; + +class PermissionHandler { + static PermissionHandler _instance; + + final MethodChannel _methodChannel; + + PermissionHandler._create( + this._methodChannel, + ); + + factory PermissionHandler() { + if (_instance == null) { + final MethodChannel methodChannel = const MethodChannel( + 'com.huawei.flutter.location/permission_methodchannel'); + _instance = PermissionHandler._create(methodChannel); + } + return _instance; + } + + Future hasLocationPermission() async { + return _methodChannel.invokeMethod('hasLocationPermission'); + } + + Future hasBackgroundLocationPermission() async { + return _methodChannel.invokeMethod('hasBackgroundLocationPermission'); + } + + Future hasActivityRecognitionPermission() async { + return _methodChannel + .invokeMethod('hasActivityRecognitionPermission'); + } + + Future requestLocationPermission() async { + return _methodChannel.invokeMethod('requestLocationPermission'); + } + + Future requestBackgroundLocationPermission() async { + return _methodChannel + .invokeMethod('requestBackgroundLocationPermission'); + } + + Future requestActivityRecognitionPermission() async { + return _methodChannel + .invokeMethod('requestActivityRecognitionPermission'); + } +} diff --git a/hms-plugins/flutter-hms-location/pubspec.yaml b/hms-plugins/flutter-hms-location/pubspec.yaml new file mode 100644 index 00000000..74c68492 --- /dev/null +++ b/hms-plugins/flutter-hms-location/pubspec.yaml @@ -0,0 +1,24 @@ +name: huawei_location +description: HUAWEI Flutter Location Kit plugin combines the GPS, Wi-Fi and base station locations to help you quickly obtain precise user locations & build up global positioning capabilities. +version: 5.0.0+301 +homepage: https://www.huawei.com +repository: https://github.com/HMS-Core/hms-flutter-plugin/tree/master/flutter-hms-location + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.13+hotfix.6 <2.0.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + plugin: + platforms: + android: + package: com.huawei.hms.flutter.location + pluginClass: LocationPlugin diff --git a/lib/pages/TestPage.dart b/lib/pages/TestPage.dart new file mode 100644 index 00000000..8ec70237 --- /dev/null +++ b/lib/pages/TestPage.dart @@ -0,0 +1,41 @@ +import 'package:diplomaticquarterapp/uitl/location_util.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; +import 'package:flutter/cupertino.dart'; + +class TestPage extends StatefulWidget{ + @override + State createState() => TestPageState(); + +} + +class TestPageState extends State{ + bool isGMS = false; + bool isHMS = false; + + @override + void initState() { + super.initState(); + // openStore(); + location(); + } + + + + openStore() async{ + openAppStore(androidPackageName: "com.ejada.hmg", iOSAppID: "733503978"); + } + + void location(){ + LocationUtils().getCurrentLocation(callBack: (latLng){ + debugPrint(latLng.toString()); + }); + } + + + @override + Widget build(BuildContext context) { + return Container( + ); + } + +} \ No newline at end of file diff --git a/lib/routes.dart b/lib/routes.dart index 738a00e9..4f1f2364 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -2,6 +2,7 @@ import 'package:diplomaticquarterapp/pages/AlHabibMedicalService/health-weather/ import 'package:diplomaticquarterapp/pages/DrawerPages/family/add-family-member.dart'; import 'package:diplomaticquarterapp/pages/DrawerPages/family/add-family_type.dart'; import 'package:diplomaticquarterapp/pages/DrawerPages/family/my-family.dart'; +import 'package:diplomaticquarterapp/pages/TestPage.dart'; import 'package:diplomaticquarterapp/pages/appUpdatePage/app_update_page.dart'; import 'package:diplomaticquarterapp/pages/landing/landing_page.dart'; import 'package:diplomaticquarterapp/pages/livecare/livecare_home.dart'; @@ -43,6 +44,7 @@ const String SETTINGS = 'settings'; const String PACKAGES_OFFERS = 'packages-offers'; const String PACKAGES_OFFERS_CART = 'packages-offers-cart'; const String PACKAGES_ORDER_COMPLETED = 'packages-offers-cart'; +const String TEST_PAGE = 'test-page'; const String HEALTH_WEATHER = 'health-weather'; const APP_UPDATE = 'app-update'; @@ -67,6 +69,7 @@ var routes = { PACKAGES_OFFERS: (_) => PackagesHomePage(), PACKAGES_OFFERS_CART: (_) => PackagesCartPage(), PACKAGES_ORDER_COMPLETED: (_) => PackageOrderCompletedPage(), + TEST_PAGE: (_) => TestPage(), HEALTH_WEATHER: (_) => HealthWeatherIndicator(), APP_UPDATE: (_) => AppUpdatePage(), SETTINGS: (_) => Settings(), diff --git a/lib/uitl/location_util.dart b/lib/uitl/location_util.dart index c3799425..e2edb2a1 100644 --- a/lib/uitl/location_util.dart +++ b/lib/uitl/location_util.dart @@ -1,11 +1,26 @@ +import 'dart:io'; + +import 'package:connectivity/connectivity.dart'; import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/widgets/dialogs/confirm_dialog.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter_hms_gms_availability/flutter_hms_gms_availability.dart'; import 'package:geolocator/geolocator.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:huawei_location/permission/permission_handler.dart'; +import 'package:huawei_location/location/fused_location_provider_client.dart'; +import 'package:huawei_location/location/location_request.dart'; +import 'package:huawei_location/location/location_settings_request.dart'; +import 'package:huawei_location/location/location_callback.dart'; +import 'package:huawei_location/location/location.dart'; +import 'package:huawei_location/location/location_settings_states.dart'; +import 'package:huawei_location/location/location_availability.dart'; +import 'package:permission_handler/permission_handler.dart'; class LocationUtils { + AppSharedPreferences sharedPref = new AppSharedPreferences(); bool isShowConfirmDialog; @@ -13,31 +28,86 @@ class LocationUtils { LocationUtils({@required this.isShowConfirmDialog, @required this.context}); - void getCurrentLocation() async { + void getCurrentLocation({Function(LatLng) callBack}) async { print("current location"); - Geolocator.isLocationServiceEnabled().then((value) { - if (value) { - Geolocator.checkPermission().then((permission) { - if (permission == LocationPermission.always || - permission == LocationPermission.whileInUse) { - Geolocator.getLastKnownPosition() - .then((value) => setLocation(value)); - } - - if (permission == LocationPermission.denied || - permission == LocationPermission.deniedForever) { - setZeroLocation(); - if (isShowConfirmDialog) showErrorLocationDialog(false); - } - }).catchError((err) { - print(err); - }); - } else { - if (isShowConfirmDialog) showErrorLocationDialog(false); - } - }).catchError((err) { - print(err); - }); + if(Platform.isAndroid && (await FlutterHmsGmsAvailability.isHmsAvailable)) { + _getHMSCurrentLocation(callBack); + }else{ + Geolocator.isLocationServiceEnabled().then((value) { + if (value) { + Geolocator.checkPermission().then((permission) { + if (permission == LocationPermission.always || + permission == LocationPermission.whileInUse) { + Geolocator.getLastKnownPosition() + .then((value){ + setLocation(value); + if(callBack != null) + callBack(LatLng(value.latitude, value.longitude)); + }); + } + + if (permission == LocationPermission.denied || + permission == LocationPermission.deniedForever) { + setZeroLocation(); + if (isShowConfirmDialog) showErrorLocationDialog(false); + } + }).catchError((err) { + print(err); + }); + } else { + if (isShowConfirmDialog) showErrorLocationDialog(false); + } + }).catchError((err) { + print(err); + }); + } + } + + LocationCallback _locationCallback; + _getHMSCurrentLocation(Function(LatLng) callBack) async{ + PermissionHandler permissionHandler = PermissionHandler(); + + int _locationUpdateCbId = 0; + doIt(){ + FusedLocationProviderClient locationService = FusedLocationProviderClient(); + LocationRequest locationRequest = LocationRequest(); + locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY; + locationRequest.interval = 1000; + List locationRequestList = [locationRequest]; + LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); + + locationService.checkLocationSettings(locationSettingsRequest).then((settings) async{ + + _locationUpdateCbId = await locationService.requestLocationUpdatesCb(locationRequest, LocationCallback(onLocationResult: (locationResult){ + Location location = locationResult.lastLocation; + locationService.removeLocationUpdatesCb(_locationUpdateCbId); + callBack(LatLng(location.latitude, location.longitude)); + setLocation(Position(latitude: location.latitude, longitude: location.longitude, altitude: location.altitude)); + }, onLocationAvailability: (locationAvailability){ + debugPrint("onLocationAvailability: $locationAvailability"); + })); + + }).catchError((error){ + + if(error.code == "LOCATION_SETTINGS_NOT_AVAILABLE"){ + // Location service not enabled. + } + + }); + + } + + + if(await permissionHandler.hasLocationPermission()){ + doIt(); + }else{ + bool has = await requestPermissions(); + if(has) + doIt(); + else if(isShowConfirmDialog) + showErrorLocationDialog(false); + + } } showErrorLocationDialog(bool isPermissionError) { @@ -70,4 +140,12 @@ class LocationUtils { this.sharedPref.setDouble(USER_LAT, 0.0); this.sharedPref.setDouble(USER_LONG, 0.0); } + + + Future requestPermissions() async { + var result = await [ + Permission.location, + ].request(); + return (result[Permission.location].isGranted || result[Permission.locationAlways].isGranted); + } } diff --git a/lib/uitl/utils.dart b/lib/uitl/utils.dart index de659eb4..67e5c6b9 100644 --- a/lib/uitl/utils.dart +++ b/lib/uitl/utils.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:core'; +import 'dart:io'; import 'dart:typed_data'; import 'package:badges/badges.dart'; @@ -36,7 +37,7 @@ import 'package:diplomaticquarterapp/widgets/dialogs/alert_dialog.dart'; import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_hms_gms_availability/flutter_hms_gms_availability.dart'; import 'package:url_launcher/url_launcher.dart'; import '../Constants.dart'; import 'app_shared_preferences.dart'; @@ -601,6 +602,20 @@ extension IndexedIterable on Iterable { return map((e) => f(e, i++)); } } + +openAppStore({String androidPackageName, String iOSAppID}) async{ + if(Platform.isAndroid){ + assert(!(androidPackageName == null), "Should have valid value in androidPackageName parameter"); + if((await FlutterHmsGmsAvailability.isGmsAvailable)) + launch("market://details?id=com.ejada.hmg"); + if((await FlutterHmsGmsAvailability.isHmsAvailable)) + launch("appmarket://details?id=com.ejada.hmg"); + + }else if(Platform.isIOS){ + assert((iOSAppID == null), "Should have valid value in iOSAppID parameter"); + launch("https://itunes.apple.com/kr/app/apple-store/$iOSAppID)"); + } +} /* userBoard.asMap().map((i, element) => MapEntry(i, Stack( GestureDetector(onTap: () { diff --git a/pubspec.yaml b/pubspec.yaml index 80cd9941..34ac3656 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -182,6 +182,10 @@ dependencies: carousel_slider: ^2.3.1 flutter_material_pickers: 1.7.4 flutter_staggered_grid_view: 0.3.4 + flutter_hms_gms_availability: ^1.0.0 + huawei_location: + path: ./hms-plugins/flutter-hms-location + # Marker Animation flutter_animarker: ^1.0.0