Development Guide for Integrating Share Kit on Huawei Phones - 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"
}
What is Share Engine
As a cross-device file transfer solution, Huawei Share uses Bluetooth to discover nearby devices and authenticate connections, then sets up peer-to-peer Wi-Fi channels, so as to allow file transfers between phones, PCs, and other devices. It delivers stable file transfer speeds that can exceed 80 Mbps if the third-party device and environment allow. Developers can use Huawei Share features using Share Engine.
The Huawei Share capabilities are sealed deep in the package, then presented in the form of a simplified engine for developers to integrate with apps and smart devices. By integrating these capabilities, PCs, printers, cameras, and other devices can easily share files with each other.
Three SDK development packages are offered to allow quick integration for Android, Linux, and Windows based apps and devices.
Working Principles
Huawei Share uses Bluetooth to discover nearby devices and authenticate connections, then sets up peer-to-peer Wi-Fi channels, so as to allow file transfers between phones, PCs, and other devices.
To ensure user experience, Huawei Share uses reliable core technologies in each phase of file transfer.
Devices are detected using in-house bidirectional device discovery technology, without sacrificing the battery or security
Connection authentication using in-house developed password authenticated key exchange (PAKE) technology
File transfer using high-speed point-to-point transmission technologies, including Huawei-developed channel capability negotiation and actual channel adjustment
For more information you can follow this link.
Let’s get into codding.
Requirements
For development, we need Android Studio V3.0.1 or later.
EMUI 10.0 or later and API level 26 or later needed for Huawei phone.
Development
1 - First we need to add this permission to AndroidManifest.xml. So that we can ask user for our app to access files.
XML:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2 - After let’s shape activity_main.xml as bellow. Thus we have one EditText for getting input and three Button in UI for using Share Engine’s features.
XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<EditText
android:id="@+id/inputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="@string/hint"
tools:ignore="Autofill,TextFields" />
<Button
android:id="@+id/sendTextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="sendText"
android:text="@string/btn1"
android:textAllCaps="false" />
<Button
android:id="@+id/sendFileButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="sendFile"
android:text="@string/btn2"
android:textAllCaps="false" />
<Button
android:id="@+id/sendFilesButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:onClick="sendMultipleFiles"
android:text="@string/btn3"
android:textAllCaps="false" />
</LinearLayout>
3 - By adding the following to strings.xml, we create button and alert texts.
XML:
<string name="btn1">Send text</string>
<string name="btn2">Send single file</string>
<string name="btn3">Send multiple files</string>
<string name="hint">Write something to send</string>
<string name="errorToast">Please write something before sending!</string>
4 - Later, let’s shape the MainActivity.java class. First of all, to provide access to files on the phone, we need to request permission from the user using the onResume method.
Java:
@Override
protected void onResume() {
super.onResume();
checkPermission();
}
private void checkPermission() {
if (checkSelfPermission(READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
String[] permissions = {READ_EXTERNAL_STORAGE};
requestPermissions(permissions, 0);
}
}
5 - Let’s initialize these parameters for later uses and call this function in onCreate method.
Java:
EditText input;
PackageManager manager;
private void initApp(){
input = findViewById(R.id.inputEditText);
manager = getApplicationContext().getPackageManager();
}
6 - We’re going to create Intent with the same structure over and over again, so let’s simply create a function and get rid of the repeated codes.
Java:
private Intent createNewIntent(String action){
Intent intent = new Intent(action);
intent.setType("*");
intent.setPackage("com.huawei.android.instantshare");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
We don’t need an SDK to transfer files between Huawei phones using the Share Engine, instead we can easily transfer files by naming the package “com.huawei.android.instantshare” to Intent as you see above.
7 - Let’s create the onClick method of sendTextButton to send text with Share Engine.
Java:
public void sendText(View v) {
String myInput = input.getText().toString();
if (myInput.equals("")){
Toast.makeText(getApplicationContext(), R.string.errorToast, Toast.LENGTH_SHORT).show();
return;
}
Intent intent = CreateNewIntent(Intent.ACTION_SEND);
List<ResolveInfo> info = manager.queryIntentActivities(intent, 0);
if (info.size() == 0) {
Log.d("Share", "share via intent not supported");
} else {
intent.putExtra(Intent.EXTRA_TEXT, myInput);
getApplicationContext().startActivity(intent);
}
}
8 - Let’s edit the onActivityResult method to get the file/s we choose from the file manager to send it with the Share Engine.
Java:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && data != null) {
ArrayList<Uri> uris = new ArrayList<>();
Uri uri;
if (data.getClipData() != null) {
// Multiple files picked
for (int i = 0; i < data.getClipData().getItemCount(); i++) {
uri = data.getClipData().getItemAt(i).getUri();
uris.add(uri);
}
} else {
// Single file picked
uri = data.getData();
uris.add(uri);
}
handleSendFile(uris);
}
}
With handleSendFile method we can perform single or multiple file sending.
Java:
private void handleSendFile(ArrayList<Uri> uris) {
if (uris.isEmpty()) {
return;
}
Intent intent;
if (uris.size() == 1) {
// Sharing a file
intent = CreateNewIntent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
} else {
// Sharing multiple files
intent = CreateNewIntent(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
List<ResolveInfo> info = manager.queryIntentActivities(intent, 0);
if (info.size() == 0) {
Log.d("Share", "share via intent not supported");
} else {
getApplicationContext().startActivity(intent);
}
}
9 - Finally, let’s edit the onClick methods of file sending buttons.
Java:
public void sendFile(View v) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("*/*");
startActivityForResult(i, 10);
}
public void sendMultipleFiles(View v) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
i.setType("*/*");
startActivityForResult(i, 10);
}
We’ve prepared all the structures we need to create, so let’s see the output.
Text Sharing:
​
Single File Sharing:
​Multiple File Sharing:
​
With this guide, you can easily understand and integrate Share Engine to transfer file/s and text with your app.
For more information: https://developer.huawei.com/consumer/en/share-kit/

Very useful writeup

Very nice integration.

