Site Kit (Nearby Search, Place Details & Place Search Suggestion) - Huawei Developers

More articles like this, you can visit HUAWEI Developer Forum
​
Before starting this article follow previous article Site Kit (Keyword Search) for better understanding and project set up.
In this article find the following concepts.
· Nearby Place Search
· Place Details
· Place Search suggestion
Nearby Place Search: Huawei Site kit feature helps to get the nearby places using the current location of the user. For the nearby search we can set the POI (Point of Interest) where results can be filtered based on POI. User can search nearby Bakery, School, ATM etc.
Follow the steps.
Step 1: Create SearchService Object from the SearchServiceFactory
Code:
searchService = SearchServiceFactory.create(getActivity(), API_KEY);
Step 2: Create the NearBySearchRequest object which is used to request body for nearby search.
Code:
NearbySearchRequest request = new NearbySearchRequest();
Parameters of Nearby Place
Mandatory
· location: current location of a user.
Code:
Coordinate location = new Coordinate(12.9716, 77.5946);
Optional
· radius: search radius, in meters. The value ranges from 1 to 50000 meters. The default value is 1000mtr.
· query: search keyword.
· poiTypes: list of POI types.
· language: language in which search results are returned. For details about the value range, refer to language codes in Language Mapping. If this parameter is not passed, the language of the query field (preferred) or the local language is used.
· politicalView: Political view parameter. The value is a two-digit country code specified in the ISO 3166-1-alpha-2 standard.
· pageSize: number of records on each page. The value ranges from 1 to 20. The default value is 20.
· pageIndex: number of the current page. The value ranges from 1 to 60. The default value is 1.
Step 3: Create the SearchResultListener object to get the nearby place results.
Step 4: Use the SearchService Object (refer Step 1) to call the nearbySearch() and pass the nearby service request (refer Step 2) and also pass the SearchResultListener (refer Step 3).
Step 5: Get the nearby search response
Code:
private void findPlaceAroundMe() {
NearbySearchRequest request = new NearbySearchRequest();
Coordinate location = new Coordinate(12.9716, 77.5946);
request.setLocation(location);
request.setQuery("India");
request.setRadius(5000);
request.setPoiType(LocationType.ADDRESS);
request.setLanguage("In");
request.setPageIndex(1);
request.setPageSize(5);
// Create a search result listener.
SearchResultListener<NearbySearchResponse> resultListener = new SearchResultListener<NearbySearchResponse>() {
// Return search results upon a successful search.
@Override
public void onSearchResult(NearbySearchResponse results) {
AddressDetail addressDetail;
List<Site> sites = results.getSites();
if (results == null || results.getTotalCount() <= 0 || sites == null || sites.size() <= 0) {
return;
}
for (Site site : sites) {
Log.i("TAG", String.format("siteId: '%s', name: %s\r\n", site.getSiteId(), site.getName()));
}
if (results.getSites() != null && results.getSites().size() > 0) {
for (Site site : results.getSites()) {
searchModel = new SearchModel();
addressDetail = site.getAddress();
searchModel.setName(site.getName());
searchModel.setFormattedAddress(site.getFormatAddress());
searchModel.setCountry(addressDetail.getCountry());
searchModel.setCountryCode(addressDetail.getCountryCode());
searchModelList.add(searchModel);
}
SearchListAdapter searchListAdapter = new SearchListAdapter(searchModelList, getActivity());
searchResultList.setAdapter(searchListAdapter);
}
}
// Return the result code and description upon a search exception.
@Override
public void onSearchError(SearchStatus status) {
Log.i("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage());
}
};
// Call the nearby place search API.
searchService.nearbySearch(request, resultListener);
}
Place Details: Huawei Site kit feature helps to search for details about a place based on the unique ID (Site Id) of the place. SiteId can get from keyword or nearby or Place Suggestion search.
In Place details we can get the location name, formatted address, location website, location postal code, location phone numbers, and list of location images URL etc.
Follow the steps.
Step 1: Create SearchService Object from the SearchServiceFactory
Code:
searchService = SearchServiceFactory.create(getActivity(), API_KEY);
Step 2: Create the DetailSearchRequest object which is used to request body for place details search.
Code:
DetailSearchRequest request = new DetailSearchRequest();
Parameters of Place Details
Mandatory
· siteId: ID of a place.
Optional
language: language in which search results are returned. For details about the value range, refer to language codes in Language Mapping. If this parameter is not passed, the local language is used.
politicalView: Political view parameter. The value is a two-digit country code specified in the ISO 3166-1-alpha-2 standard.
Step 3: Create the SearchResultListener object to get the place detail results.
Step 4: Use the SearchService Object (refer Step 1) to call the detailSearch () and pass the detail search request (refer Step 2) and also pass the SearchResultListener (refer Step 3).
Step 5: Get the detail search response.
Code:
StringBuilder stringBuilder;
private void getDetailResultOfPlaceUsingSiteId(String siteId) {
DetailSearchRequest request = new DetailSearchRequest();
request.setSiteId(siteId);
request.setLanguage("En");
// Create a search result listener.
SearchResultListener<DetailSearchResponse> resultListener = new SearchResultListener<DetailSearchResponse>() {
// Return search results upon a successful search.
@Override
public void onSearchResult(DetailSearchResponse results) {
Site site = results.getSite();
if (results == null || site == null) {
return;
}
stringBuilder = new StringBuilder();
stringBuilder.append("Site Id: " + results.getSite().getSiteId() + "\n");
stringBuilder.append("Name: " + results.getSite().getName() + "\n");
stringBuilder.append("Formatted Address: " + results.getSite().getFormatAddress() + "\n");
stringBuilder.append("Distance: " + results.getSite().getDistance() + "\n");
stringBuilder.append("Country: " + results.getSite().getAddress().getCountry() + "\n");
stringBuilder.append("Country Code: " + results.getSite().getAddress().getCountryCode() + "\n");
stringBuilder.append("Locality: " + results.getSite().getAddress().getLocality() + "\n");
stringBuilder.append("Sub locality: " + results.getSite().getAddress().getSubLocality() + "\n");
/* stringBuilder.append("Postal Code: "+results.getSite().getAddress().getPostalCode()+"\n");
stringBuilder.append("Phone: "+results.getSite().getPoi().getPhone()+"\n");
stringBuilder.append("Website: "+results.getSite().getPoi().getWebsiteUrl()+"\n");
stringBuilder.append("Rating: "+results.getSite().getPoi().getRating()+"\n");*/
resultTv.setText(stringBuilder.toString());
Log.i("TAG", String.format("siteId: '%s', name: %s\r\n", site.getSiteId(), site.getName()));
}
// Return the result code and description upon a search exception.
@Override
public void onSearchError(SearchStatus status) {
Log.i("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage());
}
};
// Call the nearby place search API.
searchService.detailSearch(request, resultListener);
}
Place Search Suggestion: This Huawei Site kit feature helps us to return search suggestions during the user input.
Follow the steps.
Step 1: Create SearchService Object from the SearchServiceFactory
Code:
searchService = SearchServiceFactory.create(getActivity(), API_KEY);
Step 2: Create the QuerySuggestionRequest object which is used to request body for place details search.
Code:
QuerySuggestionRequest request = new QuerySuggestionRequest();
Parameters of Place Search Suggestion
Mandatory
· query: search keyword.
Optional
location: longitude and latitude to which search results need to be biased.
radius: search radius, in meters. The value ranges from 1 to 50000meters. The default value is 50000meters.
poiTypes: list of POI types. The options are as follows:
{
"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"
}
countryCode: country code, which complies with the ISO 3166-1 alpha-2 standards. This parameter is used to restrict search results to the specified country.
language: language in which search results are returned. For details about the value range, refer to language codes in Language Mapping. If this parameter is not passed, the language of the query field (preferred) or the local language is used.
politicalView: Political view parameter. The value is a two-digit country code specified in the ISO 3166-1-alpha-2 standard.
Step 3: Create the SearchResultListener object to get the place search suggestion results.
Step 4: Use the SearchService Object (refer Step 1) to call the querySuggestion() and pass the query suggestion request (refer Step 2) and also pass the SearchResultListener (refer Step 3).
Step 5: Get the place suggestion search response
Code:
private void getQuerySuggestionResult(String query) {
QuerySuggestionRequest request = new QuerySuggestionRequest();
request.setQuery(query);
Coordinate location = new Coordinate(12.9716, 77.5946);
request.setLocation(location);
request.setRadius(1000);
request.setCountryCode("In");
request.setLanguage("In");
// Create a search result listener.
SearchResultListener<QuerySuggestionResponse> resultListener = new SearchResultListener<QuerySuggestionResponse>() {
// Return search results upon a successful search.
@Override
public void onSearchResult(QuerySuggestionResponse results) {
List<Site> sites = results.getSites();
if (results == null || sites == null || sites.size() <= 0) {
return;
}
AddressDetail addressDetail;
if (results.getSites() != null && results.getSites().size() > 0) {
for (Site site : results.getSites()) {
searchModel = new SearchModel();
addressDetail = site.getAddress();
searchModel.setName(site.getName());
searchModel.setFormattedAddress(site.getFormatAddress());
searchModel.setCountry(addressDetail.getCountry());
searchModel.setCountryCode(addressDetail.getCountryCode());
searchModelList.add(searchModel);
}
SearchListAdapter searchListAdapter = new SearchListAdapter(searchModelList, getActivity());
searchResultList.setAdapter(searchListAdapter);
}
}
// Return the result code and description upon a search exception.
@Override
public void onSearchError(SearchStatus status) {
Log.i("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage());
}
};
// Call the search suggestion API.
searchService.querySuggestion(request, resultListener);
}
Conclusion:
Huawei Site kit provides the following feature
· keyword search
· Nearby place search
· Place details
· Place suggestion search
From the above feature we can build application with location and place related application, we can also show all the place over Huawei map. Also search the place in the Huawei map using Site Kit.
Follow previous article Keyword Search
Result:

Related

It’s Show Time App using Huawei Location & Site Kit

More information like this, you can visit HUAWEI Developer Forum​
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
An Idea
The Idea behind creating this article is to showcase the power of Huawei Kits. In this article we are going to combine four kits that is Huawei Auth Service, Account kit, Location kit and Site kit to create a movie booking app. Earlier Sujith has already talked about integrating Huawei Auth Service and Account Kit. Refer his article in order to see our process of integrating both kits.
Introduction
Huawei Location Kit combines the GPS, Wi-Fi, and base station location functionalities into our app to build up global positioning capabilities, allowing us to provide flexible location-based services targeted at users around the globe. Currently, it provides three main capabilities: fused location, activity identification, and geofence.
With Huawei Site Kit, your app can provide users with convenient and secure access to diverse, place-related services.
Use Case
We will get user current location using location kit and use this location in site kit to get nearby movie theatres.
In this article, we will see how we have use these two kits and combine them in order to create a movie booking app.
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 we can refer to this article.
3) Create an app in the Huawei app gallery connect.
4) Provide the SHA Key in App Information Section.
5) Provide storage location.
6) Enable Site Kit in Manage API Section.
7) 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.
8) Enter below class path inside the dependencies of buildscript ( project build.gradle file )
Code:
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
9) Enter below maven url inside the repositories of buildscript and allprojects ( project build.gradle file )
Code:
maven { url 'http://developer.huawei.com/repo/' }
10) Enter below plugin in the app build.gradle file dependencies section.
Code:
implementation 'com.huawei.hms:location:4.0.2.300'
implementation 'com.huawei.hms:site:5.0.0.300'
11) If the project is using progaurd, enter the below code in the progaurd-rules.pro file.
Code:
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
12) The HUAWEI Ads SDK requires the following permissions:
Code:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
13) Now sync the app.
Demo
In this demo, user will login using their Huawei ID. After that they will view the list of movies released in their respective cities. Once user selects a movie for booking purpose, user can see theatres near to their respective location. After selecting the theatre, user can see seats available for booking.
Getting user location
1) We need to check for permission.
Code:
private void grantPermission(){
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "sdk < 28 Q");
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed");
}
}
if (requestCode == 2) {
if (grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed");
}
}
}
2) In order to get user current location, create a FusedLocationProviderClient instance using the onCreate() method of Activity and use the instance to call location-related APIs.
Code:
FusedLocationProviderClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
3) The location function of Huawei Location Kit depends on the device location settings. For example, if the location function is disabled on a device, your app cannot obtain the device location. So, it is recommended that your app check whether the device settings meet the location requirements before continuously obtaining the device location. Huawei Location Kit provides the function of checking device location settings. In order to do that, obtain the SettingsClient instance using getSettingsClient(Activity activity) of LocationServices and call the checkLocationSettings(LocationSettingsRequest locationSettingsRequest) API to obtain the device location settings.
Code:
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
4) Create a location information request.
Code:
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setNeedAddress(true);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
5) Finally we need to get last location with address using FusedLocationProviderClient, so that we will be able to make user aware of their city also we need latitude and longitude of their current location in order to get nearby theatres using Huawei Site Kit.
Code:
fusedLocationProviderClient.getLastLocationWithAddress(mLocationRequest)
.addOnSuccessListener(new OnSuccessListener<HWLocation>() {
@SuppressLint("SetTextI18n")
@Override
public void onSuccess(HWLocation hwLocation) {
System.out.println("CITY >>> " + hwLocation.getCity());
lat = hwLocation.getLatitude();
lon = hwLocation.getLongitude();
txtWelcomMsg.setText("You are right now in " +hwLocation.getCity()+ " location. Grab a pop corn and book a movie because It's Show Time.");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
Getting user nearby theatres
1) We need API key in order to work with Huawei Site Kit. To get API key,
Choose AGC console > My Project > General Information > API Key. Copy this API key and save the key somewhere in your project.
2) Declare a SearchService object and use SearchServiceFactory to instantiate the object. Also the API key which we got from AGC console, need to be encoded using encodeURI.
Code:
try {
SearchService searchService = SearchServiceFactory.create(this, URLEncoder.encode(Constant.API_KEY, "utf-8"));
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "encode apikey error");
}
3) Need to create Coordinate in order to set the location later. We will use user current location latitude and longitude fetched using Huawei Location Kit.
Code:
Coordinate location = new Coordinate(lat, lon);
4) Create a QuerySuggestionRequest object, which is used as the request body for search suggestion. Related parameters are as follows, among which query is mandatory and others are optional:
a) query: search keyword.
b) location: longitude and latitude to which search results need to be biased.
c) radius: search radius, in meters. The value ranges from 1 to 50000. The default value is 50000.
d) bounds: coordinate bounds to which search results need to be biased.
e) poiTypes: List of POI types. The value range is a subset of LocationType.
f) countryCode: code of the country where places are searched, which complies with the ISO 3166-1 alpha-2 standard. Code of the country where places are searched, which complies with the ISO 3166-1 alpha-2 standard.
g) language: language in which search results are displayed. Language in which search results are displayed.
Code:
QuerySuggestionRequest request = new QuerySuggestionRequest();
request.setQuery("Movie theater");
request.setLocation(location);
request.setRadius(50);
request.setCountryCode("IN");
request.setLanguage("en");
QuerySuggestionRequest request2 = new QuerySuggestionRequest();
request2.setQuery("PVR cinemas");
request2.setLocation(location);
request2.setRadius(50);
request2.setCountryCode("IN");
request2.setLanguage("en");
QuerySuggestionRequest request3 = new QuerySuggestionRequest();
request3.setQuery("INOX Movies");
request3.setLocation(location);
request3.setRadius(50);
request3.setCountryCode("IN");
request3.setLanguage("en");
5) Finally create a SearchResultListener object to listen for the search result. Use the created SearchService object to call the querySuggestion() API and pass the created QuerySuggestionRequest and SearchResultListener objects to the API. Obtain the QuerySuggestionResponse object using the created SearchResultListener object. You can obtain a Site object from the QuerySuggestionResponse object and then parse it to obtain specific search results.
Code:
SearchResultListener<QuerySuggestionResponse> resultListener = new SearchResultListener<QuerySuggestionResponse>() {
@Override
public void onSearchResult(QuerySuggestionResponse results) {
if (results == null) {
return;
}
List<Site> sites = results.getSites();
for (Site site : sites) {
theaterNameList.add(site.getName());
}
}
@Override
public void onSearchError(SearchStatus status) {
Log.i("TAG", "Error : " + status.getErrorCode() + " " + status.getErrorMessage());
}
};
searchService.querySuggestion(request, resultListener);
searchService.querySuggestion(request2, resultListener);
searchService.querySuggestion(request3, resultListener);
GitHub
After we complete our series we will provide the github link for your reference
For More Information
https://developer.huawei.com/consumer/en/doc/HMSCore-Guides-V5/introduction-0000001050706106-V5
https://developer.huawei.com/consumer/en/doc/HMSCore-Guides-V5/android-sdk-introduction-0000001050158571-V5

