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.
Related
More articles like this, you can visit HUAWEI Developer Forum and Medium.
https://forums.developer.huawei.com/forumPortal/en/home
In this article, We will implement the Huawei Share Kit SDK and complete our demo application.
In the previous article, we have learned about Share Kit introduction and created project. So let’s start our implementation.
I will represent the functionality of Share Kit in a simple way with a working application and give a demo.
Before start developing the application we must have the following requirement.
Hardware Requirements
1. A computer (desktop or laptop) that runs Windows 7 or Windows 10
2. A Huawei phone (with the USB cable), which is used for debugging
3. A third-party Android device, which is used for debugging
Software Requirements
1. JDK 1.8 or later
2. Android API (level 26 or higher)
3. EMUI 10.0 or later
Let’s start the development:
{
"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"
}
1. Add Share Kit SDK in project:
2. We need to add the code repository to the project root directory gradle.
Code:
maven {
url 'http://developer.huawei.com/repo/'
}
3. We need to add the following dependencies in our app gradle.
Code:
dependencies {
implementation files('libs/sharekit-1.0.1.300.aar')
implementation 'com.android.support:support-annotations:28.0.0'
implementation 'com.android.support:localbroadcastmanager:28.0.0'
implementation 'com.android.support:support-compat:28.0.0'
implementation 'com.google.guava:guava:24.1-android'
}
Note: You need to raise a ticket to get Share Kit SDK “sharekit-1.0.300.aar” file
Click on the below link and raise your ticket.
https://developer.huawei.com/consumer/en/support/feedback/#/
4. I have created following package and resource file:
5. I have mentioned all activities in manifest file:
Code:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hms.myshare">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".SplashScreen"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SearchingActivity"
android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity
android:name=".ReceiveActivity"
android:configChanges="orientation|keyboardHidden|screenSize"/>
</application>
</manifest>
Let’s create an awesome User Interface:
1. I have created a wave ripple effect which will help to find the device from a UI perspective.
I have created a SearchingView.java class:
Code:
public class SearchingView extends RelativeLayout {
private static final int DEFAULT_RIPPLE_COUNT=6;
private static final int DEFAULT_DURATION_TIME=3000;
private static final float DEFAULT_SCALE=6.0f;
private static final int DEFAULT_FILL_TYPE=0;
private int rippleColor;
private float rippleStrokeWidth;
private float rippleRadius;
private int rippleDurationTime;
private int rippleAmount;
private int rippleDelay;
private float rippleScale;
private int rippleType;
private Paint paint;
private boolean animationRunning=false;
private AnimatorSet animatorSet;
private ArrayList<Animator> animatorList;
private LayoutParams rippleParams;
private ArrayList<RippleView> rippleViewList=new ArrayList<RippleView>();
public SearchingView(Context context) {
super(context);
}
public SearchingView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SearchingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(final Context context, final AttributeSet attrs) {
if (isInEditMode())
return;
if (null == attrs) {
throw new IllegalArgumentException("Attributes should be provided to this view,");
}
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleBackground);
rippleColor=typedArray.getColor(R.styleable.RippleBackground_rb_color, getResources().getColor(R.color.rippelColor));
rippleStrokeWidth=typedArray.getDimension(R.styleable.RippleBackground_rb_strokeWidth, getResources().getDimension(R.dimen.rippleStrokeWidth));
rippleRadius=typedArray.getDimension(R.styleable.RippleBackground_rb_radius,getResources().getDimension(R.dimen.rippleRadius));
rippleDurationTime=typedArray.getInt(R.styleable.RippleBackground_rb_duration,DEFAULT_DURATION_TIME);
rippleAmount=typedArray.getInt(R.styleable.RippleBackground_rb_rippleAmount,DEFAULT_RIPPLE_COUNT);
rippleScale=typedArray.getFloat(R.styleable.RippleBackground_rb_scale,DEFAULT_SCALE);
rippleType=typedArray.getInt(R.styleable.RippleBackground_rb_type,DEFAULT_FILL_TYPE);
typedArray.recycle();
rippleDelay=rippleDurationTime/rippleAmount;
paint = new Paint();
paint.setAntiAlias(true);
if(rippleType==DEFAULT_FILL_TYPE){
rippleStrokeWidth=0;
paint.setStyle(Paint.Style.FILL);
}else
paint.setStyle(Paint.Style.STROKE);
paint.setColor(rippleColor);
rippleParams=new LayoutParams((int)(2*(rippleRadius+rippleStrokeWidth)),(int)(2*(rippleRadius+rippleStrokeWidth)));
rippleParams.addRule(CENTER_IN_PARENT, TRUE);
animatorSet = new AnimatorSet();
animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
animatorList=new ArrayList<Animator>();
for(int i=0;i<rippleAmount;i++){
RippleView rippleView=new RippleView(getContext());
addView(rippleView,rippleParams);
rippleViewList.add(rippleView);
final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleX", 1.0f, rippleScale);
scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE);
scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);
scaleXAnimator.setStartDelay(i * rippleDelay);
scaleXAnimator.setDuration(rippleDurationTime);
animatorList.add(scaleXAnimator);
final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, "ScaleY", 1.0f, rippleScale);
scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);
scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);
scaleYAnimator.setStartDelay(i * rippleDelay);
scaleYAnimator.setDuration(rippleDurationTime);
animatorList.add(scaleYAnimator);
final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, "Alpha", 1.0f, 0f);
alphaAnimator.setRepeatCount(ObjectAnimator.INFINITE);
alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);
alphaAnimator.setStartDelay(i * rippleDelay);
alphaAnimator.setDuration(rippleDurationTime);
animatorList.add(alphaAnimator);
}
animatorSet.playTogether(animatorList);
}
private class RippleView extends View {
public RippleView(Context context) {
super(context);
this.setVisibility(View.INVISIBLE);
}
@Override
protected void onDraw(Canvas canvas) {
int radius=(Math.min(getWidth(),getHeight()))/2;
canvas.drawCircle(radius,radius,radius-rippleStrokeWidth,paint);
}
}
public void startRippleAnimation(){
if(!isRippleAnimationRunning()){
for(RippleView rippleView:rippleViewList){
rippleView.setVisibility(VISIBLE);
}
animatorSet.start();
animationRunning=true;
}
}
public void stopRippleAnimation(){
if(isRippleAnimationRunning()){
animatorSet.end();
animationRunning=false;
}
}
public boolean isRippleAnimationRunning(){
return animationRunning;
}
Let’s see the implementation of this custom view inside xml layout:
Code:
<?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"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:background="@drawable/background"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center">
<com.hms.myshare.view.SearchingView
android:id="@+id/searching"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rb_color="@android:color/white"
app:rb_duration="3000"
app:rb_radius="40dp"
app:rb_rippleAmount="6"
app:rb_scale="5">
<ImageView
android:id="@+id/img_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:src="@drawable/log" />
</com.hms.myshare.view.SearchingView>
</RelativeLayout>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:textColor="@android:color/white"
android:textSize="28sp"
android:gravity="center"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Huawei Share Kit"
android:id="@+id/appCompatTextView2" />
</LinearLayout>
Let’ see the output of this view:
Let’s implement Search device and Send Data:
· We have implemented this functionality inside SearchingActivity class.
We need to perform the following operation in order to implement sending data to found device.
1. We need to instantiate SDK manager class i.e. ShareKitManager with current context of Activity inside the oncreate method.
Code:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.searching_activity);
shareKitManager = new ShareKitManager(this);
2. Add callback IShareKitInitCallback to initialize the ShareKitManager class.
Code:
IShareKitInitCallback initCallback = isSuccess -> {
Log.i(TAG, "share kit init result:" + isSuccess);
if (isSuccess) {
binding.txtError.setText(getString(R.string.sharekit_init_finish));
} else {
binding.txtError.setText(getString(R.string.sharekit_init_failed));
}
};
shareKitManager.init(initCallback);
3. Register the ShareKitManager with IWidgetCallback:
Code:
private IWidgetCallback callback = new IWidgetCallback.Stub() {
@Override
public synchronized void onDeviceFound(NearByDeviceEx nearByDeviceEx) {
String deviceId = nearByDeviceEx.getCommonDeviceId();
if (deviceId == null) {
Log.e(TAG, "onDeviceFound: deviceId is null");
return;
}
Log.i(TAG, "onDeviceFound: " + deviceId + ", btName: " + nearByDeviceEx.getBtName());
synchronized (lock) {
deviceMap.put(deviceId, nearByDeviceEx);
foundTimeMap.put(deviceId, format.format(new Date()));
updateDeviceList();
}
}
@Override
public void onDeviceDisappeared(NearByDeviceEx nearByDeviceEx) {
String deviceId = nearByDeviceEx.getCommonDeviceId();
if (deviceId == null) {
Log.e(TAG, "onDeviceDisappeared: deviceId is null");
return;
}
Log.i(TAG, "onDeviceDisappeared: " + deviceId + ", btName: " + nearByDeviceEx.getBtName());
synchronized (lock) {
deviceMap.remove(deviceId);
foundTimeMap.remove(deviceId);
updateDeviceList();
}
}
@Override
public void onTransStateChange(NearByDeviceEx nearByDeviceEx, int state, int stateValue) {
Log.i(TAG, "trans state:" + state + " value:" + stateValue);
String stateDesc = "";
switch (state) {
case STATE_PROGRESS:
stateDesc = getString(R.string.sharekit_send_progress, stateValue);
break;
case STATE_SUCCESS:
stateDesc = getString(R.string.sharekit_send_finish);
break;
case STATE_STATUS:
stateDesc = getString(R.string.sharekit_state_chg, translateStateValue(stateValue));
break;
case STATE_ERROR:
stateDesc = getString(R.string.sharekit_send_error, translateErrorValue(stateValue));
showError(getString(R.string.sharekit_send_error, translateErrorValue(stateValue)));
break;
default:
break;
}
// showToast(stateDesc);
}
@Override
public void onEnableStatusChanged() {
int status = shareKitManager.getShareStatus();
Log.i(TAG, "sharekit ability current status:" + status);
}
};
We need to pass this callback to Register api.
Code:
shareKitManager.registerCallback(callback);
4. Start searching device using Discorvey api.
Code:
shareKitManager.startDiscovery();
5. If you found the device successfully we need to call the ShareBean api for send the data.
Code:
private void doSendText() {
String text = binding.sharetext.getText().toString();
ShareBean shareBean = new ShareBean(text);
doSend(destDevice, shareBean);
}
Followed by doSend() method:
Code:
private void doSend(String deviceName, ShareBean shareBean) {
List<NearByDeviceEx> processingDevices = shareKitManager.getDeviceList();
for (NearByDeviceEx device : processingDevices) {
if (deviceName.equals(device.getBtName())) {
return;
}
}
synchronized (lock) {
for (NearByDeviceEx device : deviceMap.values()) {
if (deviceName.equals(device.getBtName())) {
shareKitManager.doSend(device, shareBean);
}
}
}
}
Let’s implement Receive data functionality:
· We have implemented this functionality inside ReceivingActivity.
· We need to enable wifi in Huawei device which receive the socket connection request from sender device.
· So we need to initialize the ShareKitManager inside this activity oncreate method.
Code:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.receiver_activity);
binding.searching.startRippleAnimation();
shareKitManager = new ShareKitManager(this);
IShareKitInitCallback initCallback = isSuccess -> {
Log.i(TAG, "share kit init result:" + isSuccess);
};
shareKitManager.init(initCallback);
shareKitManager.enable();
}
Android device (Sender):
Huawei device (Receiver):
If you have any doubts or queries. Please leave your valuable comment or post your doubts in 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"
}
In this article, we will develop an app for Huawei + Non-Huawei Android phones using Huawei Map Kit and Site Kit Widget. As you know previously Huawei Map could only be used in an HMS device but after the Map update version 5.1.0.300 (2020-12-31) Map Kit can be used on non-Huawei Android phones and in other scenarios where HMS Core (APK) is not required. Meanwhile, to use HMS Core in non-Huawei Android phones we will install the HMS Core App programmatically.
Huawei Map Kit:
Huawei Map kit allows can easily integrate map-based functions into your apps and make location-based services work better for you.
Huawei Site Kit:
Directing users to the location-based service they need makes your app accessible to more people. Give your users the power to explore their world.
Pre-Requisites
1. Integrate HMS Core in project.
2. Enable Scan and Map Kit from AGC Console.
3. Add agconnet-service.json file in the app level directory.
1. Add Dependencies & Permission:
1.1: Add the following dependencies in the app level build.gradle file:
Code:
dependencies {
//Map
implementation 'com.huawei.hms:maps:5.2.0.301'
//Map callback dependencies for using Huawei Map on Non-Huawei Devices
implementation 'com.huawei.hms:maproute-fallback:5.2.0.301'
implementation 'com.huawei.hms:hwmaps-fallback:5.2.0.301'
//Site
implementation 'com.huawei.hms:site:5.2.0.300'
}
1.2: Add the following permissions in the AndroidManifest.xml:
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"/>
// To programmatically allow user to install HMS Core App
<meta-data
android:name="com.huawei.hms.client.channel.androidMarket"
android:value="false" />
2. Add Layout Files:
2.1: Add the activity_map.xml layout file in the layout folder of the res. This is the layout view of the MapActivity in the application, which contains the Site Kit Widget and a Mapview.
XML:
<?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:orientation="vertical">
<!--Site Kit Widget-->
<fragment
android:id="@+id/widget_fragment"
android:name="com.huawei.hms.site.widget.SearchFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!--Map Kit -->
<com.huawei.hms.maps.MapView
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:mapType="normal"
map:uiCompass="true"
map:uiZoomControls="true"/>
</LinearLayout>
3. Add Classes
3.1: Add the MapActivity.java file in the App. This class extends AppCompayActivity and implements OnMapReadyCallback. Meanwhile, Site Fragment is added.
Code:
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback {
private static final String TAG = "MapViewDemoActivity";
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int REQUEST_CODE = 100;
private static final LatLng LAT_LNG = new LatLng(31.5204, 74.3587);
private HuaweiMap hmap;
private MapView mMapView;
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};
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "map onCreate:");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
//Check for required Permissions
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);
}
AGConnectServicesConfig config = AGConnectServicesConfig.fromContext(this);
MapsInitializer.setApiKey(config.getString("client/api_key"));
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
SearchFragment fragment = (SearchFragment) getSupportFragmentManager().findFragmentById(R.id.widget_fragment);
try {
fragment.setApiKey(URLEncoder.encode(config.getString("client/api_key"), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
fragment.setOnSiteSelectedListener(new SiteSelectionListener() {
@Override
public void onSiteSelected(Site data) {
if (hmap != null) {
hmap.clear();
MarkerOptions markerOptions = new MarkerOptions()
.position(new LatLng(data.getLocation().getLat(), data.getLocation().getLng()))
.title(data.getName()).snippet(data.getFormatAddress());
hmap.addMarker(markerOptions);
hmap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(data.getLocation().getLat(), data.getLocation().getLng()), 11));
}
}
@Override
public void onError(SearchStatus status) {
Toast.makeText(getApplication(), status.getErrorCode() + "\n" + status.getErrorMessage(),
Toast.LENGTH_LONG)
.show();
}
});
}
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}
@Override
public void onMapReady(HuaweiMap map) {
Log.d(TAG, "onMapReady: ");
hmap = map;
hmap.setMyLocationEnabled(true);
// move camera by CameraPosition param ,latlag and zoom params can set here
CameraPosition build = new CameraPosition.Builder().target(LAT_LNG).zoom(11).build();
CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(build);
hmap.animateCamera(cameraUpdate);
}
@Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mMapView.onLowMemory();
}
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;
}
}
4. Application Logic:
When the app is used in an HMS phone, the Map will be loaded and the Site Kit Widget will be used to search for the places and a marker will be added to the Map. Meanwhile, when the App will be used in a Non-Huawei Android phone, the Map will work fine as two of the callback dependencies have been added in the gradle file but Site Kit widget will not work therefore an HMS Core installation popup is displayed for the user to install the HMS Core App in the phone to enable the required Huawei Mobile Services.
** For Huawei Map, HMS Core App is not required in the Non-Huawei phone (Huawei Map User location doesn't work on Non-Huawei Device).
** For Site Kit Widget, HMS Core App is required in the Non-Huawei phone.
5: Run the Application:
Once all code has been added to the project, you can run the application on any Huawei or Non-Huawei android phone.
6: Demo:
7: Tips and Tricks:
1. hmap.setMyLocationEnabled(true); doesn't work in non-Huawei Android phone therefore respective Location services will be used to get the user's current location on Map.
2. Check for the permission on runtime to load the Map.
3. Map on non-Huawei Android phone will work in Map version 5.1.0.300 and onwards.
4. Encode the API Key before setting it for Site widget Fragment using URLEncoder.encode(config.getString("client/api_key"), "UTF-8")
8: Conclusion:
Huawei Map use on Non-Huawei Android phones will reduce the support cost, development efforts, and maintenance of using two different Map services for Huawei/Non-Huawei devices.
9: References:
9.1: Map Kit: click here
9.2: Site Kit: click here
Original Source
Introduction
In this article, we will learn how to implement Huawei Network kit in Android. Network kit is a basic network service suite we can utilizes scenario based REST APIs as well as file upload and download. The Network kit can provide with easy-to-use device-cloud transmission channels featuring low latency and high security.
{
"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"
}
About Huawei Network kit
Huawei Network Kit is a service 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, uploads and downloads. With Huawei Network Kit we can improve the network connection when you want to access to a URL.
Supported Devices
Huawei Network Kit is not for all devices, so first we need to validate if the device support or not, and here is the list of devices supported.
Requirements
1. Any operating system (i.e. MacOS, Linux and Windows).
2. Any IDE with Android SDK installed (i.e. IntelliJ, Android Studio).
3. Minimum API Level 19 is required.
4. Required EMUI 3.0 and later version devices.
Code Integration
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:network-embedded:5.0.1.301"
implementation "androidx.multidex:multidex:2.0.1"
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'androidx.recyclerview:recyclerview:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
Root level gradle dependencies
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.4.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.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...
</manifest>
First we need to implement HMS Network kit with this we will check Network kit initialization status.
Code:
public class HWApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
@Override
public void onCreate() {
super.onCreate();
NetworkKit.init(getApplicationContext(), new NetworkKit.Callback() {
@Override
public void onResult(boolean status) {
if (status) {
Toast.makeText(getApplicationContext(), "Network kit successfully initialized", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "Network kit initialization failed", Toast.LENGTH_SHORT).show();
}
}
});
}
}
Now we need to create ApiClient class, here we will declare the Restclient object.
Code:
public class ApiClient {
private static final String BASE_URL = "https://newsapi.org/v2/";
private static final int CONNECT_TIMEOUT = 10000;
private static final int WRITE_TIMEOUT = 1000;
private static final int TIMEOUT = 10000;
public static RestClient restClient;
public static RestClient getRestClient() {
if (restClient == null) {
restClient = new RestClient
.Builder()
.baseUrl(BASE_URL)
.httpClient(getHttpClient())
.build();
}
return restClient;
}
public static HttpClient getHttpClient() {
return new HttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT)
.readTimeout(TIMEOUT)
.writeTimeout(WRITE_TIMEOUT)
.enableQuic(false)
.build();
}
}
Code:
public interface ApiInterface {
@GET("top-headlines")
Submit<String> getMovies(@Query("country") String country, @Query("category") String category, @Query("apiKey") String apiKey);
}
In Our MainActivity.java class we need to create the instance for ApiInterface, now we need to call the Restclient object to send synchronous or asynchronous requests.
Java:
public class MainActivity extends AppCompatActivity {
ApiInterface apiInterface;
private RecyclerView recyclerView;
private List<NewsInfo.Article> mArticleList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
init();
}
private void init() {
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.smoothScrollToPosition(0);
}
@Override
protected void onResume() {
super.onResume();
loadData();
}
private void loadData() {
apiInterface = ApiClient.getRestClient().create(ApiInterface.class);
apiInterface.getMovies("us", "business", "e4d3e43d2c0b4e2bab6500ec6e469a94")
.enqueue(new Callback<String>() {
@Override
public void onResponse(Submit<String> submit, Response<String> response) {
runOnUiThread(() -> {
Gson gson = new Gson();
NewsInfo newsInfo = gson.fromJson(response.getBody(), NewsInfo.class);
mArticleList = newsInfo.articles;
recyclerView.setAdapter(new ContentAdapter(getApplicationContext(), mArticleList));
recyclerView.smoothScrollToPosition(0);
});
}
@Override
public void onFailure(Submit<String> submit, Throwable throwable) {
Log.i("TAG", "Api failure");
}
});
}
}
NewsInfo.java
Java:
public class NewsInfo {
@SerializedName("status")
public String status;
@SerializedName("totalResults")
public Integer totalResults;
@SerializedName("articles")
public List<Article> articles = null;
public class Article {
@SerializedName("source")
public Source source;
@SerializedName("author")
public String author;
@SerializedName("title")
public String title;
@SerializedName("description")
public String description;
@SerializedName("url")
public String url;
@SerializedName("urlToImage")
public String urlToImage;
@SerializedName("publishedAt")
public String publishedAt;
@SerializedName("content")
public String content;
public String getAuthor() {
return author;
}
public String getTitle() {
return title;
}
public class Source {
@SerializedName("id")
public Object id;
@SerializedName("name")
public String name;
public String getName() {
return name;
}
}
}
}
main_activity.xml
XML:
<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">
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="#f2f2f2"
tools:showIn="@layout/activity_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
ContentAdapter.java
Code:
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
private List<NewsInfo.Article> newsInfos;
private Context context;
public ContentAdapter(Context applicationContext, List<NewsInfo.Article> newsInfoArrayList) {
this.context = applicationContext;
this.newsInfos = newsInfoArrayList;
}
@Override
public ContentAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_adapter, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ContentAdapter.ViewHolder viewHolder, int i) {
viewHolder.chanelName.setText(newsInfos.get(i).source.getName());
viewHolder.title.setText(newsInfos.get(i).getTitle());
viewHolder.author.setText(newsInfos.get(i).getAuthor());
Glide.with(context).load(newsInfos.get(i).urlToImage).into(viewHolder.imageView);
}
@Override
public int getItemCount() {
return newsInfos.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView chanelName, author, title;
private ImageView imageView;
public ViewHolder(View view) {
super(view);
chanelName = view.findViewById(R.id.chanelName);
author = view.findViewById(R.id.author);
title = view.findViewById(R.id.title);
imageView = view.findViewById(R.id.cover);
itemView.setOnClickListener(v -> {
});
}
}
}
layout_adapter.xml
XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView android:layout_width="match_parent"
android:layout_height="150dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:clickable="true"
android:focusable="true"
android:elevation="60dp"
android:foreground="?android:attr/selectableItemBackground"
xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/cover"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="10dp"
android:scaleType="fitXY" />
<TextView
android:id="@+id/chanelName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/cover"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:textStyle="bold" />
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/cover"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:layout_below="@id/chanelName" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/cover"
android:layout_marginLeft="20dp"
android:layout_marginTop="25dp"
android:layout_below="@id/chanelName" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
Demo
Tips and Tricks
1. Add latest Network kit dependency.
2. Minimum SDK 19 is required.
3. Do not forget to add Internet permission in Manifest file.
4. Before sending request you can check internet connection.
Conclusion
That’s it!
This article will help you to use Network kit in your android application, as we have implemented REST API. We can get the data using either HttpClient object or RestClient object.
Thanks for reading! If you enjoyed this story, please click the Like button and Follow. Feel free to leave a Comment below.
Reference
Network kit URL
Original Source
{
"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
{
"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 can learn how to integrate the Huawei Account Kit in Money Management app along with introduction slides. The sliders will provide the quick view of the app functionalities. So, I will provide the series of articles on this Money Management App, in upcoming articles I will integrate other Huawei Kits.
Account Kit
Huawei Account Kit provides for developers with simple, secure, and quick sign-in and authorization functions. User is not required to enter accounts, passwords and waiting for authorization. User can click on Sign In with HUAWEI ID button to quickly and securely sign in to the app.
Requirements
1. Any operating system (MacOS, Linux and Windows).
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 and above installed.
4. Minimum API Level 24 is required.
5. Required EMUI 9.0.0 and later version devices.
How to integrate HMS Dependencies
1. First register as Huawei developer and complete identity verification in Huawei developers website, refer to register a Huawei ID.
2. Create a project in android studio, refer Creating an Android Studio Project.
3. Generate a SHA-256 certificate fingerprint.
4. To generate SHA-256 certificate fingerprint. On right-upper corner of android project click Gradle, choose Project Name > Tasks > android, and then click signingReport, as follows.
Note: Project Name depends on the user created name.
5. Create an App in AppGallery Connect.
6. Download the agconnect-services.json file from App information, copy and paste in android Project under app directory, as follows.
7. Enter SHA-256 certificate fingerprint and click Save button, as follows.
Note: Above steps from Step 1 to 7 is common for all Huawei Kits.
8. Click Manage APIs tab and enable Account Kit.
9. Add the below maven URL in build.gradle(Project) file under the repositories of buildscript, dependencies and allprojects, refer Add Configuration.
Java:
maven { url 'http://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
10. Add the below plugin and dependencies in build.gradle(Module) file.
Java:
apply plugin: id 'com.huawei.agconnect'
// Huawei AGC
implementation 'com.huawei.agconnect:agconnect-core:1.6.0.300'
// Huawei Account Kit
implementation 'com.huawei.hms:hwid:6.3.0.301'
11. Now Sync the gradle.
12. Add the required permission to the AndroidManifest.xml file.
Java:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
Let us move to development
I have created a project on Android studio with empty activity let us start coding.
In the MainActivity.kt we can find the business logic for Huawei login button and also introduction slides.
Java:
class MainActivity : AppCompatActivity() {
private var viewPager: ViewPager? = null
private var viewPagerAdapter: ViewPagerAdapter? = null
private lateinit var dots: Array<TextView?>
private var dotsLayout: LinearLayout? = null
companion object {
private lateinit var layouts: IntArray
}
// Account Kit variables
private var mAuthManager: AccountAuthService? = null
private var mAuthParam: AccountAuthParams? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.view_pager)
dotsLayout= findViewById(R.id.layoutDots)
// Introduction slides, create xml files under "app > res > layout"
layouts = intArrayOf(R.layout.slider_1, R.layout.slider_2, R.layout.slider_3,R.layout.slider_4)
addBottomDots(0)
// Making notification bar transparent
changeStatusBarColor()
viewPagerAdapter = ViewPagerAdapter()
viewPager!!.adapter = viewPagerAdapter
viewPager!!.addOnPageChangeListener(viewListener)
// For the next and previous buttons
btn_skip.setOnClickListener { view ->
val intent = Intent([email protected], Home::class.java)
startActivity(intent)
finish()
}
btn_next.setOnClickListener { view ->
val current: Int = getItem(+1)
if (current < layouts.size) {
// Move to another slide
viewPager!!.currentItem = current
} else {
val i = Intent([email protected], Home::class.java)
startActivity(i)
finish()
}
}
// Account kit button click Listener
btn_login.setOnClickListener(mOnClickListener)
}
// Dots functionality
private fun addBottomDots(position: Int) {
dots = arrayOfNulls(layouts!!.size)
val colorActive = resources.getIntArray(R.array.dot_active)
val colorInactive = resources.getIntArray(R.array.dot_inactive)
dotsLayout!!.removeAllViews()
for (i in dots.indices) {
dots!![i] = TextView(this)
dots[i]!!.text = Html.fromHtml("•")
dots[i]!!.textSize = 35f
dots[i]!!.setTextColor(colorInactive[position])
dotsLayout!!.addView(dots[i])
}
if (dots.size > 0) dots[position]!!.setTextColor(colorActive[position])
}
private fun getItem(i: Int): Int {
return viewPager!!.currentItem + i
}
// Viewpager change Listener
private var viewListener: OnPageChangeListener = object : OnPageChangeListener {
override fun onPageSelected(position: Int) {
addBottomDots(position)
// changing the next button text 'NEXT''
if (position == layouts!!.size - 1) {
btn_next.text = "Proceed "
btn_skip.visibility = View.GONE
} else {
btn_next.text = "Next "
btn_skip.visibility = View.VISIBLE
}
}
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
}
// Making notification bar transparent
private fun changeStatusBarColor() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window = window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = Color.TRANSPARENT
}
}
// PagerAdapter class which will inflate our sliders in our ViewPager
inner class ViewPagerAdapter : PagerAdapter() {
private var layoutInflater: LayoutInflater? = null
override fun instantiateItem(myContainer: ViewGroup, mPosition: Int): Any {
layoutInflater = getSystemService(LAYOUT_INFLATER_SERVICE) as LayoutInflater?
val v: View = layoutInflater!!.inflate(layouts[mPosition], myContainer, false)
myContainer.addView(v)
return v
}
override fun getCount(): Int {
return layouts.size
}
override fun isViewFromObject(mView: View, mObject: Any): Boolean {
return mView === mObject
}
override fun destroyItem(mContainer: ViewGroup, mPosition: Int, mObject: Any) {
val v = mObject as View
mContainer.removeView(v)
}
}
// Account kit, method to send an authorization request.
private fun signIn() {
mAuthParam = AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
.setIdToken()
.setAccessToken()
.setProfile()
.createParams()
mAuthManager = AccountAuthManager.getService([email protected], mAuthParam)
startActivityForResult(mAuthManager?.signInIntent, 1002)
}
private val mOnClickListener: View.OnClickListener = object : View.OnClickListener {
override fun onClick(v: View?) {
when (v?.id) {
R.id.btn_login -> signIn()
}
}
}
// Process the authorization result.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1002 ) {
val authAccountTask = AccountAuthManager.parseAuthResultFromIntent(data)
if (authAccountTask.isSuccessful) {
Toast.makeText(this, "SigIn success", Toast.LENGTH_LONG).show()
val intent = Intent([email protected], Home::class.java)
startActivity(intent)
} else {
Toast.makeText(this, "SignIn failed: " + (authAccountTask.exception as ApiException).statusCode, Toast.LENGTH_LONG).show()
}
}
}
}
In the activity_main.xml we can create the UI screen for Huawei image button and slides operating buttons.
Java:
<?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:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="500dp" />
<LinearLayout
android:id="@+id/layoutDots"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="132dp"
android:gravity="center"
android:orientation="horizontal">
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:alpha=".5"
android:layout_above="@id/layoutDots"
android:background="@android:color/white" />
<Button
android:id="@+id/btn_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:padding="5dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="85dp"
android:background="@null"
android:textSize="16sp"
android:text="Next"
android:textAllCaps="false"
android:textColor="@color/dot_dark_screen3" />
<Button
android:id="@+id/btn_skip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="85dp"
android:layout_marginLeft="10dp"
android:textSize="16sp"
android:background="@null"
android:textAllCaps="false"
android:text="Skip"
android:textColor="@color/dot_dark_screen3" />
<ImageView
android:id="@+id/btn_login"
android:layout_width="90dp"
android:layout_height="70dp"
android:layout_alignBottom="@id/btn_next"
android:layout_centerHorizontal="true"
android:layout_marginBottom="-83dp"
android:padding="5dp"
android:text="Sign In"
android:textAllCaps="false"
android:textColor="@color/dot_dark_screen1"
app:srcCompat="@drawable/hwid_auth_button_round_black" />
</RelativeLayout>
Create slider_1.xml and placed under layout folder for the slides view and also add the content image in drawable folder. Repeat the same process for another 3 slides also.
Java:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="600dp"
android:background="@drawable/slide_1">
</androidx.constraintlayout.widget.ConstraintLayout>
Demo
Tips and Tricks
1. Make sure you are already registered as Huawei developer.
2. Set minSDK version to 24 or later, otherwise you will get AndriodManifest merge issue.
3. Make sure you have added the agconnect-services.json file to app folder.
4. Make sure you have added SHA-256 fingerprint without fail.
5. Make sure all the dependencies are added properly.
Conclusion
In this article, we have learned how to integrate the Huawei Account Kit in Money Management app along with introduction slides. The sliders will provide the quick view of the app functionalities. So, I will provide the series of articles on this Money Management App, in upcoming articles will integrate other Huawei Kits.
I hope you have read this article. If you found it is helpful, please provide likes and comments.
Reference
Account Kit – Documentation
Account Kit – Training Video