Very useful, thank you.

Related

1 Map makes you feel easy in a strange city (Part 1)

This article is originally from HUAWEI Developer Forum
Forum link: https://forums.developer.huawei.com/forumPortal/en/home
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
HUAWEI Site Kit provides developers with convenient and secure access to diverse, place-related services.
Core Capabilities
Place search: Returns a place list based on keywords entered by the user.
Nearby place search: Searches for nearby places based on the current location of the user's device.
Place details: Searches for details about a place.
Search suggestion: Returns a list of place suggestions.
Today in this article we are going to see how to integrate HMS core site kit into your apps along with that we have used
Ø AutoCompleteTextView
Ø TextWatcher
Ø Html format
Prerequisite
1) Must have a Huawei Developer Account
2) Must have a Huawei phone with HMS 4.0.0.300 or later
3) Must have a laptop or desktop with Android Studio , Jdk 1.8, SDK platform 26 and Gradle 4.6 installed.
Things Need To Be Done
1) First we need to create a project in android studio.
2) Get the SHA Key. For getting the SHA key we can refer to this article.
3) Create an app in the Huawei app gallery connect.
4) Enable site kit setting in Manage APIs section.
5) Provide the SHA Key in App Information Section.
6) Provide storage location.
7) After completing all the above points we need to download the agconnect-services.json from App Information Section. Copy and paste the Json file in the app folder of the android project.
8) Copy and paste the below maven url inside the repositories of buildscript and allprojects ( project build.gradle file )
Code:
maven { url 'http://developer.huawei.com/repo/' }
9) Copy and paste the below plugin in the app build.gradle file
Code:
apply plugin: 'com.huawei.agconnect'
Also implementation 'com.huawei.hms:site:4.0.1.300' in the dependencies section.
10) Now Sync the gradle.
Let’s begin the chase
There are two ways we can achieve our goal using site kit.
1) Site Kit SDK
2) Site Kit APIs
Pros of Site Kit SDK is that we can use lots of core functionality and we get response it the form of model.
Pros of Site Kit APIs is that we get response in the form of Json object and since we are using APIs, the size of the apk will be less than the apk which is using Site Kit SDK.
Here, we will learn how to use Site Kit SDK in our android project and achieve the benefit.
Main Code
1) First we need to create an object of SearchService and use the SearchServiceFactory class to instantiate the object.
Code:
SearchSsearchService = SearchServiceFactory.create(this);
2) We need to create a TextSearchRequest Object, which is used as the request body for search by keyword. We can use many parameter like query, location, radius, bounds, poi types etc. but query parameter is mandatory rest are optional.
Code:
TextSearchRequest textSearchRequest = new TextSearchRequest();
textSearchRequest.setQuery(“hotel”);
3) Create SearchResultListener object to listen for the search result
Code:
searchService.textSearch(textSearchRequest,
new SearchResultListener<TextSearchResponse>() {
@Override
public void onSearchResult(TextSearchResponse textSearchResponse) {
// Do something with the response …
}
@Override
public void onSearchError(SearchStatus searchStatus) {
}
});
The Result
Main Activity class
Code:
public class MainActivity extends AppCompatActivity {
AutoCompleteTextView autoCompleteTextView;
private SearchService searchService;
ArrayList<String> list = new ArrayList<>();
ArrayAdapter<String> adapter;
private ArrayAdapter<String> mAutoCompleteAdapter;
TextView txtHeader, txtDetails;
private Timer timer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtHeader = findViewById(R.id.txtHeader);
txtDetails = findViewById(R.id.txtDetails);
txtHeader.setText("HMS SITE MAP");
autoCompleteTextView = findViewById(R.id.autoCompleteTxt);
searchService = SearchServiceFactory.create(this);
mAutoCompleteAdapter = new ArrayAdapter<String>(this, R.layout.list_item, R.id.item);
autoCompleteTextView.setThreshold(1);
autoCompleteTextView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (timer != null) {
timer.cancel();
}
}
@Override
public void afterTextChanged(final Editable s) {
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// do your actual work here
if (s.length() > 1) {
list.clear();
final TextSearchRequest textSearchRequest = new TextSearchRequest();
textSearchRequest.setQuery(s.toString());
searchService.textSearch(textSearchRequest, new SearchResultListener<TextSearchResponse>() {
@Override
public void onSearchResult(TextSearchResponse textSearchResponse) {
for (Site site : textSearchResponse.getSites()) {
String values = Html.fromHtml(site.getName() + "<br>"
+ site.getAddress().getCountry() + "<br>"
+ site.getLocation().getLat() + "<br>"
+ site.getLocation().getLng() + "<br>"
+ site.getFormatAddress() + "<br>" +
site.getPoi().getPhone()).toString();
list.add(values);
}
mAutoCompleteAdapter.clear();
mAutoCompleteAdapter.addAll(list);
mAutoCompleteAdapter.notifyDataSetChanged();
}
@Override
public void onSearchError(SearchStatus searchStatus) {
}
});
}
}
}, 200);
}
});
autoCompleteTextView.setAdapter(mAutoCompleteAdapter);
autoCompleteTextView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showDetails(mAutoCompleteAdapter.getItem(position));
}
});
}
private void showDetails(String item) {
String pattern = Pattern.quote("\\" + "n");
String[] lines = item.split("\\n+");
autoCompleteTextView.setText(lines[0]);
String details = "<font color='red'>PLACE NAME : </font>" + lines[0] + "<br>"
+ "<font color='#CD5C5C'>COUNTRY : </font>" + lines[1] + "<br>"
+ "<font color='#8E44AD'>ADDRESS : </font>" + lines[4] + "<br>"
+ "<font color='#008000'>PHONE : </font>" + lines[5];
txtDetails.setText(Html.fromHtml(details, Html.FROM_HTML_MODE_COMPACT));
}
}
The XML Class
Code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<include
android:id="@+id/include"
layout="@layout/layout_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<AutoCompleteTextView
android:id="@+id/autoCompleteTxt"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:inputType="textMultiLine"
android:background="@drawable/border"
android:drawableEnd="@drawable/search"
android:paddingStart="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/include"
app:layout_constraintVertical_bias="0.0"/>
<TextView
android:id="@+id/txtDetails"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/autoCompleteTxt"
app:layout_constraintVertical_bias="0.26999998" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="SHOW MAP"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/include"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
The Result
Show Map
In Part 2 of this article, we will get to see how to show a map.

