{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
I wanted to explain Huawei Map Kit with an app so I coded a sample Android app which name is ISS Detector. Users can instantly track the location of the ISS (International Space Station) on the map via Huawei Map Kit.
The main purpose of this article is to show how to use Huawei Map Kit, marker display operations on Huawei MapView, and polyline drawing operations.
So I will not mention parts such as MVP, binding and UI.
About the data?
used open-notify API for getting location information. You can reach the relevant API page from here.
ISS Detector Code Review
The application consists of a single Activity, this Activity has the MapView component via Huawei Map Kit. Also, I used the View Binding structure because it provides ease of access to the components.
I think it will be easier if I summarize the file structure.
data: There are response classes from the API.
service: The package containing the classes required for Retrofit2.
ui.main: UI-related classes are located here. MainContract and MainPresenter classes are included in this package because I use MVP Pattern.
util: Utility classes.
Create Project & HMS Integrations
First of all, you must integrate HMS into the project. I am not going to explain these steps You can check this article.
Code:
dependencies {
.
.
// Huawei Map Kit
implementation 'com.huawei.hms:maps:5.0.0.300'
.
.
}
After adding this implementation line to build.gradle file, we can now start using Huawei Map Kit.
Code:
<manifest ...>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
...
</manifest>
We have defined the necessary permissions in the AndroidManifest file.
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainActivity">
<com.huawei.hms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraZoom="1"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_margin="14dp"
app:cardCornerRadius="2dp"
app:cardElevation="4dp">
<CheckBox
android:id="@+id/checkBoxTrack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:checked="true"
android:text="Track ISS" />
</androidx.cardview.widget.CardView>
</RelativeLayout>
In activity_main.xml, the MapView component offers a full-screen map. Users can follow the ISS on the map, as well as go to the places they want freely while the ISS data continues to arrive.
Code:
public class MainActivity extends AppCompatActivity implements MainContract.View, OnMapReadyCallback {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
presenter = new MainPresenter(this);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(Constant.MAP_BUNDLE);
}
initHuaweiMap(mapViewBundle);
}
@Override
public void onMapReady(HuaweiMap huaweiMap) {
Log.w(Constant.TAG, "onMapReady : ");
this.huaweiMap = huaweiMap;
presenter.mapReady();
}
@Override
public void initHuaweiMap(Bundle bundle) {
Log.w(Constant.TAG, "initHuaweiMap : ");
MapsInitializer.setApiKey(Constant.MAP_KEY);
binding.mapView.onCreate(bundle);
binding.mapView.getMapAsync(this);
}
...
}
We call the initHuaweiMap() function in onCreate. When the process is completed asynchronously with the binding.mapView.getMapAsync(this) line, the override onMapReady(HuaweiMap huaweiMap) function is triggered via OnMapReadyCallback, which we implement to MainActivity.
As a result of these operations, the map is now available and the presenter.mapReady() function is called.
We’ll come back to the MainActivity class later, but let’s go to explain the Presenter class.
Code:
public class MainPresenter implements MainContract.Presenter {
...
@Override
public void mapReady() {
this.getISSLocation();
...
}
@Override
public void getISSLocation() {
Call<ResponseISSLocation> call = request.getISSLocation();
call.enqueue(new Callback<ResponseISSLocation>() {
@Override
public void onResponse(Call<ResponseISSLocation> call, Response<ResponseISSLocation> response) {
if (response.isSuccessful()) {
LatLng currentLatLng = response.body().getIssPosition().getLocationAsLatLng();
Log.w(Constant.TAG, "getISSLocation : " + currentLatLng.toString());
view.setMarker(currentLatLng);
view.drawRoute(currentLatLng);
if (isChecked)
view.moveCamera(currentLatLng);
waitAndCallRequest();
}
}
@Override
public void onFailure(Call<ResponseISSLocation> call, Throwable t) {
Log.w(Constant.TAG, "getISSLocation - onFailure : " + t.getMessage());
view.showErrorMessage(t.getMessage());
}
});
}
@Override
public void waitAndCallRequest() {
Log.d(Constant.TAG, "waitAndCallRequest ");
new android.os.Handler().postDelayed(() -> getISSLocation(), 2000
);
}
}
The getISSLocation() function is calling in mapReady() for the first time for the ISS current location.
We are going to call setMarker(), drawRoute() and moveCamera() functions in the getISSLocation() function. Finally, the waitAndCallRequest() function is calling so that we can resend the same request every 2 seconds and get new location data.
Now let’s come to the setMarker(), drawRoute(), and moveCamera() functions, which is the main purpose of the article.
Code:
@Override
public void setMarker(LatLng latLng) {
Log.w(Constant.TAG, "setMarker ");
if (marker != null)
marker.remove();
MarkerOptions options = new MarkerOptions()
.position(latLng)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_iss));
marker = huaweiMap.addMarker(options);
}
In the setMarker() function, we first check the marker, if there is a marker on the map, firstly delete this marker with the marker.remove() line. Next, we create a MarkerOptions object, here we set the position of the marker with .position(), the marker icon with .icon(), and finally, we show the marker created with the huaweiMap.addMarker(options) line on the map.
Code:
@Override
public void drawRoute(LatLng latLng) {
if (huaweiMap == null) return;
if (polyline == null) {
polyline = huaweiMap.addPolyline(new PolylineOptions()
.add(latLng)
.color(getColor(R.color.colorAccent))
.width(3));
polylineList = new ArrayList<>();
polylineList.add(latLng);
} else {
polylineList.add(latLng);
polyline.setPoints(polylineList);
}
}
We have to check if the huaweiMap is null in drawRoute() function, if the polyline object is null -the application will enter the if block when it first opens because our polyline object is still null- we set the polyline object to huaweiMap with the huaweiMap.addPolyline(new PolylineOptions()) line. We set the location data with.add(), we set the polyline color with .color(), we set the polyline width .width(). Since the polyline will not consist of a single point, we create a polylineList of type ArrayList and add the location data to this list. Let’s come to the else block, we add the location here to the list and use the polyline.setPoints() function to update the route on the map.
Code:
@Override
public void moveCamera(LatLng latLng) {
Log.w(Constant.TAG, "moveCamera ");
huaweiMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, huaweiMap.getCameraPosition().zoom));
}
moveCamera() is the last function called in these steps. We send the latLng variable returned from the request and the zoom level value we get with huaweiMap.getCameraPosition().zoom to the animateCamera() function, and as a result of this line, our map moves to the new location.
Tips & Tricks
MAP_KEY value generated from AGC for Huawei Mobile Services.
Also, you can access the source codes of the application from the Github and Huawei AppGallery links below.
Conclusion
In this article, I tried to explain how to use Huawei Map Kit, marker display operations on Huawei MapView, and polyline drawing operations. I hope it was a useful article for everyone. Thank you for taking the time to read.
References
App Gallery
Source Code
Huawei Map Kit
Original Source
Related
More information like this, you can visit HUAWEI Developer Forum
A geofence is a virtual perimeter set on a real geographic area. Combining a user position with a geofence perimeter, it is possible to know if the user is inside the geofence or if he is exiting or entering the area.
In this article, we will discuss how to use the geofence to notify the user when the device enters/exits an area using the HMS Location Kit in a Xamarin.Android application. We will also add and customize HuaweiMap, which includes drawing circles, adding pointers, and using nearby searches in search places. We are going to learn how to use the below features together:
Geofence
Reverse Geocode
HuaweiMap
Nearby Search
Project Setup
First of all, you need to be a registered Huawei Mobile Developer and create an application in Huawei App Console in order to use HMS Map Location and Site Kits. You can follow these steps to complete the configuration that required for development:
Configuring App Information in AppGallery Connect
Creating Xamarin Android Binding Libraries
Integrating the HMS Map Kit Libraries for Xamarin
Integrating the HMS Location Kit Libraries for Xamarin
Integrating the HMS Site Kit Libraries for Xamarin
Integrating the HMS Core SDK
Setting Package in Xamarin
When we create our Xamarin.Android application in the above steps, we need to make sure that the package name is the same as we entered the Console. Also, don’t forget the enable them in Console.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Manifest & Permissions
We have to update the application’s manifest file by declaring permissions that we need as shown below.
Code:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Also, add a meta-data element to embed your app id in the application tag, it is required for this app to authenticate on the Huawei’s cloud server. You can find this id in agconnect-services.json file.
Code:
<meta-data android:name="com.huawei.hms.client.appid" android:value="appid=YOUR_APP_ID" />
Request location permission
Request runtime permissions in our app in order to use Location and Map Services. The following code checks whether the user has granted the required location permissions in Main Activity.
Code:
private void RequestPermissions()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessCoarseLocation) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.AccessFineLocation) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.Internet) != (int)Permission.Granted)
{
ActivityCompat.RequestPermissions(this,
new System.String[]
{
Manifest.Permission.AccessCoarseLocation,
Manifest.Permission.AccessFineLocation,
Manifest.Permission.WriteExternalStorage,
Manifest.Permission.ReadExternalStorage,
Manifest.Permission.Internet
},
100);
}
else
GetCurrentPosition();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
if (requestCode == 100)
{
foreach (var item in permissions)
{
if (ContextCompat.CheckSelfPermission(this, item) == Permission.Denied)
{
if (ActivityCompat.ShouldShowRequestPermissionRationale(this, permissions[0]) || ActivityCompat.ShouldShowRequestPermissionRationale(this, permissions[1]))
Snackbar.Make(FindViewById<RelativeLayout>(Resource.Id.mainLayout), "You need to grant permission to use location services.", Snackbar.LengthLong).SetAction("Ask again", v => RequestPermissions()).Show();
else
Toast.MakeText(this, "You need to grant location permissions in settings.", ToastLength.Long).Show();
}
else
GetCurrentPosition();
}
}
else
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Add a Map
Within our UI, a map will be represented by either a MapFragment or MapView object. We will use the MapFragment object in this sample.
Add a <fragment> element to your activity’s layout file, activity_main.xml. This element defines a MapFragment to act as a container for the map and to provide access to the HuaweiMap object.
Also, let’s add other controls to use through this sample. That is two Button and a SeekBar. One button for clearing the map and the other for searching nearby locations. And seekbar is helping us to create a radius for the geofence.
Code:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/mapfragment"
class="com.huawei.hms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<Button
android:text="Get Geofence List"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:padding="5dp"
android:background="@drawable/abc_btn_colored_material"
android:textColor="@android:color/white"
android:id="@+id/btnGetGeofenceList" />
<Button
android:text="Clear Map"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/abc_btn_colored_material"
android:textColor="@android:color/white"
android:id="@+id/btnClearMap" />
</LinearLayout>
<SeekBar
android:visibility="invisible"
android:min="30"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/radiusBar" />
</RelativeLayout>
In our activity’s OnCreate method, set the layout file as the content view, load AGConnectService, set button’s click events, and initialize FusedLocationProviderClient. Get a handle to the map fragment by calling FragmentManager.FindFragmentById. Then use GetMapAsync to register for the map callback.
Also, implement the IOnMapReadyCallback interface to MainActivity and override OnMapReady method which is triggered when the map is ready to use.
Code:
public class MainActivity : AppCompatActivity, IOnMapReadyCallback
{
MapFragment mapFragment;
HuaweiMap hMap;
Marker marker;
Circle circle;
SeekBar radiusBar;
FusedLocationProviderClient fusedLocationProviderClient;
GeofenceModel selectedCoordinates;
List<Marker> searchMarkers;
private View search_view;
private AlertDialog alert;
public static LatLng CurrentPosition { get; set; }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(ApplicationContext);
fusedLocationProviderClient = LocationServices.GetFusedLocationProviderClient(this);
mapFragment = (MapFragment)FragmentManager.FindFragmentById(Resource.Id.mapfragment);
mapFragment.GetMapAsync(this);
FindViewById<Button>(Resource.Id.btnGeoWithAddress).Click += btnGeoWithAddress_Click;
FindViewById<Button>(Resource.Id.btnClearMap).Click += btnClearMap_Click;
radiusBar = FindViewById<SeekBar>(Resource.Id.radiusBar);
radiusBar.ProgressChanged += radiusBar_ProgressChanged; ;
RequestPermissions();
}
public void OnMapReady(HuaweiMap map)
{
hMap = map;
hMap.UiSettings.MyLocationButtonEnabled = true;
hMap.UiSettings.CompassEnabled = true;
hMap.UiSettings.ZoomControlsEnabled = true;
hMap.UiSettings.ZoomGesturesEnabled = true;
hMap.MyLocationEnabled = true;
hMap.MapClick += HMap_MapClick;
if (selectedCoordinates == null)
selectedCoordinates = new GeofenceModel { LatLng = CurrentPosition, Radius = 30 };
}
}
As you can see above, with the UiSettings property of the HuaweiMap object we set my location button, enable compass, etc. Other properties like below:
Code:
public bool CompassEnabled { get; set; }
public bool IndoorLevelPickerEnabled { get; set; }
public bool MapToolbarEnabled { get; set; }
public bool MyLocationButtonEnabled { get; set; }
public bool RotateGesturesEnabled { get; set; }
public bool ScrollGesturesEnabled { get; set; }
public bool ScrollGesturesEnabledDuringRotateOrZoom { get; set; }
public bool TiltGesturesEnabled { get; set; }
public bool ZoomControlsEnabled { get; set; }
public bool ZoomGesturesEnabled { get; set; }
Now when the app launch, directly get the current location and move the camera to it. In order to do that we use FusedLocationProviderClient that we instantiated and call LastLocation API.
LastLocation API returns a Task object that we can check the result by implementing the relevant listeners for success and failure.In success listener we are going to move the map’s camera position to the last known position.
Code:
private void GetCurrentPosition()
{
var locationTask = fusedLocationProviderClient.LastLocation;
locationTask.AddOnSuccessListener(new LastLocationSuccess(this));
locationTask.AddOnFailureListener(new LastLocationFail(this));
}
...
public class LastLocationSuccess : Java.Lang.Object, IOnSuccessListener
{
private MainActivity mainActivity;
public LastLocationSuccess(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
public void OnSuccess(Java.Lang.Object location)
{
Toast.MakeText(mainActivity, "LastLocation request successful", ToastLength.Long).Show();
if (location != null)
{
MainActivity.CurrentPosition = new LatLng((location as Location).Latitude, (location as Location).Longitude);
mainActivity.RepositionMapCamera((location as Location).Latitude, (location as Location).Longitude);
}
}
}
To change the position of the camera, we must specify where we want to move the camera, using a CameraUpdate. The Map Kit allows us to create many different types of CameraUpdate using CameraUpdateFactory.
Code:
public static CameraUpdate NewCameraPosition(CameraPosition p0);
public static CameraUpdate NewLatLng(LatLng p0);
public static CameraUpdate NewLatLngBounds(LatLngBounds p0, int p1);
public static CameraUpdate NewLatLngBounds(LatLngBounds p0, int p1, int p2, int p3);
public static CameraUpdate NewLatLngZoom(LatLng p0, float p1);
public static CameraUpdate ScrollBy(float p0, float p1);
public static CameraUpdate ZoomBy(float p0);
public static CameraUpdate ZoomBy(float p0, Point p1);
public static CameraUpdate ZoomIn();
public static CameraUpdate ZoomOut();
public static CameraUpdate ZoomTo(float p0);
There are some methods for the camera position changes as we see above. Simply these are:
1. NewLatLng: Change camera’s latitude and longitude, while keeping other properties
2. NewLatLngZoom: Changes the camera’s latitude, longitude, and zoom, while keeping other properties
3. NewCameraPosition: Full flexibility in changing the camera position
We are going to use NewCameraPosition. A CameraPosition can be obtained with a CameraPosition.Builder. And then we can set target, bearing, tilt and zoom properties.
Code:
public void RepositionMapCamera(double lat, double lng)
{
var cameraPosition = new CameraPosition.Builder();
cameraPosition.Target(new LatLng(lat, lng));
cameraPosition.Zoom(1000);
cameraPosition.Bearing(45);
cameraPosition.Tilt(20);
CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition.Build());
hMap.MoveCamera(cameraUpdate);
}
Creating Geofence
Now that we’ve created the map, we can now start to create geofences using it. In this article, we will choose the location where we want to set geofence in two different ways. The first is to select the location by clicking on the map, and the second is to search for nearby places by keyword and select one after placing them on the map with the marker.
Set the geofence location by clicking on the map
It is always easier to select a location by seeing it. After this section, we are able to set a geofence around the clicked point when the map’s clicked. We attached the Click event to our map in the OnMapReady method. In this Click event, we will add a marker to the clicked point and draw a circle around it.
After clicking the map, we will add a circle, a marker, and a custom info window to that point like this:
Also, we will use the Seekbar at the bottom of the page to adjust the circle radius.
We set selectedCoordinates variable when adding the marker. Let’s create the following method to create the marker:
Code:
private void HMap_MapClick(object sender, HuaweiMap.MapClickEventArgs e)
{
selectedCoordinates.LatLng = e.P0;
if (circle != null)
{
circle.Remove();
circle = null;
}
AddMarkerOnMap();
}
void AddMarkerOnMap()
{
if (marker != null) marker.Remove();
var markerOption = new MarkerOptions()
.InvokeTitle("You are here now")
.InvokePosition(selectedCoordinates.LatLng);
hMap.SetInfoWindowAdapter(new MapInfoWindowAdapter(this));
marker = hMap.AddMarker(markerOption);
bool isInfoWindowShown = marker.IsInfoWindowShown;
if (isInfoWindowShown)
marker.HideInfoWindow();
else
marker.ShowInfoWindow();
}
With MarkerOptions we can set the title and position properties. And for creating a custom info window, there is SetInfoWindowAdapter method. Adding MapInfoWindowAdapter class to our project for rendering the custom info model. And implement HuaweiMap.IInfoWindowAdapter interface to it.
This interface provides a custom information window view of a marker and contains two methods:
Code:
View GetInfoContents(Marker marker);
View GetInfoWindow(Marker marker);
When an information window needs to be displayed for a marker, methods provided by this adapter are called in any case.
Now let’s create a custom info window layout and named it as map_info_view.xml
Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="Add geofence"
android:width="100dp"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnInfoWindow" />
</LinearLayout>
And return it after customizing it in GetInfoWindow() method. The full code of the adapter is below:
Code:
internal class MapInfoWindowAdapter : Java.Lang.Object, HuaweiMap.IInfoWindowAdapter
{
private MainActivity activity;
private GeofenceModel selectedCoordinates;
private View addressLayout;
public MapInfoWindowAdapter(MainActivity currentActivity)
{
activity = currentActivity;
}
public View GetInfoContents(Marker marker)
{
return null;
}
public View GetInfoWindow(Marker marker)
{
if (marker == null)
return null;
//update everytime, drawcircle need it
selectedCoordinates = new GeofenceModel { LatLng = new LatLng(marker.Position.Latitude, marker.Position.Longitude) };
View mapInfoView = activity.LayoutInflater.Inflate(Resource.Layout.map_info_view, null);
var radiusBar = activity.FindViewById<SeekBar>(Resource.Id.radiusBar);
if (radiusBar.Visibility == Android.Views.ViewStates.Invisible)
{
radiusBar.Visibility = Android.Views.ViewStates.Visible;
radiusBar.SetProgress(30, true);
}
activity.FindViewById<SeekBar>(Resource.Id.radiusBar)?.SetProgress(30, true);
activity.DrawCircleOnMap(selectedCoordinates);
Button button = mapInfoView.FindViewById<Button>(Resource.Id.btnInfoWindow);
button.Click += btnInfoWindow_ClickAsync;
return mapInfoView;
}
}
This is not the end. For full content, you can visit https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201357111605920240&fid=0101187876626530001
Besides the usual offerings of flowers and handbags, what other ways are there to profess your love for your girlfriend?
John, a programmer at Huawei, provides us with a novel answer. John is currently on a business trip in France and wanted to do something different to show his appreciation for his girlfriend, who is far away in China, on March 8th – International Women's Day.
Looking out of his hotel window at the Eiffel Tower, an idea struck John's mind: What if I make an exercise tracking app to express my feelings for her? He shared the fruits of his quick labor with his girlfriend, who saw the following image when she opened the app:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
On March 8th, we present you with this special tutorial on how to use HUAWEI Location Kit to win the heart of that special person in your life as well as imbue your apps with powerful location services.
OverviewHUAWEI Location Kit can combine the GNSS, Wi-Fi, and base station positioning capabilities into your app, allowing you to provide flexible location-based services for users around the world. We also provide HUAWEI Map Kit, which is an SDK for map development that includes map data for more than 200 countries and regions across the globe, and supports over 100 languages. With this SDK, you can display your user's exercise routes on a map in real time through the use of various map display tools.
Besides being a creative way of expressing your feeling for someone, exercise tracking can be applied to a wide range of scenarios. For example, it provides health and fitness apps with location-based services, such as recording exercise routes, displaying past exercise routes, and calculating distance traveled, so that users can track how much exercise they've done and calculate how many calories they've burned.
Development Preparations1. Create an app in AppGallery Connect and configure the signing certificate fingerprint.
2. Configure the Maven repository address and add the following build dependencies to the build.gradle file in the app directory.
Code:
dependencies {
implementation 'com.huawei.hms:location: 5.1.0.301'
implementation 'com.huawei.hms:maps: 5.1.0.300'
}
3. Configure obfuscation scripts.
For details about the preceding steps, please refer to the Location Kit Development Guide on the HUAWEI Developers website.
4. Declare system permissions in the AndroidManifest.xml file.
Location Kit incorporates GNSS, Wi-Fi, and base station positioning capabilities into your app so that you can provide precise global positioning services for your users. In order to do this, it requires the network permission, precise location permission, and coarse location permission. If you want the app to continuously obtain user locations when running in the background, you also need to declare the ACCESS_BACKGROUND_LOCATION permission in the AndroidManifest.xml file.
XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
Development Procedure1. Displaying the MapCurrently, the HMS Core Map SDK supports two map containers: SupportMapFragment and MapView. This article uses SupportMapFragment as an example.
(1) Add a Fragment object in the layout file (for example, activity_main.xml), and set map attributes in the file.
XML:
<fragment
android:id="@+id/mapfragment_routeplanningdemo"
android:name="com.huawei.hms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
(2) To use a map in your app, implement the OnMapReadyCallback API.
RoutePlanningActivity extends AppCompatActivity implements OnMapReadyCallback
(3) In the code file (for example, MainActivity.java), load SupportMapFragment in the onCreate() method and call getMapAsync() to register the callback.
Code:
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.mapfragment_routeplanningdemo);
if (fragment instanceof SupportMapFragment) {
SupportMapFragment mSupportMapFragment = (SupportMapFragment) fragment;
mSupportMapFragment.getMapAsync(this);
}(4) Call the onMapReady callback to obtain the HuaweiMap object.
@Override
public void onMapReady(HuaweiMap huaweiMap) {
hMap = huaweiMap;
hMap.setMyLocationEnabled(true);
hMap.getUiSettings().setMyLocationButtonEnabled(true);
}
2. Implementing the Location Function(1) Check the location permission.
Code:
XXPermissions.with(this)
// Apply for multiple permissions.
.permission(Permission.Group.LOCATION)
.request(new OnPermission() {
@Override
public void hasPermission(List<String> granted, boolean all) {
if (all) {
getMyLoction();
} else{
Toast.makeText(getApplicationContext(),"The function may be unavailable if the permissions are not assigned.",Toast.LENGTH_SHORT).show();
}
}
@Override
public void noPermission(List<String> denied, boolean never) {
if (never) {
XXPermissions.startPermissionActivity(RoutePlanningActivity.this, denied);
} else {
XXPermissions.startPermissionActivity(RoutePlanningActivity.this, denied);
}
}
});
(2) Pinpoint the current location and display it on the map. You need to check whether the location permission is enabled. If not, the location data cannot be obtained.
Code:
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Check the device location settings.
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(locationSettingsResponse -> {
// Initiate location requests when the location settings meet the requirements.
fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(aVoid -> {
// Processing when the API call is successful.
Log.d(TAG, "onSuccess: " + aVoid);
});
})
.addOnFailureListener(e -> {
// Device location settings do not meet the requirements.
int statusCode = ((ApiException) e).getStatusCode();
if (statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED) {
try {
ResolvableApiException rae = (ResolvableApiException) e;
// Call startResolutionForResult to display a popup message requesting the user to enable relevant permissions.
rae.startResolutionForResult(RoutePlanningActivity.this, 0);
} catch (IntentSender.SendIntentException sie) {
sie.printStackTrace();
}
}
});
3. Drawing Routes on the Map Based on the Real-time Location
Code:
private void addPath(LatLng latLng1, LatLng latLng2) {
PolylineOptions options = new PolylineOptions().color(Color.BLUE).width(5);
List<LatLng> path = new ArrayList<>();
path.add(latLng1);
path.add(latLng2);
for (LatLng latLng : path) {
options.add(latLng);
}
Polyline polyline = hMap.addPolyline(options);
mPolylines.add(polyline);
}Upload the location results to the cloud in real time by using the route planning function of Map Kit. The routes will then be returned and displayed on the map.String mWalkingRoutePlanningURL = "https://mapapi.cloud.huawei.com/mapApi/v1/routeService/walking";
String url = mWalkingRoutePlanningURL + "?key=" + key;
Response response = null;
JSONObject origin = new JSONObject();
JSONObject destination = new JSONObject();
JSONObject json = new JSONObject();
try {
origin.put("lat", latLng1.latitude);
origin.put("lng", latLng1.longitude);
destination.put("lat", latLng2.latitude);
destination.put("lng", latLng2.longitude);
json.put("origin", origin);
json.put("destination", destination);
RequestBody requestBody = RequestBody.create(JSON, String.valueOf(json));
Request request = new Request.Builder().url(url).post(requestBody).build();
response = getNetClient().initOkHttpClient().newCall(request).execute();
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
ResultsOnce the code is compiled, an APK will be generated. Install it on your device and launch the app. Exercise tracks can now be drawn on the map based on your real-time location information.
More Information
To join in on developer discussion forums, go to Reddit.
To download the demo app and sample code, go to GitHub.
For solutions to integration-related issues, go to Stack Overflow.
Checkout in forum
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
In this article, we will develop an app for Huawei + Non-Huawei Android phones using Huawei Map Kit and Site Kit Widget. As you know previously Huawei Map could only be used in an HMS device but after the Map update version 5.1.0.300 (2020-12-31) Map Kit can be used on non-Huawei Android phones and in other scenarios where HMS Core (APK) is not required. Meanwhile, to use HMS Core in non-Huawei Android phones we will install the HMS Core App programmatically.
Huawei Map Kit:
Huawei Map kit allows can easily integrate map-based functions into your apps and make location-based services work better for you.
Huawei Site Kit:
Directing users to the location-based service they need makes your app accessible to more people. Give your users the power to explore their world.
Pre-Requisites
1. Integrate HMS Core in project.
2. Enable Scan and Map Kit from AGC Console.
3. Add agconnet-service.json file in the app level directory.
1. Add Dependencies & Permission:
1.1: Add the following dependencies in the app level build.gradle file:
Code:
dependencies {
//Map
implementation 'com.huawei.hms:maps:5.2.0.301'
//Map callback dependencies for using Huawei Map on Non-Huawei Devices
implementation 'com.huawei.hms:maproute-fallback:5.2.0.301'
implementation 'com.huawei.hms:hwmaps-fallback:5.2.0.301'
//Site
implementation 'com.huawei.hms:site:5.2.0.300'
}
1.2: Add the following permissions in the AndroidManifest.xml:
Code:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
// To programmatically allow user to install HMS Core App
<meta-data
android:name="com.huawei.hms.client.channel.androidMarket"
android:value="false" />
2. Add Layout Files:
2.1: Add the activity_map.xml layout file in the layout folder of the res. This is the layout view of the MapActivity in the application, which contains the Site Kit Widget and a Mapview.
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--Site Kit Widget-->
<fragment
android:id="@+id/widget_fragment"
android:name="com.huawei.hms.site.widget.SearchFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!--Map Kit -->
<com.huawei.hms.maps.MapView
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true"/>
</LinearLayout>
3. Add Classes
3.1: Add the MapActivity.java file in the App. This class extends AppCompayActivity and implements OnMapReadyCallback. Meanwhile, Site Fragment is added.
Code:
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = "MapViewDemoActivity";
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int REQUEST_CODE = 100;
private static final LatLng LAT_LNG = new LatLng(31.5204, 74.3587);
private HuaweiMap hmap;
private MapView mMapView;
private static final String[] RUNTIME_PERMISSIONS = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET};
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "map onCreate:");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
//Check for required Permissions
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
mMapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
AGConnectServicesConfig config = AGConnectServicesConfig.fromContext(this);
MapsInitializer.setApiKey(config.getString("client/api_key"));
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
SearchFragment fragment = (SearchFragment) getSupportFragmentManager().findFragmentById(R.id.widget_fragment);
try {
fragment.setApiKey(URLEncoder.encode(config.getString("client/api_key"), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
fragment.setOnSiteSelectedListener(new SiteSelectionListener() {
@Override
public void onSiteSelected(Site data) {
if (hmap != null) {
hmap.clear();
MarkerOptions markerOptions = new MarkerOptions()
.position(new LatLng(data.getLocation().getLat(), data.getLocation().getLng()))
.title(data.getName()).snippet(data.getFormatAddress());
hmap.addMarker(markerOptions);
hmap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(data.getLocation().getLat(), data.getLocation().getLng()), 11));
}
}
@Override
public void onError(SearchStatus status) {
Toast.makeText(getApplication(), status.getErrorCode() + "\n" + status.getErrorMessage(),
Toast.LENGTH_LONG)
.show();
}
});
}
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
public void onMapReady(HuaweiMap map) {
Log.d(TAG, "onMapReady: ");
hmap = map;
hmap.setMyLocationEnabled(true);
// move camera by CameraPosition param ,latlag and zoom params can set here
CameraPosition build = new CameraPosition.Builder().target(LAT_LNG).zoom(11).build();
CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(build);
hmap.animateCamera(cameraUpdate);
}
@Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
}
4. Application Logic:
When the app is used in an HMS phone, the Map will be loaded and the Site Kit Widget will be used to search for the places and a marker will be added to the Map. Meanwhile, when the App will be used in a Non-Huawei Android phone, the Map will work fine as two of the callback dependencies have been added in the gradle file but Site Kit widget will not work therefore an HMS Core installation popup is displayed for the user to install the HMS Core App in the phone to enable the required Huawei Mobile Services.
** For Huawei Map, HMS Core App is not required in the Non-Huawei phone (Huawei Map User location doesn't work on Non-Huawei Device).
** For Site Kit Widget, HMS Core App is required in the Non-Huawei phone.
5: Run the Application:
Once all code has been added to the project, you can run the application on any Huawei or Non-Huawei android phone.
6: Demo:
7: Tips and Tricks:
1. hmap.setMyLocationEnabled(true); doesn't work in non-Huawei Android phone therefore respective Location services will be used to get the user's current location on Map.
2. Check for the permission on runtime to load the Map.
3. Map on non-Huawei Android phone will work in Map version 5.1.0.300 and onwards.
4. Encode the API Key before setting it for Site widget Fragment using URLEncoder.encode(config.getString("client/api_key"), "UTF-8")
8: Conclusion:
Huawei Map use on Non-Huawei Android phones will reduce the support cost, development efforts, and maintenance of using two different Map services for Huawei/Non-Huawei devices.
9: References:
9.1: Map Kit: click here
9.2: Site Kit: click here
Original Source
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
Nowadays, everybody is using smartphones to do daily tasks like taking photos, looking up movie times, making calls etc. The best part of Android apps on mobile phones is that they are trying more and more to get to know their users. Many applications today take users' locations to provide users with locational feeds. One common example is a normal news app, where the app takes your current location and shows the news by location.
If you're a developer, you need to understand users better to give users a better experience of the application. You should know at any time what your users do. The more you know about your users, the better application for your users can build. For example, a distance calculator app lunches by itself when you start driving yourcar or bike and stops when you stop driving. Health and fitness app also uses this service to determine how many meters/kilometers you have covered on particular day.
What is Activity Identification Service?
Activity Identification Service does the heavy lifting using acceleration sensor, cellular network information and magnetometer from device to identify user’s current activity. Your app receives a list of detected activities, each of which includes possibility and identity properties.
The Activity Identification Service can detect following activities:
STILL: When the mobile device will be still, that is, the user is either sitting at someplace or the mobile device is having no motion, then the Activity Recognition Client will detect the STILL activity.
FOOT: When the mobile device is moving at a normal speed , that is, the user carrying the mobile device is either walking or running then the Activity Identification Service will detect the FOOT activity.
WALKING: This is a sub-activity of the FOOT activity which is detected by the Activity Identification Service when the user carrying the mobile device is walking.
RUNNING: This is also a sub-activity of FOOT activity which is detected by the Activity Recognition Client when the user carrying the mobile device is running.
VEHICLE: This activity detected when the mobile device is on the bus or car or some other kind of vehicle or the user holding the mobile device is present in the vehicle.
OTHERS: The Activity Identification service will show this result when the device is unable to detect any activity on the mobile device.
In this article, we will create a sample application to show user activity. When user clicks start button, we will identify user activity status along with possibility level and display the status in Textview and Imageview. And when user clicks on stop button, we will stop requesting activity identification updates.
Development Overview
Prerequisite
1. Must have a Huawei Developer Account.
2. Must have Android Studio 3.0 or later.
3. Must have Huawei phone running EMUI 5.0 or later.
4. EMUI 5.0 or later.
Software Requirements
1. Java SDK 1.7 or later.
2. Android 5.0 or later.
Preparation
1. Create an app or project in the Huawei App Gallery Connect.
2. Provide the SHA Key and App Package name of the project in App Information Section and enable the Location Kit API.
3. Download the agconnect-services.json file.
4. Create an Android project.
Integration
1. Add below to build.gradle (project) file under buildscript/repositories and allprojects/repositories.
Code:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath 'com.huawei.agconnect:agcp:1.4.2.300'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
2. Add below to build.gradle (app) file, under dependencies to use the Location kit SDK.
Code:
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation 'com.huawei.hms:location:5.0.5.300'
}
Tip: Minimum android version supported for these kits is 19.
3. Add below permissions to manifest file.
For version earlier than android Q
Code:
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION"/>
For version Android Q and later
Code:
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
Note: The above permissions are dangerous permission and need to be requested dynamically. Requesting permission dynamically is not covered in this article.
Development
We need to register static broadcast receiver in AndroidManifest.xmlto listen to activity status update identified by Activity Identification Service.
Code:
<receiver
android:name=".LocationReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.huawei.hmssample.location.LocationBroadcastReceiver.ACTION_PROCESS_LOCATION" />
</intent-filter>
</receiver>
Now the next step is to add the UI for our Main Activity. In our application, we will be having one TextView to display the name of the current activity and display corresponding image on ImageView and one TextView to display the possibility of Activity. We will have two Buttons to start and stop activity identification tracking. So, the activity_main.xml file looks something like this:
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FAF0E6"
tools:context=".MainActivity">
<ImageView
android:id="@+id/ivDisplay"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:scaleType="centerInside"
android:src="@drawable/ic_still" />
<TextView
android:id="@+id/tvidentity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/ivDisplay"
android:layout_marginTop="5dp"
android:textStyle="bold"
android:textColor="#192841"
android:textSize="25sp"
android:layout_centerHorizontal="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvpossiblity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tvidentity"
android:textSize="20sp"
android:textColor="#192841"
android:layout_centerHorizontal="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bStart"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="Start Tracking"
android:textColor="@color/upsdk_white"
android:background="#192841"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/bStop"
android:layout_margin="5dp"
android:layout_weight="1"
android:text="Stop Tracking"
android:textColor="@color/upsdk_white"
android:background="#192841"/>
</LinearLayout>
</RelativeLayout>
Now let’s create instance of ActivityIdentificationService in onCreate() method of MainActivity.java
Java:
private PendingIntent mPendingIntent;
private ActivityIdentificationService identificationService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); intializeTracker(); }
private void intializeTracker() {
identificationService = ActivityIdentification.getService(this);
mPendingIntent = obtainPendingIntent();
}
To obtain PendingIntent object
Java:
private PendingIntent obtainPendingIntent() {
Intent intent = new Intent(this, LocationReceiver.class);
intent.setAction(LocationReceiver.ACTION_NAME);
return PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
When user clicks on Start Tracking Button, we will request activity identification updates by calling createActivityIdentificationUpdates() method.
identificationService.createActivityIdentificationUpdates(5000, mPendingIntent)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@override
public void onSuccess(Void aVoid) {
Log.i(TAG, "createActivityIdentificationUpdates onSuccess");
}
})
// Define callback for request failure.
.addOnFailureListener(new OnFailureListener() {
@override
public void onFailure(Exception e) {
Log.e(TAG, "createActivityIdentificationUpdates onFailure:" + e.getMessage());
}
});
This method has two parameters: detectionIntervalMillis and pendingIntent, which indicate the detection interval (in milliseconds) and action to perform, respectively.
On click of Stop Tracking Button, we will stop activity identification updates.
Java:
identificationService.deleteActivityIdentificationUpdates(mPendingIntent)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "deleteActivityIdentificationUpdates onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "deleteActivityIdentificationUpdates onFailure:" + e.getMessage());
}
});
Finally, We can get activity identification result (containing identity and possibility) from intent received by the broadcast receiver.
Java:
public class LocationReceiver extends BroadcastReceiver {
public static final String ACTION_NAME = "com.huawei.hms.location.ACTION_PROCESS_LOCATION";
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_NAME.equals(action)) {
// Obtains ActivityIdentificationResponse from extras of the intent sent by the activity identification service.
ActivityIdentificationResponse activityIdentificationResponse = ActivityIdentificationResponse.getDataFromIntent(intent);
if(activityIdentificationResponse!= null) {
List<ActivityIdentificationData> list = activityIdentificationResponse.getActivityIdentificationDatas();
ActivityIdentificationData identificationData = list.get(list.size() -1);
int identificationIdentity = identificationData.getIdentificationActivity();
int possibility = identificationData.getPossibility();
Intent i = new Intent("activityIdentificationReceiver");
i.putExtra("identity", identificationIdentity);
i.putExtra("possibility", possibility);
context.sendBroadcast(i);
}
}
}
}
}
getActivityIdentificationDatas() API is used to obtain the list of activitiy identification list. The activity identifications are sorted by most probable activity first.
We have created Utils.java class to obtain activity status from identity code obtained from LocationReceiver
Java:
public class Utils {
public static String getActivityIdentityName(int code) {
switch(code) {
case ActivityIdentificationData.VEHICLE:
return "VEHICLE";
case ActivityIdentificationData.BIKE:
return "BIKE";
case ActivityIdentificationData.FOOT:
return "FOOT";
case ActivityIdentificationData.STILL:
return "STILL";
case ActivityIdentificationData.OTHERS:
return "OTHERS";
case ActivityIdentificationData.WALKING:
return "WALKING";
case ActivityIdentificationData.RUNNING:
return "RUNNING";
default:
return "No Data Available";
}
}
public static int getActivityIdentityDrawableID(int code) {
switch(code) {
case ActivityIdentificationData.VEHICLE:
return R.drawable.ic_driving;
case ActivityIdentificationData.BIKE:
return R.drawable.ic_on_bicycle;
case ActivityIdentificationData.FOOT:
return R.drawable.ic_still;
case ActivityIdentificationData.STILL:
return R.drawable.ic_still;
case ActivityIdentificationData.OTHERS:
return R.drawable.ic_unknown;
case ActivityIdentificationData.WALKING:
return R.drawable.ic_walking;
case ActivityIdentificationData.RUNNING:
return R.drawable.ic_running;
default:
return R.drawable.ic_unknown;
}
}
}
Code snippet of MainActivity.java
Java:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ActivityConversionRequest request;
private Button bStart, bStop;
private TextView tvPossiblity, tvIdentity;
private ImageView ivDisplay;
private PendingIntent mPendingIntent;
private ActivityIdentificationService identificationService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intializeTracker();
bStart = findViewById(R.id.bStart);
bStop = findViewById(R.id.bStop);
tvIdentity = findViewById(R.id.tvidentity);
tvPossiblity = findViewById(R.id.tvpossiblity);
ivDisplay = findViewById(R.id.ivDisplay);
bStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
identificationService.createActivityIdentificationUpdates(5000, mPendingIntent)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "createActivityIdentificationUpdates onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "createActivityIdentificationUpdates onFailure:" + e.getMessage());
}
});
}
});
bStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
identificationService.deleteActivityIdentificationUpdates(mPendingIntent)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "deleteActivityIdentificationUpdates onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "deleteActivityIdentificationUpdates onFailure:" + e.getMessage());
}
});
}
});
}
private void intializeTracker() {
identificationService = ActivityIdentification.getService(this);
mPendingIntent = obtainPendingIntent();
}
// Get PendingIntent associated with the custom static broadcast class LocationBroadcastReceiver.
private PendingIntent obtainPendingIntent() {
Intent intent = new Intent(this, LocationReceiver.class);
intent.setAction(LocationReceiver.ACTION_NAME);
return PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction("activityIdentificationReceiver");
registerReceiver(mIdentificationReceiver , filter);
}
@Override
protected void onPause() {
super.onPause();
try {
if(mIdentificationReceiver != null){
unregisterReceiver(mIdentificationReceiver);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private BroadcastReceiver mIdentificationReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
int possibility = intent.getIntExtra("possibility", 0);
int identity = intent.getIntExtra("identity", 103);
tvIdentity.setText(Utils.getActivityIdentityName(identity));
tvPossiblity.setText("Possibility : " + String.valueOf(possibility));
ivDisplay.setImageResource(Utils.getActivityIdentityDrawableID(identity));
}
};
}
Tips and Tricks
1.During writing of this article, the activity identification service cannot identify the cycling and riding activities on devices outside the Chinese mainland.
2. ACTIVITY_RECOGNITION is dangerous permission and should be requested dynamically.
Conclusion
In this article, we have learnt how to use the Activity Identification Service in our application to determine the activities that users are doing at any given time. The Activity Identification Service determines the ongoing activities based on a possibility value that tells you which activity is currently taking place.
Hope you found this story useful and interesting.
Happy coding!
References
https://developer.huawei.com/consum...troduction-0000001050706106-V5?ha_source=hms1
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
In this article, we will learn how to integrate Huawei Site kit, Map kit and Location kit in Android application KnowMyBoard. Account Kit provides seamless login functionality to the app with large user base.
Location kit SDK for Android offers location-related APIs for Android apps. These APIs mainly relate to 6 functions like fused location, activity identification, geofence, high-precision location, indoor location, and geocoding. This mode is applicable to mobile phones and Huawei tablets. We are using Location kit to get location of user.
Huawei Map SDK for Android is a set of APIs that can be called to develop maps. You can use this SDK to easily add map-related functions to your Android app, including map display, map interaction, map drawing, and map style customization.
Huawei Site kit provides place search services including keyword search, nearby place search, place detail search, and place search suggestion, helping your app provide convenient place-related services to attract more users and improve user loyalty. Site kit helps developers to quickly build place-based quality apps.
Following core capabilities in Site Kit to quickly build apps users can explore the world around them.
Keyword Search: Returns a place list based on keywords entered by the user.
Nearby Place Search: Searches for nearby places based on the current location of the user's device.
Place Detail Search: Searches for details about a place.
Place Search Suggestion: Returns a list of suggested places.
Autocomplete: Returns an autocomplete place and a list of suggested places based on the entered keyword.
Supported Devices
Development Overview
You need to install Android Studio IDE and I assume that you have prior knowledge of Android application development.
Hardware Requirements
A computer (desktop or laptop) running Windows 10.
Android phone (with the USB cable), which is used for debugging.
Software Requirements
Java JDK 1.8 or later.
Android Studio software or Visual Studio or Code installed.
HMS Core (APK) 4.X or later
Integration steps
Step 1. Huawei developer account and complete identity verification in Huawei developer website, refer to register Huawei ID.
Step 2. Create project in AppGallery Connect
Step 3. Adding HMS Core SDK
Let's start coding
navigation_graph.xml
[B]<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.LoginFragment"
android:label="LoginFragment"/>
<fragment
android:id="@+id/mainFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.MainFragment"
android:label="MainFragment"/>
<fragment
android:id="@+id/cameraFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.CameraFragment"
android:label="fragment_camera"
tools:layout="@layout/fragment_camera" />
<fragment
android:id="@+id/searchFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
</navigation>[/B]
bottom_navigation_menu.xml
[/B][/B]
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/searchFragment"
android:icon="@android:drawable/ic_menu_search"
android:title="@string/search" />
<item
android:id="@+id/loginFragment"
android:icon="@android:drawable/ic_menu_agenda"
android:title="Home" />
<item
android:id="@+id/mainFragment"
app:showAsAction="always"
android:icon="@android:drawable/ic_menu_gallery"
android:title="Gallery" />
<item
android:id="@+id/cameraFragment"
android:icon="@android:drawable/ic_menu_camera"
app:showAsAction="always"
android:title="Camera" />
</menu>
[B][B]
SearchFragment.java
[/B][/B][/B]
public class SearchFragment extends Fragment {
FragmentSearchBinding binding;
LoginViewModel loginViewModel;
View view;
NavController navController;
private SearchService searchService;
SitesAdapter adapter;
ArrayList<Site> siteArrayList = new ArrayList<>();
LocationResult locationResult = null;
public SearchFragment() {
// Required empty public constructor
}
ItemClickListener siteClicklistener = new ItemClickListener(){
@Override
public void onItemClicked(RecyclerView.ViewHolder vh, Site site, int pos){
getActivity().getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
loginViewModel.setSiteSelected(site);
navController.navigate(R.id.loginFragment);
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getActivity().getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
//binding = DataBindingUtil.inflate(inflater, R.layout.activity_my_search, container, false);
view = inflater.inflate(R.layout.fragment_search, container, false);
loginViewModel = new ViewModelProvider(getActivity()).get(LoginViewModel.class);
SearchView searchView = view.findViewById(R.id.ed_search);
RecyclerView recyclerView = view.findViewById(R.id.suggestion_rv);
navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
searchView.setFocusable(true);
searchView.onActionViewExpanded();
adapter = new SitesAdapter(siteArrayList, getContext(),siteClicklistener);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setHasFixedSize(true);
loginViewModel.getLocationResult().observeForever(new Observer<LocationResult>() {
@Override
public void onChanged(LocationResult locationResult1) {
locationResult = locationResult1;
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
if(newText.length() > 4){
//keywordSearch(newText);
nearByPlacesSearch(newText);
}
return false;
}
});
return view;
}
void keywordSearch(String search){
try {
String key = URLEncoder.encode(Constants.API_KEY, "UTF-8");
// Instantiate the SearchService object.
searchService = SearchServiceFactory.create(getContext(), key);
// Create a request body.
TextSearchRequest request = new TextSearchRequest();
request.setQuery(search);
if(locationResult!=null){
Coordinate location = new Coordinate(locationResult.getLastHWLocation().getLatitude(),locationResult.getLastHWLocation().getLongitude());
request.setLocation(location);
}
request.setRadius(1000);
//request.setHwPoiType(HwLocationType.HOTEL_MOTEL);
request.setCountryCode("IN");
request.setLanguage("en");
request.setPageIndex(1);
request.setPageSize(5);
request.setChildren(false);
// request.setCountries(Arrays.asList("en", "fr", "cn", "de", "ko","in"));
// Create a search result listener.
SearchResultListener<TextSearchResponse> resultListener = new SearchResultListener<TextSearchResponse>() {
// Return search results upon a successful search.
@Override
public void onSearchResult(TextSearchResponse results) {
if (results == null || results.getTotalCount() <= 0) {
return;
}
List<Site> sites = results.getSites();
if(sites == null || sites.size() == 0){
return;
}
siteArrayList.clear();
for (Site site : sites) {
siteArrayList.add(site);
}
siteArrayList.addAll(sites);
adapter.notifyDataSetChanged();
}
// Return the result code and description upon a search exception.
@Override
public void onSearchError(SearchStatus status) {
Log.i("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage());
}
};
// Call the keyword search API.
searchService.textSearch(request, resultListener);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
void nearByPlacesSearch(String newText){
try{
String key = URLEncoder.encode(Constants.API_KEY, "UTF-8");
// Instantiate the SearchService object.
searchService = SearchServiceFactory.create(getContext(), key);
// Create a request body.
NearbySearchRequest request = new NearbySearchRequest();
if(locationResult!=null){
Coordinate location = new Coordinate(locationResult.getLastHWLocation().getLatitude(),locationResult.getLastHWLocation().getLongitude());
request.setLocation(location);
}
request.setQuery(newText);
request.setRadius(1000);
request.setHwPoiType(HwLocationType.ADDRESS);
request.setLanguage("en");
request.setPageIndex(1);
request.setPageSize(5);
request.setStrictBounds(false);
// Create a search result listener.
SearchResultListener<NearbySearchResponse> resultListener = new SearchResultListener<NearbySearchResponse>() {
// Return search results upon a successful search.
@Override
public void onSearchResult(NearbySearchResponse results) {
if (results == null || results.getTotalCount() <= 0) {
return;
}
List<Site> sites = results.getSites();
if(sites == null || sites.size() == 0){
return;
}
siteArrayList.clear();
for (Site site : sites) {
siteArrayList.add(site);
}
siteArrayList.addAll(sites);
adapter.notifyDataSetChanged();
}
// Return the result code and description upon a search exception.
@Override
public void onSearchError(SearchStatus status) {
Log.i("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage());
}
};
// Call the nearby place search API.
searchService.nearbySearch(request, resultListener);
}catch (Exception e){
e.printStackTrace();
}
}
}
[B][B][B]
Result
Tricks and Tips
Makes sure that agconnect-services.json file added.
Make sure required dependencies are added
Make sure that service is enabled in AGC
Enable data binding in gradle.build file
Make sure bottom navigation id’s should be same as fragment id’s in navigation graph
Make sure that set apk key before calling service.
Conclusion
In this article, we have learnt how to integrate Huawei Site kit, Map kit and Location kit in Android application KnowMyBoard. You can also go through previous article part-3 here. Hoping Site kit and Map kit capabilities are helpful to you as well, like this sample, you can make use as per your requirement.
Thank you so much for reading. I hope this article helps you to understand the integration of Huawei Site kit, Map kit and Location kit in Android application KnowMyBoard.
Reference
Map Kit – Training video
Site Kit – Training video
Checkout in forum