On Device Camera Stream Text Recognition in KnowMyBoard Android App Using ML Kit, MVVM, Navigation Components - Huawei Developers

{
"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

Related

Setup Map on an Android Application in smart way

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:

Explore the world Trip Booking App- Part-3 Map and direction API

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

Geofence Notification with 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
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

Integration of ML Kit in Android KnowMyBoard App using Navigation Components, MVVM, Data Binding

{
"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, Account kit and Location kit in Android application KnowMyBoard. Account Kit provides seamless login functionality to the app with large user base.
ML Kit provides app to easily leverage Huawei’s long-term proven expertise in machine learning to support diverse artificial intelligence (AI) applications throughout a wide range of industries.
ML kit provides various services, in this application we will be integrating its text related service like text recognition, text detection and text translation services, which helps in achieve the gaol of the application.
Location kit SDK for Android offers location-related APIs for Android apps. These APIs mainly relate to 6 functions like fused location, activity identification, geofence, high-precision location, indoor location, and geocoding. This mode is applicable to mobile phones and Huawei tablets. We are using Location kit to get location of user.
Supported Devices
Supported Countries/Regions
Supported Countries/Regions
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
MainActivity.java
public class MainActivity extends AppCompatActivity {
LoginViewModel loginViewModel;
private MLTextAnalyzer mTextAnalyzer;
public Uri imagePath;
Bitmap bitmap;
ArrayList<String> result = new ArrayList<>();
MLLocalLangDetector myLocalLangDetector;
MLLocalTranslator myLocalTranslator;
String textRecognized;
ProgressDialog progressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
MyApplication.setActivity(this);
progressDialog = new ProgressDialog(this);
progressDialog.setCancelable(false);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
// Process the authorization result to obtain the authorization code from AuthAccount.
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 8888) {
Task<AuthAccount> authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data);
if (authAccountTask.isSuccessful()) {
// The sign-in is successful, and the user's ID information and authorization code are obtained.
AuthAccount authAccount = authAccountTask.getResult();
UserData userData = new UserData();
userData.setAccessToken(authAccount.getAccessToken());
userData.setCountryCode(authAccount.getCountryCode());
userData.setDisplayName(authAccount.getDisplayName());
userData.setEmail(authAccount.getEmail());
userData.setFamilyName(authAccount.getFamilyName());
userData.setGivenName(authAccount.getGivenName());
userData.setIdToken(authAccount.getIdToken());
userData.setOpenId(authAccount.getOpenId());
userData.setUid(authAccount.getUid());
userData.setPhotoUriString(authAccount.getAvatarUri().toString());
userData.setUnionId(authAccount.getUnionId());
Gson gson = new Gson();
Log.e("TAG", "sign in success : " + gson.toJson(authAccount));
loginViewModel = new ViewModelProvider(MainActivity.this).get(LoginViewModel.class);
loginViewModel.sendData(authAccount.getDisplayName());
progressDialog.dismiss();
} else {
// The sign-in failed.
Log.e("TAG", "sign in failed:" + ((ApiException) authAccountTask.getException()).getStatusCode());
progressDialog.dismiss();
}
}
if (requestCode == 2323 && resultCode == RESULT_OK && data != null) {
progressDialog.setMessage("Initializing text detection..");
progressDialog.show();
imagePath = data.getData();
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imagePath);
} catch (IOException e) {
e.printStackTrace();
Log.e("TAG", " BITMAP ERROR");
}
Log.d("TAG", "Path " + imagePath.getPath());
try {
Bitmap selectedBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imagePath);
asyncAnalyzeText(selectedBitmap);
} catch (IOException e) {
e.printStackTrace();
progressDialog.dismiss();
}
}
}
private void asyncAnalyzeText(Bitmap bitmap) {
if (mTextAnalyzer == null) {
createMLTextAnalyzer();
}
MLFrame frame = MLFrame.fromBitmap(bitmap);
Task<MLText> task = mTextAnalyzer.asyncAnalyseFrame(frame);
task.addOnSuccessListener(new OnSuccessListener<MLText>() {
@Override
public void onSuccess(MLText text) {
progressDialog.setMessage("Initializing language detection..");
Log.d("TAG", "#" + text.getStringValue());
textRecognized = text.getStringValue().trim();
if(!textRecognized.isEmpty()){
// 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();
myLocalLangDetector = factory.getLocalLangDetector(setting);
Task<String> firstBestDetectTask = myLocalLangDetector.firstBestDetect(textRecognized);
firstBestDetectTask.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String languageDetected) {
progressDialog.setMessage("Initializing text translation..");
// Processing logic for detection success.
Log.d("TAG", "Lang detect :" + languageDetected);
textTranslate(languageDetected, textRecognized, bitmap);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Processing logic for detection failure.
Log.e("TAG", "Lang detect error:" + e.getMessage());
}
});
}else{
progressDialog.dismiss();
showErrorDialog("Failed to recognize text.");
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e("TAG", "Error " + e.getMessage());
}
});
}
private void showErrorDialog(String msg) {
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle("Error");
alertDialog.setMessage(msg);
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
private void textTranslate(String languageDetected, String textRecognized, Bitmap uri) {
MLApplication.initialize(getApplication());
MLApplication.getInstance().setApiKey("fDkimdjcdjjssshwQ==");
// 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();
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(textRecognized);
// 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.
result.clear();
result.add(languageDetected.trim());
result.add(textRecognized.trim());
result.add(translated.trim());
loginViewModel.setImage(uri);
loginViewModel.setTextRecongnized(result);
progressDialog.dismiss();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Processing logic for detection failure.
progressDialog.dismiss();
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// Called when the model package fails to be downloaded.
progressDialog.dismiss();
}
});
}
private void createMLTextAnalyzer() {
MLLocalTextSetting setting = new MLLocalTextSetting.Factory()
.setOCRMode(MLLocalTextSetting.OCR_DETECT_MODE)
.create();
mTextAnalyzer = MLAnalyzerFactory.getInstance().getLocalTextAnalyzer(setting);
}
@Override
protected void onStop() {
if (myLocalLangDetector != null) {
myLocalLangDetector.stop();
}
if (myLocalTranslator!= null) {
myLocalTranslator.stop();
}
if(progressDialog!= null){
progressDialog.dismiss();
}
super.onStop();
}
}
LoginViewModel.java
public class LoginViewModel extends AndroidViewModel {
AccountAuthService service;
private MutableLiveData<String> message = new MutableLiveData<>();
private MutableLiveData<ArrayList<String>> textRecongnized = new MutableLiveData<>();
private MutableLiveData<Bitmap> image = new MutableLiveData<>();
private MutableLiveData<LocationResult> locationResult = new MutableLiveData<>();
public void sendData(String msg) {
message.setValue(msg);
}
public LiveData<String> getMessage() {
return message;
}
public MutableLiveData<ArrayList<String>> getTextRecongnized() {
return textRecongnized;
}
public void setImage(Bitmap imagePath) {
this.image.setValue(imagePath);
}
public MutableLiveData<LocationResult> getLocationResult() {
return locationResult;
}
public void setLocationResult(LocationResult locationResult) {
this.locationResult.setValue(locationResult);
}
public void setTextRecongnized(ArrayList<String> textRecongnized) {
this.textRecongnized.setValue(textRecongnized);
}
public MutableLiveData<Bitmap> getImagePath() {
return image;
}
public LoginViewModel(@NonNull Application application) {
super(application);
}
public void logoutHuaweiID() {
if (service != null) {
service.signOut();
sendData("KnowMyBoard");
Toast.makeText(getApplication(), "You are logged out from Huawei ID", Toast.LENGTH_LONG).show();
}
}
public void loginClicked(View view) {
AccountAuthParams authParams = new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM).setAuthorizationCode().createParams();
service = AccountAuthManager.getService(MyApplication.getActivity(), authParams);
MyApplication.getActivity().startActivityForResult(service.getSignInIntent(), 8888);
}
public void cancelAuthorization() {
if (service != null) {
// service indicates the AccountAuthService instance generated using the getService method during the sign-in authorization.
service.cancelAuthorization().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
// Processing after a successful authorization cancellation.
Log.i("TAG", "onSuccess: ");
sendData("KnowMyBoard");
Toast.makeText(getApplication(), "Cancelled authorization", Toast.LENGTH_LONG).show();
} else {
// Handle the exception.
Exception exception = task.getException();
if (exception instanceof ApiException) {
int statusCode = ((ApiException) exception).getStatusCode();
Log.i("TAG", "onFailure: " + statusCode);
Toast.makeText(getApplication(), "Failed to cancel authorization", Toast.LENGTH_LONG).show();
}
}
}
});
} else {
Toast.makeText(getApplication(), "Login required", Toast.LENGTH_LONG).show();
}
}
}
[B][B]
RequestLocationData.java
apublic class RequestLocationData {
static String TAG = "TAG";
SettingsClient settingsClient;
private int isLocationSettingSuccess = 0;
private LocationRequest myLocationRequest;
// Define a fusedLocationProviderClient object.
private FusedLocationProviderClient fusedLocationProviderClient;
LocationCallback myLocationCallback;
Context context;
Activity activity;
LocationResult locationResult;
LoginViewModel loginViewModel;
public RequestLocationData(Context context, FragmentActivity activity, LoginViewModel loginViewModel) {
setContext(context);
setActivity(activity);
setLoginViewModel(loginViewModel);
}
public LoginViewModel getLoginViewModel() {
return loginViewModel;
}
public void setLoginViewModel(LoginViewModel loginViewModel) {
this.loginViewModel = loginViewModel;
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public Activity getActivity() {
return activity;
}
public void setActivity(Activity activity) {
this.activity = activity;
}
public void initFusionLocationProviderClint(){
// Instantiate the fusedLocationProviderClient object.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(getActivity());
settingsClient = LocationServices.getSettingsClient(getActivity());
}
public void checkDeviceLocationSettings() {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
myLocationRequest = new LocationRequest();
builder.addLocationRequest(myLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Check the device location settings.
settingsClient.checkLocationSettings(locationSettingsRequest)
// Define the listener for success in calling the API for checking device location settings.
.addOnSuccessListener(locationSettingsResponse -> {
LocationSettingsStates locationSettingsStates =
locationSettingsResponse.getLocationSettingsStates();
StringBuilder stringBuilder = new StringBuilder();
// Check whether the location function is enabled.
stringBuilder.append(",\nisLocationUsable=")
.append(locationSettingsStates.isLocationUsable());
// Check whether HMS Core (APK) is available.
stringBuilder.append(",\nisHMSLocationUsable=")
.append(locationSettingsStates.isHMSLocationUsable());
Log.i(TAG, "checkLocationSetting onComplete:" + stringBuilder.toString());
// Set the location type.
myLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the number of location updates to 1.
myLocationRequest.setNumUpdates(1);
isLocationSettingSuccess = 1;
})
// Define callback for failure in checking the device location settings.
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.i(TAG, "checkLocationSetting onFailure:" + e.getMessage());
}
});
}
public void checkPermission() {
// Dynamically apply for required permissions if the API level is 28 or lower.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "android sdk <= 28 Q");
if (ActivityCompat.checkSelfPermission(getContext(),
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(getContext(),
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.MANAGE_MEDIA,Manifest.permission.MEDIA_CONTENT_CONTROL,Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(getActivity(), strings, 1);
}
} else {
// Dynamically apply for the android.permission.ACCESS_BACKGROUND_LOCATION permission in addition to the preceding permissions if the API level is higher than 28.
if (ActivityCompat.checkSelfPermission(getActivity(),
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(getContext(),
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(getContext(),
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.MEDIA_CONTENT_CONTROL,Manifest.permission.MANAGE_MEDIA,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(getActivity(), strings, 2);
}
}
}
public LocationResult refreshLocation() {
if (isLocationSettingSuccess == 1) {
myLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
getLoginViewModel().setLocationResult(locationResult);
}
}
};
fusedLocationProviderClient.requestLocationUpdates(myLocationRequest, myLocationCallback, Looper.getMainLooper());
} else {
Log.d(TAG, "Failed to get location settings");
}
return locationResult;
}
public void disableLocationData(){
fusedLocationProviderClient.disableBackgroundLocation();
fusedLocationProviderClient.removeLocationUpdates(myLocationCallback);
}
}
navigation_graph.xml
<?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"
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">
<action
android:id="@+id/action_loginFragment_to_mainFragment"
app:destination="@id/mainFragment" />
</fragment>
<fragment
android:id="@+id/mainFragment"
android:name="com.huawei.hms.knowmyboard.dtse.activity.fragments.MainFragment"
android:label="MainFragment">
<action
android:id="@+id/action_mainFragment_to_loginFragment"
app:destination="@id/loginFragment" />
</fragment>
</navigation>
LoginFragment.java
public class LoginFragment extends Fragment {
FragmentLoginBinding loginBinding;
LoginViewModel loginViewModel;
Menu menu;
SharedPreferences prefs;
SharedPreferences.Editor editor;
NavController navController;
private String MY_PREF_NAME = "my_pref_name";
private String TAG = "TAG";
public LoginFragment() {
// Required empty public constructor
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
navController = Navigation.findNavController(view);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
loginBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false);
loginViewModel = new ViewModelProvider(getActivity()).get(LoginViewModel.class);
loginBinding.setLoginViewModel(loginViewModel);
Log.d(TAG, " Pref " + getPreferenceValue());
if (getPreferenceValue().equals("user_name")) {
loginBinding.btnHuaweiIdAuth.setVisibility(View.VISIBLE);
} else {
enableMenu(menu);
getActivity().setTitle(getPreferenceValue());
loginBinding.btnHuaweiIdAuth.setVisibility(View.GONE);
}
loginBinding.imageLogo.setOnClickListener(v -> {
navController.navigate(R.id.action_loginFragment_to_mainFragment);
});
loginViewModel.getMessage().observeForever(new Observer<String>() {
@Override
public void onChanged(String message) {
updateMessage(message);
if (!message.equals(getResources().getString(R.string.app_name))) {
setPreferenceValue(message);
enableMenu(menu);
loginBinding.btnHuaweiIdAuth.setVisibility(View.GONE);
} else {
disableMenu(menu);
loginBinding.btnHuaweiIdAuth.setVisibility(View.VISIBLE);
setPreferenceValue("user_name");
}
}
});
return loginBinding.getRoot();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
menu.clear();
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.main, menu);
this.menu = menu;
disableMenu(menu);
}
private void disableMenu(Menu menu) {
try {
if (menu != null) {
if (getPreferenceValue().equals("user_name")) {
menu.findItem(R.id.menu_login_logout).setVisible(false);
menu.findItem(R.id.menu_cancel_auth).setVisible(false);
getActivity().setTitle(getResources().getString(R.string.app_name));
} else {
menu.findItem(R.id.menu_login_logout).setVisible(true);
menu.findItem(R.id.menu_cancel_auth).setVisible(true);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void enableMenu(Menu menu) {
try {
menu.findItem(R.id.menu_login_logout).setVisible(true);
menu.findItem(R.id.menu_cancel_auth).setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressLint("NonConstantResourceId")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_cancel_auth:
setPreferenceValue("user_name");
loginViewModel.cancelAuthorization();
loginBinding.btnHuaweiIdAuth.setVisibility(View.VISIBLE);
disableMenu(menu);
return true;
case R.id.menu_login_logout:
setPreferenceValue("user_name");
loginViewModel.logoutHuaweiID();
loginBinding.btnHuaweiIdAuth.setVisibility(View.VISIBLE);
disableMenu(menu);
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
public void updateMessage(String msg) {
getActivity().setTitle(msg);
}
void setPreferenceValue(String userName) {
editor = getActivity().getSharedPreferences(MY_PREF_NAME, MODE_PRIVATE).edit();
editor.putString("user_name", userName);
editor.apply();
}
String getPreferenceValue() {
prefs = getActivity().getSharedPreferences(MY_PREF_NAME, MODE_PRIVATE);
return prefs.getString("user_name", "user_name");
}
}
MainFragment.java
public class MainFragment extends Fragment {
static String TAG = "TAG";
FragmentMainFragmentBinding binding;
LoginViewModel loginViewModel;
RequestLocationData locationData;
public MainFragment() {
// Required empty public constructor
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main_fragment, container, false);
loginViewModel = new ViewModelProvider(getActivity()).get(LoginViewModel.class);
binding.setLoginViewModel(loginViewModel);
locationData = new RequestLocationData(getContext(),getActivity(),loginViewModel);
locationData.initFusionLocationProviderClint();
locationData.checkPermission();
locationData.checkDeviceLocationSettings();
binding.buttonScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
chooseImage();
}
});
loginViewModel.getImagePath().observeForever(new Observer<Bitmap>() {
@Override
public void onChanged(Bitmap bitmap) {
try{
binding.imageView.setImageBitmap(bitmap);
}catch (Exception e){
e.printStackTrace();
Log.e("TAG","Error : "+e.getMessage());
}
}
});
loginViewModel.getTextRecongnized().observeForever(new Observer<ArrayList<String>>() {
@Override
public void onChanged(ArrayList<String> res) {
Log.i("TAG","OBSERVER : "+"Language : "+getStringResourceByName(res.get(0).trim())+
" Detected text : "+res.get(1).trim()+
" Translated text : "+res.get(2).trim());
binding.textLanguage.setText("Language : "+getStringResourceByName(res.get(0)));
binding.textDetected.setText("Detected text : "+res.get(1));
binding.textTranslated.setText("Translated text : "+res.get(2));
}
});
loginViewModel.getLocationResult().observeForever(new Observer<LocationResult>() {
@Override
public void onChanged(LocationResult locationResult) {
binding.textDetected.setText("Latitude " + locationResult.getLastHWLocation().getLatitude() + " Longitude " + locationResult.getLastHWLocation().getLongitude());
}
});
return binding.getRoot();
}
private String getStringResourceByName(String aString) {
String packageName = getActivity().getPackageName();
int resId = getResources()
.getIdentifier(aString, "string", packageName);
if (resId == 0) {
return aString;
} else {
return getString(resId);
}
}
private void chooseImage() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
getActivity().startActivityForResult(intent, 2323);
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
menu.clear();
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.main_fragment_menu, menu);
}
@SuppressLint("NonConstantResourceId")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.option_refresh_location:
//refreshLocation();
locationData.refreshLocation();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onStop() {
super.onStop();
locationData.disableLocationData();
}
}
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
Images has clear visibility of text
Enable data binding in gradle.build file
Conclusion
In this article, we have learnt how to integrate Huawei Account kit, Location kit and ML kit in Android application KnowMyBoard. You can also go through previous article part-1 here. Hoping that the ML kit capabilities are helpful to you as well, like this sample, you can make use of ML kit to recognition of text and detection of language and translation of text, likewise you can make use of ML kit in tourism app which helps app users to understand different sign boards and get rid of the language barrier.
Thank you so much for reading. I hope this article helps you to understand the integration of Huawei ML kit, Account kit and Location kit in Android application KnowMyBoard.
Reference
ML Kit – Training video
Location Kit – Training video
Checkout in forum

On Device Text Detection and Translation from Camera Stream Using Huawei ML Kit in Android KnowMyBoard App [Navigation Components, MVVM]

{
"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

Categories

Resources