Learn Form Recognition using ML kit | JAVA

Introduction
Huawei ML kit allows to improve our lives in countless ways, streamling complex interactions with better solution such as Text detection,Face detection,Product visual search,Voice detection,Image related etc ..!
Form recognition service can recognize the information from FORM it will return table content such as table count, rows, columns, cellcoordinate, textInfo, etc..!
Use case
This service will help you in daily basis, for example after collecting a large number of data we can use this service to recognize and convert the content into electronic documents.
Suggestions
1. Forms such as questionnaires can be recognized.
2. Currently images containing multiple forms cannot be recognized.
3. Shooting Angle: The horizontal tilt angle is less than 5 degrees.
4. Form Integrity: No missing corners and no bent or segment lines
5. Form Content: Only printed content can recognized, images, hand written content, seals and watermarks in the form cannot be recognized.
6. Image Specification: Image ratio should be less than or equal 3:1, resolution must be greater than 960 x 960 px.
ML Kit Configuration.
1. Login into AppGallery Connect, select MlKitSample in My Project list.
2. Enable Ml Kit, Choose My Projects > Project settings > Manage APIs
Development Process
Create Application in Android Studio.
App level gradle dependencies.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Gradle dependencies
Code:
implementation 'com.huawei.hms:ml-computer-vision-formrecognition:2.0.4.300'
implementation 'com.huawei.hms:ml-computer-vision-formrecognition-model:2.0.4.300'
Root level gradle dependencies
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
Add the below permissions in Android Manifest file
Code:
<manifest xlmns:android...>
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application ...
</manifest>
Add the following meta data in Manifest file its automatically install machine learning model.
Code:
<meta-data
android:name="com.huawei.hms.ml.DEPENDENCY"
android:value="fr" />
1. Create Instance for MLFormRecognitionAnalyzerSetting in onCreate.
Code:
MLFormRecognitionAnalyzerSetting setting = new MLFormRecognitionAnalyzerSetting.Factory().create();
2. Create instance for MLFormRecognitionAnalyzer in onCreate.
Code:
MLFormRecognitionAnalyzerFactory analyzer = MLFormRecognitionAnalyzerFactory.getInstance().getFormRecognitionAnalyzer(setting);
3. Check Runtime permissions.
4. FormRecogActivity is the responsible for load forms from local storage and capture the live forms using FormRecognigation service and we can extract the form cells content.
Code:
public class FormRecogActivity extends AppCompatActivity {
private MLFormRecognitionAnalyzerSetting setting;
private MLFormRecognitionAnalyzer analyzer;
private ImageView mImageView;
private TextView text, textTotal;
private MLFrame mlFrame;
private Uri imageUri;
private Bitmap bitmap;
private int camRequestCode = 100;
private int storageRequestCode = 200;
private int sum = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_form_recog);
mImageView = findViewById(R.id.image);
text = findViewById(R.id.text);
textTotal = findViewById(R.id.text_total);
setting = new MLFormRecognitionAnalyzerSetting.Factory().create();
analyzer = MLFormRecognitionAnalyzerFactory.getInstance().getFormRecognitionAnalyzer(setting);
}
public void onLoadImage(View view) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, storageRequestCode);
}
public void onClikCam(View view) {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, camRequestCode);
} else {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, camRequestCode);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == camRequestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, camRequestCode);
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_LONG).show();
}
}
if (requestCode == storageRequestCode) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Intent intent = new Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
);
startActivityForResult(intent, storageRequestCode);
} else {
Toast.makeText(this, "Storage permission denied", Toast.LENGTH_LONG).show();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == storageRequestCode) {
imageUri = data.getData();
try {
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
mImageView.setImageBitmap(bitmap);
callFormService();
} catch (IOException e) {
e.printStackTrace();
}
} else if (resultCode == RESULT_OK && requestCode == camRequestCode) {
bitmap = (Bitmap) data.getExtras().get("data");
mImageView.setImageBitmap(bitmap);
callFormService();
}
}
private void callFormService() {
mlFrame = MLFrame.fromBitmap(bitmap);
analyzer = MLFormRecognitionAnalyzerFactory.getInstance().getFormRecognitionAnalyzer();
Task<JsonObject> task = analyzer.asyncAnalyseFrame(mlFrame);
task.addOnSuccessListener(new OnSuccessListener<JsonObject>() {
@Override
public void onSuccess(JsonObject jsonObject) {
if (jsonObject != null && jsonObject.get("retCode").getAsInt() == MLFormRecognitionConstant.SUCCESS) {
Gson gson = new Gson();
String result = jsonObject.toString();
MLFormRecognitionTablesAttribute mlObject = gson.fromJson(result, MLFormRecognitionTablesAttribute.class);
ArrayList<MLFormRecognitionTablesAttribute.TablesContent.TableAttribute> tableAttributeArrayList = mlObject.getTablesContent().getTableAttributes();
ArrayList<MLFormRecognitionTablesAttribute.TablesContent.TableAttribute.TableCellAttribute> tableCellAttributes = tableAttributeArrayList.get(0).getTableCellAttributes();
for (MLFormRecognitionTablesAttribute.TablesContent.TableAttribute.TableCellAttribute attribute : tableCellAttributes) {
String info = attribute.textInfo;
text.setText(text.getText().toString() + "\n" + info);
}
Toast.makeText(FormRecogActivity.this, "Successfully Form Recognized", Toast.LENGTH_LONG).show();
Log.d("TAG", "result: " + result);
} else if (jsonObject != null && jsonObject.get("retCode").getAsInt() == MLFormRecognitionConstant.FAILED) {
Toast.makeText(FormRecogActivity.this, "Form Recognition Convertion Failed", Toast.LENGTH_LONG).show();
}
textTotal.setText("Total Cart Value : "+ sum +" Rs ");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Toast.makeText(FormRecogActivity.this, "Form Recognition API Failed", Toast.LENGTH_LONG).show();
}
});
}
}
5.This Xml class for creating the UI.
Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#2196F3"
android:orientation="vertical">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_marginTop="?actionBarSize" />
<Button
android:id="@+id/btn_storage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:onClick="onLoadImage"
android:background="#F44336"
android:textColor="@color/upsdk_white"
android:text="Load Image from storage" />
<Button
android:id="@+id/btn_capture"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:onClick="onClikCam"
android:background="#F44336"
android:textColor="@color/upsdk_white"
android:text="Capture Image" />
<TextView
android:id="@+id/text_total"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:textSize="20sp"
android:textColor="@color/upsdk_white"
android:textStyle="bold" />
<ScrollView
android:layout_marginTop="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:textSize="20sp"
android:textColor="@color/upsdk_white"
android:textStyle="bold" />
</ScrollView>
</LinearLayout>
Result
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Tips & Tricks
1. Enable ML service API in AppGallery Connect.
2. Capture single form only, form service not supporting multiple forms.
3. The resolution must greater than 960 x 960px.
4. Currently this service not supporting handwritten text information.
Conclusion
This article will help you to get textinfo from form, it will extract the individual cells data with coordinates. This service will help you in daily basis.
Thank you for reading and if you have enjoyed this article I would suggest you implement this and provide your experience.
Reference
ML Kit – Form Recognition
Refer the URL