Huawei Wallet Kit Server API ( Node js Server )

More information like this, you can visit HUAWEI Developer Forum
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Co-author by Sanghati Mukherjee and Sujith.
The article is the continuity of our Huawei kit series. The series contain four parts. Below are the links:
1) It’s Show Time using Huawei Auth Service and Account kit.
2) It’s Show Time using Huawei Location and Site kit.
3) It’s Show Time using Huawei Wallet kit.
The fourth part is this article where we would learn how to implement the server side of Huawei wallet kit using local Node js server and how to call them in client side in order to create ticket and save the ticket on user wallet app for future use.
Why having your own server?
Using our own server to communicate with app is generally the best option for creating cards or tickets in our application. Because, our app will recognize and trust our own server, allowing us to control all transactions between our server and user devices. That is what we are going to learn today.
Setting up the server
If you are not familiar with setting up your own local server and connecting the server with the device, I would strongly recommend you to go through my previous article i.e. “Build your own server from scratch to send push notification“. My previous article will help you setting up local server from scratch also it is healthy to learn something new every day as our brain can store more information than a computer.
Prerequisite
1) We must have latest version of Node installed.
2) We must have latest version of Visual Studio Code installed.
3) We must have latest version of MongoDB database installed.
4) Laptop/desktop and Huawei mobile device must share same Wi-Fi connection.
5) We must have a working app integrate with HMS Wallet Kit.
We will divide this article into two parts
1) Server Side: The server side contains Node, Express, Request and JavaScript.
2) Client Side: The client side contains Android Native, Java, Retrofit and HMS Wallet Kit.
Demo
Server Side
Obtaining app-level access token API
The request header uses AccessToken for authentication, which is obtained using the service API provided by the open platform. The service API provides two modes to obtain AccessToken: authorization code mode and client password mode. This API uses the client password mode.
Do not apply for a new app-level access token each time before the server API is called. Frequent application requests may trigger rejection, leading to application failure within a specified period. Each access token has a validity period.
Within the validity period, it can be used repeatedly. You are advised to apply for an access token again only when the server API is accessed and HTTP result code 401 is returned.
Code:
var request = require("request");
const getAppToken = (callBack) => {
var options = {
method: 'POST',
url: 'https://oauth-login.cloud.huawei.com/oauth2/v3/token',
headers:
{
'content-type': 'application/x-www-form-urlencoded',
host: 'Login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1'
},
form:
{
grant_type: "client_credentials",
client_secret: 'Put Your Client Secret Here...',
client_id: 'Put Your APP ID Here ...'
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
var tokenValue = JSON.parse(body);
callBack(tokenValue.access_token);
});
}
exports.getAppToken = getAppToken;
Wallet server address
Set the {url} variable based on the region where the server is located. For details, please refer to Wallet Server Address as shown below.
Creating a HwWalletObject
We need to set HwWalletObject as body parameter in order to call wallet event ticket APIs. To know more about HwWalletObject follow the link below:
https://developer.huawei.com/consumer/en/doc/HMSCore-References-V5/def-0000001050160319-V5
Creating an event ticket model API
We call this method to add an event ticket model to the Huawei server.
URL: https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/model
API
Code:
var request = require("request");
const getEventTIcketModelObject = (authorizationVal, ticketBookingDate,callBack) => {
var options = {
method: 'POST',
url: 'https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/model',
headers:
{
'cache-control': 'no-cache',
accept: 'application/json',
authorization: 'Bearer '+authorizationVal,
'content-type': 'application/json'
},
body:
{ passVersion: '1.0',
passTypeIdentifier: 'YOUR_SERVICE_ID',
passStyleIdentifier: 'YOUR_MODEL_ID',
organizationName: 'Huawei',
fields:
{ countryCode: 'zh',
locationList: [ { longitude: '114.0679603815', latitude: '22.6592051284' } ],
commonFields:
[
{ key: 'logo', value: 'https://contentcenter-drcn.dbankcdn.com/cch5/Wallet-WalletKit/picres/cloudRes/coupon_logo.png' },
{ key: 'name', value: 'Is Show Time Movie Ticket' },
{ key: 'merchantName',
value: 'Huawei',
localizedValue: 'merchantNameI18N' },
{ key: 'address', value: 'INOX Cinema' },
{ key: 'ticketType', value: 'Movie ticket' } ],
appendFields: [ { key: 'backgroundColor', value: '#3e454f' } ],
timeList:
[ { key: 'startTime', value: ticketBookingDate },
{ key: 'endTime', value: '2020-08-20T00:00:00.111Z' } ],
localized:
[ { key: 'merchantNameI18N', language: 'zh-cn', value: '华为' },
{ key: 'merchantNameI18N', language: 'en', value: 'Huawei' }
]
}
},
json: true };
request(options, function (error, response, body) {
if (error) {
callBack(error,error);
}
else{
callBack(body);
}
});
}
exports.getEventTIcketModelObject = getEventTIcketModelObject;
The value of passTypeIdentifier parameter is the Service ID, which can be obtained when we apply for HUAWEI Wallet Kit service in AGC and the value of passStyleIdentifier is the Model ID, which can also be obtained from wallet kit service in AGC. Both are mandatory.
Adding an event ticket instance API
We call this method to add the event ticket instance of a user to the Huawei server. After the instance is added using this API, a thin JWE should be used as well to link the instance to the user's HUAWEI ID. Alternatively, a JWE can be used, rather this API, to directly add the instance to the Huawei server and link it to the user's HUAWEI ID.
URL: https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/instance
API
Code:
var request = require("request");
const getEventTIcketInstanceObject = (authorizationVal, serialNumber, ticketBookingDate,seatNumber,userName, movieName,callback) => {
var options = {
method: 'POST',
url: 'https://passentrust-dra.wallet.hicloud.com/hmspass/v1/eventticket/instance',
headers:
{
'cache-control': 'no-cache',
accept: 'application/json',
authorization: 'Bearer '+authorizationVal,
'content-type': 'application/json' },
body:
{
organizationPassId: 'YOUR_APP_ID',
passTypeIdentifier: 'YOUR_SERVICE_ID',
passStyleIdentifier: 'YOUR_MODEL_ID',
serialNumber: serialNumber,
fields:
{ status:
{ state: 'active',
effectTime: ticketBookingDate,
expireTime: '2020-08-20T00:00:00.111Z' },
barCode:
{ text: '562348969211212',
type: 'codabar',
value: '562348969211212',
encoding: 'UTF-8' },
commonFields:
[ { key: 'ticketNumber', value: serialNumber },
{ key: 'title', value: 'Its Show Time' },
{ key: 'name', value: movieName },
{key:'programImage', value:'https://contentcenter-drcn.dbankcdn.com/cch5/Wallet-WalletKit/picres/cloudRes/coupon_logo.png'}
],
appendFields:
[ { key: 'gate', value: 'Gate 2', label: 'Gate' },
{ key: 'seat', value: '24', label: 'Seat' },
{ key: 'userName', value: userName }
]
}
},
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
//console.log(body);
var data = JSON.parse(JSON.stringify(body));
// console.log(data)
callback(body);
});
}
exports.getEventTIcketInstanceObject = getEventTIcketInstanceObject;
The value of organizationPassId parameter is the APP ID, which can be obtained from our AGC.
Create ticket instance API for client
We need an API for client to call the above two APIs on server in order to fetch wallet instance and use it to create a movie ticket.
Code:
app.post('/createTicket', (req, res, next) => {
appTokenWallet.getAppToken((callBack) => {
let authorization = callBack
console.log(authorization);
let serialNumber = Math.floor(Math.random() * 50000) + 100000;
console.log(serialNumber);
let ticketBookingDate = convertDateToUTC();
createTicketModel.getEventTIcketModelObject(authorization,ticketBookingDate, callBackMessage => {
console.log(callBackMessage);
});
createTicketInstance.getEventTIcketInstanceObject(authorization,serialNumber,ticketBookingDate,req.body.seat,req.body.username,"Dil Bechara",callBackMsg => {
if(callBackMsg!=null){
res.send(""+serialNumber);
}
});
});
});
Client Side (Android Native)
We need Retrofit in order to call our restful Apis. Include the following dependencies in app build.gradle file.
Code:
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
Create retrofit interface
Create a new Interface class and name it GetDataService. Open the class, copy and paste below code:
Code:
public interface GetDataService {
@POST("/createTicket")
Call<Object> createMovieTicket(@Body HashMap<String, String> map);
}
Create retrofit instance
Create a new class and name it RetrofitClientInstance. Open the class, copy and paste below code:
Code:
public class RetrofitClientInstance {
private static Retrofit retrofit;
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Note: The BASE_URL is important here. We will put our IPv4 Address of our server machine instead localhost. To find our machine IPv4 Address, we will go to command prompt and type ipconfig. Also make sure that the device is connected to the same Wi-Fi the server machine is connected too.
Use retrofit instance in the activity
Create an object of retrofit instance in onCreate() method of the activity as show below:
Code:
service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
After that use this retrofit instance object to call the create ticket instance API of server in order to fetch instance Id and use it to create thin JWE which will be used to create a ticket.
Code:
public void proceedToPay(View view) {
progressBar.setVisibility(View.VISIBLE);
HashMap<String, String> ticket = new HashMap<>();
ticket.put("seat", seats);
ticket.put("username","Sanghati Mukherjee");
Call<Object> call = service.createMovieTicket(ticket);
call.enqueue(new Callback<Object>() {
@Override
public void onResponse(Call<Object> call, Response<Object> response) {
passObject = "{\"instanceIds\": [\""+response.body().toString().replace(".0","").trim()+"\"]}";
progressBar.setVisibility(View.GONE);
generateJWEStr();
}
@Override
public void onFailure(Call<Object> call, Throwable t) {
Toast.makeText(PaymentActivity.this, t.getMessage(), Toast.LENGTH_LONG).show();
System.out.println("RESPONSE >>> " + t.getMessage());
progressBar.setVisibility(View.GONE);
}
});
}
When JWE is created the Huawei server will call browser to create ticket as shown below:
Now we need to add the ticket in our wallet app for future use which in this case is a movie ticket for INOX cinemas.
GitHub Links
Client Side: https://github.com/DTSE-India-Community/ItsShowTime
Server Side: https://github.com/DTSE-India-Community/Huawei-In-App-Purchase-Push-Kit-Server_Side-And-Wallet-Kit-Server-Side-Implementation
For More Information
https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/create-model-0000001050158460-V5
https://developer.huawei.com/consumer/en/doc/development/HMSCore-References-V5/add-instance-0000001050158466-V5

Samachar a news application using HMS Push Kit Client + Server Side (Part2)

More information like this, you can visit HUAWEI Developer Forum
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
This article is the continuity of my previous article. To get a better picture or knowledge, refer my previous article first. Below is the link:
https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0202326236067510002&fid=0101187876626530001
Why you are having own server?
Using our own server to communicate with our app is generally the best option for sending push notification directly to our application. Because, our app will recognize and trust our own server which will allow us to control all transactions between our server and user devices.
Also if we need to send notification using Huawei AGC console, we need to copy and paste our users push token every time, which is a tedious job. Having our own server will allow us to store our users token and can send notification easily by fetching the entire list of token from database. That is what we are going to learn today.
Setting up the server
If you are not familiar with setting up your own local server and connecting the server with the device, refer my previous article “Build your own server from scratch to send push notification“. My previous article will help you setting up local server from scratch also, it is healthy to learn something new every day as our brain can store more information than a computer.
Prerequisite
1) We must have latest version of Node installed.
2) We must have latest version of Visual Studio Code installed.
3) We must have latest version of MongoDB database installed.
4) Laptop/desktop and Huawei mobile device must share same Wi-Fi connection.
5) We must have a working app integrate with HMS Push Kit.
We will divide this article into two parts
1) Server Side: The server side contains Node, Express, Request and JavaScript.
2) Client Side: The client side contains Android Native, Java, Retrofit and HMS Push Kit.
Demo
Send notification from server
Notification on device side
Goal
Our goal here is to subscribe, unsubscribe and send notification using topic-based message sending, also when user will tap the notification it will take user to a specific page. All this will be done using our local server side.
Server Side
Obtaining app-level access token API
The request header uses AccessToken for authentication, which is obtained using the service API provided by the open platform. The service API provides two modes to obtain AccessToken: authorization code mode and client password mode. This API uses the client password mode.
Do not apply for a new app-level access token each time before the server API is called. Frequent application requests may trigger rejection, leading to application failure within a specified period. Each access token has a validity period.
Within the validity period, it can be used repeatedly. You are advised to apply for an access token again, only when the server API is accessed and HTTP result code 401 is return
Code:
var request = require("request");
const getAppToken = (callBack) => {
var options = {
method: 'POST',
url: 'https://oauth-login.cloud.huawei.com/oauth2/v3/token',
headers:
{
'content-type': 'application/x-www-form-urlencoded',
host: 'Login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1'
},
form:
{
grant_type: "client_credentials",
client_secret: 'Put Your Client Secret Here...',
client_id: 'Put Your APP ID Here ...'
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
var tokenValue = JSON.parse(body);
callBack(tokenValue.access_token);
});
}
exports.getAppToken = getAppToken;
Subscribe to a topic API
The API is used to subscribe to a topic for one or more users. A maximum of 1,000 tokens are allowed in each call request. Currently, this API only supports Android apps.
URL: https://push-api.cloud.huawei.com/v1/[appid]/topic:subscribe
API
subscribeToTopic.js
Code:
var request = require("request");
const subscribeToTopicNotify = (appLevelToken, tokenVal, topicSubscribeVal, callBack) => {
var options = {
method: 'POST',
url: 'https://push-api.cloud.huawei.com/v1/[appid]/topic:subscribe',
headers:
{
authorization: 'Bearer ' + appLevelToken,
host: 'oauth-login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1',
'content-type': 'application/json'
},
body:
{
topic: topicSubscribeVal,
tokenArray: tokenVal
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// console.log(body);
var notify = JSON.parse(JSON.stringify(body));
// console.log(">>>"+notify.msg);
callBack(notify.msg);
});
}
exports.subscribeToTopicNotify = subscribeToTopicNotify;
app.js
Code:
app.post('/subscribeToTopic', (req, res, next) => {
// topicSubscribeVal = req.body.topic;
let topic = {
topic: req.body.topic,
};
console.log(topic.topic);
// Fetching Token from mongodb ...
dbase.collection('token').find().toArray((err, results) => {
if (results) {
//clear the token list ...
arrToken = [];
// adding token in the list ...
for (var i = 0; i < results.length; i++) {
console.log("Token " + i + " " + results[i].token);
arrToken.push(results[i].token);
}
// if error code 401 returned it means access token becomes invalid
// and we need to obtain a new token. Token Validity is 60 mins...
// fetchin app level access token here ...
appToken.getAppToken((callBack) => {
console.log("TOKEN >>>" + callBack);
appLevelAccesToken = callBack;
subscribeToTopic.subscribeToTopicNotify(appLevelAccesToken, arrToken, topic.topic,callBackMessage => {
notifySucesssMessage = callBackMessage;
console.log(notifySucesssMessage);
if(callBackMessage!=null){
res.send(callBackMessage);
}
});
});
}
});
});
Unsubscribe to a topic API
The API is used to unsubscribe to a topic for one or more users. A maximum of 1,000 tokens are allowed in each call request. Currently, this API only supports Android apps.
URL: https://push-api.cloud.huawei.com/v1/[appid]/topic:unsubscribe
API
unSubscribeToTopic.js
Code:
var request = require("request");
const unSubscribeToTopicNotify = (appLevelToken, tokenVal, topicSubscribeVal, callBack) => {
var options = {
method: 'POST',
url: 'https://push-api.cloud.huawei.com/v1/[appid]/topic:unsubscribe',
headers:
{
authorization: 'Bearer ' + appLevelToken,
host: 'oauth-login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1',
'content-type': 'application/json'
},
body:
{
topic: topicSubscribeVal,
tokenArray: tokenVal
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// console.log(body);
var notify = JSON.parse(JSON.stringify(body));
// console.log(">>>"+notify.msg);
callBack(notify.msg);
});
}
exports.unSubscribeToTopicNotify = unSubscribeToTopicNotify;
app.js
Code:
// Push Kit unsubscribe to a topic ...
app.post('/unsubscribeToTopic', (req, res, next) => {
let topic = {
topic: req.body.topic,
};
// Fetching Token from mongodb ...
dbase.collection('token').find().toArray((err, results) => {
if (results) {
//clear the token list ...
arrToken = [];
// adding token in the list ...
for (var i = 0; i < results.length; i++) {
console.log("Token " + i + " " + results[i].token);
arrToken.push(results[i].token);
}
// if error code 401 returned it means access token becomes invalid
// and we need to obtain a new token. Token Validity is 60 mins...
// fetchin app level access token here ...
appToken.getAppToken((callBack) => {
console.log("TOKEN >>>" + callBack);
appLevelAccesToken = callBack;
unSubscribeToTopic.unSubscribeToTopicNotify(appLevelAccesToken, arrToken, topic.topic,callBackMessage => {
notifySucesssMessage = callBackMessage;
console.log(notifySucesssMessage);
if(callBackMessage!=null){
res.send(callBackMessage);
}
});
});
}
});
});
Querying the topic subscription list API
The API is used to query the topic subscription list of a token. Currently, this API only supports Android apps.
URL: https://push-api.cloud.huawei.com/v1/[appid]/topic:list
API
getTopicList.js
Code:
var request = require("request");
const getTopicList = (appLevelToken, tokenVal, callBack) => {
var options = {
method: 'POST',
url: 'https://push-api.cloud.huawei.com/v1/[appid]/topic:list',
headers:
{
authorization: 'Bearer ' + appLevelToken,
host: 'oauth-login.cloud.huawei.com',
post: '/oauth2/v2/token HTTP/1.1',
'content-type': 'application/json'
},
body:
{
token: tokenVal
},
json: true
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
// console.log(body);
var notify = JSON.parse(JSON.stringify(body));
console.log(">>>"+notify);
callBack(notify.topics);
});
}
exports.getTopicList = getTopicList;
app.js
Code:
app.post('/getTopicList', (req, res, next) => {
console.log("CALLLLLING")
// Fetching Token from mongodb ...
dbase.collection('token').find().toArray((err, results) => {
if (results) {
//clear the token list ...
arrToken = [];
// adding token in the list ...
for (var i = 0; i < results.length; i++) {
console.log("Token " + i + " " + results[i].token);
arrToken.push(results[i].token);
}
var uniqueItems = Array.from(new Set(arrToken));
// if error code 401 returned it means access token becomes invalid
// and we need to obtain a new token. Token Validity is 60 mins...
// fetchin app level access token here ...
appToken.getAppToken((callBack) => {
console.log("TOKEN VA L >>>" + uniqueItems);
appLevelAccesToken = callBack;
getTopicList.getTopicList(appLevelAccesToken, uniqueItems[0],callBackMessage => {
notifySucesssMessage = callBackMessage;
console.log("MSG >>>>>>>>>"+notifySucesssMessage);
res.send(notifySucesssMessage);
});
});
}
});
});
Sending notification using topic subscription API
The API is used to send notification using topic subscription, also an intent to send user to specific page. In this case we are sending URL as a data in an intent which will make user to view the website in android WebActivity page on taping the notification.
URL: https://push-api.cloud.huawei.com/v1/[appid]/messages:send
This is not the end. For full content, you can visit https://forums.developer.huawei.com/forumPortal/en/topicview?tid=0201327882870070045&fid=0101187876626530001

