More articles 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"
}
Introduction
Now days, most of the application integrate an Maps. This article I will give you the process on how to do. Millions of users look up directions, plan their commutes, catch a ride.as well as touch on the many things available through maps to enhance the user experience in mobile apps.
Let’s Start how to Integrate Map:
Step1: create a new project in Android studio.
Step 2: Configure your app into AGC.
Step 3: Enable required Api & add SHA-256.
Step 4: Download the agconnect-services.json from AGC. Paste into app directory.
Step 5: Add the below dependency in app.gradle file.
Code:
implementation 'com.huawei.hms:maps:4.0.0.301'
Step 6: Add the below dependency in root.gradle file
Code:
maven { url 'http://developer.huawei.com/repo/' }
Step 7: Add appId & permissions in AndoridManifest.xml file
Code:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=*******" />
Step 8: Sync your Project
Let’s Discuss functionality:
1.OnMapReady()
2.OnMapClick()
3.OnMarkerClick()
4.Create Circle
5.Create Custom Marker
1. OnMapReady: This Callback interface for when map is ready to be used
Code:
@Override
public void onMapReady(HuaweiMap map) {
mHuaweiMap = map;
enableUiSettings();
mHuaweiMap.setMaxZoomPreference(15);
mHuaweiMap.setMinZoomPreference(2);
}
2. OnMapClick : This callback interface when the user makes tap on the map.
Code:
@Override
public void onMapClick(LatLng latLng) {
try {
createMarker(latLng);
} catch (IOException e) {
e.printStackTrace();
}
}
3. OnMarkerClick : This callback interface when a marker is clicked
Code:
@Override
public boolean onMarkerClick(Marker marker) {
marker.showInfoWindow();
return true;
}
4. How to create circle on map:
Code:
private void addCircleToCurrentLocation() {
mHuaweiMap.addCircle(new CircleOptions()
.center(new LatLng(12.9716, 77.5946))
.radius(1000)
.strokeWidth(10)
.strokeColor(Color.GREEN)
.fillColor(Color.argb(128, 255, 0, 0))
.clickable(true));
}
5. How to create marker:
Code:
private void createMarker(LatLng latLng) throws IOException {
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.snippet("Address : " + featchAddress(latLng))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location));
mHuaweiMap.addMarker(markerOptions);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng) // Sets the center of the map to location user
.zoom(20) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(40) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
mHuaweiMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
mHuaweiMap.setOnMarkerClickListener(this);
}
In this article I covered few basics callbacks. below is the final code
Code:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, HuaweiMap.OnMapClickListener, HuaweiMap.OnMarkerClickListener {
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int REQUEST_CODE = 100;
private static final LatLng LAT_LNG = new LatLng(12.9716, 77.5946);
private HuaweiMap mHuaweiMap;
private MapView mMapView;
private Button btnCustom;
private static final String[] RUNTIME_PERMISSIONS = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET};
private Marker marker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnCustom = findViewById(R.id.btn_custom);
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
mMapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
init(mapViewBundle);
btnCustom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mHuaweiMap.setOnMapClickListener(MainActivity.this);
}
});
}
private void init(Bundle mapViewBundle) {
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
}
@Override
public void onMapReady(HuaweiMap map) {
mHuaweiMap = map;
enableUiSettings();
mHuaweiMap.setMaxZoomPreference(15);
mHuaweiMap.setMinZoomPreference(2);
addCircleToCurrentLocation();
}
/*
Enable Ui Settings
*/
private void enableUiSettings() {
mHuaweiMap.setMyLocationEnabled(true);
mHuaweiMap.getUiSettings().setMyLocationButtonEnabled(true);
mHuaweiMap.getUiSettings().setCompassEnabled(true);
mHuaweiMap.getUiSettings().setZoomControlsEnabled(true);
mHuaweiMap.getUiSettings().setMyLocationButtonEnabled(true);
}
/*
Create Circle to current location
*/
private void addCircleToCurrentLocation() {
mHuaweiMap.addCircle(new CircleOptions()
.center(new LatLng(12.9716, 77.5946))
.radius(1000)
.strokeWidth(10)
.strokeColor(Color.GREEN)
.fillColor(Color.argb(128, 255, 0, 0))
.clickable(true));
}
/*
Create Marker when you click on map
*/
private void createMarker(LatLng latLng) throws IOException {
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.snippet("Address : " + featchAddress(latLng))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location));
mHuaweiMap.addMarker(markerOptions);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng) // Sets the center of the map to location user
.zoom(20) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(40) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
mHuaweiMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
mHuaweiMap.setOnMarkerClickListener(this);
}
/*
Convert from latlong to Address
*/
private String featchAddress(LatLng latLng) throws IOException {
Geocoder geocoder = new Geocoder(this, Locale.ENGLISH);
List<Address> addresses = geocoder.getFromLocation(latLng.latitude, latLng.latitude, 1);
Toast.makeText(this, addresses.get(0).getLocality() + ", "
+ addresses.get(0).getAdminArea() + ", "
+ addresses.get(0).getCountryName(), Toast.LENGTH_SHORT).show();
return addresses.get(0).getLocality() + ", "
+ addresses.get(0).getAdminArea() + ", "
+ addresses.get(0).getCountryName();
}
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
@Override
public void onMapClick(LatLng latLng) {
try {
createMarker(latLng);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean onMarkerClick(Marker marker) {
marker.showInfoWindow();
return true;
}
Output:
Conclusion:
In this article you’ve learned how to create custom markers, how callbacks will work, as well as new ways for users to interact with the map.
https://developer.huawei.com/consumer/en/doc/development/HMS-References/hms-map-cameraupdate
Reference:
Related
This article is orginally from HUAWEI Developer Forum.
Forum link: https://forums.developer.huawei.com/forumPortal/en/home
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Before we start learning about today’s topic, I strongly recommend you to go through my previous article i.e. HMS Site Map (Part 1). It will help you to have a clear picture.
Let’s Begin
In the previous article, we were successfully able to get details after selecting the place that we want to search using Site Kit. Today in this article we are going to see how to show a map using Map Kit after fetching the Latitude and Longitude from the details we selected. Also we are going to see how to use the Site APIs and Map APIs using POSTMAN in our Part 3 article.
One Step at a time
First we need to add Map Kit dependencies in the app gradle file and sync the app.
implementation 'com.huawei.hms:maps:4.0.1.300'
After adding the dependencies we need to provide permission in AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
Let’s Code
Main Activity class
Code:
private void showDetails(String item) {
String pattern = Pattern.quote("\\" + "n");
String[] lines = item.split("\\n+");
autoCompleteTextView.setText(lines[0]);
mLat = lines[2]; // This is latitude
mLon = lines[3]; // This is longitude
title = lines[0]; // This is title or place name
String details = "<font color='red'>PLACE NAME : </font>" + lines[0] + "<br>"
+ "<font color='#CD5C5C'>COUNTRY : </font>" + lines[1] + "<br>"
+ "<font color='#8E44AD'>ADDRESS : </font>" + lines[4] + "<br>"
+ "<font color='#008000'>PHONE : </font>" + lines[5];
txtDetails.setText(Html.fromHtml(details, Html.FROM_HTML_MODE_COMPACT));
}
private void showMap(){
Intent intent = new Intent(MainActivity.this, MapActivity.class);
intent.putExtra("lat",mLat); // Here we are passing Latitude and Longitude
intent.putExtra("lon",mLon); // and titile from MainActivity class to
intent.putExtra("title",title);// MapActivity class…
startActivity(intent);
}v
Main Code
1) First we need to understand whether we are showing the map in view or fragment. Because there are two way we can show our map.
a) Fragment way
In fragment way we add MapFragment in the layout file of an activity.
Code:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapfragment_mapfragmentdemo"
class="com.huawei.hms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraTargetLat="48.893478"
map:cameraTargetLng="2.334595"
map:cameraZoom="10" />
b) MapView way
Here we add MapView in the layout file of an activity.
Code:
<com.huawei.hms.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true"
map:cameraTargetLat="51"
map:cameraTargetLng="10"
map:cameraZoom="8.5"/>
2) Here we are going with MapView.
3) For both Fragment as well as View, we need to implement OnMapReadyCallback API in our MapActivity to use a Map. After implementing this API, it will ask to implement onMapReady method.
Code:
public void onMapReady(HuaweiMap map) {
Log.d(TAG, "onMapReady: ");
hMap = map;
}
4) The only difference which we will see between MapFragment and MapView is instantiating Map.
a) MapFragement
Code:
private MapFragment mMapFragment;
mMapFragment = (MapFragment) getFragmentManager()
.findFragmentById(R.id.mapfragment_mapfragmentdemo);
mMapFragment.getMapAsync(this);
b) MapView
Code:
private MapView mMapView;
mMapView = findViewById(R.id.mapview_mapviewdemo);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle("MapViewBundleKey");
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
5) Permission we need to check
Code:
//Put this in the top of the onCreate() method …
private static final String[] RUNTIME_PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.INTERNET
};
// This will placed in the onCreate() method …
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
// Use this method to check Permission …
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
MapActivity Class
Code:
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = "MapActivity";
private MapView mMapView;
private HuaweiMap hmap;
private Marker mMarker;
private static final String[] RUNTIME_PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.INTERNET
};
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int REQUEST_CODE = 100;
private String mLatitude, mLongitude,mTitle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
mLatitude = getIntent().getExtras().getString("lat");
mLongitude = getIntent().getExtras().getString("lon");
mTitle = getIntent().getExtras().getString("title");
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
mMapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
}
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
@Override
public void onMapReady(HuaweiMap huaweiMap) {
Log.d(TAG, "onMapReady: ");
hmap = huaweiMap;
hmap.setMyLocationEnabled(true);
hmap.setMapType(HuaweiMap.MAP_TYPE_NORMAL);
hmap.setMaxZoomPreference(15);
hmap.setMinZoomPreference(5);
CameraPosition build = new CameraPosition.Builder()
.target(new LatLng(Double.parseDouble(mLatitude), Double.parseDouble(mLongitude)))
.build();
CameraUpdate cameraUpdate = CameraUpdateFactory
.newCameraPosition(build);
hmap.animateCamera(cameraUpdate);
MarkerOptions options = new MarkerOptions()
.position(new LatLng(Double.parseDouble(mLatitude),
Double.parseDouble(mLongitude)))
.title(mTitle);
mMarker = hmap.addMarker(options);
mMarker.showInfoWindow();
hmap.setOnMarkerClickListener(new HuaweiMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
Toast.makeText(getApplicationContext(), "onMarkerClick:" +
marker.getTitle(), Toast.LENGTH_SHORT).show();
return false;
}
});
}
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
}
Core Functionality of Map
1) Types of Map
There are five types map:
· HuaweiMap.MAP_TYPE_NORMAL
· HuaweiMap.MAP_TYPE_NONE
· HuaweiMap.MAP_TYPE_SATELLITE
· HuaweiMap.MAP_TYPE_HYBRID
· HuaweiMap.MAP_TYPE_TERRAIN
But we can only use MAP_TYPE_NORMAL and MAP_TYPE_NONE. Normal type is a standard map, which shows roads, artificial structures, and natural features such as rivers. None type is an empty map without any data.
The Rest Map type is in development phase.
2) Camera Movement
Huawei maps are moved by simulating camera movement. You can control the visible region of a map by changing the camera's position. To change the camera's position, create different types of CameraUpdate objects using the CameraUpdateFactory class, and use these objects to move the camera.
Code:
CameraPosition build = new CameraPosition.Builder().target(new
LatLng(Double.parseDouble(mLatitude),
Double.parseDouble(mLongitude))).build();
CameraUpdate cameraUpdate = CameraUpdateFactory
.newCameraPosition(build);
hmap.animateCamera(cameraUpdate);
In the above code we are using Map camera in animation mode. When moving the map camera in animation mode, you can set the animation duration and API to be called back when the animation stops. By default, the animation duration is 250 ms.
3) My Location in Map
We can get our location in our Map by simply enabling my-location layer. Also, we can display my-location icon in the Map.
Code:
hmap.setMyLocationEnabled(true);
hmap.getUiSettings().setMyLocationButtonEnabled(true);
4) Show Marker in Map
We can add markers to a map to identify locations such as stores and buildings, and provide additional information with information windows.
Code:
MarkerOptions options = new MarkerOptions()
.position(new LatLng(Double.parseDouble(mLatitude),
Double.parseDouble(mLongitude)))
.title(mTitle); // Adding the title here …
mMarker = hmap.addMarker(options);
mMarker.showInfoWindow();
We can customize our marker according to our need using BitmapDescriptor object.
Code:
Bitmap bitmap = ResourceBitmapDescriptor.drawableToBitmap(this,
ContextCompat.getDrawable(this, R.drawable.badge_ph));
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);
mMarker.setIcon(bitmapDescriptor);
We can title to the Marker as shown in the above code. We can also make the marker clickable as shown below.
Code:
hmap.setOnMarkerClickListener(new HuaweiMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
Toast.makeText(getApplicationContext(), "onMarkerClick:" +
marker.getTitle(), Toast.LENGTH_SHORT).show();
return false;
}
});
5) Map comes in shape
a) Polyline
b) Polygon
c) Circle
We can use Polyline if we need to show routes from one place to another. We can combine Direction API with Polyline to show routes for walking, bicycling and driving also calculating routes distance.
If we need to show radius like the location under 500 meter or something we use Circle shape to show in the map.
The Result
Any questions about this process, you can try to acquire answers from HUAWEI Developer Forum.
Introduction
This article is based on Huawei Mobile Services application. I have developed Trip Booking Android app. We can provide the solution for HMS based multiple kits such as Account Kit, Huawei Ads, Huawei Map, and Huawei Analysts to use in Trip Booking. So users can book any trip.
In this application, users can plan trips and book their trips. It will provide the ongoing trip cities wise with weather forecasting so that user can easily plan a trip.
In this article, I will integrate Weather API, Huawei Map, and Huawei Map Direction API, so that users can check the route and plan their trips, and book with the trip weather forecast.
Huawei Map
HMS Core Map SDK is a set of APIs for map development in Android. The map data covers most countries outside China and supports multiple languages. The Map SDK uses the WGS 84 GPS coordinate system, which can meet most requirements of map development outside China. You can easily add map-related functions in your Android app, including:
1. Map display: Displays buildings, roads, water systems, and Points of Interest (POIs).
2. Map interaction: Controls the interaction gestures and buttons on the map.
3. Map drawing: Adds location markers, map layers, overlays, and various shapes.
Prerequisite
1. A computer (desktop or laptop)
2. A Huawei phone, which is used to debug the developed app
3. HUAWEI Analytics Kit 5.0.3
4. Android SDK applicable to devices using Android API-Level 19 (Android 4.4 KitKat) or higher
5. Android Studio
6. Java JDK 1.7 or later (JDK 1.8 recommended).
Things Need To Be Done
To integrate HUAWEI HMS Core services, you must complete the following preparations:
1. Create an app in AppGallery Connect.
2. Create an Android Studio project.
3. Add the app package name and save the configuration file.
4. Configure the Maven repository address and AppGallery Connect gradle plug-in.
Integration
1. Sign in to AppGallery Connect and select my projects.
2. Navigate to app to enable Map Kit.
{
"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"
}
3. Navigate to project setting and download the configuration file.
4. Add the Maven repository address to repositories.
Code:
buildscript {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath 'com.huawei.agconnect:agcp:1.2.0.300'
}
}
allprojects {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
}
5. Add the AppGallery Connect dependency to dependencies.
Code:
//map
implementation 'com.huawei.hms:maps:4.0.0.301'
6. I have created the following class in which I have implemented Map Kit.
Code:
public class PolylineActivity extends AppCompatActivity implements OnMapReadyCallback {
public static final String TAG = "PolylineActivity";
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private HuaweiMap hmap;
private MapView mMapView;
private Marker mMarker;
private List<LatLng> latLngList;
private MapApiViewModel mapApiViewModel;
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_polyline);
mapApiViewModel = ViewModelProviders.of(this).get(MapApiViewModel.class);
mMapView = findViewById(R.id.mapview_mapviewdemo);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(PolylineActivity.this);
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
public void onMapReady(HuaweiMap map) {
hmap = map;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
hmap.setMyLocationEnabled(false);
hmap.setTrafficEnabled(true);
hmap.getUiSettings().setRotateGesturesEnabled(true);
hmap.getUiSettings().setCompassEnabled(false);
hmap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLngList.get(0), 12.0f));
hmap.addMarker(new MarkerOptions().position(latLngList.get(0)));
mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
});
}
Huawei Map Direction API
Huawei Map provides Direction API, so that user can access all the information related to Map in RESTful API.
Huawei has provide the following API endpoint to access Direction API.
https://mapapi.cloud.huawei.com/mapApi/v1
Huawei provide the following direction API:
1. Walking Route Planning
2. Bicycling Route Planning
3. Driving Route Planning
I have implemented the Driving Route API with the help of Retrofit and MVVM.
Retrofit Client
I have created MapApiClient class for accessing the Direction API.
Code:
public class MapApiClient {
private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient;
public static Service getClient() {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Consants.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit.create(Service.class);
}
public interface Service {
@POST("mapApi/v1/routeService/driving")
Single<PolylineResponse> getPolylines(
@Query("key") String apiKey,
@Body PolylineBody polylineBody);
}
}
API Repository
I have created MapApiRepo class for accessing the API client.
Code:
public class MapApiRepo {
private MapApiClient.Service mService;
public MapApiRepo() {
this.mService = MapApiClient.getClient();
}
public Single<PolylineResponse> executeMapApi(PolylineBody polylineBody) {
return mService.getPolylines(Consants.API_KEY, polylineBody);
}
}
ViewModel
I have created MapApiViewModel class for handling the API calls.
Code:
public class MapApiViewModel extends ViewModel {
private final CompositeDisposable disposables = new CompositeDisposable();
private MapApiRepo mapApiRepo = new MapApiRepo();
private MutableLiveData<PolylineResponse> mPolylineLiveData = new MutableLiveData<>();
public LiveData<PolylineResponse> getPolylineLiveData(PolylineBody body) {
disposables.add(mapApiRepo.executeMapApi(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mPolylineLiveData.setValue(result),
throwable -> mPolylineLiveData.setValue(null)
));
return mPolylineLiveData;
}
@Override
protected void onCleared() {
disposables.clear();
}
}
Drawing Polyline
I have implemented this functionality in the following activity.
Code:
mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
}); private PolylineBody getPolylineBody() {
PolylineBody polylineBody = new PolylineBody();
Origin origin = new Origin();
origin.setLat("30.0444");
origin.setLng("31.2357");
Destination destination = new Destination();
destination.setLat("30.0131");
destination.setLng("31.2089");
polylineBody.setDestination(destination);
polylineBody.setOrigin(origin);
return polylineBody;
}
public void getPolylineData(PolylineResponse polylineResponse) {
List<Routes> routesList = polylineResponse.getRoutes();
List<Paths> paths = new ArrayList<>();
List<Steps> steps = new ArrayList<>();
List<Polyline> polylines = new ArrayList<>();
latLngList = new ArrayList<>();
for (int x = 0; x < routesList.size(); x++) {
//here we can access each array list with main.get(x).
for (Paths paths1 : routesList.get(x).getPaths()) {
paths.add(paths1);
}
for (int y = 0; y < paths.size(); y++) {
for (Steps step :
paths.get(y).getSteps()) {
steps.add(step);
}
}
for (int i = 0; i < steps.size(); i++) {
for (Polyline polyline :
steps.get(i).getPolyline()) {
polylines.add(polyline);
}
}
}
for (int i = 0; i < polylines.size(); i++) {
latLngList.add(new LatLng(Double.valueOf(polylines.get(i).getLat())
, Double.valueOf(polylines.get(i).getLng())));
}
hmap.addPolyline(new PolylineOptions()
.addAll(latLngList)
.color(Color.BLUE)
.width(3));
}
Weather API
I have used weatherstack api to get city weather condition.
https://api.weatherstack.com/
WeatherRetrofit Client
I have implemented Weather API using retrofit library with RxJava2.
Code:
public class Client {
private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient;
public static Service getClient() {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Utils.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit.create(Service.class);
}
public interface Service {
@GET("current")
Single<CurrentWeather> getCurrentWeather(
@Query("access_key") String apiKey,
@Query("query") String cityName);
@GET("forecast")
Single<ForcastWeather> getForecastWeather(
@Query("access_key") String apiKey,
@Query("query") String cityName,
@Query("forecast_days") String days);
}
}
App Development
I have created the following package inside the project. In which I have integrated Huawei Id Login, Huawei Analytics, Huawei Banner Ads, Weather API, Huawei Map, and Huawei Direction APIs.
LoginActivity
In this screen, I have integrated login functionality with Huawei Id along with Analytics Kit which logs the event.
Code:
if (authHuaweiIdTask.isSuccessful()) {
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i(TAG, huaweiAccount.getDisplayName() + " signIn success ");
Log.i(TAG, "AccessToken: " + huaweiAccount.getAccessToken());
Bundle bundle = new Bundle();
bundle.putString(TAG,huaweiAccount.getDisplayName() + " signIn success ");
Analystics.getInstance(this).setEvent("login",bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("user", huaweiAccount.getDisplayName());
startActivity(intent);
this.finish();
}
HomeFragment
In this screen, I have implemented Huawei Ads and Analytics kit.
Which helps to log the user activity and shown banner ads.
Loading Banner Ads
Code:
private void initAds(View view) {
HwAds.init(getActivity());
hwBannerView = view.findViewById(R.id.huawei_banner_view);
hwBannerView.setVisibility(View.VISIBLE);
AdParam adParam = new AdParam.Builder().build();
hwBannerView.loadAd(adParam);
hwBannerView.setAdListener(adListener);
}
Log User Events
Code:
Bundle bundle = new Bundle();
bundle.putString(TAG,"City loaded");
Analystics.getInstance(getActivity()).setEvent("City",bundle);
cityList.setLayoutManager(new GridLayoutManager(getActivity(), 2));
cityList.setAdapter(new CityAdapter(cities, (item) -> {
Bundle bundle1 = new Bundle();
bundle.putString(TAG,"City Clicked"+item.getCityName());
Analystics.getInstance(getActivity()).setEvent("City",bundle1);
PopularCity popularCity = item;
Intent intent = new Intent(getActivity(), CityInfoDetailActivity.class);
intent.putExtra("name", popularCity.getCityName());
intent.putExtra("url", popularCity.getImageUrl());
startActivity(intent);
}));
CityInfoDetailActivity
In this screen, I have implemented the Huawei Banner ads and Huawei Analytics.
Loading Banner Ads
Code:
HwAds.init(this);
hwBannerView = findViewById(R.id.huawei_banner_view);
hwBannerView.setVisibility(View.VISIBLE);
AdParam adParam = new AdParam.Builder().build();
hwBannerView.loadAd(adParam);
hwBannerView.setAdListener(adListener);
Log User Events
Code:
if (extras != null) {
String name = extras.getString("name");
String imageUrl = extras.getString("url");
setTitle(name);
Glide.with(this).load(imageUrl).into(cityImage);
Bundle bundle = new Bundle();
bundle.putString(TAG,"City Info");
Analystics.getInstance(this).setEvent("City Details",bundle);
}
AllTripActivity
Code:
public class AllTripActivity extends AppCompatActivity {
private RecyclerView tripList;
private static final String TAG= AllTripActivity.class.getName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alltrips);
init();
}
private void init() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
tripList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
tripList.setAdapter(new TripAdapter(list, (item) -> {
Intent intent = new Intent(this, PolylineActivity.class);
startActivity(intent);
}));
}
}
TripAdapter
Code:
public class TripAdapter extends RecyclerView.Adapter<TripAdapter.ViewHolder> {
private List<TripModel> list;
private ItemTripBinding mBinding;
private OnItemClickListener<TripModel> mOnItemClickListener;
public TripAdapter(List<TripModel> list, OnItemClickListener<TripModel> onItemClickListener) {
this.list = list;
this.mOnItemClickListener = onItemClickListener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.item_trip, parent, false);
return new ViewHolder(mBinding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(list.get(position), mOnItemClickListener);
}
@Override
public int getItemCount() {
return list.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private ItemTripBinding cityBinding;
public ViewHolder(@NonNull ItemTripBinding cityBinding) {
super(cityBinding.getRoot());
this.cityBinding = cityBinding;
}
public void bind(TripModel item, OnItemClickListener<TripModel> listener) {
cityBinding.setData(item);
itemView.setOnClickListener(v -> listener.onItemClick(item));
}
}
}
WeatherDetailActivity
In this screen, I have implemented the weather related information so that users can identify the city weather condition.
Code:
public class WeatherDetailActivity extends AppCompatActivity {
private ActivityWeatherBinding mMainBinding;
private WeatherViewModel mWeatherViewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_weather);
mWeatherViewModel = ViewModelProviders.of(this).get(WeatherViewModel.class);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String name = extras.getString("name");
mMainBinding.txtCityName.setText(name);
mMainBinding.txtTemperature.setText("21" + "\u00B0");
fetchWeatherDetails(name, false);
setForcastData();
}
}
@SuppressLint("SetTextI18n")
private void fetchWeatherDetails(String cityName, boolean isSearching) {
mWeatherViewModel.getCurrentWeatherLiveData(cityName).observeForever(result -> {
if (result != null) {
mMainBinding.txtCityName.setText(result.getLocation().getName());
mMainBinding.txtTemperature.setText(result.getCurrent().getTemperature() + "\u00B0");
}
});
}
private void setForcastData() {
List<CurrentWeather.Current> currents = new ArrayList<>();
for (int i = 1; i <= 7; i++) {
CurrentWeather.Current current = new CurrentWeather().new Current();
current.setObservationTime("Day");
current.setTemperature(21);
current.setIsDay(url);
currents.add(current);
}
mMainBinding.recycleWeeklyWeather.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
mMainBinding.recycleWeeklyWeather.setAdapter(new ForcastAdapter(currents));
}
}
WeatherViewModel
Code:
public class WeatherViewModel extends ViewModel {
private WeatherRepository mWeatherRepository = new WeatherRepository();
private final CompositeDisposable disposables = new CompositeDisposable();
private MutableLiveData<CurrentWeather> mWeatherLiveData = new MutableLiveData<>();
private MutableLiveData<ForcastWeather> mForcastWeatherLiveData = new MutableLiveData<>();
public LiveData<CurrentWeather> getCurrentWeatherLiveData(String city) {
disposables.add(mWeatherRepository.executeCurrentWeatherApi(city)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mWeatherLiveData.setValue(result),
throwable -> mWeatherLiveData.setValue(null)
));
return mWeatherLiveData;
}
public LiveData<ForcastWeather> getForcastWeatherLiveData(String city) {
disposables.add(mWeatherRepository.executeForcastWeatherApi(city)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mForcastWeatherLiveData.setValue(result),
throwable -> mForcastWeatherLiveData.setValue(null)
));
return mForcastWeatherLiveData;
}
@Override
protected void onCleared() {
disposables.clear();
}
}
Launch the application
Let us launch our application, see the result
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
Hello everyone, In this article, I will talk about how we can use together Geofence and Push Kit. When the device enters a set location, we will send a notification to the user using Push Kit.
Geofence : It is an important feature in the Location Kit. Geofence is actually used to draw a geographic virtual boundary.
Push Kit : Push kit is essentially a messaging service. There are two different message types. These are notification and data messages. We will use the notification messages in this article.
1- Huawei Core Integration
To use Geofence and Push kit services, you must first integrate the necessary kits into your project. You can use the document in the link to easily integrate the Location and Push kit into your project.
2- Add Permissions
After the HMS Core integration is finished, we need to add permissions to the AndroidManifest.xml file in order to access the user’s location and internet.
XML:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
3- Developing the Push Kit Part
To send a notification to the device using a push kit, firstly the device must receive a push token.
Java:
private void getPushToken() {
new Thread() {
@Override
public void run() {
super.run();
try {
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
if (!TextUtils.isEmpty(token)) {
DataStore.pushToken = token;
}
} catch (ApiException e) {
Log.e("TokenFailed", "get token failed" + e);
}
}
}.start();
}
We have received a push token, now we need to reach the access token, and we will do this through the service. We will obtain access token through the service, you must also complete the Retrofit implementations. Add Retrofit libraries app level build.gradle
Code:
implementation "com.squareup.retrofit2:retrofit:2.3.0"
implementation "com.squareup.retrofit2:converter-gson:2.3.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
In order to send access token, first we should prepare our request. This request should have grant_type ,client_id ,client_secret and will return AccessToken. Then, we will use this AccessToken for out further requests.
Java:
public interface AccessTokenInterface {
@FormUrlEncoded
@POST("v2/token")
Call<AccessToken> GetAccessToken(
@Field("grant_type") String grantType,
@Field("client_id") int clientId,
@Field("client_secret") String clientSecret);
}
Now let’s handle the method by which we will obtain the access token. We need a Base URL to use in this method. The variable defined as OAUTH_BASE_URL represents our base URL. Do not forget to fill in client_credentials, YOUR_CLIENT_ID, YOUR_CLIENT_SECRET parts according to your project. This getAccessToken() was created using Synchronous Call. You can do this with Asynchronous Call according to the needs of your own project.
Java:
public void getAccessToken() {
String YOUR_CLIENT_SECRET =" ";
int YOUR_CLIENT_ID = ;
AccessInterface apiInterface = RetrofitClient.getClient(OAUTH_BASE_URL).create(AccessInterface.class);
Call<AccessToken> call = apiInterface.GetAccessToken("client_credentials",YOUR_CLIENT_ID , YOUR_CLIENT_SECRET);
try{
Response<AccessToken> response = call.execute();
accessToken = String.format("Bearer %s",response.body().getAccessToken());
}catch (IOException e){
e.printStackTrace();
}
}
After obtaining the access token, we create an interface to send a notification with the push kit. Do not forget to fill the {YOUR_APP_ID} part of your project app ID.
Java:
public interface NotificationInterface {
@Headers("Content-Type:application/json")
@POST("{YOUR_APP_ID}/messages:send")
Call<PushParameter> sendNotification(
@Header("Authorization") String authorization,
@Body NotificationMessage notificationMessage);
}
Java:
public void sendNotification(String accesstoken, String geofenceDetail) {
NotificationInterface notiInterface = RetrofitClient.getClient(PUSH_API_URL).create(NotificationInterface.class);
NotificationMessage notificationMessage = (new NotificationMessage
.Builder("Title of Notification", geofenceDetail, DataStore.pushToken, "1"))
.build();
Call<PushParameter> callNoti = notiInterface.sendNotification(accesstoken, notificationMessage);
callNoti.enqueue(new Callback<PushParameter>() {
@Override
public void onResponse(Call<PushParameter> call, Response<PushParameter> response) {
Log.i("SendNotification", response.body().getMsg());
}
@Override
public void onFailure(Call<PushParameter> call, Throwable t) {
Log.i("SendNotification Failure", t.toString());
}
});
}
4- Developing the Geofence Part
We have set up the push kit to send notifications, now let’s see how we will send these notifications for geofence. First we create a broadcast receiver for geofence.
Java:
public class GeofenceBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
GeofenceNotification.enqueueWork(context,intent);
}
}
When the Broadcast Receiver is triggered, our geofence notifications will be sent through this class. You can see the accessToken and sendNotification methods we use for push kit in this class.
Java:
public class GeofenceNotification extends JobIntentService {
public static final String PUSH_API_URL = "https://push-api.cloud.huawei.com/v1/";
public static final String OAUTH_BASE_URL = "https://login.cloud.huawei.com/oauth2/";
private String accessToken;
public static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, GeofenceNotification.class, 573, intent);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
GeofenceData geofenceData = GeofenceData.getDataFromIntent(intent);
if (geofenceData != null) {
int conversion = geofenceData.getConversion();
ArrayList<Geofence> geofenceTransition = (ArrayList<Geofence>) geofenceData.getConvertingGeofenceList();
String geofenceTransitionDetails = getGeofenceTransitionDetails(conversion,
geofenceTransition);
getAccessToken();
sendNotification(accessToken, geofenceTransitionDetails);
}
}
private String getGeofenceTransitionDetails(int conversion, ArrayList<Geofence> triggeringGeofences) {
String geofenceConversion = getConversionString(conversion);
ArrayList<String> triggeringGeofencesIdsList = new ArrayList<>();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getUniqueId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return String.format("%s: %s",geofenceConversion,triggeringGeofencesIdsString);
}
private String getConversionString(int conversionType) {
switch (conversionType) {
case Geofence.ENTER_GEOFENCE_CONVERSION:
return getString(R.string.geofence_transition_entered);
case Geofence.EXIT_GEOFENCE_CONVERSION:
return getString(R.string.geofence_transition_exited);
case Geofence.DWELL_GEOFENCE_CONVERSION:
return getString(R.string.geofence_transition_dwell);
default:
return getString(R.string.unknown_geofence_transition);
}
}
public void sendNotification(String accesstoken, String geofenceDetail) {
NotificationInterface notiInterface = RetrofitClient.getClient(PUSH_API_URL).create(NotificationInterface.class);
NotificationMessage notificationMessage = (new NotificationMessage.Builder("Title of Notification", geofenceDetail, DataClass.pushToken, "1")).build();
Call<PushParameter> callNoti = notiInterface.sendNotification(accesstoken, notificationMessage);
callNoti.enqueue(new Callback<PushParameter>() {
@Override
public void onResponse(Call<PushParameter> call, Response<PushParameter> response) {
Log.i("SendNotification", response.body().getMsg());
}
@Override
public void onFailure(Call<PushParameter> call, Throwable t) {
Log.i("SendNotification Failure", t.toString());
}
});
}
public void getAccessToken() {
String YOUR_CLIENT_SECRET =" ";
int YOUR_CLIENT_ID = ;
AccessInterface apiInterface = RetrofitClient.getClient(OAUTH_BASE_URL).create(AccessInterface.class);
Call<AccessToken> call = apiInterface.GetAccessToken("client_credentials",YOUR_CLIENT_ID , YOUR_CLIENT_SECRET);
try{
Response<AccessToken> response = call.execute();
accessToken = String.format("Bearer %s",response.body().getAccessToken());
}catch (IOException e){
e.printStackTrace();
}
}
}
Then we add the methods we use to create a geofence list. In this project, I have defined geofences as static. You can adjust these geofence information according to the needs of your application. For example, if your location information is kept in the database, you can use geofence locations from the database. When adding geofences in the completeGeofenceList method, pay attention to the unique id part. If you try to add geofences with the same ids, you will get an error.
Java:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = "MainActivity";
private FusedLocationProviderClient fusedLocationProviderClient;
private PendingIntent geofencePendingIntent;
private ArrayList<Geofence> geofenceList;
private GeofenceService geofenceService;
private SettingsClient settingsClient;
LocationCallback locationCallback;
LocationRequest locationRequest;
private String pushToken;
private Marker mMarker;
private MapView mapView;
private HuaweiMap hMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
permissionCheck();
mapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle("MapViewBundleKey");
}
mapView.onCreate(mapViewBundle);
mapView.getMapAsync(this);
geofenceService = LocationServices.getGeofenceService(getApplicationContext());
getPushToken();
completeGeofenceList();
createGeofence();
}
public void onMapReady(HuaweiMap huaweiMap) {
hMap = huaweiMap;
hMap.setMyLocationEnabled(true);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull 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");
}
}
else 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");
}
}
}
private void permissionCheck(){
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,
Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
}
private GeofenceRequest getGeofencingRequest() {
return new GeofenceRequest.Builder()
.setInitConversions(GeofenceRequest.ENTER_INIT_CONVERSION)
.createGeofenceList(geofenceList)
.build();
}
private PendingIntent getGeofencePendingIntent() {
Intent intent = new Intent(MainActivity.this, GeofenceBroadcast.class);
geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return geofencePendingIntent;
}
private void completeGeofenceList() {
Geofence.Builder geoBuild = new Geofence.Builder();
geofenceList = new ArrayList<>();
geofenceList.add(geoBuild.setUniqueId("Home").setRoundArea(39.617841289998736,27.429383486070098,200).setValidContinueTime(Geofence.GEOFENCE_NEVER_EXPIRE).setConversions(Geofence.ENTER_GEOFENCE_CONVERSION).setDwellDelayTime(1000).build());
geofenceList.add(geoBuild.setUniqueId("Office").setRoundArea(38.14893633264862,26.82832426954628,200).setValidContinueTime(Geofence.GEOFENCE_NEVER_EXPIRE).setConversions(Geofence.ENTER_GEOFENCE_CONVERSION).setDwellDelayTime(1000).build());
}
private void createGeofence() {
geofenceService.createGeofenceList(getGeofencingRequest(), getGeofencePendingIntent());
}
private void getPushToken() {
new Thread() {
@Override
public void run() {
super.run();
try {
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String token = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
if (!TextUtils.isEmpty(token)) {
DataStore.pushToken1 = token;
}
} catch (ApiException e) {
Log.e("TokenFailed", "get token failed" + e);
}
}
}.start();
}
}
Sample application outputs for the use of push kit with geofence are as follows;
Conclusion
By using the push kit features, you can personalize your notifications according to the needs of your application. In this article I explained how to use the Push kit for Geofence notifications. I hope you will like it. Thank you for reading. If you have any questions, you can leave a comment.
References
Geofence Service
Push Kit
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
In this article, we will learn how to integrate Huawei ML kit camera stream, Map kit and Location kit in Android application KnowMyBoard. Account Kit provides seamless login functionality to the app with large user base.
The text recognition service can extract text from images of receipts, business cards, and documents. This service is useful for industries such as printing, education, and logistics. You can use it to create apps that handle data entry and check tasks.
The text recognition service is able to recognize text in both static images and dynamic camera streams with a host of APIs, which you can call synchronously or asynchronously to build your text recognition-enabled apps.
Precautions
Development Overview
You need to install Android Studio IDE and I assume that you have prior knowledge of Android application development.
Hardware Requirements
A computer (desktop or laptop) running Windows 10.
Android phone (with the USB cable), which is used for debugging.
Software Requirements
Java JDK 1.8 or later.
Android Studio software or Visual Studio or Code installed.
HMS Core (APK) 4.X or later
Integration steps
Step 1. Huawei developer account and complete identity verification in Huawei developer website, refer to register Huawei ID.
Step 2. Create project in AppGallery Connect
Step 3. Adding HMS Core SDK
Let’s start coding
navigation_graph.xml
[/B][/B]
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.LoginFragment"
android:label="LoginFragment"/>
<fragment
android:id="@+id/mainFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.MainFragment"
android:label="MainFragment"/>
<fragment
android:id="@+id/searchFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
</navigation>
[B][B]
TextRecognitionActivity.java
[/B][/B][/B]
public final class TextRecognitionActivity extends BaseActivity
implements OnRequestPermissionsResultCallback, View.OnClickListener {
private static final String TAG = "TextRecognitionActivity";
private LensEngine lensEngine = null;
private LensEnginePreview preview;
private GraphicOverlay graphicOverlay;
private ImageButton takePicture;
private ImageButton imageSwitch;
private RelativeLayout zoomImageLayout;
private ZoomImageView zoomImageView;
private ImageButton zoomImageClose;
CameraConfiguration cameraConfiguration = null;
private int facing = CameraConfiguration.CAMERA_FACING_BACK;
private Camera mCamera;
private boolean isLandScape;
private Bitmap bitmap;
private Bitmap bitmapCopy;
private LocalTextTransactor localTextTransactor;
private Handler mHandler = new MsgHandler(this);
private Dialog languageDialog;
private AddPictureDialog addPictureDialog;
private TextView textCN;
private TextView textEN;
private TextView textJN;
private TextView textKN;
private TextView textLN;
private TextView tv_language,tv_translated_txt;
private String textType = Constant.POSITION_CN;
private boolean isInitialization = false;
MLTextAnalyzer analyzer;
private static class MsgHandler extends Handler {
WeakReference<TextRecognitionActivity> mMainActivityWeakReference;
public MsgHandler(TextRecognitionActivity mainActivity) {
this.mMainActivityWeakReference = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
TextRecognitionActivity mainActivity = this.mMainActivityWeakReference.get();
if (mainActivity == null) {
return;
}
//Log.d(TextRecognitionActivity.TAG, "msg what :" + msg.what);
//Log.e("TAG", "msg what :" + msg.getTarget().getMessageName(msg));
if (msg.what == Constant.SHOW_TAKE_PHOTO_BUTTON) {
mainActivity.setVisible();
} else if (msg.what == Constant.HIDE_TAKE_PHOTO_BUTTON) {
mainActivity.setGone();
}
}
}
private void setVisible() {
if (this.takePicture.getVisibility() == View.GONE) {
this.takePicture.setVisibility(View.VISIBLE);
}
}
private void setGone() {
if (this.takePicture.getVisibility() == View.VISIBLE) {
this.takePicture.setVisibility(View.GONE);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_text_recognition);
if (savedInstanceState != null) {
this.facing = savedInstanceState.getInt(Constant.CAMERA_FACING);
}
this.tv_language = this.findViewById(R.id.tv_lang);
this.tv_translated_txt = this.findViewById(R.id.tv_translated_txt);
this.preview = this.findViewById(R.id.live_preview);
this.graphicOverlay = this.findViewById(R.id.live_overlay);
this.cameraConfiguration = new CameraConfiguration();
this.cameraConfiguration.setCameraFacing(this.facing);
this.initViews();
this.isLandScape = (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
this.createLensEngine();
this.setStatusBar();
}
private void initViews() {
this.takePicture = this.findViewById(R.id.takePicture);
this.takePicture.setOnClickListener(this);
this.imageSwitch = this.findViewById(R.id.text_imageSwitch);
this.imageSwitch.setOnClickListener(this);
this.zoomImageLayout = this.findViewById(R.id.zoomImageLayout);
this.zoomImageView = this.findViewById(R.id.take_picture_overlay);
this.zoomImageClose = this.findViewById(R.id.zoomImageClose);
this.zoomImageClose.setOnClickListener(this);
this.findViewById(R.id.back).setOnClickListener(this);
this.findViewById(R.id.language_setting).setOnClickListener(this);
this.createLanguageDialog();
this.createAddPictureDialog();
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.takePicture) {
this.takePicture();
} else if (view.getId() == R.id.zoomImageClose) {
this.zoomImageLayout.setVisibility(View.GONE);
this.recycleBitmap();
} else if (view.getId() == R.id.text_imageSwitch) {
this.showAddPictureDialog();
} else if (view.getId() == R.id.language_setting) {
this.showLanguageDialog();
} else if (view.getId() == R.id.simple_cn) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_CN);
this.languageDialog.dismiss();
this.restartLensEngine(Constant.POSITION_CN);
} else if (view.getId() == R.id.english) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_EN);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_EN);
} else if (view.getId() == R.id.japanese) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_JA);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_JA);
} else if (view.getId() == R.id.korean) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_KO);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_KO);
} else if (view.getId() == R.id.latin) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_LA);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_LA);
} else if (view.getId() == R.id.back) {
releaseLensEngine();
this.finish();
}
}
private void restartLensEngine(String type) {
if (this.textType.equals(type)) {
return;
}
this.lensEngine.release();
this.lensEngine = null;
this.createLensEngine();
this.startLensEngine();
if (this.lensEngine == null || this.lensEngine.getCamera() == null) {
return;
}
this.mCamera = this.lensEngine.getCamera();
try {
this.mCamera.setPreviewDisplay(this.preview.getSurfaceHolder());
} catch (IOException e) {
Log.d(TextRecognitionActivity.TAG, "initViews IOException");
}
}
@Override
public void onBackPressed() {
if (this.zoomImageLayout.getVisibility() == View.VISIBLE) {
this.zoomImageLayout.setVisibility(View.GONE);
this.recycleBitmap();
} else {
super.onBackPressed();
releaseLensEngine();
}
}
private void createLanguageDialog() {
this.languageDialog = new Dialog(this, R.style.MyDialogStyle);
View view = View.inflate(this, R.layout.dialog_language_setting, null);
// Set up a custom layout
this.languageDialog.setContentView(view);
this.textCN = view.findViewById(R.id.simple_cn);
this.textCN.setOnClickListener(this);
this.textEN = view.findViewById(R.id.english);
this.textEN.setOnClickListener(this);
this.textJN = view.findViewById(R.id.japanese);
this.textJN.setOnClickListener(this);
this.textKN = view.findViewById(R.id.korean);
this.textKN.setOnClickListener(this);
this.textLN = view.findViewById(R.id.latin);
this.textLN.setOnClickListener(this);
this.languageDialog.setCanceledOnTouchOutside(true);
// Set the size of the dialog
Window dialogWindow = this.languageDialog.getWindow();
WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes();
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.gravity = Gravity.BOTTOM;
dialogWindow.setAttributes(layoutParams);
}
private void showLanguageDialog() {
this.initDialogViews();
this.languageDialog.show();
}
private void createAddPictureDialog() {
this.addPictureDialog = new AddPictureDialog(this, AddPictureDialog.TYPE_NORMAL);
final Intent intent = new Intent(TextRecognitionActivity.this, RemoteDetectionActivity.class);
intent.putExtra(Constant.MODEL_TYPE, Constant.CLOUD_TEXT_DETECTION);
this.addPictureDialog.setClickListener(new AddPictureDialog.ClickListener() {
@Override
public void takePicture() {
lensEngine.release();
isInitialization = false;
intent.putExtra(Constant.ADD_PICTURE_TYPE, Constant.TYPE_TAKE_PHOTO);
TextRecognitionActivity.this.startActivity(intent);
}
@Override
public void selectImage() {
intent.putExtra(Constant.ADD_PICTURE_TYPE, Constant.TYPE_SELECT_IMAGE);
TextRecognitionActivity.this.startActivity(intent);
}
@Override
public void doExtend() {
}
});
}
private void showAddPictureDialog() {
this.addPictureDialog.show();
}
private void initDialogViews() {
String position = SharedPreferencesUtil.getInstance(this).getStringValue(Constant.POSITION_KEY);
this.textType = position;
this.textCN.setSelected(false);
this.textEN.setSelected(false);
this.textJN.setSelected(false);
this.textLN.setSelected(false);
this.textKN.setSelected(false);
switch (position) {
case Constant.POSITION_CN:
this.textCN.setSelected(true);
break;
case Constant.POSITION_EN:
this.textEN.setSelected(true);
break;
case Constant.POSITION_LA:
this.textLN.setSelected(true);
break;
case Constant.POSITION_JA:
this.textJN.setSelected(true);
break;
case Constant.POSITION_KO:
this.textKN.setSelected(true);
break;
default:
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(Constant.CAMERA_FACING, this.facing);
super.onSaveInstanceState(outState);
}
private void createLensEngine() {
MLLocalTextSetting setting = new MLLocalTextSetting.Factory()
.setOCRMode(MLLocalTextSetting.OCR_DETECT_MODE)
// Specify languages that can be recognized.
.setLanguage("ko")
.create();
analyzer = MLAnalyzerFactory.getInstance().getLocalTextAnalyzer(setting);
//analyzer = new MLTextAnalyzer.Factory(this).create();
if (this.lensEngine == null) {
this.lensEngine = new LensEngine(this, this.cameraConfiguration, this.graphicOverlay);
}
try {
this.localTextTransactor = new LocalTextTransactor(this.mHandler, this);
this.lensEngine.setMachineLearningFrameTransactor(this.localTextTransactor);
// this.lensEngine.setMachineLearningFrameTransactor((ImageTransactor) new ObjectAnalyzerTransactor());
isInitialization = true;
} catch (Exception e) {
Toast.makeText(
this,
"Can not create image transactor: " + e.getMessage(),
Toast.LENGTH_LONG)
.show();
}
}
private void startLensEngine() {
if (this.lensEngine != null) {
try {
this.preview.start(this.lensEngine, false);
} catch (IOException e) {
Log.e(TextRecognitionActivity.TAG, "Unable to start lensEngine.", e);
this.lensEngine.release();
this.lensEngine = null;
}
}
}
@Override
public void onResume() {
super.onResume();
if (!isInitialization){
createLensEngine();
}
this.startLensEngine();
}
@Override
protected void onStop() {
super.onStop();
this.preview.stop();
}
private void releaseLensEngine() {
if (this.lensEngine != null) {
this.lensEngine.release();
this.lensEngine = null;
}
recycleBitmap();
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseLensEngine();
if (analyzer != null) {
try {
analyzer.stop();
} catch (IOException e) {
// Exception handling.
Log.e(TAG,"Error while releasing analyzer");
}
}
}
private void recycleBitmap() {
if (this.bitmap != null && !this.bitmap.isRecycled()) {
this.bitmap.recycle();
this.bitmap = null;
}
if (this.bitmapCopy != null && !this.bitmapCopy.isRecycled()) {
this.bitmapCopy.recycle();
this.bitmapCopy = null;
}
}
private void takePicture() {
this.zoomImageLayout.setVisibility(View.VISIBLE);
LocalDataProcessor localDataProcessor = new LocalDataProcessor();
localDataProcessor.setLandScape(this.isLandScape);
this.bitmap = BitmapUtils.getBitmap(this.localTextTransactor.getTransactingImage(), this.localTextTransactor.getTransactingMetaData());
float previewWidth = localDataProcessor.getMaxWidthOfImage(this.localTextTransactor.getTransactingMetaData());
float previewHeight = localDataProcessor.getMaxHeightOfImage(this.localTextTransactor.getTransactingMetaData());
if (this.isLandScape) {
previewWidth = localDataProcessor.getMaxHeightOfImage(this.localTextTransactor.getTransactingMetaData());
previewHeight = localDataProcessor.getMaxWidthOfImage(this.localTextTransactor.getTransactingMetaData());
}
this.bitmapCopy = Bitmap.createBitmap(this.bitmap).copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(this.bitmapCopy);
float min = Math.min(previewWidth, previewHeight);
float max = Math.max(previewWidth, previewHeight);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
localDataProcessor.setCameraInfo(this.graphicOverlay, canvas, min, max);
} else {
localDataProcessor.setCameraInfo(this.graphicOverlay, canvas, max, min);
}
localDataProcessor.drawHmsMLVisionText(canvas, this.localTextTransactor.getLastResults().getBlocks());
this.zoomImageView.setImageBitmap(this.bitmapCopy);
// Create an MLFrame object using the bitmap, which is the image data in bitmap format.
MLFrame frame = MLFrame.fromBitmap(bitmap);
Task<MLText> task = analyzer.asyncAnalyseFrame(frame);
task.addOnSuccessListener(new OnSuccessListener<MLText>() {
@Override
public void onSuccess(MLText text) {
String detectText = text.getStringValue();
// Processing for successful recognition.
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Processing logic for recognition failure.
Log.e("TAG"," Text : Processing logic for recognition failure");
}
});
}
}
[B][B][B]
Result
Please refer forum page to see result.
Tricks and Tips
Makes sure that agconnect-services.json file added.
Make sure required dependencies are added
Make sure that service is enabled in AGC
Enable data binding in gradle.build file
Make sure bottom navigation id’s should be same as fragment id’s in navigation graph
Make sure that set apk key before calling service.
Make sure that you added the module-text from below link
Make changes in gradle file application to library in module-text
Conclusion
In this article, we have learnt how to integrate Huawei ML kit camera stream, where you can extract text on device camera stream in Android application KnowMyBoard. You can check the desired result in the result section. You can also go through previous article part-4 here. Hoping Huawei ML kit capabilities are helpful to you as well, like this sample, you can make use as per your requirement.
Thank you so much for reading. I hope this article helps you to understand the integration of Huawei ML kit in Android application KnowMyBoard.
Reference
Huawei ML Kit – Training video
ML Text Recognition
Module-text
Checkout in forum
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
In this article, we will learn how to integrate Huawei ML kit camera stream in Android application KnowMyBoard. Account Kit provides seamless login functionality to the app with large user base.
The text recognition service can extract text from images of receipts, business cards, and documents. This service is useful for industries such as printing, education, and logistics. You can use it to create apps that handle data entry and check tasks.
The text recognition service is able to recognize text in both static images and dynamic camera streams with a host of APIs, which you can call synchronously or asynchronously to build your text recognition-enabled apps.
The on-device language detection service can detect the language of text when the Internet is unavailable. ML Kit detects languages in text and returns the language codes (which comply with the ISO 639-1 standard) and their respective confidences or the language code with the highest confidence. Currently, 56 languages can be detected.
Similar to the real-time translation service, the on-device translation service can be widely used in scenarios where translation between different languages is required. For example, travel apps can integrate this service to translate road signs and menus in other languages into tourists' native languages, providing more considerate services for them. Different from real-time translation, on-device translation does not require the Internet connection. You can easily use the translation service even if the Internet is disconnected.
Precautions
Development Overview
You need to install Android Studio IDE and I assume that you have prior knowledge of Android application development.
Hardware Requirements
A computer (desktop or laptop) running Windows 10.
Android phone (with the USB cable), which is used for debugging.
Software Requirements
Java JDK 1.8 or later.
Android Studio software or Visual Studio or Code installed.
HMS Core (APK) 4.X or later
Integration steps
Step 1. Huawei developer account and complete identity verification in Huawei developer website, refer to register Huawei ID.
Step 2. Create project in AppGallery Connect
Step 3. Adding HMS Core SDK
Let's start coding
navigation_graph.xml
[/B]
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation_graph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.LoginFragment"
android:label="LoginFragment"/>
<fragment
android:id="@+id/mainFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.MainFragment"
android:label="MainFragment"/>
<fragment
android:id="@+id/searchFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
</navigation>
[B]
TextRecognitionActivity.java
[/B]
public final class TextRecognitionActivity extends BaseActivity
implements OnRequestPermissionsResultCallback, View.OnClickListener {
private static final String TAG = "TextRecognitionActivity";
private LensEngine lensEngine = null;
private LensEnginePreview preview;
private GraphicOverlay graphicOverlay;
private ImageButton takePicture;
private ImageButton imageSwitch;
private RelativeLayout zoomImageLayout;
private ZoomImageView zoomImageView;
private ImageButton zoomImageClose;
CameraConfiguration cameraConfiguration = null;
private int facing = CameraConfiguration.CAMERA_FACING_BACK;
private Camera mCamera;
private boolean isLandScape;
private Bitmap bitmap;
private Bitmap bitmapCopy;
private LocalTextTransactor localTextTransactor;
private Handler mHandler = new MsgHandler(this);
private Dialog languageDialog;
private AddPictureDialog addPictureDialog;
private TextView textCN;
private TextView textEN;
private TextView textJN;
private TextView textKN;
private TextView textLN;
private TextView tv_language,tv_translated_txt;
private String textType = Constant.POSITION_CN;
private boolean isInitialization = false;
MLTextAnalyzer analyzer;
private static class MsgHandler extends Handler {
WeakReference<TextRecognitionActivity> mMainActivityWeakReference;
public MsgHandler(TextRecognitionActivity mainActivity) {
this.mMainActivityWeakReference = new WeakReference<>(mainActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
TextRecognitionActivity mainActivity = this.mMainActivityWeakReference.get();
if (mainActivity == null) {
return;
}
if (msg.what == Constant.SHOW_TAKE_PHOTO_BUTTON) {
mainActivity.setVisible();
} else if (msg.what == Constant.HIDE_TAKE_PHOTO_BUTTON) {
mainActivity.setGone();
}
}
}
private void setVisible() {
if (this.takePicture.getVisibility() == View.GONE) {
this.takePicture.setVisibility(View.VISIBLE);
}
}
private void setGone() {
if (this.takePicture.getVisibility() == View.VISIBLE) {
this.takePicture.setVisibility(View.GONE);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_text_recognition);
if (savedInstanceState != null) {
this.facing = savedInstanceState.getInt(Constant.CAMERA_FACING);
}
this.tv_language = this.findViewById(R.id.tv_lang);
this.tv_translated_txt = this.findViewById(R.id.tv_translated_txt);
this.preview = this.findViewById(R.id.live_preview);
this.graphicOverlay = this.findViewById(R.id.live_overlay);
this.cameraConfiguration = new CameraConfiguration();
this.cameraConfiguration.setCameraFacing(this.facing);
this.initViews();
this.isLandScape = (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
this.createLensEngine();
this.setStatusBar();
}
private void initViews() {
this.takePicture = this.findViewById(R.id.takePicture);
this.takePicture.setOnClickListener(this);
this.imageSwitch = this.findViewById(R.id.text_imageSwitch);
this.imageSwitch.setOnClickListener(this);
this.zoomImageLayout = this.findViewById(R.id.zoomImageLayout);
this.zoomImageView = this.findViewById(R.id.take_picture_overlay);
this.zoomImageClose = this.findViewById(R.id.zoomImageClose);
this.zoomImageClose.setOnClickListener(this);
this.findViewById(R.id.back).setOnClickListener(this);
this.findViewById(R.id.language_setting).setOnClickListener(this);
this.createLanguageDialog();
this.createAddPictureDialog();
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.takePicture) {
this.takePicture();
} else if (view.getId() == R.id.zoomImageClose) {
this.zoomImageLayout.setVisibility(View.GONE);
this.recycleBitmap();
} else if (view.getId() == R.id.text_imageSwitch) {
this.showAddPictureDialog();
} else if (view.getId() == R.id.language_setting) {
this.showLanguageDialog();
} else if (view.getId() == R.id.simple_cn) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_CN);
this.languageDialog.dismiss();
this.restartLensEngine(Constant.POSITION_CN);
} else if (view.getId() == R.id.english) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_EN);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_EN);
} else if (view.getId() == R.id.japanese) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_JA);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_JA);
} else if (view.getId() == R.id.korean) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_KO);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_KO);
} else if (view.getId() == R.id.latin) {
SharedPreferencesUtil.getInstance(this)
.putStringValue(Constant.POSITION_KEY, Constant.POSITION_LA);
this.languageDialog.dismiss();
this.preview.release();
this.restartLensEngine(Constant.POSITION_LA);
} else if (view.getId() == R.id.back) {
releaseLensEngine();
this.finish();
}
}
private void restartLensEngine(String type) {
if (this.textType.equals(type)) {
return;
}
this.lensEngine.release();
this.lensEngine = null;
this.createLensEngine();
this.startLensEngine();
if (this.lensEngine == null || this.lensEngine.getCamera() == null) {
return;
}
this.mCamera = this.lensEngine.getCamera();
try {
this.mCamera.setPreviewDisplay(this.preview.getSurfaceHolder());
} catch (IOException e) {
Log.d(TextRecognitionActivity.TAG, "initViews IOException");
}
}
@Override
public void onBackPressed() {
if (this.zoomImageLayout.getVisibility() == View.VISIBLE) {
this.zoomImageLayout.setVisibility(View.GONE);
this.recycleBitmap();
} else {
super.onBackPressed();
releaseLensEngine();
}
}
private void createLanguageDialog() {
this.languageDialog = new Dialog(this, R.style.MyDialogStyle);
View view = View.inflate(this, R.layout.dialog_language_setting, null);
// Set up a custom layout
this.languageDialog.setContentView(view);
this.textCN = view.findViewById(R.id.simple_cn);
this.textCN.setOnClickListener(this);
this.textEN = view.findViewById(R.id.english);
this.textEN.setOnClickListener(this);
this.textJN = view.findViewById(R.id.japanese);
this.textJN.setOnClickListener(this);
this.textKN = view.findViewById(R.id.korean);
this.textKN.setOnClickListener(this);
this.textLN = view.findViewById(R.id.latin);
this.textLN.setOnClickListener(this);
this.languageDialog.setCanceledOnTouchOutside(true);
// Set the size of the dialog
Window dialogWindow = this.languageDialog.getWindow();
WindowManager.LayoutParams layoutParams = dialogWindow.getAttributes();
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.gravity = Gravity.BOTTOM;
dialogWindow.setAttributes(layoutParams);
}
private void showLanguageDialog() {
this.initDialogViews();
this.languageDialog.show();
}
private void createAddPictureDialog() {
this.addPictureDialog = new AddPictureDialog(this, AddPictureDialog.TYPE_NORMAL);
final Intent intent = new Intent(TextRecognitionActivity.this, RemoteDetectionActivity.class);
intent.putExtra(Constant.MODEL_TYPE, Constant.CLOUD_TEXT_DETECTION);
this.addPictureDialog.setClickListener(new AddPictureDialog.ClickListener() {
@Override
public void takePicture() {
lensEngine.release();
isInitialization = false;
intent.putExtra(Constant.ADD_PICTURE_TYPE, Constant.TYPE_TAKE_PHOTO);
TextRecognitionActivity.this.startActivity(intent);
}
@Override
public void selectImage() {
intent.putExtra(Constant.ADD_PICTURE_TYPE, Constant.TYPE_SELECT_IMAGE);
TextRecognitionActivity.this.startActivity(intent);
}
@Override
public void doExtend() {
}
});
}
private void showAddPictureDialog() {
this.addPictureDialog.show();
}
private void initDialogViews() {
String position = SharedPreferencesUtil.getInstance(this).getStringValue(Constant.POSITION_KEY);
this.textType = position;
this.textCN.setSelected(false);
this.textEN.setSelected(false);
this.textJN.setSelected(false);
this.textLN.setSelected(false);
this.textKN.setSelected(false);
switch (position) {
case Constant.POSITION_CN:
this.textCN.setSelected(true);
break;
case Constant.POSITION_EN:
this.textEN.setSelected(true);
break;
case Constant.POSITION_LA:
this.textLN.setSelected(true);
break;
case Constant.POSITION_JA:
this.textJN.setSelected(true);
break;
case Constant.POSITION_KO:
this.textKN.setSelected(true);
break;
default:
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(Constant.CAMERA_FACING, this.facing);
super.onSaveInstanceState(outState);
}
private void createLensEngine() {
MLLocalTextSetting setting = new MLLocalTextSetting.Factory()
.setOCRMode(MLLocalTextSetting.OCR_DETECT_MODE)
// Specify languages that can be recognized.
.setLanguage("ko")
.create();
analyzer = MLAnalyzerFactory.getInstance().getLocalTextAnalyzer(setting);
//analyzer = new MLTextAnalyzer.Factory(this).create();
if (this.lensEngine == null) {
this.lensEngine = new LensEngine(this, this.cameraConfiguration, this.graphicOverlay);
}
try {
this.localTextTransactor = new LocalTextTransactor(this.mHandler, this);
this.lensEngine.setMachineLearningFrameTransactor(this.localTextTransactor);
// this.lensEngine.setMachineLearningFrameTransactor((ImageTransactor) new ObjectAnalyzerTransactor());
isInitialization = true;
} catch (Exception e) {
Toast.makeText(
this,
"Can not create image transactor: " + e.getMessage(),
Toast.LENGTH_LONG)
.show();
}
}
private void startLensEngine() {
if (this.lensEngine != null) {
try {
this.preview.start(this.lensEngine, false);
} catch (IOException e) {
Log.e(TextRecognitionActivity.TAG, "Unable to start lensEngine.", e);
this.lensEngine.release();
this.lensEngine = null;
}
}
}
@Override
public void onResume() {
super.onResume();
if (!isInitialization){
createLensEngine();
}
this.startLensEngine();
}
@Override
protected void onStop() {
super.onStop();
this.preview.stop();
}
private void releaseLensEngine() {
if (this.lensEngine != null) {
this.lensEngine.release();
this.lensEngine = null;
}
recycleBitmap();
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseLensEngine();
if (analyzer != null) {
try {
analyzer.stop();
} catch (IOException e) {
// Exception handling.
Log.e(TAG,"Error while releasing analyzer");
}
}
}
private void recycleBitmap() {
if (this.bitmap != null && !this.bitmap.isRecycled()) {
this.bitmap.recycle();
this.bitmap = null;
}
if (this.bitmapCopy != null && !this.bitmapCopy.isRecycled()) {
this.bitmapCopy.recycle();
this.bitmapCopy = null;
}
}
private void takePicture() {
this.zoomImageLayout.setVisibility(View.VISIBLE);
LocalDataProcessor localDataProcessor = new LocalDataProcessor();
localDataProcessor.setLandScape(this.isLandScape);
this.bitmap = BitmapUtils.getBitmap(this.localTextTransactor.getTransactingImage(), this.localTextTransactor.getTransactingMetaData());
float previewWidth = localDataProcessor.getMaxWidthOfImage(this.localTextTransactor.getTransactingMetaData());
float previewHeight = localDataProcessor.getMaxHeightOfImage(this.localTextTransactor.getTransactingMetaData());
if (this.isLandScape) {
previewWidth = localDataProcessor.getMaxHeightOfImage(this.localTextTransactor.getTransactingMetaData());
previewHeight = localDataProcessor.getMaxWidthOfImage(this.localTextTransactor.getTransactingMetaData());
}
this.bitmapCopy = Bitmap.createBitmap(this.bitmap).copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(this.bitmapCopy);
float min = Math.min(previewWidth, previewHeight);
float max = Math.max(previewWidth, previewHeight);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
localDataProcessor.setCameraInfo(this.graphicOverlay, canvas, min, max);
} else {
localDataProcessor.setCameraInfo(this.graphicOverlay, canvas, max, min);
}
localDataProcessor.drawHmsMLVisionText(canvas, this.localTextTransactor.getLastResults().getBlocks());
this.zoomImageView.setImageBitmap(this.bitmapCopy);
// Create an MLFrame object using the bitmap, which is the image data in bitmap format.
MLFrame frame = MLFrame.fromBitmap(bitmap);
Task<MLText> task = analyzer.asyncAnalyseFrame(frame);
task.addOnSuccessListener(new OnSuccessListener<MLText>() {
@Override
public void onSuccess(MLText text) {
String detectText = text.getStringValue();
// Processing for successful recognition.
// Create a local language detector.
MLLangDetectorFactory factory = MLLangDetectorFactory.getInstance();
MLLocalLangDetectorSetting setting = new MLLocalLangDetectorSetting.Factory()
// Set the minimum confidence threshold for language detection.
.setTrustedThreshold(0.01f)
.create();
MLLocalLangDetector myLocalLangDetector = factory.getLocalLangDetector(setting);
Task<String> firstBestDetectTask = myLocalLangDetector.firstBestDetect(detectText);
firstBestDetectTask.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String languageDetected) {
// Processing logic for detection success.
Log.d("TAG", "Lang detect :" + languageDetected);
Log.d("TAG", " detectText :" + detectText);
translate(languageDetected,detectText);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Processing logic for detection failure.
Log.e("TAG", "Lang detect error:" + e.getMessage());
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Processing logic for recognition failure.
Log.e("TAG"," Text : Processing logic for recognition failure");
}
});
}
private void translate(String languageDetected, String detectText) {
MLApplication.initialize(getApplication());
MLApplication.getInstance().setApiKey("DAEDAF48ZIMI4ettQdTfCKlXgaln/E+TO/PrsX+LpP2BubkmED/iC0iVEps5vfx1ol27rHvuwiq64YphpPkGYWbf9La8XjnvC9qhwQ==");
// Create an offline translator.
MLLocalTranslateSetting setting = new MLLocalTranslateSetting.Factory()
// Set the source language code. The ISO 639-1 standard is used. This parameter is mandatory. If this parameter is not set, an error may occur.
.setSourceLangCode(languageDetected)
// Set the target language code. The ISO 639-1 standard is used. This parameter is mandatory. If this parameter is not set, an error may occur.
.setTargetLangCode("en")
.create();
MLLocalTranslator myLocalTranslator = MLTranslatorFactory.getInstance().getLocalTranslator(setting);
// Set the model download policy.
MLModelDownloadStrategy downloadStrategy = new MLModelDownloadStrategy.Factory()
.needWifi()// It is recommended that you download the package in a Wi-Fi environment.
.create();
// Create a download progress listener.
MLModelDownloadListener modelDownloadListener = new MLModelDownloadListener() {
@Override
public void onProcess(long alreadyDownLength, long totalLength) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// Display the download progress or perform other operations.
}
});
}
};
myLocalTranslator.preparedModel(downloadStrategy, modelDownloadListener).
addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Called when the model package is successfully downloaded.
// input is a string of less than 5000 characters.
final Task<String> task = myLocalTranslator.asyncTranslate(detectText);
// Before translation, ensure that the models have been successfully downloaded.
task.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String translated) {
// Processing logic for detection success.
Log.e("TAG"," Translated Text : "+translated);
tv_translated_txt.setText(translated);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Processing logic for detection failure.
Log.e("TAG"," Translation failed "+e.getMessage());
Toast.makeText(TextRecognitionActivity.this,"Please check internet connection.",Toast.LENGTH_SHORT).show();
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Called when the model package fails to be downloaded.
Log.e("TAG"," Translation failed onFailure "+e.getMessage());
}
});
}
}
[B]
MainFragment.java