Create and Draw Huawei Map on XAMARIN!

{
"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"
}
Hi, as you know maps are used for different purposes in most apps. In this article, I would like to show you how to use Huawei map in Xamarin and make customizations on the map. First of all, we will follow what we need to do to add Hms Map kit to our project step by step.
Then we will focus on map customizations.
Of course, before that, we will talk about the problems you may encounter until you get to this step.
Integrating the HMS Map Kit Libraries to Xamarin Project
Let’s start. First of all, we need a Huawei developer account to use Huawei mobile services and configure our app with AppGallery Connect. Please follow the link for configuration.
How to configure app in AppGallery Connect <url>https://developer.huawei.com/consumer/en/doc/development/HMS-Plugin-Guides/config-agc-0000001050143025<url>
To use the HUAWEI Map Kit SDK for Xamarin, you need to create Xamarin Android bindings libraries from HMS Map Kit SDK for Android. After downloading the SDK files at the address below, we open a blank solution in visual studio and add these projects.
Map Kit SDK for Xamarin <url>https://developer.huawei.com/consumer/en/doc/development/HMS-Plugin-Guides/libbinding-0000001050143027<url>
Then please update the aar files in the projects.
Now we will add this solution to the our project as a reference where we will go and integrate the map kit.
We added our solution to our reference. Now we will be able to use related HMS classes in our project.
Manifest & Permissions
We have to update the application’s manifest file by declaring permissions. *Before starting this, please make sure the package name is correct since the project has been added to AppGallery Connect. Make sure that the Sha256 key is entered correctly for the respective build type and agconnect-services.json file is added to your project.
Code:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Also, we need to add a meta-data element to embed your app id in the application tag between <application> tag. It is required for this app to authenticate on the Huawei’s cloud server. You can find this id in agconnect-services.json file. If you don’t do that app couldn’t render the map.
Code:
<meta-data android:name="com.huawei.hms.client.appid" android:value="appid=YOUR_APPID"/>
Creating a Map
Currently, the HMS Core Map SDK supports two map containers: MapFragment and MapView.
MapFragment is a subclass of the Android Fragment class. You can use it to place a map within a fragment. It can also function as a map container and provide an entry for accessing a HuaweiMap object. In this article we will use a Map Fragment. First off all please add following lines to your XML file to which one you want to show map.
Code:
<fragment
android:id="@+id/mapfragment_mapfragmentdemo"
class="com.huawei.hms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraTargetLat="41.019879"
map:cameraTargetLng="29.007822"
map:cameraZoom="12"
/>
In our activity’s OnCreate method, set the layout file as the content view, load AGConnectService. Get a handle to the map fragment by calling FragmentManager.FindFragmentById. Then use GetMapAsync to register for the map callback.
Also, implement the IOnMapReadyCallback interface to our Activity and override OnMapReady method which is triggered when the map is ready to use.
Code:
class NewActivty : AppCompatActivity, IOnMapReadyCallback
{
private MapView mMapView;
private HuaweiMap hMap;
private MapFragment mapFragment;
string[] permissions = {
Android.Manifest.Permission.AccessCoarseLocation,
Android.Manifest.Permission.AccessFineLocation,
Android.Manifest.Permission.Internet };
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
mapFragment = FragmentManager.FindFragmentById<MapFragment>(Resource.Id.mapfragment_mapfragmentdemo);
mapFragment.GetMapAsync(this);
ActivityCompat.RequestPermissions(this, permissions, 100);
}
public void OnMapReady(HuaweiMap huaweiMap)
{
this.hMap = huaweiMap;
}
}
Show current location
To enable this function, set the MyLocationEnabled (true) of the HuaweiMap object
Code:
public void OnMapReady(HuaweiMap huaweiMap)
{
this.hMap = huaweiMap;
hMap.UiSettings.MyLocationButtonEnabled = true;
hMap.MyLocationEnabled = true;
}
Adding a Marker and Customization
I used more than one marker in my own project and I marked them on the map by customizing them.
You can do this directly in a method where you import the hmap object, but I created this function and I wanted to use this method by giving the necessary parameters externally.
Code:
private void addMarker(LatLng position, String title, String description)
{
Marker marker;
MarkerOptions marker3Options = new MarkerOptions()
.InvokePosition(position)
.InvokeTitle(title)
.InvokeSnippet(description);
Bitmap bitmap1 = ResourceBitmapDescriptor.DrawableToBitmap(this, ContextCompat.GetDrawable(this, Resource.Drawable.markerblue));
marker3Options.InvokeIcon(BitmapDescriptorFactory.FromBitmap(bitmap1));
marker = hMap.AddMarker(marker3Options);
hMap.MarkerDragStart += OnMarkerDragStart;
hMap.MarkerDrag += OnMarkerDrag;
hMap.MarkerDragEnd += OnMarkerDragEnd;
}
Now let’s see how our additions look on the map.
Custom Information Window
When the markers are clicked, we can display custom information messages to inform. let’s see how to do this. First, create a layout for custom info.
Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:background="@color/colorPrimary"
android:orientation="horizontal">
<ImageView
android:id="@+id/customInfoImage"
android:layout_width="45dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:adjustViewBounds="true" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/customInfoTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/customInfoDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:ellipsize="end"
android:singleLine="false"
android:maxLines="2"
android:textColor="@android:color/white"
android:textSize="12sp" />
<RatingBar
android:id="@+id/customInfoRatingBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:numStars="5"
android:outlineAmbientShadowColor="@android:color/white"
android:layout_gravity="center_vertical"
/>
</LinearLayout>
</LinearLayout>
We need to create a adapter class to be related to this layout. We are implementing HuaweiMap.IInfoWindowAdapter to this class.
Code:
class CustomMapInfoWindow : Java.Lang.Object, HuaweiMap.IInfoWindowAdapter
{
private Activity m_context;
private View m_View;
private Marker m_currentMarker;
public CustomMapInfoWindow(Activity activity)
{
m_context = activity;
m_View = m_context.LayoutInflater.Inflate(Resource.Layout.custom_info_contents, null);
}
public View GetInfoContents(Marker marker)
{
return null;
}
public View GetInfoWindow(Marker marker)
{
if (marker == null)
return null;
m_currentMarker = marker;
ImageView imageview = m_View.FindViewById<ImageView>(Resource.Id.customInfoImage);
TextView textviewTitle = m_View.FindViewById<TextView>(Resource.Id.customInfoTitle);
TextView textviewDescription = m_View.FindViewById<TextView>(Resource.Id.customInfoDescription);
RatingBar ratingBar = m_View.FindViewById<RatingBar>(Resource.Id.customInfoRatingBar);
if (marker.Title != null)
imageview.SetImageResource(Resource.Drawable.maplogo);
textviewTitle.Text = marker.Title;
textviewDescription.Text = marker.Snippet;
ratingBar.Rating = 5;
return m_View;
}
}
Then we add it to the class in which the click event of the marker is triggered.
Code:
hMap.SetInfoWindowAdapter(new CustomMapInfoWindow(this));
public void OnMarkerClick(object sender, HuaweiMap.MarkerClickEventArgs e)
{
Toast.MakeText(this, $"Marker Click Marker ID: {e.P0.Id}", ToastLength.Short).Show();
}
Now let’s see how it looks on map.
Shape
With the HMS Core Map SDK, you can add different shapes to a map, including polylines, polygons, and circles. We can use these features for many purposes, such as creating a route between two points, expressing a specific region, or expressing the areas covered by certain regions. So the limit here is your imagination or coverage of your project.
Adding a Polyline
You can use the code block below to draw polyline. You can add as many points as you want for the route and you can give these points dynamically according to the setup of your project.
Code:
private void drawPolyline()
{
Polyline polyline;
PolylineOptions polylineOptions = new PolylineOptions()
.Add(new LatLng(41.03472222, 28.90027778), new LatLng(41.00166667, 28.97111111), new LatLng(41.00415, 29.012449), new LatLng(40.985996056, 29.035333192));
polylineOptions.InvokeColor(Color.Red);
polylineOptions.Clickable(true);
polyline = hMap.AddPolyline(polylineOptions);
}
Adding a Polygon
You can use the code block below to denote a specific region.
Code:
private void drawPolygone()
{
Polygon polygon;
PolygonOptions polygonOptions = new PolygonOptions()
.Add(new LatLng(41.01929, 28.967267), new LatLng(41.016785, 28.986971), new LatLng(41.014623, 28.999753), new LatLng(41.001917, 28.978743), new LatLng(41.002298, 28.954132));
polygonOptions.InvokeFillColor(Color.Argb(60, 255, 200, 0));
polygonOptions.InvokeStrokeColor(Color.Green);
polygonOptions.InvokeStrokeWidth(30);
polygonOptions.Clickable(true);
polygonOptions.InvokeZIndex(2);
polygon = hMap.AddPolygon(polygon1Options);
}
Adding a Circle
Whether a circle is solid or hollow can be controlled by amending the circle attributes. By default, a circle is solid. You can create your apartment by adding the code block below. You can customize many features such as scanning the inside of the circle thickness according to your purpose.
Code:
private void drawCircle()
{
Circle circle;
LatLng circleLatLng = new LatLng(40.985996056, 29.035333192);
CircleOptions circleOptions = new CircleOptions();
circle = hMap.AddCircle(circleOptions);
circleOptions.InvokeCenter(circleLatLng);
circleOptions.InvokeRadius(1800);
circleOptions.InvokeStrokeWidth(5);
circleOptions.InvokeStrokeColor(Color.Blue);
circleOptions.InvokeStrokeWidth(30);
circleOptions.Clickable(true);
circleOptions.InvokeZIndex(2);
circle = hMap.AddCircle(circleOptions);
circleOptions.Clickable(true);
hMap.CircleClick += OnCircleClick;
}
How it looks on map
Final
For any questions, please contact me. I hope this article helped you add Huawei map to your own project and learn how to use it.
Thanks for reading!
References:
Github Link : <url>https://github.com/mbatuhanulper/Hms-Xamarin-Map-Demo<url>
Docs : <url>https://developer.huawei.com/consumer/en/doc/development/HMS-Plugin-Guides/drawmap-0000001050143035<url>
How can i draw dotted and dashed polyline?