What is Huawei Cloud DB? How To Use?

{
"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"
}
Hello everyone,
In this article, I would like to tell you about Cloud DB, which online storage that Huawei offers to developers.
What is Cloud DB ?
Although Cloud DB is still in beta version, it is a successful and seamless database structure. In addition to the ease of use, attracts developers with its management and a user-friendly interface. In addition to providing data availability, consistency and security, CloudDB provides seamless data synchronization between the device and the cloud.
If you do not have a server when developing applications, Cloud DB server easily solves our data storage, maintenance and distribution. Also CloudDB is free.
Cloud DB provides 500 GB of data volume for each application, and supports 2000 connections. Looking at their counterparts, it is understood how large and how important these numbers are.
Cloud DB Structure
Object Type: It represents each table in the standard database. In other words, each table containing data and columns is called Object Type.
Cloud DB Zone : Represents the data zone on the cloud side. According to the classic database, Cloud DB Zone is the name of the database or schema name.
Data Entires : It is the area that shows the added data. Here, you can add, update and delete data. When you add data, you will realize that the tables you are used to are in the same way. And while using this technology, it prevents you from suffering from strangeness.
How To Using Cloud DB ?
Now let’s see how to use Cloud DB. Since Cloud DB is still in beta, you have to send a mail to activate this service in your app. To use Cloud DB, you need to create an app after creating your Huawei Developer account. After completing all the necessary steps, you have to request the activation of the service by sending an e-mail to [email protected] with the sample header below. The service will be activated within 1–3 business days after your mail arrives. And you can start using it freely.
Cloud DB –Company Name — Developer ID — App ID
After your service activated, log in to AppGallery Connect and select your app under the heading “My Apps”. You can then access the “Cloud DB” panel under the “Build” tab from the menu on the left side of the page, by moving to the “Develop” tab in the upper left. After the page is loaded, activate the service by clicking the “Enable Now” button in the upper right corner.
A Cloud DB Zone must first be created. After then object types should be created. When creating the object type, column names must be entered and the primary key must be identified in the window that opens. Also, at this step, you can edit access control options for the object type. In the image of the below you can find, which users have which permissions should be given. After creating DB Zone and Object Type, Cloud DB provides to export data models and Helper class as JSON or Java for use them in the app without wasting time. After exporting the model classes as Java, you have to add these classes to the relevant directory of the app and start communicate with Cloud DB.
Cloud DB library should be added to the build.gradle file under the project’s app directory and the compatibility mode of the Java source code should be set as 1.8. For this, the following codes should be added to the gradle file and wait downloading the necessary dependencies by click Sync Now.
Code:
dependencies {
implementation 'com.huawei.agconnect:agconnect-database:1.2.1.301'
}
compileOptions {
targetCompatibility = 1.8
}
Let’s create a class named CloudDBZoneWrapper for all database operations. By defining all the upsert, query operations in this class, call these methods in the activity/fragment to be used. Thanks to this metods you can coding your app without clutter.
Firstly, Cloud DB objects should created to be used in this class.
Code:
private AGConnectCloudDB mCloudDB;
private CloudDBZone mCloudDBZone;
private ListenerHandler mRegister;
private CloudDBZoneConfig mConfig;
After then create an instance from the AGConnectCloudDB object in a constructor method.
Code:
public CloudDBZoneWrapper() {
mCloudDB = AGConnectCloudDB.getInstance();
}
Then, initAGConnectCloudDB method must be created for calling on the app landing page. This method must be run before the app is opened, before starting all DB operations. I follow the code by adding a log in every process step to follow the errors more easily. In this way, you can easily find out which line is wrong.
Code:
public static void initAGConnectCloudDB(Context context) {
AGConnectCloudDB.initialize(context);
Log.w(Constants.DB_ZONE_WRAPPER, "initAGConnectCloudDB" );
}
Next, creating Object Type, and open/close DBZone operations should be coding. These methods will be used before upsert and query operations for open DBZone and create Object Types.
Code:
public void createObjectType() {
try {
mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());
Log.w(Constants.DB_ZONE_WRAPPER, "createObjectTypeSuccess " );
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "createObjectTypeError: " + e.getMessage());
}
}
public void openCloudDBZone() {
mConfig = new CloudDBZoneConfig("DB ZONE NAME HERE",
CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
mConfig.setPersistenceEnabled(true);
Log.w(Constants.DB_ZONE_WRAPPER, "openCloudDBZoneSuccess " );
try {
mCloudDBZone = mCloudDB.openCloudDBZone(mConfig, true);
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "openCloudDBZoneError: " + e.getMessage());
}
}
public void closeCloudDBZone() {
try {
mCloudDB.closeCloudDBZone(mCloudDBZone);
Log.w(Constants.DB_ZONE_WRAPPER, "closeCloudDBZoneSuccess " );
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "closeCloudDBZoneError: " + e.getMessage());
}
}
Now, the necessary methods for upsert and query operations should be written. But first, a few callbacks have to be added to get the results of these actions to the activities and fragments where the actions are operated. In this way, all DB operations will be gathered in a single class, without the activity being too tired and without crowd of code.
Code:
public interface UiCallBack {
void onAddOrQuery(List<TableUser> userList);
void isLastID(int lastID);.
void isDataUpsert(Boolean state);
}
public void addCallBacks(UiCallBack uiCallBack) {
mUiCallBack = uiCallBack;
}
Now the necessary method for upsert operation should be written. Upsert contains both insert and update operations. If upsert with a certain ID, the data in the related line will update. If it is upsert by new ID, a new line will added. So, both insert and update are carried out with the same method.
First, it should be checked whether DBZone is created or not. If DBZone has an error, will not upsert data. Then, upsert with CloudDBZoneTask object. Since I will add data to the User table, I gave the user object as a parameter to this method. If you need to add data to other tables, you should create a new method and give the object of the related table as a parameter. When upsert operation complated , if upsert is successful, it must return true, if occured an error, return false. For this, at the beginning of the method, a variable named Boolean state was defined and its first value was set as false. Then, if upsert is successful, state is set to true, and if error occurs, method will return false.
Code:
public void insertUser(TableUser user) {
boolean state = false;
if (mCloudDBZone == null) {
Log.w(Constants.DB_ZONE_WRAPPER, "INSERT USER : CloudDBZone is null, try re-open it");
return;
}
CloudDBZoneTask<Integer> upsertTask = mCloudDBZone.executeUpsert(user);
if (mUiCallBack == null) {
return;
}
upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
state = true;
Log.w(Constants.DB_ZONE_WRAPPER, "INSERT USER : upsert " + cloudDBZoneResult + " records");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
state = false;
mUiCallBack.updateUiOnError("INSERT USER : Insert user info failed");
}
});
if (mUiCallBack != null) {
mUiCallBack.isDataUpsert(state);
}
}
Now, let’s make a query in the this class. For this, I will get the list of users which I added to the database with the same model class. Two methods are required when making query request. The first is the getAllUsers method for DB operations, and the other is called userListResult method, for add data to array. Firstly, CloudDBZone control should be done in getAllUsers method. Then, query request will make by creating a task. If the request is successful, the userListResult method is calling with the user object. If the request is successful, the userListResult method will called with the user object. An arrayList is created in the userListResult method, and all results are thrown into this list. Then, by adding a callback, the results can be called up in the activity or fragment.
Code:
public void getAllUsers() {
if (mCloudDBZone == null) {
Log.w(Constants.DB_ZONE_WRAPPER, "GET USER DETAIL : CloudDBZone is null, try re-open it");
return;
}
CloudDBZoneTask<CloudDBZoneSnapshot<TableUser>> queryTask = mCloudDBZone.executeQuery(
CloudDBZoneQuery.where(TableUser.class),
CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
queryTask.addOnSuccessListener(new OnSuccessListener<CloudDBZoneSnapshot<TableUser>>() {
@Override
public void onSuccess(CloudDBZoneSnapshot<TableUser> snapshot) {
userListResult (snapshot);
Log.w(Constants.DB_ZONE_WRAPPER, "GET USER DETAIL : GoResults: ");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (mUiCallBack != null) {
mUiCallBack.updateUiOnError("GET USER DETAIL : Query user list from cloud failed");
}
}
});
}
private void userListResult (CloudDBZoneSnapshot<TableUser> snapshot) {
CloudDBZoneObjectList<TableUser> userInfoCursor = snapshot.getSnapshotObjects();
List<TableUser> userInfoList = new ArrayList<>();
try {
while (userInfoCursor.hasNext()) {
TableUser userInfo = userInfoCursor.next();
userInfoList.add(userInfo);
Log.w(Constants.DB_ZONE_WRAPPER, "USER DETAIL RESULT : processQueryResult: " + userInfo.getUser_city());
}
} catch (AGConnectCloudDBException e) {
Log.w(Constants.DB_ZONE_WRAPPER, "USER DETAIL RESULT : processQueryResult: " + e.getMessage());
}
snapshot.release();
if (mUiCallBack != null) {
mUiCallBack.onAddOrQuery(userInfoList);
}
}
Thus, all database operations within the CloudDBZoneWrapper class have been completed. Now let’s examine how to data upsert or query in activity or fragment.
The UiCallBack method in the CloudDBZoneWrapper class should be implement as the CloudDBZoneWrapper.UiCallBack in the class you which want to do database operations. In this way, all added call back methods will override in this class. Then the CloduDBZoneWrapper object and a new Handler need to be created in the activity. The CloudDBZoneWrapper object must be called within the constructor method. Sample codes are as follows.
Code:
private MyHandler mHandler = new MyHandler();
private CloudDBZoneWrapper mCloudDBZoneWrapper;
private static final class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
// dummy
}
}
public ProfileFragment() {
mCloudDBZoneWrapper = new CloudDBZoneWrapper();
}
Then the object type must be created in onCreate and Cloud DB Zone must be opened. If query is not related to an event, if the data should be loaded while the page is opening, call the getAllUsers method after creating the object type and opening the DB Zone in onCreate.
Code:
mHandler.post(() -> {
mCloudDBZoneWrapper.addCallBacks(ProfileFragment.this);
mCloudDBZoneWrapper.createObjectType();
mCloudDBZoneWrapper.openCloudDBZone();
mCloudDBZoneWrapper.getAllUsers();
});
The callback method, which was added to the getAllUsers method in the CloudDBZoneWrapper class, was override in the fragment. In this override method, all data can be used. For example, if wants to access the information of a user with ID = 3 in the user list, the turned list by insert a for loop, user information with ID = 3 is obtained.
Code:
@Override
public void onAddOrQuery(List<TableUser> userList) {
for(int i = 0; i <= userList.size()-1; i++){
if(userList.get(i).getId().equals(“3”)){
userName = userList.get(i).getUser_name());
userPhone = userList.get(i).getUser_phone();
userMail = userList.get(i).getUser_mail();
userAge = userList.get(i).getUser_age();
userGender = userList.get(i).getUser_gender();
}
}
}
Now let’s make an upsert. As I writed before, Upsert includes update and insert operations. Both operations are uses the same method. If you want to update a row of data, you must post with the ID information of the data in that row. If a new data is to be added, it should be posted with a new ID.
At this point, Cloud DB has a lack. Unfortunately, the auto increment don’t have when creating the object type. In other words, ID value does not increase automatically when data is added. It has to be given manually. I solved this problem by getting the last ID in the table and increasing it.
Now, create a method called updateUser to update and send back the user information I have previously got in this fragment. Next, a new user object should created here and the values ​​should set. If there is an not to be changed data (as in the example, age and gender ), old values ​​must be set in them. Finally, make post request by calling the insertUser method created in the CloudDBZoneWrapper class.
Code:
public void updateProfile(){
TableUser user = new TableUser();
user.setUser_id(“3”);
user.setUser_name(“Yeni İsim”));
user.setUser_phone(“Yeni Telefon”);
user.setUser_mail(“Yeni Mail”);
user.setUser_age(userAge);
user.setUser_gender(userGender);
mHandler.post(() -> {
mCloudDBZoneWrapper.insertUser(user);
});
}
In the fragment, help from call back methods should be obtained to find out whether the transaction is successful. Status check can be done in call back method added to insertUser method.
Code:
@Override
public void isDataUpsert(Boolean state) {
if(state){
//successful
}else{
//unsuccessful
}
}
Finally, it is worth mentioning that there is an authentication requirement to upsert. Since Cloud DB is still in beta, absolutely has some errors. But as you can see, all of them are solved easily. For authentication, Auth Service offered by Huawei to developers should be used. A service that is very easy to use. You can find the Auth Service link on the below. After the authentication, your upsert will work. If authentication is not done, the result of upsert will return false.
"https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-auth-service-introduction"
Well explained, can we store large amount of data into cloud Db, is there any limitations.
sujith.e said:
Well explained, can we store large amount of data into cloud Db, is there any limitations.
Click to expand...
Click to collapse
Yes, Cloud DB has a limit but I think you can store large data in Cloud DB. Because Cloud DB provides 500 GB of data volume for each application, and supports 2000 connections.

