If we briefly talk about what HMS Site Kit is, you can provide users to explore the world faster with Site Kit. You can search for locations by keywords, find places which are close to the specified coordinate point, get detailed information about a place and get suggestions for places by keyword.
Thanks to Nearby Place Search feature provided by Site Kit, we can get a list of places around based on user’s current location. While performing this search, we can configure the search results according to our application by specifying certain parameters.
First of all, I will show you how you can use Nearby Place Search feature on a function. Then we will examine these parameters and code in detail.
Code:
fun nearbySearch(coordinate: Coordinate, keyword: String, radius: Int, locationType: LocationType,
hwLocationType: HwLocationType, pageIndex: Int, pageSize: Int){
val searchService = SearchServiceFactory.create(context,
URLEncoder.encode(
"Your-API-KEY",
"utf-8"))
var request = NearbySearchRequest()
request.location = coordinate
request.query = keyword
request.radius = radius
request.hwPoiType = hwLocationType
request.poiType = locationType
request.pageIndex = pageIndex
request.pageSize = pageSize
request.language = Locale.getDefault().language // Getting system language
searchService.nearbySearch(request, object: SearchResultListener<NearbySearchResponse>{
override fun onSearchError(searchStatus: SearchStatus?) {
Log.e("SITE_KIT","${searchStatus?.errorCode} - ${searchStatus?.errorMessage}")
}
override fun onSearchResult(nearbySearchResponse: NearbySearchResponse?) {
var siteList = nearbySearchResponse?.sites
siteList?.let {
for(site in siteList){
Log.i("SITE_KIT", "Name => ${site.name}," +
"Format address => ${site.formatAddress}, " +
"Coordinate => ${site.location.lat} - ${site.location.lng}, " +
"Phone => ${site.poi.phone}, " +
"Photo URLS => ${site.poi.photoUrls}, " +
"Rating => ${site.poi.rating}, " +
"Address Detail => ${site.address.thoroughfare}, ${site.address.subLocality}, " +
"${site.address.locality}, ${site.address.adminArea}, ${site.address.country}")
}
} ?: kotlin.run {
Log.e("SITE_KIT","There is not any site")
}
}
})
}
First, we need to create a SearchService object from the SearchServiceFactory class. For this, we can use the create() method of the SearchServiceFactory class. We need to declare two parameters in create() method.
The first of these parameters is context value. It is recommended that Context value should be in Activity type. Otherwise, when HMS Core(APK) needs to be updated, we can not receive any notification about it.
The second parameter is API Key value that we can access via AppGallery Connect. This value is generated automatically by AppGallery Connect when a new app is created. We need to encode API parameter as encodeURI.
After creating our SearchService object as I described above, we can create a NearbySearchRequest object. We will determine all the criteria on this project which we will perform on search.
To search for nearby places, we first need to create a NearbySearchRequest object. We will specify certain criteria on this object to configure our search results. These criteria provide our search results to modify according to the application logic. Let’s examine these criteria in detail:
Location: Here we need to report user’s location. We can determine a value by ourselves, but it will be more useful in terms of the working logic our application if we declare current location of user or last know location of user in order to obtain more healthy result.
Query: If we want to return places for specific keywords instead of searching for all places, we can use this parameter. It narrows and customizes the search result according to the keywords we have specified.
Radius: It is used to make the search results within in a radius determined in meters. It can take values between 1 and 50000, and its default value is 50000.
PoiType: With PoiType, we can significantly change our search results. It takes an object of type LocationType. With LocationType object, we can specify parameters such as LocationType.AIRPORT, LocationType.MUSEUM, LocationType.PARKING. Thus, our search results can be limited as airport, museum, parking lot etc.
HwPoiType: HwPoiType has joined us with 5.0.0 version of Site Kit. It is used for similar purposes with PoiType. However, HwPoiType contains much more details. In this way, we can have the opportunity to further customize our searches. To use this feature, we need to use values of HwLocationType enum.
To learn more about the difference between LocationType and HwLocationType, let’s examine the differences with an example.
Let’s consider an application that user wants to search for restaurants. If we want to search for restaurants with LocationType object, we need to specify a parameter such as LocationType.RESTAURANT. This will return us all restaurants.
But what should we do when user wants to customize the restaurants? This is where HwLocationType is coming our help right here. With HwLocationType, we can further customize the restaurants we want to search for. For example, if user wants to go to Chinese restaurants, we can specify HwLocationType.CHINESE_RESTAURANT parameter, if user wants to go to Turkish restaurants, we can specify HwLocationType.TURKISH_RESTAURANT parameter.
Note: If we use both PoiType and HwPoiType parameters simultaneously, HwPoiType parameter will be priority.
PageSize: Results return with the Pagination structure. This parameter is used to determine the number of Sites to be found in each page.
PageIndex: It is used to specify the number of the page to be returned with the Pagination structure.
Language: It is used to specify the language that search results have to be returned. If this parameter is not specified, language of the query field we have specified in the query field is accepted by default. In example code snippet in above, language of device has been added automatically in order to get a healthy result.
After determining parameters of search process, we can perform our search process. We need to use nearbySearch() method of SearchService object ot perform the search operation for Nearby Place Search feature. This method takes two parameters.
For the first parameter, we need to specify NearbySearchRequest object we have defined above.
For the second parameter, we have to implement SearchResultListener interface. Since this interface has a generic structure, we need to specify class belonging to the values to be returned. We can get the incoming values by specifying NearbySearchResponse object. Two methods should be override with this interface. onSearchError() method is executed if operation fails, and onSearchResult() method is executed if operation is successful. There are 2 different values in NearbySearchResponse. These are sites and totalCount values. sites, keeps a list of Site object. And, totalCount indicates number of return values.
Related
More articles like this one, you can visit HUAWEI Developer Forum and Medium.
All About Maps
Let's talk about maps. I started an open source project called All About Maps (https://github.com/ulusoyca/AllAboutMaps). In this project I aim to demonstrate how we can implement the same map related use cases with different map providers in one codebase. We will use Mapbox Maps, Google Maps, and Huawei HMS Map Kit. This project uses following libraries and patterns:
MVVM pattern with Android Jetpack Libraries
Kotlin Coroutines for asynchronous operations
Dagger2 Dependency Injection
Android Clean Architecture
Note: The codebase changes by time. You can always find the latest code in develop branch. The code when this article is written can be seen by choosing the tag: episode_1-parse-gpx:
https://github.com/ulusoyca/AllAboutMaps/tree/episode_1-parse-gpx/
Motivation
Why do we need maps in our apps? What are the features a developer would expect from a map SDK? Let's try to list some:
Showing a coordinate on a map with camera options (zoom, tilt, latitude, longitude, bearing)
Adding symbols, photos, polylines, polygons to map
Handle user gestures (click, pinch, move events)
Showing maps with different map styles (Outdoor, Hybrid, Satallite, Winter, Dark etc.)
Data visualization (heatmaps, charts, clusters, time-lapse effect)
Offline map visualization (providing map tiles without network connectivity)
Generate snapshot image of a bounded region
We can probably add more items but I believe this is the list of features which all map provider companies would most likely provide. Knowing that we can achieve the same tasks with different map providers, we should not create huge dependencies to any specific provider in our codebase. When a product owner (PO) tells to developers to switch from Google Maps to Mapbox Maps, or Huawei Maps, developers should never see it as a big deal. It is software development. Business as usual.
One would probably think why a PO would want to switch from one map provider to another. In many cases, the reason is not the technical details. For example, Google Play Services may not be available in some devices or regions like China. Another case is when a company X which has a subscription to Mapbox, acquires company Y which uses Google Maps. In this case the transition to one provider is more efficient. Change in the terms of services, and pricing might be other motivations.
We need competition in the market! Let's switch easily when needed but how dependencies make things worse? Problematic dependencies in the codebase are usually created by developing software like there is no tomorrow. It is not always developers' fault. Tight schedules, anti-refactoring minded teams, unorganized plannings may cause careless coding and then eventually to technical depts. In this project, I aim to show how we can encapsulate the import lines below belonging to three different map providers to minimum number of classes with minimum lines:
import com.huawei.hms.maps.*
import com.google.android.gms.maps.*
import com.mapbox.mapboxsdk.maps.*
It should be noted that the way of achieving this in this post is just one proposal. There are always alternative and better ways of implementations. In the end, as software developers, we should deliver our tasks time-efficiently, without over-engineering.
About the project
In the home page of the project you will see the list of tutorials. Since this is the first blog post, there is only one item for now. To make our life easier with RecyclerViews, I use Epoxy library by Airbnb in the project. Once you click the buttons in the card, it will take to the detail page. Using bottom sheet we can switch between map providers. Note that Huawei Map Kit requires a Huawei mobile phone.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
In this first blog post, we will parse the GPX file of 120 km route of Cappadocia Ultra Trail race and show the route and check points (food stations) on map. I finished this race in 23 hours 45 mins and you can also read my experience here (https://link.medium.com/uWmrWLAzR6). GPX is an open standart which contains route points that constructs a polyline and waypoints which are the attraction location. In this case, the waypoints represents the food and aid stations in the race. We will show the route with a polyline and waypoints with markers on map.
Architecture
Architecture is definitely not an overrated concept. Since the early days of Android, we have been seeking for the best architectural patterns that suits with Android development. We have heard of MVC, MVP, MVVM, MVI and many other patterns will emerge. The change and adaptation to a new pattern is inevitable by time. We should keep in mind some basic and commonly accepted concepts like SOLID principles, seperation of concerns, maintainability, readibility, testablity etc. so that we can switch to between patterns easily when needed.
Nowadays, widely accepted architecture in Android community is modularization with Clean Architecture. If you have time to invest more, I would strongly suggest Joe Birch's clean architecture tutorials. As Joe suggests in his tutorials, we do not have to apply every rule line by line but instead we take whatever we feel like is needed. Here is my take and how I modularized the All About Maps app:
Note that dependency injection with Dagger2 is the core of this implementation. If you are not familiar with the concept, I strongly suggest you to read the best Dagger2 tutorial in the wild Dagger2 world by Nimrod Dayan.
Domain Module
Many of us are excited to start implementation with UI to see the results immediately but we should patiently build our blocks. We shall start with the domain module since we will put our business logic and define the entities and user interactions there.
First question: What entities do we need for a Map app?
We don't have to put every entity at once. Since our first tutorial is about drawing polylines and symbols we will need the following data:
LatLng class which holds Latitude and Longitude
Point which represents a geo-coordinate.
RouteInfo that holds points to be used to draw route and waypoints
Let's see the implementations:
Code:
inline class Latitude(val value: Float)
inline class Longitude(val value: Float)
Code:
data class LatLng(
val latitude: Latitude,
val longitude: Longitude
)
Code:
data class Point(
val latitude: Latitude,
val longitude: Longitude,
val altitude: Float? = null,
val name: String? = null
) {
val latLng: LatLng
get() = LatLng(latitude, longitude)
}
Code:
data class RouteInfo(
val routePoints: List<Point> = emptyList(),
val wayPoints: List<Point> = emptyList()
)
I could have used Float primitive type for Latitude and Longitude fields. However, I strongly suggest you to take advantage of Kotlin inline classes. In my relatively long career of working on maps, I spent hours on issues caused by mistakenly using longitude for latitude values.
Note that LatLng class is available in all Map SDKs. However, all the modules below the domain layer should use only our own LatLng to prevent the dependency to map SDKs in those modules. In the app layer we can map our LatLng class to corresponding classes:
Code:
import com.ulusoy.allaboutmaps.domain.entities.LatLng
import com.mapbox.mapboxsdk.geometry.LatLng as MapboxLatLng
import com.huawei.hms.maps.model.LatLng as HuaweiLatLng
import com.google.android.gms.maps.model.LatLng as GoogleLatLang
fun LatLng.toMapboxLatLng() = MapboxLatLng(
latitude.value.toDouble(),
longitude.value.toDouble()
)
fun LatLng.toHuaweiLatLng() = HuaweiLatLng(
latitude.value.toDouble(),
longitude.value.toDouble()
)
fun LatLng.toGoogleLatLng() = GoogleLatLang(
latitude.value.toDouble(),
longitude.value.toDouble()
)
Second question: What actions user can trigger?
Domain module contains the uses cases (interactors) that an application can perform to achieve goals based on user interactions. The code in this module is less likely to change compared to other modules. Business is business. For example, this application has one job for now: showing the route info with a polyline and markers. It can get the route info from a web server, a database or in this case from application resource file which is a GPX file. Neither the app module nor the domain module doesn't care where the route points and waypoints are retrieved from. It is not their concern. The concerns are seperated.
Lets see the use case definition in our domain module:
Code:
class GetRouteInfoUseCase
@Inject constructor(
private val routeInfoRepository: RouteInfoRepository
) {
suspend operator fun invoke(): RouteInfo {
return routeInfoRepository.getRouteInfo()
}
}
Code:
interface RouteInfoRepository {
suspend fun getRouteInfo(): RouteInfo
}
RouteInfoRepository is an interface that lives in the domain module and it is a contract between domain and datasource modules. Its concrete implementation lives in the datasource module.
Datasource Module
Datasource module is an abstraction world. Life here is based on interfaces. The domain module communicates with datasource module through the repository interface, then datasource module orchestrates the data flow in repository class and returns the final value.
Here, the domain module asks for the route info. Datasource module decides what to return after retrieving data from different data sources. For the sake of simplicity, in this case we have only one datasource: GPX parser. The route info is extracted from a GPX file. We don't know where, and how. Let's see the code:
Here is the concrete implementation of RouteInfoRepository interface. Route info datasource is injected as constructor parameter to this class.
Code:
class RouteInfoDataRepository
@Inject constructor(
@Named("GPX_DATA_SOURCE")
private val gpxFileDatasource: RouteInfoDatasource
) : RouteInfoRepository {
override suspend fun getRouteInfo(): RouteInfo {
return gpxFileDatasource.parseGpxFile()
}
}
Here is our one only route info data source: GpxFileDataSource. It still doesn't know how to get the data from gpx file. However, it knows where to get the data from thanks to contract GpxFileParser
Code:
class GpxFileDatasource
@Inject constructor(
private val gpxFileParser: GpxFileParser
): RouteInfoDatasource {
override suspend fun parseGpxFile(): RouteInfo {
return gpxFileParser.parseGpxFile()
}
}
What is a GPX file? How is it parsed? Where is the file located? Datasource doesn't care about these details. It only knows that the concrete implementation of GpxFileParser will return the RouteInfo. Here is the contract between the datasource and the concrete implementation:
Code:
interface GpxFileParser {
suspend fun parseGpxFile(): RouteInfo
}
Is it already too confusing with too many abstractions around? Is it overengineering? You might be right and choose to have less abstractions when you have one datasource like in this case. However, in real world, we have multiple datasources. Data is all around us. It may come from web server, from database or from our connected devices such as wearables. The benefit here is when things get more complicated with multiple datasources. Let's think about this complicated scenario.
App asks for the route info through a use case class.
Domain module forwards the request to data source.
Datasource orchestrates the data in the repository class
It first asks to Web servers (remote data source) to get the route info. However, the user is offline. Thus, the remote data source is not available.
Then it checks what we have locally by first checking if the route info is available in database or not.
It is not available in database but we have a gpx file in our resource folder (I know it doesn't make sense but to give an example).
The repository class asks GPX parser to parse the file and return the desired RouteInfo data.
Too complicated? It would be this much easier to implement this code in repository class based on the scenario:
Code:
class RouteInfoDataRepository
@Inject constructor(
@Named("GPX_DATA_SOURCE")
private val gpxFileDatasource: RouteInfoDatasource,
@Named("REMOTE_DATA_SOURCE")
private val remoteDatasource: RouteInfoDatasource,
@Named("DATABASE_SOURCE")
private val localDatasource: RouteInfoDatasource
) : RouteInfoRepository {
override suspend fun getRouteInfo(): RouteInfo? {
var routeInfo = remoteDatasource.parseGpxFile()
if (routeInfo == null) {
Timber.d("Route info is not available in remote source, now trying local database")
routeInfo = localDatasource.parseGpxFile()
if (routeInfo == null) {
Timber.d("Route info is not available in local database. Let's hope we have a gpx file in the app resource folder")
gpxFileDatasource.parseGpxFile()
}
}
return routeInfo
}
}
Thanks to Kotlin coroutines we can write these asynchronous operations sequentially.
For full content, you can visit HUAWEI Developer Forum.
If we briefly talk about what HMS Site Kit is, you can provide users to explore the world faster with Site Kit. You can search for locations by keywords, find places which are close to the specified coordinate point, get detailed information about a place and get suggestions for places by keyword.
With Place Search Suggestion which is another feature provided by the Site Kit, we can list the place suggestions of the values entered by user. If we given an example through a scenario, when user enters a value for search, we can show suggestions to user belong to places. For this case, we can use Place Search Suggestion feature provided by Site Kit.
Before I explain the use of Place Search Suggestion, I would like to share with you a function that we can use this feature.
Code:
fun placeSearchSuggestion(coordinate: Coordinate, keyword: String, radius: Int, locationTypes: List<LocationType>,
countryCode: String){
val searchService = SearchServiceFactory.create(context,
URLEncoder.encode(
"Your-API-KEY",
"utf-8"))
var request = QuerySuggestionRequest()
request.location = coordinate
request.query = keyword
request.radius = radius
request.poiTypes = locationTypes
request.countryCode = countryCode
request.language = Locale.getDefault().language // Getting system language
searchService.querySuggestion(request, object: SearchResultListener<QuerySuggestionResponse>{
override fun onSearchError(searchStatus: SearchStatus?) {
Log.e("SITE_KIT","${searchStatus?.errorCode} - ${searchStatus?.errorMessage}")
}
override fun onSearchResult(querySuggestionResponse: QuerySuggestionResponse?) {
var sites = querySuggestionResponse?.sites
sites?.let {
for(site in sites){
Log.i("SITE_KIT", "Name => ${site.name}," +
"Format address => ${site.formatAddress}, " +
"Coordinate => ${site.location.lat} - ${site.location.lng}, " +
"Phone => ${site.poi.phone}, " +
"Photo URLS => ${site.poi.photoUrls}, " +
"Rating => ${site.poi.rating}, " +
"Address Detail => ${site.address.thoroughfare}, ${site.address.subLocality}, " +
"${site.address.locality}, ${site.address.adminArea}, ${site.address.country}")
}
} ?: kotlin.run {
Log.e("SITE_KIT","No query suggestion found")
}
}
})
}
First, we need to create a SearchService object from the SearchServiceFactory class. For this, we can use the create() method of the SearchServiceFactory class. We need to declare two parameters in create() method.
The first of these parameters is context value. It is recommended that Context value should be in Activity type. Otherwise, when HMS Core(APK) needs to be updated, we can not receive any notification about it.
The second parameter is API Key value that we can access via AppGallery Connect. This value is generated automatically by AppGallery Connect when a new app is created. We need to encode API parameter as encodeURI.
After creating our SearchService object as I described above, we can create a QuerySuggestionRequest object. With this object, we will define the parameters that we have determined to get place suggestions.
To get place suggestions for the criteria which we have specified, we must first create a QuerySuggestionRequest object. The criteria we have determined allows place suggestions to change according to the needs of our application. Let us examine these parameters one by one if you want:
Location: We need to specify the coordinate points of region where we want to search. By creating a Coordinate object with latitude and longitude object, we can specify it in this parameter. Coordinate class is a class that Huawei provides to us. It holds latitude and longitude information.
Query: If we want to return places for specific keywords instead of searching for all places, we can use this parameter. It narrows and customizes search results according to the keywords we have specified.
Radius: It is used to make the search results within in a radius determined in meters. It can take values between 1 and 50000, and its default value is 50000.
PoiTypes: With PoiType, we can significantly change our search suggestions. PoiTypes takes values from LocationType class. For example, we can use LocationTypes.MUSEUM to search for museums and LocationTypes.RESTAURANT to search for restaurants. In the QuerySuggestionRequest object, poiTypes takes LocationType values in list type. In this way, we can expand results of our place suggestions by specifying more than one LocationType.
CountryCode: It is using to limit search results according to certain country borders.
Language: It is used to specify the language that search results have to be returned. If this parameter is not specified, language of the query field we have specified in the query field is accepted by default. In example code snippet in above, language of device has been added automatically in order to get a healthy result.
After determining the parameters belong to place suggestion process, we can perform the process of receiving place suggestions. We need to use querySuggestion() method of SearchService object to perform receiving the place suggestions belong to Place Search Suggestion feature. This method takes two parameters.
For the first parameter, we must specify the QuerySuggestionRequest object we have defined above.
For the second parameter, we have to implement SearchResultListener interface. Since this interface has a generic structure, we need to specify class belonging to the values to be returned. We can get the incoming values by specifying QuerySuggestionResponse object. Two methods must be override with this interface. onSearchError() method is executed if operation fails, and onSearchResult() method is executed if operation is successful. There is one value in QuerySuggestionResponse class. This value maintains a list of Site objects of places. We can access returned values with this Site object.
Your sharing actually helps a lot
Hi Well explained,what are all types we can search using sitekit search,can you explain.
sujith.e said:
Hi Well explained,what are all types we can search using sitekit search,can you explain.
Click to expand...
Click to collapse
Hi, thank you so much! With PoiTypes of SiteKit feature, we can configure places in specified categories such as AIRPORT, MUSEUM, BANK, HOSPITAL and many more different categories. For example, we can search for near museums in our application to show museums to user. To do that, we can give PoiTypes as LocationType.MUSEUM. LocationType is a model class provided by Huawei.
With the HMS Core 5.0, Huawei created new LocationType which is called as HwLocationType. We use this parameter with hwPoiType. HwLocationType works in same way with LocationType. But HwLocationType is detailing places as sub categories. For example, if we want to search for restaurants with LocationType object, we need to specify a parameter such as LocationType.RESTAURANT. This will return us all restaurants.
With HwLocationType, we can further customize the restaurants we want to search for. For example, if user wants to go to Chinese restaurants, we can specify HwLocationType.CHINESE_RESTAURANT parameter, if user wants to go to Turkish restaurants, we can specify HwLocationType.TURKISH_RESTAURANT parameter.
For now, HwLocationType works only with NearbySearchRequest feature.
Can we create multiple query using HMS Site kit using QuerySuggestionRequest() ?
With the unveiling of so many new functions and pages on HUAWEI AppGallery, it’s required a wide range of new redirection solutions on the platform.
However, the types of links, functions, apps, and scenarios that they are used, can vary great. It can be quite confusing sorting this all out, so I’ve outlined some common redirection scenarios for your reference.
Feel free to correct me, if you think that I’ve got something wrong, or am missing something.
1. Redirecting to the AppGallery Home Page
Usage case: You want to redirect the user from an app to the AppGallery home page, so that the user can search for related apps or activities quickly.
Method: Use the action method from Intent to implement the function.
action: com.huawei.appmarket.intent.action.MainActivity
Example:
Java:
public void launchAGHomePage() {
Intent intent = new Intent("com.huawei.appmarket.intent.action.MainActivity");
startActivity(intent);
}
2. Redirecting to an App’s Details Page on AppGallery
2.1 Intent-based Redirection from an In-App Page
Usage case: You want to redirect the user from an in-app page to the app’s details page, so the user can rate or comment on the app.
Methods: Use the action method from Intent to implement the function.
1. Method 1: by app ID
action:com.huawei.appmarket.appmarket.intent.action.AppDetail. withid
setPackage("com.huawei.appmarket");
name: "appId", value: "C100170981"
2. Method 2: by package name
action: com.huawei.appmarket.intent.action.AppDetail
setPackage("com.huawei.appmarket");
name: "APP_PACKAGENAME", value: "com.huawei.browser"
Note: Compared with method 2, method 1 includes additional appmarket and withid parameters from the action method.
Parameters involved:
{
"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"
}
Examples:
Method 1:by app ID
Java:
public void launchAppDetilPage1() {
Intent intent = new Intent("com.huawei.appmarket.appmarket.intent.action.AppDetail.withid");
intent.setPackage("com.huawei.appmarket");
intent.putExtra("appId", "C100170981");
startActivity(intent);
}
Method 2:by package name
Java:
public void launchAppDetilPage2() {
Intent intent = new Intent("com.huawei.appmarket.intent.action.AppDetail");
intent.setPackage("com.huawei.appmarket");
intent.putExtra("APP_PACKAGENAME", "com.huawei.browser");
startActivity(intent);
}
2.2 URL-based Redirection
Usage case: You want to redirect a user who clicks on a shared URL to an app’s details page.
Method: Create a URL with the following format:
hiapplink://com.huawei.appmarket?appId=yourAppID&channelId=yourChannelId&referrer=yourReferrer
Note: You’ll need to change the strings in either italic or bold only.
Parameters involved:
Example:
By app id
Java:
public void launchAppDetilWithURL1() {
String text1 = "hiapplink://com.huawei.appmarket?appId=C100170981&channelId=HwBrowserSearch&referrer=Keywords";
Uri uri = Uri.parse(text1);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
3. Launching All App Stores Installed on a Device to Redirect Users to an App’s Details Page in an App Store via MARKET
Usage case: You want to launch all app stores installed on the user’s device by passing a package name or app ID, so that the user can choose to go to the app’s details page in a desired app store.
Methods: Pass the link whose scheme is market://. Android supports the standard MARKET protocol, to ensure that all app stores can be launched on Android devices. The methods are as follows:
Method 1: market://details?id=pkgName // for all stores
Method 2: appmarket://details?id=pkgName // only for AppGallery
Method 3: market://com.huawei.appmarket.applink?appId=App ID" // only for AppGallery
Note: Method 1 is a standard method for Android devices, and is applicable to all app stores, such as Google Play and Tecent Appstore.
Parameters involved:
Examples:
// Method 1
Java:
public void launchAppDetilOnMarket1() {
String text1 = "market://details?id=com.huawei.browser";
Uri uri = Uri.parse(text1);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
// Method 2
Java:
public void launchAppDetilOnMarket2() {
String text1 = "appmarket://details?id=com.huawei.browser";
Uri uri = Uri.parse(text1);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
// Method 3
Java:
public void launchAppDetilOnMarket3() {
String text1 = "market://com.huawei.appmarket.applink?appId=C100170981";
Uri uri = Uri.parse(text1);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
4. Redirecting from a Web Link to an App’s Detail Page on the Web Version of AppGallery
Usage case: You want to redirect the user to an app’s details page on the web version of AppGallery, when the user clicks on a web link from the app’s official website, or a promotional web page.
Methods:
Method 1:https://appgallery.huawei.com/#/app/YOUR_APPID
Method 2::https://appgallery.cloud.huawei.com/appDetail?pkgName=pkgName
Method 3:https://appgallery.huawei.com/#/app/YOUR_APPID?pkgName=pkgName
Method 4:https://appgallery.cloud.huawei.com/marketshare/app/ YOUR_APPID?locale=LOCALE&shareTo=WAP&shareFrom=channeID
Parameters involved:
Examples:
// Method 1: by app ID
https://appgallery.huawei.com/#/app/C100170981
// Method 2: by package name
https://appgallery.cloud.huawei.com/appDetail?pkgName=com.huawei.browser
// Method 3: by app ID and package name
https://appgallery.huawei.com/#/app/C100170981?pkgName=com.huawei.browser
// Method 4: by complete link with optional parameters (not commonly used, and generally used for badge-based promotions).
HUAWEI Browser
Huawei AppGallery
appgallery.cloud.huawei.com
5. Redirecting from a Badge Link to the App Detail Page on AppGallery
When a badge link of AppGallery appears as an image of AppGallery, by clicking this link, the user will be redirected to an app’s details page on AppGallery. If you hope to market your app, you can use this badge directly. In essence, it still functions as a link, no different than any other web link.
Use cases: If you have developed and released an app, and want to divert traffic from your website or another source to AppGallery, a badge can be ideal.
How to make a badge: The method for creating a badge is rather simple. You only need to sign in to AppGallery Connect, and click AppGallery Download. Then you proceed to make the badge as prompted.
Restrictions: You can only make a badge for apps that have been released, with a maximum of one badge for each app. Once a badge has been created, you can query for it on the Search badge tab page.
Badge use instructions:
You can find a created badge on the Search badge tab page, and download the badge or copy its link. I’ll walk you through some common operations:
Badge download: You will obtain a PNG image, which can be displayed on your website, or on a marketing HTML5 page.
Link creation: You can create a link for a specific channel, for example, Facebook or Baidu.
Link copying: You can download a link for a specific channel.
Usage example:
// 1. Typical link
https://appgallery.huawei.com/#/app...EDCCBCD90A86F29A8DA2400AA4163&detailType=0&v=
// 2. Typical badge with a link, which can be clicked
6. App Linking, with Cross-Platform Link Support
App Linking is a new service provided by AppGallery Connect. Since it’s new, I’ll give you a brief overview of what it is.
The service provides links that can work on a diverse range of platforms, such as Android, iOS, and PC browsers, making it similar in this way to Dynamic Links of Firebase, enabling you to quickly build cross-platform links, which can be easily shared.
In what scenarios can I use App Linking? I’ll give you an example to help illustrate how the service can be used. It’s an app that’s available on both Android and iOS devices, and thus requires a promotional activity that reaches users on both platforms. To engage users, we’ll need to send them an activity invitation link that works on Android and iOS alike. Also, some users may be opening the link in a PC browser, and in this case, that link should also support an HTML5 page for the activity.
What benefits does App Linking offer? Here, two scenarios should be considered:
The user’s device has already installed the app: App Linking will automatically open the app, and show the user the target page.
The user’s device has not installed the app: The link will prompt the user to open the app’s details page on AppGallery, or any other app store (configurable), and download the app. Then, when the user opens the downloaded app, the target page will appear.
How does App Linking work? Links of App Linking can be created in any of the following ways:
Creation on the console: All operations are performed in AppGallery Connect. You’ll need to click My projects, click your project, and go to Grow > App Linking. To create a link of App Linking, create a URL prefix first.This mode is recommended if you’re not familiar with coding. A step will require you to provide a deep link, which will need to be obtained from your R&D personnel.
Creation in your iOS app: This mode is similar to the previous one.
The only difference is that these links are for iOS users.
What if the user’s device is not a Huawei device?
Since App Linking supports both Android and iOS, many of you may be wondering how this service works on non-Huawei Android devices.
1. Can App Linking work for non-Huawei devices? You can be rest assured App Linking is not dependent on HMS Core, and thus is supported on all Android devices, regardless of whether they are running GMS or HMS.
2. What if the user has not installed the app and AppGallery? For Android devices that don’t have AppGallery installed, you can choose to open the link in the local app store that is installed. As long as your package name on that app store is the same as that on AppGallery, the user will be able to open your app’s details page on that app store.
Usage example:
// 1. Typical URL prefix
https://photoplaza.drcn.agconnect.link // In the prefix, photoplaza is unique to the app, and drcn.agconnect.link is a fixed.
// 2. Typical link of App Linking
https://photoplaza.drcn.agconnect.link/vm3Y
// 3. Code for creating a typical link of App Linking for Android
Java:
private static final String DOMAIN_URI_PREFIX = "https://photoplaza.drcn.agconnect.link";private static final String DEEP_LINK = "https://developer.huawei.com";public void createAppLinking() {
AppLinking.Builder builder = new AppLinking.Builder()
.setUriPrefix(DOMAIN_URI_PREFIX)
.setDeepLink(Uri.parse(DEEP_LINK))
.setAndroidLinkInfo(new AppLinking.AndroidLinkInfo.Builder().build());
String LongAppLinking = builder.buildAppLinking().getUri().toString();
}
// 4. Typical link of App Linking for iOS
Objective-C:
- (IBAction)CreatLink:(id)sender {
AGCAppLinkingComponents *component = [[AGCAppLinkingComponents alloc] init];
component.uriPrefix = @"https://photoplaza.drcn.agconnect.link";
component.deepLink = @"https://www.developer.huawei.com";
component.iosBundleId = @"com.lucky.agc.demo";
component.iosDeepLink = @"agckit://ios/detail"; self.longlink.text = component.buildLongLink.absoluteString;
}
7. For Reference
Badge link document: https://developer.huawei.com/consum...ct-Guides/appgallery-agd-introduction-stamped
App Linking document: https://developer.huawei.com/consum.../agc-applinking-introduction-0000001054143215
Guide for adding a referrer: https://developer.huawei.com/consum...connect-Guides/appgallery-referrer-createlink
Guide for querying attribution information: https://developer.huawei.com/consum...ry-connect-Guides/appgallery-referrer-develop
Why Do I Need a Background RemoverA background removal tool is not really a new feature, but rather its importance has grown as the world shifted over to online working and learning over the last few years. And I did not find how important this tool could be until just two weeks ago. On a warm, sunny morning with a coffee on hand, I joined an online conference. During this conference, one of my colleagues pointed out to me that they could see my untidy desk and an overflowing bin in the background. Naturally, this left me feeling embarrassed. I just wish I could travel back in time to use a background remover.
Now, I cannot travel in time, but I can certainly create a background removal tool. So, with this new-found motive, I looked online for some solutions and came across the body or head segmentation capability from HMS Core Video Editor Kit, and developed a demo app with it.
This service can divide the body or head from an input image or video and then generate a video, an image, or a sticker of the divided part. In this way, the body or head segmentation service helps realize the background removal effect.
Now, let's go deeper into the technical details about the service.
How the Background Remover Is ImplementedThe algorithm of the service performs a series of operations on the input video, including extracting frames, using an AI model to process the video, and encoding. Among all these, the core is the AI model. How a service performs is affected by factors like device computing power and power consumption. Considering these, the development team of the service manages to equip it with a light-weight AI model that does a good job in feature extraction, by taking measures like compression, quantization, and pruning. In this way, the processing duration of the AI model is decreased to a relatively low level, without compromising the segmentation accuracy.
The mentioned algorithm supports both images and videos. An image takes the algorithm a single inference for the segmentation result. A video is actually a collection of images. If a model features poor segmentation capability, the segmentation accuracy for each image will be low. As a result, the segmentation results of consecutive images will be different from each other, and the segmentation result of the whole video will appear shaking. To resolve this, the team adopts technologies like inter-frame stabilization and the objective function for inter-frame consistency. Such measures do not compromise the model inference speed yet fully utilize the time sequence information of a video. Consequently, the algorithm sees its inter-frame stabilization improved, which contributes to an ideal segmentation effect.
By the way, the service requires that the input image or video contains up to 5 people whose contours should be visible. Besides, the service supports common motions of the people in the input image or video, like standing, lying, walking, sitting, and more.
The technical basics of the service conclude here, and let's see how it can be integrated with an app.
How to Equip an App with the Background Remover FunctionalityPreparationsGo to AppGallery Connect and configure the app's details. In this step, we need to register a developer account, create an app, generate a signing certificate fingerprint, and activate the required services.
Integrate the HMS Core SDK.
Configure the obfuscation scripts.
Declare necessary permissions.
Setting Up a Video Editing ProjectPrerequisites1. Set the app authentication information either by:
Using an access token: Call the setAccessToken method to set an access token when the app is started. The access token needs to be set only once.
Code:
MediaApplication.getInstance().setAccessToken("your access token");
Using an API key: Call the setApiKey method to set an API key when the app is started. The API key needs to be set only once.
Code:
MediaApplication.getInstance().setApiKey("your ApiKey");
2. Set a License ID. The ID is used to manage the usage quotas of the kit, so make sure the ID is unique.
Code:
MediaApplication.getInstance().setLicenseId("License ID");
Initializing the Runtime Environment for the Entry ClassA HuaweiVideoEditor object serves as the entry class of a whole video editing project. The lifecycle of this object and the project should be the same. Therefore, when creating a video editing project, create a HuaweiVideoEditor object first and then initialize its runtime environment. Remember to release this object when exiting the project.
1. Create a HuaweiVideoEditor object.
Code:
HuaweiVideoEditor editor = HuaweiVideoEditor.create(getApplicationContext());
2. Determine the preview area position.
This area renders video images, a process that is implemented by creating SurfaceView within the SDK. Make sure that the position of this area is specified before the area is created.
Code:
<LinearLayout
android:id="@+id/video_content_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/video_edit_main_bg_color"
android:gravity="center"
android:orientation="vertical" />
// Specify the preview area position.
LinearLayout mSdkPreviewContainer = view.findViewById(R.id.video_content_layout);
// Specify the layout of the preview area.
editor.setDisplay(mSdkPreviewContainer);
3. Initialize the runtime environment of HuaweiVideoEditor. LicenseException will be reported when the license verification fails.
The HuaweiVideoEditor object, after being created, has not occupied any system resources. We need to manually set the time for initializing its runtime environment, and then the necessary threads and timers will be created in the SDK.
Code:
try {
editor.initEnvironment();
} catch (LicenseException error) {
SmartLog.e(TAG, "initEnvironment failed: " + error.getErrorMsg());
finish();
return;
}
Integrating the Segmentation Capability
Code:
// Initialize the segmentation engine. segPart indicates the segmentation type, whose value is an integer. Value 1 indicates body segmentation, and a value other than 1 indicates head segmentation.
visibleAsset.initBodySegEngine(segPart, new HVEAIInitialCallback() {
@Override
public void onProgress(int progress) {
// Callback when the initialization progress is received.
}
@Override
public void onSuccess() {
// Callback when the initialization is successful.
}
@Override
public void onError(int errorCode, String errorMessage) {
// Callback when the initialization failed.
}
});
// After the initialization is successful, apply the segmentation effect.
visibleAsset.addBodySegEffect(new HVEAIProcessCallback() {
@Override
public void onProgress(int progress) {
// Callback when the application progress is received.
}
@Override
public void onSuccess() {
// Callback when the effect is successfully applied.
}
@Override
public void onError(int errorCode, String errorMsg) {
// Callback when the effect failed to be applied.
}
});
// Stop applying the segmentation effect.
visibleAsset.interruptBodySegEffect();
// Remove the segmentation effect.
visibleAsset.removeBodySegEffect();
// Release the segmentation engine.
visibleAsset.releaseBodySegEngine();
And now the app is capable of removing the image or video background.
This function is ideal for e-conferencing apps, where the background is not important. For learning apps, it allows teachers to change the background to the theme of the lesson, for better immersion. Not only that, but when it's used in a short video app, users can put themselves in unusual backgrounds, such as space and the sea, to create fun and fantasy-themed videos.
Have you got any better ideas of how to use the background remover? Let us know in the comments section below.
Wrap upBackground removal tools are trending among apps in different fields, given that such a tool helps images and videos look better by removing unnecessary or messy backgrounds, as well as protecting user privacy.
The body or head segmentation service from Video Editor Kit is one such solution for removing a background. It supports both images and videos, and outputs a video, an image, or a sticker of the segmented part for further editing. Its streamlined integration makes it a perfect choice for enhancing videos and images.
In the mobile Internet era, people are increasingly using mobile apps for a variety of different purposes, such as buying products online, hailing taxis, and much more. When using such an app, a user usually needs to manually enter their address for package delivery or search for an appropriate pick-up and drop-off location when they hail a taxi, which can be inconvenient.
To improve user experience, many apps nowadays allow users to select a point on the map and then use the selected point as the location, for example, for package delivery or getting on or off a taxi. Each location has a longitude-latitude coordinate that pinpoints its position precisely on the map. However, longitude-latitude coordinates are simply a string of numbers and provide little information to the average user. It would therefore be useful if there was a tool which an app can use to convert longitude-latitude coordinates into human-readable addresses.
Fortunately, the reverse geocoding function in HMS Core Location Kit can obtain the nearest address to a selected point on the map based on the longitude and latitude of the point. Reverse geocoding is the process of converting a location as described by geographic coordinates (longitude and latitude) to a human-readable address or place name, which is much more useful information for users. It permits the identification of nearby street addresses, places, and subdivisions such as neighborhoods, counties, states, and countries.
Generally, the reverse geocoding function can be used to obtain the nearest address to the current location of a device, show the address or place name when a user taps on the map, find the address of a geographic location, and more. For example, with reverse geocoding, an e-commerce app can show users the detailed address of a selected point on the map in the app; a ride-hailing or takeout delivery app can show the detailed address of a point that a user selects by dragging the map in the app or tapping the point on the map in the app, so that the user can select the address as the pick-up address or takeout delivery address; and an express delivery app can utilize reverse geocoding to show the locations of delivery vehicles based on the passed longitude-latitude coordinates, and intuitively display delivery points and delivery routes to users.
Bolstered by a powerful address parsing capability, the reverse geocoding function in this kit can display addresses of locations in accordance with local address formats with an accuracy as high as 90%. In addition, it supports 79 languages and boasts a parsing latency as low as 200 milliseconds.
{
"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"
}
DemoThe file below is a demo of the reverse geocoding function in this kit.
Preparations
Before getting started with the development, you will need to make the following preparations:
Register as a Huawei developer and complete identity verification on the HUAWEI Developers website. You can click here to find out the detailed registration and identity verification procedure.
Create a project and then create an app in the project in AppGallery Connect. Before doing so, you must have a Huawei developer account and complete identity verification.
Generate a signing certificate fingerprint and configure it in AppGallery Connect. The signing certificate fingerprint is used to verify the authenticity of an app. Before releasing an app, you must generate a signing certificate fingerprint locally based on the signing certificate and configure it in AppGallery Connect.
Integrate the Location SDK into your app. If you are using Android Studio, you can integrate the SDK via the Maven repository.
Here, I won't be describing how to generate and configure a signing certificate fingerprint and integrate the SDK. You can click here to learn about the detailed procedure.
Development Procedure
After making relevant preparations, you can perform the steps below to use the reverse geocoding service in your app. Before using the service, ensure that you have installed HMS Core (APK) on your device.
1. Create a geocoding service client.
In order to call geocoding APIs, you first need to create a GeocoderService instance in the onClick() method of GeocoderActivity in your project. The sample code is as follows:
Code:
Locale locale = new Locale("zh", "CN");
GeocoderService geocoderService = LocationServices.getGeocoderService(GeocoderActivity.this, locale);
2. Obtain the reverse geocoding information.
To empower your app to obtain the reverse geocoding information, you need to call the getFromLocation() method of the GeocoderService object in your app. This method will return a List<HWLocation> object containing the location information based on the set GetFromLocationRequest object.
a. Set reverse geocoding request parameters.
There are three request parameters in the GetFromLocationRequest object, which indicate the latitude, longitude, and maximum number of returned results respectively. The sample code is as follows:
Code:
// Parameter 1: latitude
// Parameter 2: longitude
// Parameter 3: maximum number of returned results
// Pass valid longitude-latitude coordinates. If the coordinates are invalid, no geographical information will be returned. Outside China, pass longitude-latitude coordinates located outside China and ensure that the coordinates are correct.
GetFromLocationRequest getFromLocationRequest = new GetFromLocationRequest(39.985071, 116.501717, 5);
b. Call the getFromLocation() method to obtain reverse geocoding information.
The obtained reverse geocoding information will be returned in a List<HWLocation> object. You can add listeners using the addOnSuccessListener() and addOnFailureListener() methods, and obtain the task execution result using the onSuccess() and onFailure() methods.
The sample code is as follows:
Code:
private void getReverseGeocoding() {
// Initialize the GeocoderService object.
if (geocoderService == null) {
geocoderService = new GeocoderService(this, new Locale("zh", "CN"));
}
geocoderService.getFromLocation(getFromLocationRequest)
.addOnSuccessListener(new OnSuccessListener<List<HWLocation>>() {
@Override
public void onSuccess(List<HWLocation> hwLocation) {
// TODO: Define callback for API call success.
if (null != hwLocation && hwLocation.size() > 0) {
Log.d(TAG, "hwLocation data set quantity: " + hwLocation.size());
Log.d(TAG, "CountryName: " + hwLocation.get(0).getCountryName());
Log.d(TAG, "City: " + hwLocation.get(0).getCity());
Log.d(TAG, "Street: " + hwLocation.get(0).getStreet());
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// TODO: Define callback for API call failure.
}
});
}
Congratulations, your app is now able to use the reverse geocoding function to obtain the address of a location based on its longitude and latitude.
Conclusion
The quick development and popularization of the mobile Internet has caused many changes to our daily lives. One such change is that more and more people are using mobile apps on a daily basis, for example, to buy daily necessities or hail a taxi. These tasks traditionally require users to manually enter the delivery address or pick-up and drop-off location addresses. Manually entering such addresses is inconvenient and prone to mistakes.
To solve this issue, many apps allow users to select a point on the in-app map as the delivery address or the address for getting on or off a taxi. However, the point on the map is usually expressed as a set of longitude-latitude coordinates, which most users will find hard to understand.
As described in this post, my app resolves this issue using the reverse geocoding function, which is proven a very effective way for obtaining human-readable addresses based on longitude-latitude coordinates. If you are looking for a solution to such issues, have a try to find out if this is what your app needs.