Network Operation in Android with Huawei Network 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
Hi everyone, In this article, we’ll take a look at the Huawei Network Kit and how to use it with Rest APIs. Then, we will develop a demo app using Kotlin in the Android Studio. Finally, we’ll talk about the most common types of errors when making network operations on Android and how you can avoid them.
Huawei Network Kit
Network Kit is a service suite that allows us to perform our network operations quickly and safely. It provides a powerful interacting with Rest APIs and sending synchronous and asynchronous network requests with annotated parameters. Also, it allows us to quickly and easily upload or download files with additional features such as multitasking, multithreading, resumable uploads, and downloads. Lastly, we can use it with other Huawei kits such as hQUIC Kit and Wireless Kit to get faster network traffic.
Our Sample Project
In this application, we'll get a user list from a Rest Service and show the user information on the list. When we are developing the app, we'll use these libraries:
RecyclerView
DiffUtil
Kotlinx Serialization
ViewBinding
To make it simple, we don't use an application architecture like MVVM and a progress bar to show the loading status of the data.
The file structure of our sample app:
Website for Rest API
JsonPlaceHolder is a free online Rest API that we can use whenever we need some fake data. We’ll use the fake user data from the below link. And, it gives us the user list as Json, click Here.
Why we are going to use Kotlin Serialization instead of Gson ?
Firstly, we need a serialization library to convert JSON data to objects in our app. Gson is a very popular library for serializing and deserializing Java objects and JSON. But, we are using the Kotlin language and Gson is not suitable for Kotlin. Because Gson doesn’t respect non-null types in Kotlin.
If we try to parse such as a string with GSON, we’ll find out that it doesn’t know anything about Kotlin default values, so we’ll get the NullPointerExceptions as an error. Instead of Kotlinx Serialization, you can also use serialization libraries that offer Kotlin-support, like Jackson or Moshi. We will go into more detail on the implementation of the Kotlinx Serialization.
Setup the Project
We are not going to go into the details of integrating Huawei HMS Core into a project. You can follow the instructions to integrate HMS Core into your project via official docs or codelab. After integrating HMS Core, let’s add the necessary dependencies.
Add the necessary dependencies to build.gradle (app level).
Code:
plugins {
id 'com.huawei.agconnect' // HUAWEI agconnect Gradle plugin'
id 'org.jetbrains.kotlin.plugin.serialization' // Kotlinx Serialization
}
android {
buildFeatures {
// Enable ViewBinding
viewBinding true
}
}
dependencies {
// HMS Network Kit
implementation 'com.huawei.hms:network-embedded:5.0.1.301'
// Kotlinx Serialization
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1'
}
We’ll use viewBinding instead of findViewById. It generates a binding class for each XML layout file present in that module. With the instance of a binding class, we can access the view hierarchy with type and null safety.
We used the kotlinx-servialization-json:1.01 version instead of the latest version 1.1.0 in our project. If you use version 1.1.0 and your Kotlin version is smaller than 1.4.30-M1, you will get an error like this:
Code:
Your current Kotlin version is 1.4.10, while kotlinx.serialization core runtime 1.1.0 requires at least Kotlin 1.4.30-M1.
Therefore, if you want to use the latest version of Kotlinx Serialization, please make sure that your Kotlin version is higher than 1.4.30-M1.
Add the necessary dependencies to build.gradle (project level)
Code:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
classpath 'com.huawei.agconnect:agcp:1.4.1.300' // HUAWEI Agcp plugin
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" // Kotlinx Serialization
}
}
Declaring Required Network Permissions
To use functions of Network Kit, we need to declare required permissions in the AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
Initialize the Network Kit
Let’s create an Application class and initialize the Network Kit here.
Code:
class App : Application() {
private val TAG = "Application"
override fun onCreate() {
super.onCreate()
initNetworkKit()
}
private fun initNetworkKit() {
NetworkKit.init(applicationContext, object : NetworkKit.Callback() {
override fun onResult(result: Boolean) {
if (result) {
Log.i(TAG, "NetworkKit init success")
} else {
Log.i(TAG, "NetworkKit init failed")
}
}
})
}
}
Note: Don’t forget to add the App class to the Android Manifest file.
Code:
<manifest ...>
...
<application
android:name=".App"
...
</application>
</manifest>
ApiClient
getApiClient(): It returns the RestClient instance as a Singleton. We can set the connection time out value here. Also, we specified the base URL.
Code:
const val BASE_URL = "https://jsonplaceholder.typicode.com/"
class ApiClient {
companion object {
private var restClient: RestClient? = null
fun getApiClient(): RestClient {
val httpClient = HttpClient.Builder()
.callTimeout(1000)
.connectTimeout(10000)
.build()
if (restClient == null) {
restClient = RestClient.Builder()
.baseUrl(BASE_URL)
.httpClient(httpClient)
.build()
}
return restClient!!
}
}
}
ApiInterface
We specified the request type as GET and pass the relative URL as “users”. And, it returns us the results as String.
Code:
interface ApiInterface {
@GET("users")
fun fetchUsers(): Submit<String>
}
User  - Model Class
As I mentioned earlier, we get the data as a string. Then, we’ll convert data to User object help of the Kotlinx Serialization library. To perform this process, we have to add some annotations to our data class.
@serializable -> We can make a class serializable by annotating it.
@SerialName() -> The variable name in our data must be the same as we use in the data class. If we want to set different variable names, we should use @SerialName annotation.
Code:
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class User(
@SerialName("id")
val Id: Int = 0,
val name: String = "",
val username: String = "",
val email: String = "",
)
UserDiffUtil
To tell the RecyclerView that an item in the list has changed, we’ll use the DiffUtil instead of the notifyDataSetChanged().
DiffUtil is a utility class that can calculate the difference between two lists and output a list of update operations that converts the first list into the second one. And, it uses The Myers Difference Algorithm to do this calculation.
What makes notifyDataSetChanged() inefficient is that it forces to recreate all visible views as opposed to just the items that have changed. So, it is an expensive operation.
Code:
class UserDiffUtil(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].Id == newList[newItemPosition].Id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
row_user.xml
We have two TextView to show userId and the userName. We’ll use this layout in the RecylerView.
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:id="@+id/tv_userId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textColor="@color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1" />
<View
android:id="@+id/divider_vertical"
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@android:color/darker_gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/tv_userId"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_userName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textColor="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/divider_vertical"
app:layout_constraintTop_toTopOf="parent"
tools:text="Antonio Vivaldi" />
<View
android:id="@+id/divider_horizontal"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@android:color/darker_gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
UserAdapter
It contains the adapter and the ViewHolder class.
Code:
class UserAdapter : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
private var oldUserList = emptyList<User>()
class UserViewHolder(val binding: RowUserBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
return UserViewHolder(
RowUserBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.binding.tvUserId.text = oldUserList[position].Id.toString()
holder.binding.tvUserName.text = oldUserList[position].name
}
override fun getItemCount(): Int = oldUserList.size
fun setData(newUserList: List<User>) {
val diffUtil = UserDiffUtil(oldUserList, newUserList)
val diffResults = DiffUtil.calculateDiff(diffUtil)
oldUserList = newUserList
diffResults.dispatchUpdatesTo(this)
}
}
activity_main.xml
It contains only a recyclerview to show the user list.
Code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
userAdapter: We create a adapter for the RecyclerView.
apiClient: We create a request API object using the RestClient object (ApiClient).
Network Kit provides two ways to send network request: synchronous and asynchronous.
Synchronous requests block the client until the operation completes. We can only get data after it finishes its task.
An asynchronous request doesn’t block the client and we can receive a callback when the data has been received.
getUsersAsSynchronous(): We use synchronous requests here. Firstly, we get the response from RestApi. Then, we need to convert the JSON data to User objects. We use the decodeFromString function to do this. Also, we set ignoreUnknownKeys = true, because we don’t use all user information inside the JSON file. We just get the id, name, username, and email. If you don’t put all information inside your Model Class (User), you have to set this parameter as true. Otherwise, you will get an error like:
Code:
Use ‘ignoreUnknownKeys = true’ in ‘Json {}’ builder to ignore unknown keys.
We call this function inside the onCreate. But, we are in the main thread, and we cannot call this function directly from the main thread. If we try to do this, it will crash and give an error like:
Code:
Caused by: android.os.NetworkOnMainThreadException
We should change our thread. So, we call getUsersAsSynchronous() function inside the tread. Then, we get the data successfully. But, there is still one problem. We changed our thread and we cannot change any view without switching to the main thread. If we try to change a view before switching the main thread, it will give an error:
Code:
D/MainActivity: onFailure: Only the original thread that created a view hierarchy can touch its views.
So, we use the runOnUiThread function to run our code in the main thread. Finally, we send our data to the recyclerview adapter to show on the screen as a list.
getUsersAsAsynchronous() - We use asynchronous requests here. We send a network request and wait for the response without blocking the thread. When we get the response, we can show the user list on the screen. Also, we don’t need to call our asynchronous function inside a different thread. But, if we want to use any view, we should switch to the main thread. So, we use the runOnUiThread function to run our code in the main thread again.
Code:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val TAG = "MainActivity"
private val userAdapter by lazy { UserAdapter() }
private val apiClient by lazy {
ApiClient.getApiClient().create(ApiInterface::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.recyclerView.apply {
layoutManager = LinearLayoutManager([email protected])
adapter = userAdapter
}
getUsersAsAsynchronous()
/*
thread(start = true) {
getUsersAsSynchronous()
}
*/
}
private fun getUsersAsSynchronous() {
val response = apiClient.fetchUsers().execute()
if (response.isSuccessful) {
val userList =
Json { ignoreUnknownKeys = true }.decodeFromString<List<User>>(response.body)
runOnUiThread {
userAdapter.setData(userList)
}
}
}
private fun getUsersAsAsynchronous() {
apiClient.fetchUsers().enqueue(object : Callback<String>() {
override fun onResponse(p0: Submit<String>?, response: Response<String>?) {
if (response?.isSuccessful == true) {
val userList = Json {
ignoreUnknownKeys = true
}.decodeFromString<List<User>>(response.body)
runOnUiThread {
userAdapter.setData(userList)
}
}
}
override fun onFailure(p0: Submit<String>?, p1: Throwable?) {
Log.d(TAG, "onFailure: ${p1?.message.toString()}")
}
})
}
}
Tips and Tricks
You can use Coroutines to manage your thread operations and perform your asynchronous operations easily.
You can use Sealed Result Class to handle the network response result based on whether it was a success or failure.
Before sending network requests, you can check that you’re connected to the internet using the ConnectivityManager.
Conclusion
In this article, we have learned how to use Network Kit in your network operations. And, we’ve developed a sample app that lists user information obtained from the REST Server. In addition to sending requests using either an HttpClient object or a RestClient object, Network Kit offers file upload and download featuring. Please do not hesitate to ask your questions as a comment.
Thank you for your time and dedication. I hope it was helpful. See you in other articles.
References
Huawei Network Kit Official Documentation
Huawei Network Kit Official Codelab
Original Source
Is it available for cross platform(Xamarin)?

