{
"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"
}
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
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 there steps in to complete the configuration that required for development.
Configuring App Information in AppGallery Connect --> shorturl.at/rL347
Creating Xamarin Android Binding Libraries --> shorturl.at/rBP46
Integrating the HMS Map Kit Libraries for Xamarin --> shorturl.at/vAHPX
Integrating the HMS Location Kit Libraries for Xamarin --> shorturl.at/dCX07
Integrating the HMS Site Kit Libraries for Xamarin --> shorturl.at/bmDX6
Integrating the HMS Core SDK --> shorturl.at/qBISV
Setting Package in Xamarin --> shorturl.at/brCU1
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.
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
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();
}
Add a Map
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.
Code:
<fragment
android:id="@+id/mapfragment"
class="com.huawei.hms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Implement the IOnMapReadyCallback interface to MainActivity and override OnMapReady method which is triggered when the map is ready to use. Then use GetMapAsync to register for the map callback.
We request the address corresponding to a given latitude/longitude. Also specified that the output must be in JSON format.
Code:
public class MainActivity : AppCompatActivity, IOnMapReadyCallback
{
...
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. 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
{
...
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.
There are some methods for the camera position changes as we see above. Simply these are:
NewLatLng: Change camera’s latitude and longitude, while keeping other properties
NewLatLngZoom: Changes the camera’s latitude, longitude, and zoom, while keeping other properties
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
In this part, 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.
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();
}
Adding MapInfoWindowAdapter class to our project for rendering the custom info model. And implement HuaweiMap.IInfoWindowAdapter interface to it. 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="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;
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;
}
}
Now we create a method to arrange a circle around the marker that representing the geofence radius. Create a new DrawCircleOnMap method in MainActivity for this. To construct a circle, we must specify the Center and Radius. Also, I set other properties like StrokeColor etc.
Code:
public void DrawCircleOnMap(GeofenceModel geoModel)
{
if (circle != null)
{
circle.Remove();
circle = null;
}
CircleOptions circleOptions = new CircleOptions()
.InvokeCenter(geoModel.LatLng)
.InvokeRadius(geoModel.Radius)
.InvokeFillColor(Color.Argb(50, 0, 14, 84))
.InvokeStrokeColor(Color.Yellow)
.InvokeStrokeWidth(15);
circle = hMap.AddCircle(circleOptions);
}
private void radiusBar_ProgressChanged(object sender, SeekBar.ProgressChangedEventArgs e)
{
selectedCoordinates.Radius = e.Progress;
DrawCircleOnMap(selectedCoordinates);
}
We will use SeekBar to change the radius of the circle. As the value changes, the drawn circle will expand or shrink.
Reverse Geocoding
Now let’s handle the click event of the info window.
But before open that window, we need to reverse geocoding selected coordinates to getting a formatted address. HUAWEI Site Kit provides us a set of HTTP API including the one that we need, reverseGeocode.
Let’s add the GeocodeManager class to our project and update it as follows:
Code:
public async Task<Site> ReverseGeocode(double lat, double lng)
{
string result = "";
using (var client = new HttpClient())
{
MyLocation location = new MyLocation();
location.Lat = lat;
location.Lng = lng;
var root = new ReverseGeocodeRequest();
root.Location = location;
var settings = new JsonSerializerSettings();
settings.ContractResolver = new LowercaseSerializer();
var json = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var url = "siteapi.cloud.huawei.com/mapApi/v1/siteService/reverseGeocode?key=" + Android.Net.Uri.Encode(ApiKey);
var response = await client.PostAsync(url, data);
result = response.Content.ReadAsStringAsync().Result;
}
return JsonConvert.DeserializeObject<ReverseGeocodeResponse>(result).sites.FirstOrDefault();
}
In the above code, we request the address corresponding to a given latitude/longitude. Also specified that the output must be in JSON format.
siteapi.cloud.huawei.com/mapApi/v1/siteService/reverseGeocode?key=APIKEY
Click to expand...
Click to collapse
Request model:
Code:
public class MyLocation
{
public double Lat { get; set; }
public double Lng { get; set; }
}
public class ReverseGeocodeRequest
{
public MyLocation Location { get; set; }
}
Note that the JSON response contains three root elements:
“returnCode”: For details, please refer to Result Codes.
“returnDesc”: description
“sites” contains an array of geocoded address information
Generally, only one entry in the “sites” array is returned for address lookups, though the geocoder may return several results when address queries are ambiguous.
Add the following codes to our MapInfoWindowAdapter where we get results from the Reverse Geocode API and set the UI elements.
Code:
private async void btnInfoWindow_ClickAsync(object sender, System.EventArgs e)
{
addressLayout = activity.LayoutInflater.Inflate(Resource.Layout.reverse_alert_layout, null);
GeocodeManager geocodeManager = new GeocodeManager(activity);
var addressResult = await geocodeManager.ReverseGeocode(selectedCoordinates.LatLng.Latitude, selectedCoordinates.LatLng.Longitude);
if (addressResult.ReturnCode != 0)
return;
var address = addressResult.Sites.FirstOrDefault();
var txtAddress = addressLayout.FindViewById<TextView>(Resource.Id.txtAddress);
var txtRadius = addressLayout.FindViewById<TextView>(Resource.Id.txtRadius);
txtAddress.Text = address.FormatAddress;
txtRadius.Text = selectedCoordinates.Radius.ToString();
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.SetView(addressLayout);
builder.SetTitle(address.Name);
builder.SetPositiveButton("Save", (sender, arg) =>
{
selectedCoordinates.Conversion = GetSelectedConversion();
GeofenceManager geofenceManager = new GeofenceManager(activity);
geofenceManager.AddGeofences(selectedCoordinates);
});
builder.SetNegativeButton("Cancel", (sender, arg) => { builder.Dispose(); });
AlertDialog alert = builder.Create();
alert.Show();
}
Now, after selecting the conversion, we can complete the process by calling the AddGeofence method in the GeofenceManager class by pressing the save button in the dialog window.
Code:
public void AddGeofences(GeofenceModel geofenceModel)
{
//Set parameters
geofenceModel.Id = Guid.NewGuid().ToString();
if (geofenceModel.Conversion == 5) //Expiration value that indicates the geofence should never expire.
geofenceModel.Timeout = Geofence.GeofenceNeverExpire;
else
geofenceModel.Timeout = 10000;
List<IGeofence> geofenceList = new List<IGeofence>();
//Geofence Service
GeofenceService geofenceService = LocationServices.GetGeofenceService(activity);
PendingIntent pendingIntent = CreatePendingIntent();
GeofenceBuilder somewhereBuilder = new GeofenceBuilder()
.SetUniqueId(geofenceModel.Id)
.SetValidContinueTime(geofenceModel.Timeout)
.SetRoundArea(geofenceModel.LatLng.Latitude, geofenceModel.LatLng.Longitude, geofenceModel.Radius)
.SetDwellDelayTime(10000)
.SetConversions(geofenceModel.Conversion); ;
//Create geofence request
geofenceList.Add(somewhereBuilder.Build());
GeofenceRequest geofenceRequest = new GeofenceRequest.Builder()
.CreateGeofenceList(geofenceList)
.Build();
//Register geofence
var geoTask = geofenceService.CreateGeofenceList(geofenceRequest, pendingIntent);
geoTask.AddOnSuccessListener(new CreateGeoSuccessListener(activity));
geoTask.AddOnFailureListener(new CreateGeoFailListener(activity));
}
In the AddGeofence method, we need to set the geofence request parameters, like the selected conversion, unique Id and timeout according to conversion, etc. with GeofenceBuilder. We create GeofenceBroadcastReceiver and display a toast message when a geofence action occurs.
Code:
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "com.huawei.hms.geofence.ACTION_PROCESS_ACTIVITY" })]
class GeofenceBroadcastReceiver : BroadcastReceiver
{
public static readonly string ActionGeofence = "com.huawei.hms.geofence.ACTION_PROCESS_ACTIVITY";
public override void OnReceive(Context context, Intent intent)
{
if (intent != null)
{
var action = intent.Action;
if (action == ActionGeofence)
{
GeofenceData geofenceData = GeofenceData.GetDataFromIntent(intent);
if (geofenceData != null)
{
Toast.MakeText(context, "Geofence triggered: " + geofenceData.ConvertingLocation.Latitude +"\n" + geofenceData.ConvertingLocation.Longitude + "\n" + geofenceData.Conversion.ToConversionName(), ToastLength.Long).Show();
}
}
}
}
}
After that in CreateGeoSuccessListener and CreateGeoFailureListener that we implement IOnSuccessListener and IOnFailureListener respectively, we display a toast message to the user like this:
Code:
public class CreateGeoFailListener : Java.Lang.Object, IOnFailureListener
{
public void OnFailure(Java.Lang.Exception ex)
{
Toast.MakeText(mainActivity, "Geofence request failed: " + GeofenceErrorCodes.GetErrorMessage((ex as ApiException).StatusCode), ToastLength.Long).Show();
}
}
public class CreateGeoSuccessListener : Java.Lang.Object, IOnSuccessListener
{
public void OnSuccess(Java.Lang.Object data)
{
Toast.MakeText(mainActivity, "Geofence request successful", ToastLength.Long).Show();
}
}
Set geofence location using Nearby Search
On the main layout when the user clicks the Search Nearby Places button, a search dialog like below appears:
Create search_alert_layout.xml with a search input In Main Activity, create click event of that button and open an alert dialog after it’s view is set to search_alert_layout. And make NearbySearch when clicking the Search button:
Code:
private void btnGeoWithAddress_Click(object sender, EventArgs e)
{
search_view = base.LayoutInflater.Inflate(Resource.Layout.search_alert_layout, null);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.SetView(search_view);
builder.SetTitle("Search Location");
builder.SetNegativeButton("Cancel", (sender, arg) => { builder.Dispose(); });
search_view.FindViewById<Button>(Resource.Id.btnSearch).Click += btnSearchClicked;
alert = builder.Create();
alert.Show();
}
private void btnSearchClicked(object sender, EventArgs e)
{
string searchText = search_view.FindViewById<TextView>(Resource.Id.txtSearch).Text;
GeocodeManager geocodeManager = new GeocodeManager(this);
geocodeManager.NearbySearch(CurrentPosition, searchText);
}
We pass search text and Current Location into the GeocodeManager NearbySearch method as parameters. We need to modify GeoCodeManager class and add nearby search method to it.
Code:
public void NearbySearch(LatLng currentLocation, string searchText)
{
ISearchService searchService = SearchServiceFactory.Create(activity, Android.Net.Uri.Encode("YOUR_API_KEY"));
NearbySearchRequest nearbySearchRequest = new NearbySearchRequest();
nearbySearchRequest.Query = searchText;
nearbySearchRequest.Language = "en";
nearbySearchRequest.Location = new Coordinate(currentLocation.Latitude, currentLocation.Longitude);
nearbySearchRequest.Radius = (Integer)2000;
nearbySearchRequest.PageIndex = (Integer)1;
nearbySearchRequest.PageSize = (Integer)5;
nearbySearchRequest.PoiType = LocationType.Address;
searchService.NearbySearch(nearbySearchRequest, new QuerySuggestionResultListener(activity as MainActivity));
}
And to handle the result we must create a listener and implement the ISearchResultListener interface to it.
Code:
public class NearbySearchResultListener : Java.Lang.Object, ISearchResultListener
{
public void OnSearchError(SearchStatus status)
{
Toast.MakeText(context, "Error Code: " + status.ErrorCode + " Error Message: " + status.ErrorMessage, ToastLength.Long);
}
public void OnSearchResult(Java.Lang.Object results)
{
NearbySearchResponse nearbySearchResponse = (NearbySearchResponse)results;
if (nearbySearchResponse != null && nearbySearchResponse.TotalCount > 0)
context.SetSearchResultOnMap(nearbySearchResponse.Sites);
}
}
In OnSearchResult method, NearbySearchResponse object return. We will insert markers to the mapper element in this response. The map will look like this:
In Main Activity create a method named SetSearchResultOnMap and pass IList<Site> as a parameter to insert multiple markers on the map.
Code:
public void SetSearchResultOnMap(IList<Com.Huawei.Hms.Site.Api.Model.Site> sites)
{
hMap.Clear();
if (searchMarkers != null && searchMarkers.Count > 0)
foreach (var item in searchMarkers)
item.Remove();
searchMarkers = new List<Marker>();
for (int i = 0; i < sites.Count; i++)
{
MarkerOptions marker1Options = new MarkerOptions()
.InvokePosition(new LatLng(sites[i].Location.Lat, sites[i].Location.Lng))
.InvokeTitle(sites[i].Name).Clusterable(true);
hMap.SetInfoWindowAdapter(new MapInfoWindowAdapter(this));
var marker1 = hMap.AddMarker(marker1Options);
searchMarkers.Add(marker1);
RepositionMapCamera(sites[i].Location.Lat, sites[i].Location.Lng);
}
hMap.SetMarkersClustering(true);
alert.Dismiss();
}
Now, we add markers as we did above. But here we use SetMarkersClustering(true) to consolidates markers into clusters when zooming out of the map.
You can download the source code from below:
github.com/stugcearar/HMSCore-Xamarin-Android-Samples/tree/master/LocationKit/HMS_Geofence
Also if you have any questions, ask away in Huawei Developer Forums.
Errors
If your location permission set “Allowed only while in use instead” of ”Allowed all the time” below exception will be thrown.
int GEOFENCE_INSUFFICIENT_PERMISSION
Insufficient permission to perform geofence-related operations.
You can see all result codes including errors, in here for Location service.
You can find result codes with details here for Geofence request.
Related
This article is orginally from HUAWEI Developer Forum.
Forum link: https://forums.developer.huawei.com/forumPortal/en/home
{
"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"
}
Before we start learning about today’s topic, I strongly recommend you to go through my previous article i.e. HMS Site Map (Part 1). It will help you to have a clear picture.
Let’s Begin
In the previous article, we were successfully able to get details after selecting the place that we want to search using Site Kit. Today in this article we are going to see how to show a map using Map Kit after fetching the Latitude and Longitude from the details we selected. Also we are going to see how to use the Site APIs and Map APIs using POSTMAN in our Part 3 article.
One Step at a time
First we need to add Map Kit dependencies in the app gradle file and sync the app.
implementation 'com.huawei.hms:maps:4.0.1.300'
After adding the dependencies we need to provide permission in AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
Let’s Code
Main Activity class
Code:
private void showDetails(String item) {
String pattern = Pattern.quote("\\" + "n");
String[] lines = item.split("\\n+");
autoCompleteTextView.setText(lines[0]);
mLat = lines[2]; // This is latitude
mLon = lines[3]; // This is longitude
title = lines[0]; // This is title or place name
String details = "<font color='red'>PLACE NAME : </font>" + lines[0] + "<br>"
+ "<font color='#CD5C5C'>COUNTRY : </font>" + lines[1] + "<br>"
+ "<font color='#8E44AD'>ADDRESS : </font>" + lines[4] + "<br>"
+ "<font color='#008000'>PHONE : </font>" + lines[5];
txtDetails.setText(Html.fromHtml(details, Html.FROM_HTML_MODE_COMPACT));
}
private void showMap(){
Intent intent = new Intent(MainActivity.this, MapActivity.class);
intent.putExtra("lat",mLat); // Here we are passing Latitude and Longitude
intent.putExtra("lon",mLon); // and titile from MainActivity class to
intent.putExtra("title",title);// MapActivity class…
startActivity(intent);
}v
Main Code
1) First we need to understand whether we are showing the map in view or fragment. Because there are two way we can show our map.
a) Fragment way
In fragment way we add MapFragment in the layout file of an activity.
Code:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapfragment_mapfragmentdemo"
class="com.huawei.hms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraTargetLat="48.893478"
map:cameraTargetLng="2.334595"
map:cameraZoom="10" />
b) MapView way
Here we add MapView in the layout file of an activity.
Code:
<com.huawei.hms.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
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"
map:cameraTargetLat="51"
map:cameraTargetLng="10"
map:cameraZoom="8.5"/>
2) Here we are going with MapView.
3) For both Fragment as well as View, we need to implement OnMapReadyCallback API in our MapActivity to use a Map. After implementing this API, it will ask to implement onMapReady method.
Code:
public void onMapReady(HuaweiMap map) {
Log.d(TAG, "onMapReady: ");
hMap = map;
}
4) The only difference which we will see between MapFragment and MapView is instantiating Map.
a) MapFragement
Code:
private MapFragment mMapFragment;
mMapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.mapfragment_mapfragmentdemo);
mMapFragment.getMapAsync(this);
b) MapView
Code:
private MapView mMapView;
mMapView = findViewById(R.id.mapview_mapviewdemo);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle("MapViewBundleKey");
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
5) Permission we need to check
Code:
//Put this in the top of the onCreate() method …
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
};
// This will placed in the onCreate() method …
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
// Use this method to check Permission …
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;
}
MapActivity Class
Code:
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = "MapActivity";
private MapView mMapView;
private HuaweiMap hmap;
private Marker mMarker;
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
};
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int REQUEST_CODE = 100;
private String mLatitude, mLongitude,mTitle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
mLatitude = getIntent().getExtras().getString("lat");
mLongitude = getIntent().getExtras().getString("lon");
mTitle = getIntent().getExtras().getString("title");
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);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
}
@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
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
@Override
public void onMapReady(HuaweiMap huaweiMap) {
Log.d(TAG, "onMapReady: ");
hmap = huaweiMap;
hmap.setMyLocationEnabled(true);
hmap.setMapType(HuaweiMap.MAP_TYPE_NORMAL);
hmap.setMaxZoomPreference(15);
hmap.setMinZoomPreference(5);
CameraPosition build = new CameraPosition.Builder()
.target(new LatLng(Double.parseDouble(mLatitude), Double.parseDouble(mLongitude)))
.build();
CameraUpdate cameraUpdate = CameraUpdateFactory
.newCameraPosition(build);
hmap.animateCamera(cameraUpdate);
MarkerOptions options = new MarkerOptions()
.position(new LatLng(Double.parseDouble(mLatitude),
Double.parseDouble(mLongitude)))
.title(mTitle);
mMarker = hmap.addMarker(options);
mMarker.showInfoWindow();
hmap.setOnMarkerClickListener(new HuaweiMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
Toast.makeText(getApplicationContext(), "onMarkerClick:" +
marker.getTitle(), Toast.LENGTH_SHORT).show();
return false;
}
});
}
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;
}
}
Core Functionality of Map
1) Types of Map
There are five types map:
· HuaweiMap.MAP_TYPE_NORMAL
· HuaweiMap.MAP_TYPE_NONE
· HuaweiMap.MAP_TYPE_SATELLITE
· HuaweiMap.MAP_TYPE_HYBRID
· HuaweiMap.MAP_TYPE_TERRAIN
But we can only use MAP_TYPE_NORMAL and MAP_TYPE_NONE. Normal type is a standard map, which shows roads, artificial structures, and natural features such as rivers. None type is an empty map without any data.
The Rest Map type is in development phase.
2) Camera Movement
Huawei maps are moved by simulating camera movement. You can control the visible region of a map by changing the camera's position. To change the camera's position, create different types of CameraUpdate objects using the CameraUpdateFactory class, and use these objects to move the camera.
Code:
CameraPosition build = new CameraPosition.Builder().target(new
LatLng(Double.parseDouble(mLatitude),
Double.parseDouble(mLongitude))).build();
CameraUpdate cameraUpdate = CameraUpdateFactory
.newCameraPosition(build);
hmap.animateCamera(cameraUpdate);
In the above code we are using Map camera in animation mode. When moving the map camera in animation mode, you can set the animation duration and API to be called back when the animation stops. By default, the animation duration is 250 ms.
3) My Location in Map
We can get our location in our Map by simply enabling my-location layer. Also, we can display my-location icon in the Map.
Code:
hmap.setMyLocationEnabled(true);
hmap.getUiSettings().setMyLocationButtonEnabled(true);
4) Show Marker in Map
We can add markers to a map to identify locations such as stores and buildings, and provide additional information with information windows.
Code:
MarkerOptions options = new MarkerOptions()
.position(new LatLng(Double.parseDouble(mLatitude),
Double.parseDouble(mLongitude)))
.title(mTitle); // Adding the title here …
mMarker = hmap.addMarker(options);
mMarker.showInfoWindow();
We can customize our marker according to our need using BitmapDescriptor object.
Code:
Bitmap bitmap = ResourceBitmapDescriptor.drawableToBitmap(this,
ContextCompat.getDrawable(this, R.drawable.badge_ph));
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);
mMarker.setIcon(bitmapDescriptor);
We can title to the Marker as shown in the above code. We can also make the marker clickable as shown below.
Code:
hmap.setOnMarkerClickListener(new HuaweiMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
Toast.makeText(getApplicationContext(), "onMarkerClick:" +
marker.getTitle(), Toast.LENGTH_SHORT).show();
return false;
}
});
5) Map comes in shape
a) Polyline
b) Polygon
c) Circle
We can use Polyline if we need to show routes from one place to another. We can combine Direction API with Polyline to show routes for walking, bicycling and driving also calculating routes distance.
If we need to show radius like the location under 500 meter or something we use Circle shape to show in the map.
The Result
Any questions about this process, you can try to acquire answers from HUAWEI Developer Forum.
More information like this, you can visit HUAWEI Developer Forum
Original link: https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201345979826040147&fid=0101187876626530001
In this article we will learn to use Huawei Health Kit in a workout app.
Huawei Health Kit
Health Kit based on users Huawei Id and authorization, provide best service by accessing their health and fitness data. Using this service, developers do not need to worry about capturing their user’s data from any Bluetooth device like fitness tracker watch or Fitbit and record them. It helps developers to create a hassle-free, effective and productive application to their users.
HMS Health Kit Services
1) DataController: Developers can use this API to insert, delete, update, and read data, as well as listen to data updates by registering a listener.
2) SensorsController: Developers can use this API to receiving data reported by the sensor like steps count.
3) AutoRecordController: Developers can use this API to automatically record sensor data, stop recording sensor data, and obtain the record information.
4) ActivityRecordsController: Developers can use this API to create and manage user activities.
Here in this article we will use two services provided by HMS Health Kit and that is SensorsController and ActivityRecordsController.
Use case
To lose weight, you can walk or run. When you walk or run will lose some calories from our body. So better to track our daily steps, if you track daily steps, then you can be active and also to avoid health problems like heart attacks or broken bones, a new study suggests.
Increasing our walking and maintaining the steps can reduce our risk of heart attacks, strokes and fractures over the next few years. Pedometers can be helpful for us to use, as they give us a clear idea of how much we are doing (self-monitoring) and can be used to set realistic goals for increasing our walking gradually.
HMS Health Kit works as a pedometer, count our steps and provide the information directly to us. We can also record our steps using HMS Health Kit and use it for self-monitoring.
Demo
{
"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"
}
Prerequisite
1) Must have a Huawei Developer Account.
2) Must have a Huawei phone with HMS 4.0.0.300 or later.
3) Must have a laptop or desktop with Android Studio, Jdk 1.8, SDK platform 26 and Gradle 4.6 installed.
Things need to be done
1) Create a project in android studio.
2) Get the SHA Key. For getting the SHA key, refer this article.
3) Create an app in the Huawei AppGallery connect.
4) Click on Health kit in console.
5) Apply for Health Kit
6) Select Product Type as Mobile App, APK Name as your project package name and check the required permission required for your application to work, as shown below:
Finally click on submit button to apply for health kit.
7) Provide the SHA Key in App Information Section.
8) Provide storage location.
9) Add the app ID generated when the creating the app on HUAWEI Developers to the application section
Code:
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="YOUR_APP_ID" />
10) After completing all the above points we need to download the agconnect-services.json from App Information Section. Copy and paste the Json file in the app folder of the android project.
11) Enter the below maven url inside the repositories of buildscript and allprojects (project build.gradle file):
Code:
maven { url ‘http://developer.huawei.com/repo/’ }
12) Enter the below plugin in the app build.gradle file:
Code:
apply plugin: ‘com.huawei.agconnect’
13) Enter the below Health Kit, account kit and auth service dependencies in the dependencies section:
Code:
implementation 'com.huawei.agconnect:agconnect-core:1.4.0.300'
implementation 'com.huawei.agconnect:agconnect-auth:1.4.0.300'
implementation 'com.huawei.hms:hwid:4.0.4.300'
implementation 'com.huawei.hms:hihealth-base:5.0.0.300'
14) Enter the below permission in android manifest file
Code:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
15) Now Sync the gradle.
Huawei ID authentication
Sign in with Huawei ID authentication and apply for the scope to obtain the permissions to access the Health Kit APIs. Different scopes correspond to different permissions. Developers can apply for permissions based on service requirements.
Code:
public void doLogin(View view) {
Log.i(TAG, "begin sign in");
List<Scope> scopeList = new ArrayList<>();
scopeList.add(new Scope(Scopes.HEALTHKIT_STEP_BOTH));
scopeList.add(new Scope(Scopes.HEALTHKIT_HEIGHTWEIGHT_BOTH));
scopeList.add(new Scope(Scopes.HEALTHKIT_ACTIVITY_BOTH));
HuaweiIdAuthParamsHelper authParamsHelper = new HuaweiIdAuthParamsHelper(
HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM);
mHuaweiIdAuthParams = authParamsHelper.setIdToken()
.setAccessToken()
.setScopeList(scopeList)
.createParams();
mHuaweiIdAuthService = HuaweiIdAuthManager.getService(SplashActivity.this, mHuaweiIdAuthParams);
startActivityForResult(mHuaweiIdAuthService.getSignInIntent(), REQUEST_SIGN_IN_LOGIN);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_SIGN_IN_LOGIN) {
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
if (authHuaweiIdTask.isSuccessful()) {
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i("TAG", "accessToken:" + huaweiAccount.getAccessToken());
AGConnectAuthCredential credential = HwIdAuthProvider.credentialWithToken(huaweiAccount.getAccessToken());
AGConnectAuth.getInstance().signIn(credential).addOnSuccessListener(new OnSuccessListener<SignInResult>() {
@Override
public void onSuccess(SignInResult signInResult) {
mUser = AGConnectAuth.getInstance().getCurrentUser();
Intent intent = new Intent(SplashActivity.this,InstructionActivity.class);
startActivity(intent);
finish();
}
});
} else {
Log.e("TAG", "sign in failed : " + ((ApiException) authHuaweiIdTask.getException()).getStatusCode());
}
}
}
Below is the link to know more about the mapping between scopes and permissions:
https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/scopes-0000001050092713-V5
SensorController
As the name suggested it will gather information using sensor of android mobile device. Using SensorController of HMS Health Kit:
1) We can connect to a built-in sensor of the phone such as a pedometer to obtain data.
2) We can connect to an external BLE device such as heart rate strap to obtain data.
Here we will use SensorController to count the number of steps taken by our users. In order to that we need to directly call the register method of SensorsController to register a listener to obtain the data reported by the built-in sensor.
Obtain SensorController object
Code:
HiHealthOptions options = HiHealthOptions.builder().build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(options);
sensorsController = HuaweiHiHealth.getSensorsController(this, signInHuaweiId);
Register a listener to listen to the step count
Code:
private OnSamplePointListener onStepPointListener = new OnSamplePointListener() {
@Override
public void onSamplePoint(SamplePoint samplePoint) {
// The step count, time, and type data reported by the pedometer is called back to the app through
// samplePoint.
showSamplePoint();
mCurrentSamplePoint = samplePoint;
if (mLastSamplePoint == null) {
mLastSamplePoint = samplePoint;
}
}
};
public void registerSteps() {
if (sensorsController == null) {
Toast.makeText(StepsTrackerActivity.this, "SensorsController is null", Toast.LENGTH_LONG).show();
return;
}
DataCollector dataCollector = new DataCollector.Builder()
.setDataType(DataType.DT_CONTINUOUS_STEPS_TOTAL)
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.setPackageName(StepsTrackerActivity.this)
.setDeviceInfo(new DeviceInfo("hw", "hw", "hw", 0))
.build();
SensorOptions.Builder builder = new SensorOptions.Builder();
builder.setDataType(DataType.DT_CONTINUOUS_STEPS_TOTAL);
builder.setDataCollector(dataCollector);
sensorsController.register(builder.build(), onStepPointListener)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Toast.makeText(StepsTrackerActivity.this, "Register Success", Toast.LENGTH_LONG).show();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(StepsTrackerActivity.this, "Register Failed", Toast.LENGTH_LONG).show();
}
});
}
Show SamplePoint data
Code:
private void showSamplePoint() {
if (mLastSamplePoint != null && mCurrentSamplePoint != null) {
SamplePoint samplePoint = mCurrentSamplePoint;
System.out.println("STEPS >>>" + (samplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()
- mLastSamplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()));
runOnUiThread(new Runnable() {
@Override
public void run() {
txtSteps.setText(String.valueOf(Integer.parseInt(txtSteps.getText().toString()) + samplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()
- mLastSamplePoint.getFieldValue(Field.FIELD_STEPS).asIntValue()));
if(selectedWeight.equalsIgnoreCase("Kg")) {
txtCalorie.setText(String.format("%.2f", Integer.parseInt(weight.getText().toString()) * Long.parseLong(txtSteps.getText().toString()) * 0.4 * 0.001 * 1.036) + " Kcal");
}else{
txtCalorie.setText(String.format("%.2f", Integer.parseInt(weight.getText().toString()) * 2.2046226218 * Long.parseLong(txtSteps.getText().toString()) * 0.4 * 0.001 * 1.036) + " Kcal");
}
mLastSamplePoint = samplePoint;
}
});
}
}
Un-Register a listener to stop the step count
Code:
public void unregisterSteps() {
if (sensorsController == null) {
Toast.makeText(StepsTrackerActivity.this, "SensorsController is null", Toast.LENGTH_LONG).show();
return;
}
// Unregister the listener for the step count.
sensorsController.unregister(onStepPointListener).addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
Toast.makeText(StepsTrackerActivity.this, "UnregisterSteps Succeed ...", Toast.LENGTH_LONG).show();
mLastSamplePoint = null;
mCurrentSamplePoint = null;
txtSteps.setText("0");
txtCalorie.setText("0");
btnStartStop.setText("Start Tracking");
isStartStop = true;
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(StepsTrackerActivity.this, "UnregisterSteps Failed ...", Toast.LENGTH_LONG).show();
}
});
}
Result
ActivityRecordsController
As the name suggested it will record / insert the activity data of user. Here we will insert the steps count taken by user.
Start time and end time for ActivityRecords
We need start and end time to insert the user steps data as show below:
Code:
public static long getEndTime() {
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
return endTime;
}
public static long getStartTime() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();
return startTime;
}
Insert ActivityRecords
Using SamplePoint of HMS Health Kit we will insert the step count of user as shown below:
Code:
private void insertActivityRecords(){
HiHealthOptions hihealthOptions = HiHealthOptions.builder().build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(hihealthOptions);
ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(StepsTrackerActivity.this, signInHuaweiId);
ActivityRecord activityRecord = new ActivityRecord.Builder()
.setName("AddStepsRecord")
.setDesc("This is Steps record")
.setId("StepId")
.setActivityTypeId(HiHealthActivities.ON_FOOT)
.setStartTime(startTime, TimeUnit.MILLISECONDS)
.setEndTime(endTime, TimeUnit.MILLISECONDS)
.build();
DataCollector dataCollector = new DataCollector.Builder()
.setDataType(DataType.DT_CONTINUOUS_STEPS_DELTA)
.setDataGenerateType(DataCollector.DATA_TYPE_RAW)
.setPackageName(getApplicationContext())
.setDataCollectorName("AddStepsRecord")
.build();
SampleSet sampleSet = SampleSet.create(dataCollector);
SamplePoint samplePoint = sampleSet.createSamplePoint().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
samplePoint.getFieldValue(Field.FIELD_STEPS_DELTA).setIntValue(Integer.parseInt(txtSteps.getText().toString()));
sampleSet.addSample(samplePoint);
ActivityRecordInsertOptions insertOption =
new ActivityRecordInsertOptions.Builder().setActivityRecord(activityRecord).addSampleSet(sampleSet).build();
Task<Void> addTask = activityRecordsController.addActivityRecord(insertOption);
addTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
unregisterSteps();
Log.i("ActivityRecords","ActivityRecord add was successful!");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
Log.i("ActivityRecords",errorCode + ": " + errorMsg);
}
});
}
DatePicker
Code:
private void getDatePicker(EditText edtVal){
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
datePickerVal = "";
DatePickerDialog datePickerDialog = new DatePickerDialog(GetRecords.this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
// edtVal.setText(dayOfMonth + "-" + (monthOfYear + 1) + "-" + year);
edtVal.setText(year + "-" + (monthOfYear + 1) + "-" + dayOfMonth);
// datePickerVal = dayOfMonth + "-" + (monthOfYear + 1) + "-" + year;
}
}, mYear, mMonth, mDay);
datePickerDialog.show();
}
TimePicker
Code:
private void getTimePicker(EditText edtVal){
final Calendar c = Calendar.getInstance();
mHour = c.get(Calendar.HOUR_OF_DAY);
mMinute = c.get(Calendar.MINUTE);
// Launch Time Picker Dialog
TimePickerDialog timePickerDialog = new TimePickerDialog(GetRecords.this,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay,
int minute) {
edtVal.setText(hourOfDay + ":" + minute);
}
}, mHour, mMinute, false);
timePickerDialog.show();
}
Start & End Time milliseconds conversion
Code:
private long getDateTime(String dateval,String timeval) {
String dateStr = dateval+" "+timeval+":00";
long milliseconds = 0;
try {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = dateFormat.parse(dateStr);
System.out.println("DATE TIME >>>"+date.getTime());
milliseconds = date.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return milliseconds;
}
Reading ActivityRecords and Associated Data
Here we can obtain all ActivityRecords within a specific period of time for particular data, or obtain a specific ActivityRecord by name or ID as shown below:
Code:
private void readActivityRecords(){
HiHealthOptions hihealthOptions = HiHealthOptions.builder().build();
AuthHuaweiId signInHuaweiId = HuaweiIdAuthManager.getExtendedAuthResult(hihealthOptions);
ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(getApplicationContext(), signInHuaweiId);
ActivityRecordReadOptions readOption =
new ActivityRecordReadOptions.Builder().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
.readActivityRecordsFromAllApps()
.read(DataType.DT_CONTINUOUS_STEPS_DELTA)
.build();
Task<ActivityRecordReply> getTask = activityRecordsController.getActivityRecord(readOption);
getTask.addOnSuccessListener(new OnSuccessListener<ActivityRecordReply>() {
@Override
public void onSuccess(ActivityRecordReply activityRecordReply) {
Log.i("ActivityRecords","Get ActivityRecord was successful!");
//Print ActivityRecord and corresponding activity data in the result.
List<ActivityRecord> activityRecordList = activityRecordReply.getActivityRecords();
for (ActivityRecord activityRecord : activityRecordList) {
DateFormat dateFormat = DateFormat.getDateInstance();
DateFormat timeFormat = DateFormat.getTimeInstance();
for (SampleSet sampleSet : activityRecordReply.getSampleSet(activityRecord)) {
for (SamplePoint dp : sampleSet.getSamplePoints()) {
String values = "Start: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)) + " " + timeFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS))+"\n"+
"End: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS))+ " " + timeFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS));
for (Field field : dp.getDataType().getFields()) {
/*Log.i("ActivityRecordSample",
"\tField: " + field.toString() + " Value: " + dp.getFieldValue(field));*/
values = values +"\n"+"Step Taken: " + dp.getFieldValue(field);
}
samplePointList.add(values);
}
}
}
showTheList();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
String errorCode = e.getMessage();
String errorMsg = HiHealthStatusCodes.getStatusCodeMessage(Integer.parseInt(errorCode));
Log.i("ActivityRecords",errorCode + ": " + errorMsg);
}
});
}
Result
What we learn?
We learn how to make the usage of two beautiful services provided by HMS Health Kit and also situation where we need the most, in this case fetching user steps and inserting/fetching the data when we need to show in the app.
GitHub
Very soon I am going to post the entire code on GitHub. So come back again to check it out.
For more reference
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/service-introduction-0000001050071661-V5
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/sensorscontroller-develop-0000001050069728-V5
https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/activityrecord-develop-0000001050069730-V5
More information like this, you can visit HUAWEI Developer Forum
In this article we are going to take a look at Huawei Mobile Services (HMS) Push Kit Plugin for Xamarin.Android then we will send our first notification and data message by Huawei Console. After that we will also send them by Push Kit APIs.
{
"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"
}
HUAWEI Push Kit
Push Kit, is a messaging service provided by Huawei for developers. It offers sending real time notifications and information messages. This helps developers maintain closer ties with users and increases user awareness and engagement. Furthermore, you are free to use on any different meaningful purposes.
HUAWEI Push Kit Xamarin.Android Integration
First of all, you need to be a Huawei Developer to use Huawei Push Kit. If you do not have a HUAWEI ID, register as a Huawei Developer and complete real-name authentication. For details, see Registering as a Developer.
Next, Sign in to AppGallery Connect using the approved Huawei ID and click on the “My projects” button.
Then click the “Add project” button and enter a name for your project.
After that, set project data storage location. For this Project Setting > General information > Project > Data storage location. As a location choose Germany then submit it. Afterwards, click “Add app” button in the same page then fill all the fields (if you do not know what “Package name” is just copy from Xamarin.Android Solution > Project > Properties >AndroidManifest.xml > package) after that submit.
Adding Push Kit to Your Xamarin.Android App
Firstly, we must generate the “.keystore” file. You can copy following text into batch file and change toolpath and aliasName then run it in “C:\Program Files\Java\jdk1.8.0\bin” folder. For details, see Pre-development Procedure.
Code:
SET toolPath="C:\tmp\tool.jks"
SET aliasName=aliasname
keytool -genkey -keystore %toolPath% -alias %aliasName% -keyalg RSA -dname "o=Huawei"
pause
keytool -list -v -keystore %toolPath%
pause
Keep these Alias name and SHA256 code. Open the project in Visual Studio then Properties of project afterwards fill the fields. It would be better to do this for both Debug and Release because later when you release the project you have to fill these areas.
Now, it is turn to use SHA256 fingerprint. Copy it and paste SHA-256 certificate fingerprint section then click to tick icon to save it.
Next, download “agconnect-services.json” file, afterwards right click on “Assets” folder in your project then Add > Existing Item and choose this file. At last enable the Push Kit.
Adding HUAWEI Push Kit Plugins in Our Project
Firstly, you need to create Android Binding Libraries for Xamarin. For this, see Creating Android Binding Libraries for Xamarin.
Secondly, you need to add these libraries to your project. For this, see Integrating the Xamarin HMS Push Kit Libraries.
Finally, HUAWEI Push Kit requires some permissions to work correctly. For this right click your project >Properties > Android Manifest then scroll to the bottom of the page. Enable following permissions.
· ACCESS_NETWORK_STATE
· INTERNET
· WAKE_LOCK
· DEFAULT (android.intent.category)
· BROWSABLE (android.intent.category)
· DEFAULT (android.permission)
Integrating HMS Core SDK
Add new class as “HmsLazyInputStream.cs” then paste following code.
Code:
using System;
using System.IO;
using Android.Util;
using Android.Content;
using Com.Huawei.Agconnect.Config;
namespace XamarinHmsPushDemo.Hmssample
{
class HmsLazyInputStream : LazyInputStream
{
public HmsLazyInputStream(Context context) : base(context)
{
}
public override Stream Get(Context context)
{
try
{
return context.Assets.Open("agconnect-services.json");
}
catch (Exception e)
{
Log.Error(e.ToString(), "Can't open agconnect file");
return null;
}
}
}
}
Next, add “using Com.Huawei.Agconnect.Config;” library in your MainActivity class afterwards add following code.
Code:
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(context);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(context);
config.OverlayWith(new HmsLazyInputStream(context));
}
Push Notification
When we send push notification, phone shows this notification on Notification Center. Before we send notification by HUAWEI Push Kit, first the device needs to get a push token.
Add following method into your MainActivity class afterwards call from OnCreate() method then copy the token from Output.
Code:
private void GetToken()
{
System.Threading.Thread thread = new System.Threading.Thread(() =>
{
try
{
string appid = AGConnectServicesConfig.FromContext(this).GetString("client/app_id");
string token = HmsInstanceId.GetInstance(this).GetToken(appid, "HCM");
Log.Info("token", token);
}
catch (Exception e)
{
Log.Info("token", e.ToString());
}
}
);
thread.Start();
}
Open your project in AppGallery Connect then Growing > Push Kit > Add notification. Fill the areas as you wish then click “Test effect” button and paste your token there.
Data Message
When we send data message to a device, OnMessageReceived method will be triggered then we can use this message. In this example we will send data message and show on application.
To receive a push data message and call other related Push Kit APIs, first you need to create a class that implements “HmsMessageService”. You can use all the code in this link but I will just use omitted OnMessageReceived method for clarity. Furthermore, I want to show you this message on Toast therefore we will also add MyBroadcastReceiver class. Moreover you should get instance of MyBroadcastReceiver in OnCreate method.
Code:
using Android.App;
using Android.Content;
using Android.Widget;
using Com.Huawei.Hms.Push;
namespace PushDemo
{
[Service]
[IntentFilter(new[] { "com.huawei.push.action.MESSAGING_EVENT" })]
public class MyMessagingService:HmsMessageService
{
private readonly static string PUSHDEMO_ACTION = "com.companyname.pushdemo.action";
public override void OnMessageReceived(RemoteMessage message)
{
Intent intent = new Intent(PUSHDEMO_ACTION);
intent.PutExtra("msg", message.Data);
SendBroadcast(intent);
}
}
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "com.companyname.pushdemo.action" })]
public class MyBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, intent.GetStringExtra("msg"), ToastLength.Long).Show();
}
}
}
You will get the Toast, when you click Test effect then OK.
Using Push Kit APIs
We have sent notification and data message by HUAWEI console, now it is time to send it by HUAWEI Push Kit APIs. At this part main point is showing sample code. You can use this sample code on your other application (WFA, Web APIs, and Web Site etc.). Here is a sample code and little bit modify on GetToken and OnCreate :
Code:
using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Runtime;
using Android.Widget;
using Com.Huawei.Agconnect.Config;
using Android.Content;
using XamarinHmsPushDemo.Hmssample;
using Com.Huawei.Hms.Aaid;
using Android.Util;
using System;
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Text;
using System.Threading.Tasks;
namespace PushDemoForArticle
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
string tokenFromGetToken;//GetToken()
string appID = "appID";//AppGallery Connect > Project Setting > App information > App ID
string appKey = "APIkey";//AppGallery Connect > Project Setting > App information > App key
private static readonly HttpClient client = new HttpClient();
Button btnGetToken;
Button btnNotification;
Button btnDataMessage;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
btnGetToken = FindViewById<Button>(Resource.Id.btnGetToken);
btnGetToken.Click += (sender, args) => GetToken();
btnNotification = FindViewById<Button>(Resource.Id.btnNotification);
btnNotification.Click += (sender, args) => SendNotification();
btnDataMessage = FindViewById<Button>(Resource.Id.btnDataMessage);
btnDataMessage.Click += (sender, args) => SendDataMessage();
MyBroadcastReceiver myReceiver = new MyBroadcastReceiver();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(context);
AGConnectServicesConfig config = AGConnectServicesConfig.FromContext(context);
config.OverlayWith(new HmsLazyInputStream(context));
}
private void GetToken()
{
System.Threading.Thread thread = new System.Threading.Thread(() =>
{
try
{
string appid = AGConnectServicesConfig.FromContext(this).GetString("client/app_id");
tokenFromGetToken = HmsInstanceId.GetInstance(this).GetToken(appid, "HCM");
Log.Info("token", tokenFromGetToken);
RunOnUiThread(() =>
{
btnNotification.Visibility = Android.Views.ViewStates.Visible;
btnDataMessage.Visibility = Android.Views.ViewStates.Visible;
});
}
catch (Exception e)
{
Log.Info("token", e.ToString());
}
}
);
thread.Start();
}
public async Task<string> GetAccessToken()
{
string uri = "https://oauth-login.cloud.huawei.com/oauth2/v3/token";
var values = new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "client_id", appID },
{ "client_secret", appKey }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync(uri, content);
var jsonResponse = JObject.Parse(await response.Content.ReadAsStringAsync()); //Install-Package Newtonsoft.Json
string accessToken = jsonResponse["access_token"].ToString(); //It is valid for 3600 seconds
return accessToken;
}
public async void SendNotification()
{
string uriNot = "https://push-api.cloud.huawei.com/v1/" + appID + "/messages:send";
var jObject = new
{
message = new
{
notification = new
{
title = "This is title",
body = "This is body part"
},
android = new
{
notification = new
{
click_action = new
{
type = 3
}
}
},
token = new[] { tokenFromGetToken }
}
};
string myJson = JsonConvert.SerializeObject(jObject, Formatting.Indented);
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", await GetAccessToken());
var responseData = await client.PostAsync(uriNot, new StringContent(myJson, Encoding.UTF8, "application/json"));
}
public async void SendDataMessage()
{
string uriNot = "https://push-api.cloud.huawei.com/v1/" + appID + "/messages:send";
var jObject = new
{
message = new
{
data = JsonConvert.SerializeObject(new
{
title = "Message Title",
text = "Message Body",
randomKey = "You can write any key and value"
}, Formatting.None),
token = new[] { tokenFromGetToken }
}
};
string myJson = JsonConvert.SerializeObject(jObject, Formatting.Indented);
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", await GetAccessToken());
var responseData = await client.PostAsync(uriNot, new StringContent(myJson, Encoding.UTF8, "application/json"));
}
}
}
copy
<LinearLayout
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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="Get Token"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="25px"
android:minHeight="150px"
android:id="@+id/btnGetToken" />
<Button
android:text="Send Notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="25px"
android:minHeight="150px"
android:visibility="invisible"
android:id="@+id/btnNotification" />
<Button
android:text="Send Data Message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="25px"
android:minHeight="150px"
android:visibility="invisible"
android:id="@+id/btnDataMessage" />
</LinearLayout>
Re- How to use Huawei Push Kit on Xamarin.Android
This is quite a helpful guide for the integration process.
{
"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"
}
"Hey, they say it's five centimeters per second. The falling speed of a cherry blossom petal. Five centimeters per second."
Upon hearing these famous lines from a well-known Japanese anime, John, a Huawei programmer, realized that cherry trees are currently blossoming.
John's girlfriend, Jenny, also loves cherry blossoms and planned to visit Paris's most famous park for cherry blossoms, Parc de Sceaux, on the weekend. John, unfortunately, was still on a business trip that weekend and could not go with his girlfriend.
So John said to himself, "How about I make an intelligent travel app, I am a programmer after all, for Jenny, so that she can enjoy the cherry blossoms in the best possible way?" John then listed the following requirements for the app he was about to quickly create:
l Considerate travel reminders: remind her of important events in advance in her schedule, such as when to depart.
l Weather forecast: provide suggestions on what to bring and wear based on the weather conditions at her destination.
l Push messages: push helpful tips and discount information to her once she arrives at the destination.
...
Luckily for John, the preceding capabilities can be implemented without hassle using the time and weather awareness capabilities of HUAWEI Awareness Kit, the geofence capabilities of HUAWEI Location Kit, and the message pushing capabilities of HUAWEI Push Kit.
OverviewAwareness Kit provides your app the ability to obtain contextual information including users' current time, location, behavior, headset status, weather, ambient light, car stereo connection status, and beacon connection status, which can be combined to create various barriers that run in the background and trigger once the predefined context is met.
Location Kit combines GNSS, Wi-Fi, and base station positioning capabilities into your app, allowing you to provide flexible location-based services for users around the world.
Push Kit is a messaging service tailored for developers, which helps create a cloud-to-device messaging channel. With Push Kit integrated, your app can send messages to users' devices in real time.
Code Development1. Awareness Kit IntegrationPreparationsThe following three key steps are required for integrating Awareness Kit. For details, please refer to the development guide on the HUAWEI Developers website.
1. Configure app information in AppGallery Connect.
2. Integrate the HMS Core Awareness SDK.
3. Configure obfuscation scripts.
Development Procedure 1. Declare required permissions in the AndroidManifest.xml file.
XML:
<p style="line-height: 1.5em;"><
uses-permission
android
:name
="android.permission.ACCESS_FINE_LOCATION"
/>
<
uses-permission
android
:name
="android.permission.ACCESS_BACKGROUND_LOCATION"
/></p>
2. Obtain weather information based on the city name.
Java:
String city = edCity.getText().toString();
if (city != null && !city.equals("")) {
WeatherPosition weatherPosition = new WeatherPosition();
weatherPosition.setCity(city);
// Pass the language type of the passed address. The value format is "Language_country", such as "zh_CN", "en_US".
weatherPosition.setLocale("en_US");
// Obtain the Capture Client of Awareness Kit, and call the weather query capability.
Awareness.getCaptureClient(getApplicationContext()).getWeatherByPosition(weatherPosition)
.addOnSuccessListener(new OnSuccessListener<WeatherStatusResponse>() {
@Override
public void onSuccess(WeatherStatusResponse weatherStatusResponse) {
// Process the returned weather data.
WeatherStatus weatherStatus = weatherStatusResponse.getWeatherStatus();
WeatherSituation weatherSituation = weatherStatus.getWeatherSituation();
Situation situation = weatherSituation.getSituation();
String weather;
// Match the weather ID with the weather.
weather = getApplicationContext().getResources().getStringArray(R.array.cnWeather)[situation.getCnWeatherId()];
// Update UI.
((TextView) findViewById(R.id.tv_weather)).setText(weather);
((TextView) findViewById(R.id.tv_windDir)).setText(situation.getWindDir());
((TextView) findViewById(R.id.tv_windSpeed)).setText(situation.getWindSpeed() + " km/h");
((TextView) findViewById(R.id.tv_temperature)).setText(situation.getTemperatureC() + "℃");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
3. Implement scheduled reminders and message pushing once a user arrives at the destination.
(1) Register a static broadcast receiver to receive notifications when the app is terminated.
The sample code in the AndroidManifest.xml file is as follows:
XML:
<receiver android:name=".BarrierReceiver">
<intent-filter>
<action android:name="com.test.awarenessdemo.TimeBarrierReceiver.BARRIER_RECEIVER_ACTION"/>
</intent-filter>
</receiver>
The Java sample code is as follows:
Java:
Intent intent = new Intent();
intent.setComponent(new ComponentName(MainActivity.this, BarrierReceiver.class));
mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
(2) Define the time barrier and corresponding label, then add the barrier.
Java:
// Obtain the entered time.
String timeHour = edTimeHour.getText().toString();
String timeMinute = edTimeMinute.getText().toString();
int hour = 0;
int minute = 0;
if (!timeHour.equals("")) {
hour = Integer.parseInt(timeHour);
if (!timeMinute.equals("")) {
minute = Integer.parseInt(timeMinute);
}
}
long oneHourMilliSecond = 60 * 60 * 1000L;
long oneMinuteMilliSecond = 60 * 1000L;
// Define the duringPeriodOfDay barrier to send notifications within a specified time period in a specified time zone.
AwarenessBarrier periodOfDayBarrier = TimeBarrier.duringPeriodOfDay(TimeZone.getDefault(),
// Set the notification time to two hours in advance.
(hour - 2) * oneHourMilliSecond + minute * oneMinuteMilliSecond,
hour * oneHourMilliSecond + minute * oneMinuteMilliSecond);
String timeBarrierLabel = "period of day barrier label";
// Define a request for updating a barrier.
BarrierUpdateRequest.Builder builder = new BarrierUpdateRequest.Builder();
BarrierUpdateRequest request = builder.addBarrier(timeBarrierLabel, periodOfDayBarrier, mPendingIntent).build();
Awareness.getBarrierClient(getApplicationContext()).updateBarriers(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
(3) Define the location barrier and corresponding label, then add the barrier.
Java:
if (city != null && !city.equals("")) {
// Obtain the longitude and latitude of the city based on the city name from the assets folder.
String data = cityMap.get(city);
if (data != null){
int flag = data.indexOf(",");
double latitude = Double.parseDouble(data.substring(flag+1));
double longitude = Double.parseDouble(data.substring(0,flag));
double radius = 50;
long timeOfDuration = 5000;
// Define the stay barrier. If a user enters a specified area and stays for a specified time period, a barrier event is triggered and reported.
AwarenessBarrier stayBarrier = LocationBarrier.stay(latitude, longitude, radius, timeOfDuration);
String stayBarrierLabel = "stay barrier label";
// Define a request for updating a barrier.
BarrierUpdateRequest.Builder builder = new BarrierUpdateRequest.Builder();
BarrierUpdateRequest request = builder.addBarrier(stayBarrierLabel, stayBarrier, mPendingIntent).build();
Awareness.getBarrierClient(getApplicationContext()).updateBarriers(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
}
(4) Define the broadcast receiver to listen for the barrier event for further processing
Java:
class BarrierReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String label = barrierStatus.getBarrierLabel();
int barrierPresentStatus = barrierStatus.getPresentStatus();
String city = intent.getStringExtra("city");
switch (label) {
case DURING_PERIOD_OF_DAT_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
initNotification(context,"1","time_channel","Travel reminder","Two hours before departure");
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
showToast(context, "It's not between ");
} else {
showToast(context, "The time status is unknown.");
}
break;
case STAY_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.TRUE) {
initNotification(context,"2","area_channel","Welcome to"+city,"View travel plans");
} else if (barrierPresentStatus == BarrierStatus.FALSE) {
showToast(context,"You are not staying in the area set by locationBarrier" +
" or the time of duration is not enough.");
} else {
showToast(context, "The location status is unknown.");
}
break;
}
}
}
2. Location-based Message Pushing
Preparations1. Add the Huawei Maven repository address to the build.gradle file in the root directory of your project. The sample code is as follows:
XML:
<p style="line-height: 1.5em;">buildscript {
repositories {
maven { url 'http://developer.huawei.com/repo/'}
}
dependencies {
...
// Add AppGallery Connect plugin configuration.
classpath 'com.huawei.agconnect:agcp:1.4.2.300'
}
}allprojects {
repositories {
maven { url 'http://developer.huawei.com/repo/'}
}
}</p>
2. Add dependencies on the Location and Push SDKs to the build.gradle file in the app directory of your project.
Java:
<p style="line-height: 1.5em;">dependencies {
implementation 'com.huawei.hms:location:5.0.2.300'
implementation 'com.huawei.hms:push: 5.0.2.301'
}</p>
Key Steps1. Declare system permissions in the AndroidManifest.xml file.
Location Kit incorporates GNSS, Wi-Fi, and base station positioning capabilities into your app. In order to do this, it requires the network, precise location, and coarse location permissions. 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" />
Note: The ACCESS_FINE_LOCATION, WRITE_EXTERNAL_STORAGE, and READ_EXTERNAL_STORAGE permissions are dangerous system permissions, so you need to dynamically apply for these permissions. If your app does not have the permissions, Location Kit will be unable to provide services for your app.
2. Create a geofence and trigger it.
Create a geofence or geofence group as needed, and set related parameters.
Java:
LocationSettingsRequest.Builder builders = new LocationSettingsRequest.Builder();
builders.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builders.build();
// Before requesting location update, call checkLocationSettings to check device settings.
Task<LocationSettingsResponse> locationSettingsResponseTasks = mSettingsClient.checkLocationSettings(locationSettingsRequest);
locationSettingsResponseTasks.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
Log.i(TAG, "check location settings success");
mFusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallbacks, Looper.getMainLooper())
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
LocationLog.i(TAG, "geoFence onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
LocationLog.e(TAG,
"geoFence onFailure:" + e.getMessage());
}
});
}
})
3. Trigger message pushing.
Send a push message when onReceive of GeoFenceBroadcastReceiver detects that the geofence is triggered successfully. The message will be displayed in the notification panel on the device.
Java:
if (geofenceData != null) {
int errorCode = geofenceData.getErrorCode();
int conversion = geofenceData.getConversion();
ArrayList<Geofence> list = (ArrayList<Geofence>) geofenceData.getConvertingGeofenceList();
Location myLocation = geofenceData.getConvertingLocation();
boolean status = geofenceData.isSuccess();
sb.append("errorcode: " + errorCode + next);
sb.append("conversion: " + conversion + next);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
sb.append("geoFence id :" + list.get(i).getUniqueId() + next);
}
}
if (myLocation != null) {
sb.append("location is :" + myLocation.getLongitude() + " " + myLocation.getLatitude() + next);
}
sb.append("is successful :" + status);
LocationLog.i(TAG, sb.toString());
Toast.makeText(context, "" + sb.toString(), Toast.LENGTH_LONG).show();
//
new PushSendUtils().netSendMsg(sb.toString());
}
Note: The geofence created using the sample code will trigger two callbacks for conversion types 1 and 4. One is triggered when a user enters the geofence and the other is triggered when the user stays in the geofence. If Trigger is set to 7 in the code, callbacks for all scenarios, including entering, staying, and leaving the geofence, are configured.
Let's see this Demo:
For more details, you can go to:
l Our official website
l Our Development Documentation page, to find the documents you need
l Experience the easy-integration process on Codelabs
l GitHub to download demos and sample codes
l Stack Overflow to solve any integration problem
| Original Source
This is so nice, and a great app too.
{
"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
HarmonyOs is a next-generation operating system that empowers interconnection and collaboration between smart devices. It delivers smooth simple interaction that is reliable in all scenarios.
SQLite is an open-source relational database which is used to perform database operations on devices such as storing, manipulating or retrieving persistent data from the database.
HarmonyOs uses SQLite DB for managing local database and called it is as HarmonyOs RDB (relational database).
Takeaways
Integrate HarmonyOs RDB in the application.
Navigate from one Ability Slice to another and sending data while doing it.
Learn to create UI using Directional Layout.
Default and customize Dialog.
Providing background color to buttons or layout programmatically.
HarmonyOs Animation.
Demo
To understand how HarmonyOs works with SQLite DB, I have created a Quiz App and inserted all the questions data using SQLite database as shown below:
Integrating HarmonyOs RDB
Step 1: Create Questions model (POJO) class.
Java:
public class Questions {
private int id;
private String topic;
private String question;
private String optionA;
private String optionB;
private String optionC;
private String optionD;
private String answer;
public Questions(String topc, String ques, String opta, String optb, String optc, String optd, String ans) {
topic = topc;
question = ques;
optionA = opta;
optionB = optb;
optionC = optc;
optionD = optd;
answer = ans;
}
public Questions() {
id = 0;
topic = "";
question = "";
optionA = "";
optionB = "";
optionC = "";
optionD = "";
answer = "";
}
public void setId(int id) {
this.id = id;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getOptionA() {
return optionA;
}
public void setOptionA(String optionA) {
this.optionA = optionA;
}
public String getOptionB() {
return optionB;
}
public void setOptionB(String optionB) {
this.optionB = optionB;
}
public String getOptionC() {
return optionC;
}
public void setOptionC(String optionC) {
this.optionC = optionC;
}
public String getOptionD() {
return optionD;
}
public void setOptionD(String optionD) {
this.optionD = optionD;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
}
Step 2: Create a class and name it as QuizDatabaseHelper.
Step 3: Extends the class with DatabaseHelper class.
Step 4: After that we need to configure the RDB store. For that we need to use StoreConfig.
Java:
StoreConfig config = StoreConfig.newDefaultConfig("QuizMania.db");
Step 5: Use RdbOpenCallback abstract class to create the table and if we need to modify the table, we can use this class to upgrade the version of the Database to avoid crashes.
Java:
RdbOpenCallback callback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore store) {
store.executeSql("CREATE TABLE " + TABLE_NAME + " ( " + ID + " INTEGER PRIMARY KEY AUTOINCREMENT , " + TOPIC + " VARCHAR(255), " + QUESTION + " VARCHAR(255), " + OPTIONA + " VARCHAR(255), " + OPTIONB + " VARCHAR(255), " + OPTIONC + " VARCHAR(255), " + OPTIOND + " VARCHAR(255), " + ANSWER + " VARCHAR(255))");
}
@Override
public void onUpgrade(RdbStore store, int oldVersion, int newVersion) {
}
};
Step 6: Use DatabaseHelper class to obtain the RDB store.
Java:
DatabaseHelper helper = new DatabaseHelper(context);
store = helper.getRdbStore(config, 1, callback, null);
Step 7: In order to insert question data we will use ValueBucket of RDB.
Java:
private void insertAllQuestions(ArrayList<Questions> allQuestions){
ValuesBucket values = new ValuesBucket();
for(Questions question : allQuestions){
values.putString(TOPIC, question.getTopic());
values.putString(QUESTION, question.getQuestion());
values.putString(OPTIONA, question.getOptionA());
values.putString(OPTIONB, question.getOptionB());
values.putString(OPTIONC, question.getOptionC());
values.putString(OPTIOND, question.getOptionD());
values.putString(ANSWER, question.getAnswer());
long id = store.insert("QUIZMASTER", values);
}
}
Step 8: In order to retrieve all the question data we will use RdbPredicates and ResultSet. RdbPredicates helps us to combine SQL statements simply by calling methods using this class, such as equalTo, notEqualTo, groupBy, orderByAsc, and beginsWith. ResultSet on the other hand helps us to retrieve the data that we have queried.
Java:
public List<Questions> getAllListOfQuestions(String topicName) {
List<Questions> questionsList = new ArrayList<>();
String[] columns = new String[] {ID, TOPIC, QUESTION, OPTIONA,OPTIONB,OPTIONC,OPTIOND,ANSWER};
RdbPredicates rdbPredicates = new RdbPredicates(TABLE_NAME).equalTo(TOPIC, topicName);
ResultSet resultSet = store.query(rdbPredicates, columns);
while (resultSet.goToNextRow()){
Questions question = new Questions();
question.setId(resultSet.getInt(0));
question.setTopic(resultSet.getString(1));
question.setQuestion(resultSet.getString(2));
question.setOptionA(resultSet.getString(3));
question.setOptionB(resultSet.getString(4));
question.setOptionC(resultSet.getString(5));
question.setOptionD(resultSet.getString(6));
question.setAnswer(resultSet.getString(7));
questionsList.add(question);
}
return questionsList;
}
Step 9: Let's call the QuizDatabaseHelper class in Ability Slice and get all the question from the stored database.
Java:
QuizDatabaseHelper quizDatabaseHelper = new QuizDatabaseHelper(getContext());
quizDatabaseHelper.initDb();
if (quizDatabaseHelper.getAllListOfQuestions(topicName).size() == 0) {
quizDatabaseHelper.listOfAllQuestion();
}
List<Questions> list = quizDatabaseHelper.getAllListOfQuestions(topicName);
Collections.shuffle(list);
Questions questionObj = list.get(questionId);
QuizDatabaseHelper.java
Java:
public class QuizDatabaseHelper extends DatabaseHelper {
Context context;
StoreConfig config;
RdbStore store;
private static final String TABLE_NAME = "QUIZMASTER";
private static final String ID = "_ID";
private static final String TOPIC = "TOPIC";
private static final String QUESTION = "QUESTION";
private static final String OPTIONA = "OPTIONA";
private static final String OPTIONB = "OPTIONB";
private static final String OPTIONC = "OPTIONC";
private static final String OPTIOND = "OPTIOND";
private static final String ANSWER = "ANSWER";
public QuizDatabaseHelper(Context context) {
super(context);
this.context = context;
}
public void initDb(){
config = StoreConfig.newDefaultConfig("QuizMania.db");
RdbOpenCallback callback = new RdbOpenCallback() {
@Override
public void onCreate(RdbStore store) {
store.executeSql("CREATE TABLE " + TABLE_NAME + " ( " + ID + " INTEGER PRIMARY KEY AUTOINCREMENT , " + TOPIC + " VARCHAR(255), " + QUESTION + " VARCHAR(255), " + OPTIONA + " VARCHAR(255), " + OPTIONB + " VARCHAR(255), " + OPTIONC + " VARCHAR(255), " + OPTIOND + " VARCHAR(255), " + ANSWER + " VARCHAR(255))");
}
@Override
public void onUpgrade(RdbStore store, int oldVersion, int newVersion) {
}
};
DatabaseHelper helper = new DatabaseHelper(context);
store = helper.getRdbStore(config, 1, callback, null);
}
public void listOfAllQuestion() {
// Generic type is Questions POJO class.
ArrayList<Questions> arraylist = new ArrayList<>();
// General Knowledge Questions...
arraylist.add(new Questions("gk","India has largest deposits of ____ in the world.", "Gold", "Copper", "Mica", "None of the above", "Mica"));
arraylist.add(new Questions("gk","Who was known as Iron man of India ?", "Govind Ballabh Pant", "Jawaharlal Nehru", "Subhash Chandra Bose", "Sardar Vallabhbhai Patel", "Sardar Vallabhbhai Patel"));
arraylist.add(new Questions("gk", "India participated in Olympics Hockey in", "1918", "1928", "1938", "1948", "1928"));
arraylist.add(new Questions("gk","Who is the Flying Sikh of India ?", "Mohinder Singh", "Joginder Singh", "Ajit Pal Singh", "Milkha singh", "Milkha singh"));
arraylist.add(new Questions("gk","How many times has Brazil won the World Cup Football Championship ?", "Four times", "Twice", "Five times", "Once", "Five times"));
// Sports Questions..
arraylist.add(new Questions("sp","Which was the 1st non Test playing country to beat India in an international match ?", "Canada", "Sri Lanka", "Zimbabwe", "East Africa", "Sri Lanka"));
arraylist.add(new Questions("sp","Ricky Ponting is also known as what ?", "The Rickster", "Ponts", "Ponter", "Punter", "Punter"));
arraylist.add(new Questions("sp","India won its first Olympic hockey gold in...?", "1928", "1932", "1936", "1948", "1928"));
arraylist.add(new Questions("sp","The Asian Games were held in Delhi for the first time in...?", "1951", "1963", "1971", "1982", "1951"));
arraylist.add(new Questions("sp","The 'Dronacharya Award' is given to...?", "Sportsmen", "Coaches", "Umpires", "Sports Editors", "Coaches"));
// History Questions...
arraylist.add(new Questions("his","The Battle of Plassey was fought in", "1757", "1782", "1748", "1764", "1757"));
arraylist.add(new Questions("his","The title of 'Viceroy' was added to the office of the Governor-General of India for the first time in", "1848 AD", "1856 AD", "1858 AD", "1862 AD", "1858 AD"));
arraylist.add(new Questions("his","Tipu sultan was the ruler of", "Hyderabad", "Madurai", "Mysore", "Vijayanagar", "Mysore"));
arraylist.add(new Questions("his","The Vedas contain all the truth was interpreted by", "Swami Vivekananda", "Swami Dayananda", "Raja Rammohan Roy", "None of the above", "Swami Dayananda"));
arraylist.add(new Questions("his","The Upanishads are", "A source of Hindu philosophy", "Books of ancient Hindu laws", "Books on social behavior of man", "Prayers to God", "A source of Hindu philosophy"));
// General Science Questions...
arraylist.add(new Questions("gs","Which of the following is a non metal that remains liquid at room temperature ?", "Phosphorous", "Bromine", "Chlorine", "Helium", "Bromine"));
arraylist.add(new Questions("gs","Which of the following is used in pencils?", "Graphite", "Silicon", "Charcoal", "Phosphorous", "Graphite"));
arraylist.add(new Questions("gs","The gas usually filled in the electric bulb is", "Nitrogen", "Hydrogen", "Carbon Dioxide", "Oxygen", "Nitrogen"));
arraylist.add(new Questions("gs","Which of the gas is not known as green house gas ?", "Methane", "Nitrous oxide", "Carbon dioxide", "Hydrogen", "Hydrogen"));
arraylist.add(new Questions("gs","The hardest substance available on earth is", "Gold", "Iron", "Diamond", "Platinum", "Diamond"));
this.insertAllQuestions(arraylist);
}
private void insertAllQuestions(ArrayList<Questions> allQuestions){
ValuesBucket values = new ValuesBucket();
for(Questions question : allQuestions){
values.putString(TOPIC, question.getTopic());
values.putString(QUESTION, question.getQuestion());
values.putString(OPTIONA, question.getOptionA());
values.putString(OPTIONB, question.getOptionB());
values.putString(OPTIONC, question.getOptionC());
values.putString(OPTIOND, question.getOptionD());
values.putString(ANSWER, question.getAnswer());
long id = store.insert("QUIZMASTER", values);
}
}
public List<Questions> getAllListOfQuestions(String topicName) {
List<Questions> questionsList = new ArrayList<>();
String[] columns = new String[] {ID, TOPIC, QUESTION, OPTIONA,OPTIONB,OPTIONC,OPTIOND,ANSWER};
RdbPredicates rdbPredicates = new RdbPredicates(TABLE_NAME).equalTo(TOPIC, topicName);
ResultSet resultSet = store.query(rdbPredicates, columns);
while (resultSet.goToNextRow()){
Questions question = new Questions();
question.setId(resultSet.getInt(0));
question.setTopic(resultSet.getString(1));
question.setQuestion(resultSet.getString(2));
question.setOptionA(resultSet.getString(3));
question.setOptionB(resultSet.getString(4));
question.setOptionC(resultSet.getString(5));
question.setOptionD(resultSet.getString(6));
question.setAnswer(resultSet.getString(7));
questionsList.add(question);
}
return questionsList;
}
}
HarmonyOs Navigation
An Ability Slice represents a single screen and its control logic. In terms of Android, it is like a Fragment and Page Ability is like an Activity in Android. An ability slice's lifecycle is bound to the Page ability that hosts it.
Now, if we need to navigate with data from one Ability Slice to another, we need to use present method of HarmonyOs.
Java:
public final void present(AbilitySlice targetSlice, Intent intent) {
throw new RuntimeException("Stub!");
}
GameAbilitySlice.java
Java:
private void goToQuizPage(String topic){
Intent intent = new Intent();
intent.setParam("TEST_KEY", topic);
present(new QuizAbilitySlice(), intent);
}
Here the targetSlice is QuizAbilitySlice.
QuizAbilitySlice.java
Java:
String topicName = intent.getStringParam("TEST_KEY");
Here we getting the value from the source Ability Slice.
HarmonyOs User Interface
Layouts
There six layouts available in HarmonyOs:
DirectionalLayout
DependentLayout
StackLayout
TableLayout
PositionLayout
AdaptiveBoxLayout
We will be using DirectionalLayout for our UI. In terms of Android, it is like LinearLayout. It has orientation, weight and many more which we will find in LinearLayout as well.
Text and Button Components
Yes you heard it right. Any widget in HarmonyOs is treated as Component. Here Text as well Button are Component of HarmonyOs. As HarmonyOs uses XML for UI, all those XML properties which we see in Android can be use here. The only difference which we will find here is providing the background colour to Buttons or Layout. In order to provide background colour, we need to create a graphic XML file under the graphic folder of resource.
btn_option.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="20"/>
<solid
ohos:color="#2c3e50"/>
</shape>
After that we will use button_option.xml file as background colour for buttons using background_element property.
Java:
<Button
ohos:id="$+id:btnD"
ohos:height="80fp"
ohos:width="match_parent"
ohos:margin="10fp"
ohos:text_color="#ecf0f1"
ohos:text_size="30fp"
ohos:text="Gold"
ohos:background_element="$graphic:btn_option"/>
ability_quiz.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<DirectionalLayout
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical"
ohos:weight="0.5"
ohos:alignment="center"
ohos:background_element="$graphic:background_question_area">
<Text
ohos:id="$+id:txtQuestion"
ohos:height="match_content"
ohos:width="match_content"
ohos:text_alignment="center"
ohos:multiple_lines="true"
ohos:margin="20fp"
ohos:text_size="40vp"
ohos:text="Question"
/>
</DirectionalLayout>
<DirectionalLayout
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical"
ohos:alignment="center"
ohos:weight="1">
<Button
ohos:id="$+id:btnA"
ohos:height="80fp"
ohos:width="match_parent"
ohos:margin="10fp"
ohos:text_color="#ecf0f1"
ohos:text_size="30fp"
ohos:text="Gold"
ohos:background_element="$graphic:btn_option"
/>
<Button
ohos:id="$+id:btnB"
ohos:height="80fp"
ohos:width="match_parent"
ohos:margin="10fp"
ohos:text_color="#ecf0f1"
ohos:text_size="30fp"
ohos:text="Gold"
ohos:background_element="$graphic:btn_option"
/>
<Button
ohos:id="$+id:btnC"
ohos:height="80fp"
ohos:width="match_parent"
ohos:margin="10fp"
ohos:text_color="#ecf0f1"
ohos:text_size="30fp"
ohos:text="Gold"
ohos:background_element="$graphic:btn_option"
/>
<Button
ohos:id="$+id:btnD"
ohos:height="80fp"
ohos:width="match_parent"
ohos:margin="10fp"
ohos:text_color="#ecf0f1"
ohos:text_size="30fp"
ohos:text="Gold"
ohos:background_element="$graphic:btn_option"
/>
</DirectionalLayout>
</DirectionalLayout>
HarmonyOs Dialogs
There are five Dialog available in HarmonyOs to use:
DisplayDialog
CommonDialog
BaseDialog
PopupDialog
ListDialog
ToastDialog
We will be using CommonDialog to show default as well as customize dialog in our application. Dialog in HarmonyOs is also known as Component. CommonDialog helps us to provide Button like functionality as we see in Android Dialogs.
Default CommonDialog
Java:
private void wrongAnsDialog(){
CommonDialog commonDialog = new CommonDialog(getContext());
commonDialog.setTitleText("WRONG ANSWER");
commonDialog.setSize(1000,300);
commonDialog.setButton(1, "OKAY", new IDialog.ClickedListener() {
@Override
public void onClick(IDialog iDialog, int i) {
commonDialog.hide();
present(new GameAbilitySlice(), new Intent());
}
});
commonDialog.show();
}
Customize CommonDialog
Java:
private void correctAnsDialog(){
CommonDialog commonDialog = new CommonDialog(getContext());
DependentLayout dependentLayout = new DependentLayout (getContext());
dependentLayout.setWidth(DependentLayout.LayoutConfig.MATCH_PARENT);
dependentLayout.setHeight(DependentLayout.LayoutConfig.MATCH_PARENT);
dependentLayout.setBackground(new ShapeElement(this,ResourceTable.Graphic_correct_dialog));
Text text = new Text(getContext());
text.setText("CORRECT ANSWER");
text.setTextSize(60);
text.setTextColor(Color.WHITE);
DependentLayout.LayoutConfig textConfig = new DependentLayout.LayoutConfig(DependentLayout.LayoutConfig.MATCH_CONTENT,
DependentLayout.LayoutConfig.MATCH_CONTENT);
textConfig.addRule(DependentLayout.LayoutConfig.CENTER_IN_PARENT);
textConfig.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_TOP);
text.setLayoutConfig(textConfig);
Button btnNext = new Button(getContext());
btnNext.setText("NEXT QUESTION");
btnNext.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
commonDialog.hide();
questionId++;
questionObj = list.get(questionId);
onNextQuestionAndOption();
resetButtonColors();
enableAllButtons();
}
});
btnNext.setBackground(new ShapeElement(this,ResourceTable.Graphic_btn_next));
btnNext.setTextColor(Color.BLACK);
btnNext.setPadding(20,20,20,20);
btnNext.setTextSize(50);
DependentLayout.LayoutConfig btnConfig = new DependentLayout.LayoutConfig(DependentLayout.LayoutConfig.MATCH_PARENT,
DependentLayout.LayoutConfig.MATCH_CONTENT);
btnConfig.addRule(DependentLayout.LayoutConfig.CENTER_IN_PARENT);
btnConfig.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_BOTTOM);
btnNext.setLayoutConfig(btnConfig);
dependentLayout.addComponent(text);
dependentLayout.addComponent(btnNext);
commonDialog.setContentCustomComponent(dependentLayout);
commonDialog.setSize(1000,300);
commonDialog.show();
}
Programmatically changing color
In order to change color programmatically to buttons or layout we use ShapeElement class.
Java:
// For Buttons …
private void resetButtonColors() {
btnA.setBackground(new ShapeElement(this,ResourceTable.Graphic_btn_option));
btnB.setBackground(new ShapeElement(this,ResourceTable.Graphic_btn_option));
btnC.setBackground(new ShapeElement(this,ResourceTable.Graphic_btn_option));
btnD.setBackground(new ShapeElement(this,ResourceTable.Graphic_btn_option));
}
// For Layouts …
DependentLayout dependentLayout = new DependentLayout (getContext());
dependentLayout.setWidth(DependentLayout.LayoutConfig.MATCH_PARENT);
dependentLayout.setHeight(DependentLayout.LayoutConfig.MATCH_PARENT);
dependentLayout.setBackground(new ShapeElement(this,ResourceTable.Graphic_correct_dialog));
Here ResourceTable is treated same as R in Android.
HarmonyOs Animation
HarmonyOs provides three major classes for animation:
FrameAnimationElement
AnimatorValue
AnimatorProperty
AnimatorGroup.
We will be using AnimatorProperty to do our animation in our splash screen.
Step 1: We need to create AnimatorProperty Object.
Java:
AnimatorProperty topAnim = logImg.createAnimatorProperty();
topAnim.alphaFrom((float) 0.1).alpha((float) 1.0).moveFromY(0).moveToY(700).setDuration(2000);
Here logImg is an Image.
Step 2: Create animator_property.xml file in resource/base/animation folder.
Code:
<?xml version="1.0" encoding="UTF-8" ?>
<animator xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:duration="2000"/>
Step 3: Parse the animator_property.xml file and use its configuration using AnimatorScatter class.
Java:
AnimatorScatter scatter = AnimatorScatter.getInstance(getContext());
Animator animator = scatter.parse(ResourceTable.Animation_topanim);
if (animator instanceof AnimatorProperty) {
topAnim = (AnimatorProperty) animator;
topAnim.setTarget(logImg);
topAnim.moveFromY(0).moveToY(700);
}
logImg.setBindStateChangedListener(new Component.BindStateChangedListener() {
@Override
public void onComponentBoundToWindow(Component component) {
topAnim.start();
}
@Override
public void onComponentUnboundFromWindow(Component component) {
topAnim.stop();
}
});
Step 4: Start Animation
Java:
topAnim.start();
Tips & Tricks
Kindly follow my article, my entire article is full of tips & tricks. I have also mentioned Android keywords to make android developers familiar with the terminology of HarmonyOs.
Conclusion
In this article, we learn how to integrate SQLite DB in HarmonyOs application. Now you can use this knowledge and create application such as Library Management, School Management, Games etc.
Feel free to comment, share and like the article. Also you can follow me to get awesome article like this every week.
For more reference
https://developer.harmonyos.com/en/docs/documentation/doc-guides/database-relational-overview-0000000000030046
https://developer.harmonyos.com/en/docs/documentation/doc-guides/ui-java-overview-0000000000500404
Original Source