Demystifying HMS ML Kit with Product Visual Search API using Xamarin

Overview
In this article, I will create a demo app along with the integration of HMS ML Kit which based on Cross-platform Technology Xamarin. User can easily scan any items from this application with camera Product Vision Search ML Kit technique and choose best price and details of product.
{
"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"
}
Service Introduction
HMS ML Kit allows your apps to easily leverage Huawei's long-term proven expertise in machine learning to support diverse artificial intelligence (AI) applications throughout a wide range of industries.
A user can take a photo of a product. Then the Product Visual Search service searches for the same or similar products in the pre-established product image library and returns the IDs of those products and related information. In addition, to better manage products in real-time, this service supports offline product import, online product addition, deletion, modification, query, and product distribution.
We can capture any kind of image for products to buy or check the price of a product using Machine Learning. It will give the other options so that you can improve your buying skills.
Prerequisite
1. Xamarin Framework
2. Huawei phone
3. Visual Studio 2019
App Gallery Integration process
1. Sign In and Create or Choose a project on AppGallery Connect portal.
2. Add SHA-256 key.
3. Navigate to Project settings and download the configuration file.
4. Navigate to General Information, and then provide Data Storage location.
5. Navigate to Manage APIs and enable APIs which require by application.
Xamarin ML Kit Setup Process
1. Download Xamarin Plugin all the aar and zip files from below url:
https://developer.huawei.com/consum...Library-V1/xamarin-plugin-0000001053510381-V1
2. Open the XHms-ML-Kit-Library-Project.sln solution in Visual Studio.
3. Navigate to Solution Explore and right-click on jar Add > Exsiting Item and choose aar file which download in Step 1.
4. Right click on added aar file then choose Properties > Build Action > LibraryProjectZip
Note: Repeat Step 3 & 4 for all aar file.
5. Build the Library and make dll files.
Xamarin App Development
1. Open Visual Studio 2019 and Create A New Project.
2. Navigate to Solution Explore > Project > Assets > Add Json file.
3. Navigate to Solution Explore > Project > Add > Add New Folder.
4. Navigate to Folder(created) > Add > Add Existing and add all DLL files.
5. Select all DLL files.
6. Right-click on Properties, choose Build Action > None.
7. Navigate to Solution Explore > Project > Reference > Right Click > Add References, then navigate to Browse and add all DLL files from recently added folder.
8. Added reference, then click OK.
ML Product Visual Search API Integration
1. Create an analyzer for product visual search. You can create the analyzer using the MLRemoteProductVisionSearchAnalyzerSetting class.
C#:
// Method 1: Use default parameter settings.
MLRemoteProductVisionSearchAnalyzer analyzer = MLAnalyzerFactory.Instance.RemoteProductVisionSearchAnalyzer;
// Method 2: Use customized parameter settings.
MLRemoteProductVisionSearchAnalyzerSetting settings = new MLRemoteProductVisionSearchAnalyzerSetting.Factory()
// Set the maximum number of products that can be returned.
.SetLargestNumOfReturns(16)
.Create();
MLRemoteProductVisionSearchAnalyzer analyzer = MLAnalyzerFactory.Instance.GetRemoteProductVisionSearchAnalyzer(settings);
2. Create an MLFrame object by using Android.Graphics.Bitmap. JPG, JPEG, PNG, and BMP images are supported.
C#:
// Create an MLFrame object using the bitmap, which is the image data in bitmap format.
MLFrame frame = MLFrame.FromBitmap(bitmap);
3. Implement image detection.
Code:
Task<IList<MLProductVisionSearch>> task = this.analyzer.AnalyseFrameAsync(frame);
await task;
if (task.IsCompleted && task.Result != null)
{
// Analyze success.
var productVisionSearchList = task.Result;
if (productVisionSearchList.Count != 0)
{
//Product detected successfully
}
else
{
//Product not found
}
}
4. After the recognition is complete, stop the analyzer to release recognition resources.
if (analyzer != null) {
analyzer.Stop();
}
ProductVisionSearchAnalyseActivity.cs
This activity performs all the operation regarding product search with camera.
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Android;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using AndroidX.AppCompat.App;
using AndroidX.Core.App;
using AndroidX.Core.Content;
using Com.Huawei.Hms.Mlplugin.Productvisionsearch;
using Com.Huawei.Hms.Mlsdk;
using Com.Huawei.Hms.Mlsdk.Common;
using Com.Huawei.Hms.Mlsdk.Productvisionsearch;
using Com.Huawei.Hms.Mlsdk.Productvisionsearch.Cloud;
using Java.Lang;
namespace HmsXamarinMLDemo.MLKitActivities.ImageRelated.ProductVisionSearch
{
[Activity(Label = "ProductVisionSearchAnalyseActivity")]
public class ProductVisionSearchAnalyseActivity : AppCompatActivity, View.IOnClickListener
{
private const string Tag = "ProductVisionSearchTestActivity";
private static readonly int PermissionRequest = 0x1000;
private int CameraPermissionCode = 1;
private static readonly int MaxResults = 1;
private TextView mTextView;
private ImageView productResult;
private Bitmap bitmap;
private MLRemoteProductVisionSearchAnalyzer analyzer;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
this.SetContentView(Resource.Layout.activity_image_product_vision_search_analyse);
this.mTextView = (TextView)this.FindViewById(Resource.Id.result);
this.productResult = (ImageView)this.FindViewById(Resource.Id.image_product);
this.bitmap = BitmapFactory.DecodeResource(this.Resources, Resource.Drawable.custom_model_image);
this.productResult.SetImageResource(Resource.Drawable.custom_model_image);
this.FindViewById(Resource.Id.product_detect_plugin).SetOnClickListener(this);
this.FindViewById(Resource.Id.product_detect).SetOnClickListener(this);
// Checking Camera Permissions
if (!(ActivityCompat.CheckSelfPermission(this, Manifest.Permission.Camera) == Permission.Granted))
{
this.RequestCameraPermission();
}
}
private void RequestCameraPermission()
{
string[] permissions = new string[] { Manifest.Permission.Camera };
if (!ActivityCompat.ShouldShowRequestPermissionRationale(this, Manifest.Permission.Camera))
{
ActivityCompat.RequestPermissions(this, permissions, this.CameraPermissionCode);
return;
}
}
private void CheckPermissions(string[] permissions)
{
bool shouldRequestPermission = false;
foreach (string permission in permissions)
{
if (ContextCompat.CheckSelfPermission(this, permission) != Permission.Granted)
{
shouldRequestPermission = true;
}
}
if (shouldRequestPermission)
{
ActivityCompat.RequestPermissions(this, permissions, PermissionRequest);
return;
}
StartVisionSearchPluginCapture();
}
private async void RemoteAnalyze()
{
// Use customized parameter settings for cloud-based recognition.
MLRemoteProductVisionSearchAnalyzerSetting setting =
new MLRemoteProductVisionSearchAnalyzerSetting.Factory()
// Set the maximum number of products that can be returned.
.SetLargestNumOfReturns(MaxResults)
.SetProductSetId("vmall")
.SetRegion(MLRemoteProductVisionSearchAnalyzerSetting.RegionDrChina)
.Create();
this.analyzer = MLAnalyzerFactory.Instance.GetRemoteProductVisionSearchAnalyzer(setting);
// Create an MLFrame by using the bitmap.
MLFrame frame = MLFrame.FromBitmap(bitmap);
Task<IList<MLProductVisionSearch>> task = this.analyzer.AnalyseFrameAsync(frame);
try
{
await task;
if (task.IsCompleted && task.Result != null)
{
// Analyze success.
var productVisionSearchList = task.Result;
if(productVisionSearchList.Count != 0)
{
Toast.MakeText(this, "Product detected successfully", ToastLength.Long).Show();
this.DisplaySuccess(productVisionSearchList);
}
else
{
Toast.MakeText(this, "Product not found", ToastLength.Long);
}
}
else
{
// Analyze failure.
Log.Debug(Tag, " remote analyze failed");
}
}
catch (System.Exception e)
{
// Operation failure.
this.DisplayFailure(e);
}
}
private void StartVisionSearchPluginCapture()
{
// Set the config params.
MLProductVisionSearchCaptureConfig config = new MLProductVisionSearchCaptureConfig.Factory()
//Set the largest OM detect Result,default is 20,values in 1-100
.SetLargestNumOfReturns(16)
//Set the fragment you created (the fragment should implement AbstractUIExtendProxy)
.SetProductFragment(new ProductFragment())
//Set region,current values:RegionDrChina,RegionDrSiangapore,RegionDrGerman,RegionDrRussia
.SetRegion(MLProductVisionSearchCaptureConfig.RegionDrChina)
//设set product id,you can get the value by AGC
//.SetProductSetId("xxxxx")
.Create();
MLProductVisionSearchCapture capture = MLProductVisionSearchCaptureFactory.Instance.Create(config);
//Start plugin
capture.StartCapture(this);
}
private void DisplayFailure(System.Exception exception)
{
string error = "Failure. ";
try
{
MLException mlException = (MLException)exception;
error += "error code: " + mlException.ErrCode + "\n" + "error message: " + mlException.Message;
}
catch (System.Exception e)
{
error += e.Message;
}
this.mTextView.Text = error;
}
private void DrawBitmap(ImageView imageView, Rect rect, string product)
{
Paint boxPaint = new Paint();
boxPaint.Color = Color.White;
boxPaint.SetStyle(Paint.Style.Stroke);
boxPaint.StrokeWidth = (4.0f);
Paint textPaint = new Paint();
textPaint = new Paint();
textPaint.Color = Color.White;
textPaint.TextSize = 100.0f;
imageView.DrawingCacheEnabled = true;
Bitmap bitmapDraw = Bitmap.CreateBitmap(this.bitmap.Copy(Bitmap.Config.Argb8888, true));
Canvas canvas = new Canvas(bitmapDraw);
canvas.DrawRect(rect, boxPaint);
canvas.DrawText("product type: " + product, rect.Left, rect.Top, textPaint);
this.productResult.SetImageBitmap(bitmapDraw);
}
private void DisplaySuccess(IList<MLProductVisionSearch> productVisionSearchList)
{
List<MLVisionSearchProductImage> productImageList = new List<MLVisionSearchProductImage>();
foreach (MLProductVisionSearch productVisionSearch in productVisionSearchList)
{
this.DrawBitmap(this.productResult, productVisionSearch.Border, productVisionSearch.Type);
foreach (MLVisionSearchProduct product in productVisionSearch.ProductList)
{
productImageList.AddRange(product.ImageList);
}
}
StringBuffer buffer = new StringBuffer();
foreach (MLVisionSearchProductImage productImage in productImageList)
{
string str = "ProductID: " + productImage.ProductId + "\nImageID: " + productImage.ImageId + "\nPossibility: " + productImage.Possibility;
buffer.Append(str);
buffer.Append("\n");
}
this.mTextView.Text = buffer.ToString();
this.bitmap = BitmapFactory.DecodeResource(this.Resources, Resource.Drawable.custom_model_image);
this.productResult.SetImageResource(Resource.Drawable.custom_model_image);
}
public void OnClick(View v)
{
switch (v.Id)
{
case Resource.Id.product_detect:
this.RemoteAnalyze();
break;
case Resource.Id.product_detect_plugin:
CheckPermissions(new string[]{Manifest.Permission.Camera, Manifest.Permission.ReadExternalStorage,
Manifest.Permission.WriteExternalStorage, Manifest.Permission.AccessNetworkState});
break;
default:
break;
}
}
protected override void OnDestroy()
{
base.OnDestroy();
if (this.analyzer == null)
{
return;
}
this.analyzer.Stop();
}
}
}
Xamarin App Build
1. Navigate to Solution Explore > Project > Right Click > Archive/View Archive to generate SHA-256 for build release and Click on Distribute.
2. Choose Distribution Channel > Ad Hoc to sign apk.
3. Choose Demo Keystore to release apk.
4. Finally here is the Result.
Tips and Tricks
1. HUAWEI ML Kit complies with GDPR requirements for data processing.
2. HUAWEI ML Kit does not support the recognition of the object distance and colour.
3. Images in PNG, JPG, JPEG, and BMP formats are supported. GIF images are not supported.
Conclusion
In this article, we have learned how to integrate HMS ML Kit in Xamarin based Android application. User can easily search items online with the help of product visual search API in this application.
Thanks for reading this article. Be sure to like and comments to this article, if you found it helpful. It means a lot to me.
References
https://developer.huawei.com/consum...-Plugin-Guides/about-service-0000001052602130

Categories

Resources