Huawei Map Kit - ISS Detector Sample App

{
"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
I wanted to explain Huawei Map Kit with an app so I coded a sample Android app which name is ISS Detector. Users can instantly track the location of the ISS (International Space Station) on the map via Huawei Map Kit.
The main purpose of this article is to show how to use Huawei Map Kit, marker display operations on Huawei MapView, and polyline drawing operations.
So I will not mention parts such as MVP, binding and UI.
About the data?
used open-notify API for getting location information. You can reach the relevant API page from here.
ISS Detector Code Review
The application consists of a single Activity, this Activity has the MapView component via Huawei Map Kit. Also, I used the View Binding structure because it provides ease of access to the components.
I think it will be easier if I summarize the file structure.
data: There are response classes from the API.
service: The package containing the classes required for Retrofit2.
ui.main: UI-related classes are located here. MainContract and MainPresenter classes are included in this package because I use MVP Pattern.
util: Utility classes.
Create Project & HMS Integrations
First of all, you must integrate HMS into the project. I am not going to explain these steps You can check this article.
Code:
dependencies {
.
.
// Huawei Map Kit
implementation 'com.huawei.hms:maps:5.0.0.300'
.
.
}
After adding this implementation line to build.gradle file, we can now start using Huawei Map Kit.
Code:
<manifest ...>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA"/>
...
</manifest>
We have defined the necessary permissions in the AndroidManifest file.
Code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:map="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.MainActivity">
<com.huawei.hms.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraZoom="1"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_margin="14dp"
app:cardCornerRadius="2dp"
app:cardElevation="4dp">
<CheckBox
android:id="@+id/checkBoxTrack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:checked="true"
android:text="Track ISS" />
</androidx.cardview.widget.CardView>
</RelativeLayout>
In activity_main.xml, the MapView component offers a full-screen map. Users can follow the ISS on the map, as well as go to the places they want freely while the ISS data continues to arrive.
Code:
public class MainActivity extends AppCompatActivity implements MainContract.View, OnMapReadyCallback {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
presenter = new MainPresenter(this);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(Constant.MAP_BUNDLE);
}
initHuaweiMap(mapViewBundle);
}
@Override
public void onMapReady(HuaweiMap huaweiMap) {
Log.w(Constant.TAG, "onMapReady : ");
this.huaweiMap = huaweiMap;
presenter.mapReady();
}
@Override
public void initHuaweiMap(Bundle bundle) {
Log.w(Constant.TAG, "initHuaweiMap : ");
MapsInitializer.setApiKey(Constant.MAP_KEY);
binding.mapView.onCreate(bundle);
binding.mapView.getMapAsync(this);
}
...
}
We call the initHuaweiMap() function in onCreate. When the process is completed asynchronously with the binding.mapView.getMapAsync(this) line, the override onMapReady(HuaweiMap huaweiMap) function is triggered via OnMapReadyCallback, which we implement to MainActivity.
As a result of these operations, the map is now available and the presenter.mapReady() function is called.
We’ll come back to the MainActivity class later, but let’s go to explain the Presenter class.
Code:
public class MainPresenter implements MainContract.Presenter {
...
@Override
public void mapReady() {
this.getISSLocation();
...
}
@Override
public void getISSLocation() {
Call<ResponseISSLocation> call = request.getISSLocation();
call.enqueue(new Callback<ResponseISSLocation>() {
@Override
public void onResponse(Call<ResponseISSLocation> call, Response<ResponseISSLocation> response) {
if (response.isSuccessful()) {
LatLng currentLatLng = response.body().getIssPosition().getLocationAsLatLng();
Log.w(Constant.TAG, "getISSLocation : " + currentLatLng.toString());
view.setMarker(currentLatLng);
view.drawRoute(currentLatLng);
if (isChecked)
view.moveCamera(currentLatLng);
waitAndCallRequest();
}
}
@Override
public void onFailure(Call<ResponseISSLocation> call, Throwable t) {
Log.w(Constant.TAG, "getISSLocation - onFailure : " + t.getMessage());
view.showErrorMessage(t.getMessage());
}
});
}
@Override
public void waitAndCallRequest() {
Log.d(Constant.TAG, "waitAndCallRequest ");
new android.os.Handler().postDelayed(() -> getISSLocation(), 2000
);
}
}
The getISSLocation() function is calling in mapReady() for the first time for the ISS current location.
We are going to call setMarker(), drawRoute() and moveCamera() functions in the getISSLocation() function. Finally, the waitAndCallRequest() function is calling so that we can resend the same request every 2 seconds and get new location data.
Now let’s come to the setMarker(), drawRoute(), and moveCamera() functions, which is the main purpose of the article.
Code:
@Override
public void setMarker(LatLng latLng) {
Log.w(Constant.TAG, "setMarker ");
if (marker != null)
marker.remove();
MarkerOptions options = new MarkerOptions()
.position(latLng)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_iss));
marker = huaweiMap.addMarker(options);
}
In the setMarker() function, we first check the marker, if there is a marker on the map, firstly delete this marker with the marker.remove() line. Next, we create a MarkerOptions object, here we set the position of the marker with .position(), the marker icon with .icon(), and finally, we show the marker created with the huaweiMap.addMarker(options) line on the map.
Code:
@Override
public void drawRoute(LatLng latLng) {
if (huaweiMap == null) return;
if (polyline == null) {
polyline = huaweiMap.addPolyline(new PolylineOptions()
.add(latLng)
.color(getColor(R.color.colorAccent))
.width(3));
polylineList = new ArrayList<>();
polylineList.add(latLng);
} else {
polylineList.add(latLng);
polyline.setPoints(polylineList);
}
}
We have to check if the huaweiMap is null in drawRoute() function, if the polyline object is null -the application will enter the if block when it first opens because our polyline object is still null- we set the polyline object to huaweiMap with the huaweiMap.addPolyline(new PolylineOptions()) line. We set the location data with.add(), we set the polyline color with .color(), we set the polyline width .width(). Since the polyline will not consist of a single point, we create a polylineList of type ArrayList and add the location data to this list. Let’s come to the else block, we add the location here to the list and use the polyline.setPoints() function to update the route on the map.
Code:
@Override
public void moveCamera(LatLng latLng) {
Log.w(Constant.TAG, "moveCamera ");
huaweiMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, huaweiMap.getCameraPosition().zoom));
}
moveCamera() is the last function called in these steps. We send the latLng variable returned from the request and the zoom level value we get with huaweiMap.getCameraPosition().zoom to the animateCamera() function, and as a result of this line, our map moves to the new location.
Tips & Tricks
MAP_KEY value generated from AGC for Huawei Mobile Services.
Also, you can access the source codes of the application from the Github and Huawei AppGallery links below.
Conclusion
In this article, I tried to explain how to use Huawei Map Kit, marker display operations on Huawei MapView, and polyline drawing operations. I hope it was a useful article for everyone. Thank you for taking the time to read.
References
App Gallery
Source Code
Huawei Map Kit
Original Source

Categories

Resources