[Huawei Developers] Freemind R:
Introduction

This article is based on Huawei Mobile Services application. I have developed Trip Booking Android app. We can provide the solution for HMS based multiple kits such as Account Kit, Huawei Ads, Huawei Map, and Huawei Analysts to use in Trip Booking. So users can book any trip.

In this application, users can plan trips and book their trips. It will provide the ongoing trip cities wise with weather forecasting so that user can easily plan a trip.

In this article, I will integrate Weather API, Huawei Map, and Huawei Map Direction API, so that users can check the route and plan their trips, and book with the trip weather forecast.

Huawei Map

HMS Core Map SDK is a set of APIs for map development in Android. The map data covers most countries outside China and supports multiple languages. The Map SDK uses the WGS 84 GPS coordinate system, which can meet most requirements of map development outside China. You can easily add map-related functions in your Android app, including:

1. Map display: Displays buildings, roads, water systems, and Points of Interest (POIs).

2. Map interaction: Controls the interaction gestures and buttons on the map.

3. Map drawing: Adds location markers, map layers, overlays, and various shapes.

Prerequisite

1. A computer (desktop or laptop)

2. A Huawei phone, which is used to debug the developed app

3. HUAWEI Analytics Kit 5.0.3

4. Android SDK applicable to devices using Android API-Level 19 (Android 4.4 KitKat) or higher

5. Android Studio

6. Java JDK 1.7 or later (JDK 1.8 recommended).

Things Need To Be Done

To integrate HUAWEI HMS Core services, you must complete the following preparations:

1. Create an app in AppGallery Connect.

2. Create an Android Studio project.

3. Add the app package name and save the configuration file.

4. Configure the Maven repository address and AppGallery Connect gradle plug-in.

Integration

1. Sign in to AppGallery Connect and select my projects.

2. Navigate to app to enable Map Kit.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200918144812.36935210994142477726580587344886:50510928022351:2800:7BCB3B90B300AE4B94B4216C605D4053B7984CE4220F70B10125638A6E703D41.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200918144812.36935210994142477726580587344886:50510928022351:2800:7BCB3B90B300AE4B94B4216C605D4053B7984CE4220F70B10125638A6E703D41.png[/IMG]

3. Navigate to project setting and download the configuration file.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200911140402.22530274869415046067610269419851:50510928022351:2800:2696AA1D995AA6F45E7BAD1E0138B9F1DA8730820A30B482F9855CC599FD8686.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200911140402.22530274869415046067610269419851:50510928022351:2800:2696AA1D995AA6F45E7BAD1E0138B9F1DA8730820A30B482F9855CC599FD8686.png[/IMG]

4. Add the Maven repository address to repositories.

buildscript {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()

}
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
classpath 'com.huawei.agconnect:agcp:1.2.0.300'

}
}

allprojects {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
}

5. Add the AppGallery Connect dependency to dependencies.

//map
implementation 'com.huawei.hms:maps:4.0.0.301'

6. I have created the following class in which I have implemented Map Kit.

public class PolylineActivity extends AppCompatActivity implements OnMapReadyCallback {

public static final String TAG = "PolylineActivity";
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private HuaweiMap hmap;
private MapView mMapView;
private Marker mMarker;
private List<LatLng> latLngList;

private MapApiViewModel mapApiViewModel;

@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_polyline);
mapApiViewModel = ViewModelProviders.of(this).get(MapApiViewModel.class);

mMapView = findViewById(R.id.mapview_mapviewdemo);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(PolylineActivity.this);
}

@Override
protected void onResume() {
super.onResume();
mMapView.onResume();
}

@Override
protected void onPause() {
super.onPause();
mMapView.onPause();
}

@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}

@Override
protected void onDestroy() {
super.onDestroy();
mMapView.onDestroy();
}

@Override
public void onMapReady(HuaweiMap map) {

hmap = map;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
hmap.setMyLocationEnabled(false);
hmap.setTrafficEnabled(true);

hmap.getUiSettings().setRotateGesturesEnabled(true);
hmap.getUiSettings().setCompassEnabled(false);

hmap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLngList.get(0), 12.0f));
hmap.addMarker(new MarkerOptions().position(latLngList.get(0)));

mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
});
}

Huawei Map Direction API

Huawei Map provides Direction API, so that user can access all the information related to Map in RESTful API.

Huawei has provide the following API endpoint to access Direction API.

https://mapapi.cloud.huawei.com/mapApi/v1

Huawei provide the following direction API:

1. Walking Route Planning

2. Bicycling Route Planning

3. Driving Route Planning

I have implemented the Driving Route API with the help of Retrofit and MVVM.

Retrofit Client

I have created MapApiClient class for accessing the Direction API.

public class MapApiClient {

private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient;

public static Service getClient() {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Consants.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();

return retrofit.create(Service.class);
}

public interface Service {

@POST("mapApi/v1/routeService/driving")
Single<PolylineResponse> getPolylines(
@Query("key") String apiKey,
@Body PolylineBody polylineBody);

}
}

API Repository

I have created MapApiRepo class for accessing the API client.

public class MapApiRepo {
private MapApiClient.Service mService;

public MapApiRepo() {
this.mService = MapApiClient.getClient();
}

public Single<PolylineResponse> executeMapApi(PolylineBody polylineBody) {
return mService.getPolylines(Consants.API_KEY, polylineBody);
}
}

ViewModel

I have created MapApiViewModel class for handling the API calls.

public class MapApiViewModel extends ViewModel {

private final CompositeDisposable disposables = new CompositeDisposable();
private MapApiRepo mapApiRepo = new MapApiRepo();
private MutableLiveData<PolylineResponse> mPolylineLiveData = new MutableLiveData<>();

public LiveData<PolylineResponse> getPolylineLiveData(PolylineBody body) {
disposables.add(mapApiRepo.executeMapApi(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mPolylineLiveData.setValue(result),
throwable -> mPolylineLiveData.setValue(null)
));
return mPolylineLiveData;
}

@Override
protected void onCleared() {
disposables.clear();
}
}

Drawing Polyline

I have implemented this functionality in the following activity.

mapApiViewModel.getPolylineLiveData(getPolylineBody()).observe(this, result -> {
Log.d(TAG, result.toString());
getPolylineData(result);
}); private PolylineBody getPolylineBody() {
PolylineBody polylineBody = new PolylineBody();
Origin origin = new Origin();
origin.setLat("30.0444");
origin.setLng("31.2357");

Destination destination = new Destination();
destination.setLat("30.0131");
destination.setLng("31.2089");

polylineBody.setDestination(destination);
polylineBody.setOrigin(origin);

return polylineBody;
}

public void getPolylineData(PolylineResponse polylineResponse) {
List<Routes> routesList = polylineResponse.getRoutes();
List<Paths> paths = new ArrayList<>();
List<Steps> steps = new ArrayList<>();
List<Polyline> polylines = new ArrayList<>();
latLngList = new ArrayList<>();

for (int x = 0; x < routesList.size(); x++) {
//here we can access each array list with main.get(x).
for (Paths paths1 : routesList.get(x).getPaths()) {
paths.add(paths1);
}
for (int y = 0; y < paths.size(); y++) {
for (Steps step :
paths.get(y).getSteps()) {
steps.add(step);
}
}
for (int i = 0; i < steps.size(); i++) {
for (Polyline polyline :
steps.get(i).getPolyline()) {
polylines.add(polyline);
}
}
}

for (int i = 0; i < polylines.size(); i++) {
latLngList.add(new LatLng(Double.valueOf(polylines.get(i).getLat())
, Double.valueOf(polylines.get(i).getLng())));
}

hmap.addPolyline(new PolylineOptions()
.addAll(latLngList)
.color(Color.BLUE)
.width(3));
}

Weather API

I have used weatherstack api to get city weather condition.

https://api.weatherstack.com/

WeatherRetrofit Client

I have implemented Weather API using retrofit library with RxJava2.

public class Client {

private final static HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
private static OkHttpClient okHttpClient;

public static Service getClient() {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Utils.BASE_URL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();

return retrofit.create(Service.class);
}

public interface Service {

@GET("current")
Single<CurrentWeather> getCurrentWeather(
@Query("access_key") String apiKey,
@Query("query") String cityName);

@GET("forecast")
Single<ForcastWeather> getForecastWeather(
@Query("access_key") String apiKey,
@Query("query") String cityName,
@Query("forecast_days") String days);

}
}

App Development

I have created the following package inside the project. In which I have integrated Huawei Id Login, Huawei Analytics, Huawei Banner Ads, Weather API, Huawei Map, and Huawei Direction APIs.

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200918144651.58311429128023124697103235428062:50510928022351:2800:90C6D79D13DD3976853139CEC1B208093F95839AA69F882F5DFE856C83232A54.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200918144651.58311429128023124697103235428062:50510928022351:2800:90C6D79D13DD3976853139CEC1B208093F95839AA69F882F5DFE856C83232A54.png[/IMG]

LoginActivity

In this screen, I have integrated login functionality with Huawei Id along with Analytics Kit which logs the event.

if (authHuaweiIdTask.isSuccessful()) {
AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
Log.i(TAG, huaweiAccount.getDisplayName() + " signIn success ");
Log.i(TAG, "AccessToken: " + huaweiAccount.getAccessToken());

Bundle bundle = new Bundle();
bundle.putString(TAG,huaweiAccount.getDisplayName() + " signIn success ");
Analystics.getInstance(this).setEvent("login",bundle);

Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("user", huaweiAccount.getDisplayName());
startActivity(intent);
this.finish();

}

HomeFragment

In this screen, I have implemented Huawei Ads and Analytics kit.

Which helps to log the user activity and shown banner ads.

Loading Banner Ads

private void initAds(View view) {
HwAds.init(getActivity());
hwBannerView = view.findViewById(R.id.huawei_banner_view);
hwBannerView.setVisibility(View.VISIBLE);
AdParam adParam = new AdParam.Builder().build();
hwBannerView.loadAd(adParam);
hwBannerView.setAdListener(adListener);

}

Log User Events

Bundle bundle = new Bundle();
bundle.putString(TAG,"City loaded");
Analystics.getInstance(getActivity()).setEvent("City",bundle);

cityList.setLayoutManager(new GridLayoutManager(getActivity(), 2));
cityList.setAdapter(new CityAdapter(cities, (item) -> {
Bundle bundle1 = new Bundle();
bundle.putString(TAG,"City Clicked"+item.getCityName());
Analystics.getInstance(getActivity()).setEvent("City",bundle1);

PopularCity popularCity = item;
Intent intent = new Intent(getActivity(), CityInfoDetailActivity.class);
intent.putExtra("name", popularCity.getCityName());
intent.putExtra("url", popularCity.getImageUrl());
startActivity(intent);
}));

CityInfoDetailActivity

In this screen, I have implemented the Huawei Banner ads and Huawei Analytics.

Loading Banner Ads

HwAds.init(this);
hwBannerView = findViewById(R.id.huawei_banner_view);
hwBannerView.setVisibility(View.VISIBLE);
AdParam adParam = new AdParam.Builder().build();
hwBannerView.loadAd(adParam);
hwBannerView.setAdListener(adListener);

Log User Events

if (extras != null) {
String name = extras.getString("name");
String imageUrl = extras.getString("url");
setTitle(name);
Glide.with(this).load(imageUrl).into(cityImage);

Bundle bundle = new Bundle();
bundle.putString(TAG,"City Info");
Analystics.getInstance(this).setEvent("City Details",bundle);

}

AllTripActivity

public class AllTripActivity extends AppCompatActivity {
private RecyclerView tripList;

private static final String TAG= AllTripActivity.class.getName();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alltrips);
init();
}

private void init() {
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);

tripList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
tripList.setAdapter(new TripAdapter(list, (item) -> {
Intent intent = new Intent(this, PolylineActivity.class);
startActivity(intent);
}));
}
}

TripAdapter

public class TripAdapter extends RecyclerView.Adapter<TripAdapter.ViewHolder> {

private List<TripModel> list;
private ItemTripBinding mBinding;
private OnItemClickListener<TripModel> mOnItemClickListener;

public TripAdapter(List<TripModel> list, OnItemClickListener<TripModel> onItemClickListener) {
this.list = list;
this.mOnItemClickListener = onItemClickListener;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.item_trip, parent, false);
return new ViewHolder(mBinding);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(list.get(position), mOnItemClickListener);
}

@Override
public int getItemCount() {
return list.size();
}

static class ViewHolder extends RecyclerView.ViewHolder {

private ItemTripBinding cityBinding;

public ViewHolder(@NonNull ItemTripBinding cityBinding) {
super(cityBinding.getRoot());
this.cityBinding = cityBinding;
}

public void bind(TripModel item, OnItemClickListener<TripModel> listener) {
cityBinding.setData(item);
itemView.setOnClickListener(v -> listener.onItemClick(item));
}
}
}

WeatherDetailActivity

In this screen, I have implemented the weather related information so that users can identify the city weather condition.

public class WeatherDetailActivity extends AppCompatActivity {

private ActivityWeatherBinding mMainBinding;
private WeatherViewModel mWeatherViewModel;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_weather);
mWeatherViewModel = ViewModelProviders.of(this).get(WeatherViewModel.class);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String name = extras.getString("name");
mMainBinding.txtCityName.setText(name);
mMainBinding.txtTemperature.setText("21" + "\u00B0");
fetchWeatherDetails(name, false);
setForcastData();
}
}

@SuppressLint("SetTextI18n")
private void fetchWeatherDetails(String cityName, boolean isSearching) {
mWeatherViewModel.getCurrentWeatherLiveData(cityName).observeForever(result -> {
if (result != null) {
mMainBinding.txtCityName.setText(result.getLocation().getName());
mMainBinding.txtTemperature.setText(result.getCurrent().getTemperature() + "\u00B0");
}
});
}

private void setForcastData() {
List<CurrentWeather.Current> currents = new ArrayList<>();
for (int i = 1; i <= 7; i++) {
CurrentWeather.Current current = new CurrentWeather().new Current();
current.setObservationTime("Day");
current.setTemperature(21);
current.setIsDay(url);
currents.add(current);
}
mMainBinding.recycleWeeklyWeather.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true));
mMainBinding.recycleWeeklyWeather.setAdapter(new ForcastAdapter(currents));
}
}

WeatherViewModel

public class WeatherViewModel extends ViewModel {

private WeatherRepository mWeatherRepository = new WeatherRepository();

private final CompositeDisposable disposables = new CompositeDisposable();

private MutableLiveData<CurrentWeather> mWeatherLiveData = new MutableLiveData<>();
private MutableLiveData<ForcastWeather> mForcastWeatherLiveData = new MutableLiveData<>();

public LiveData<CurrentWeather> getCurrentWeatherLiveData(String city) {
disposables.add(mWeatherRepository.executeCurrentWeatherApi(city)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mWeatherLiveData.setValue(result),
throwable -> mWeatherLiveData.setValue(null)
));
return mWeatherLiveData;
}

public LiveData<ForcastWeather> getForcastWeatherLiveData(String city) {
disposables.add(mWeatherRepository.executeForcastWeatherApi(city)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> mForcastWeatherLiveData.setValue(result),
throwable -> mForcastWeatherLiveData.setValue(null)
));
return mForcastWeatherLiveData;
}

@Override
protected void onCleared() {
disposables.clear();
}
}

Launch the application

Let us launch our application, see the result

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200911141436.70705057171387483165911069556125:50510928022351:2800:880E62D2432C64A2A4A99FBF528D406238F9D1846EEF9D845ADCCB3AD18CC9C1.jpg"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200911141436.70705057171387483165911069556125:50510928022351:2800:880E62D2432C64A2A4A99FBF528D406238F9D1846EEF9D845ADCCB3AD18CC9C1.jpg[/IMG]

[IMG="https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200918145027.03379923806120950630110280161895:50510928022351:2800:EE250DE74EEBEB72BF0ED850A39FD62A7074CC135FE684293F80D3E6ED940F9A.png"]https://communityfile-dre.op.hicloud.com/FileServer/getFile/cmtybbs/001/768/541/2640091000001768541.20200918145027.03379923806120950630110280161895:50510928022351:2800:EE250DE74EEBEB72BF0ED850A39FD62A7074CC135FE684293F80D3E6ED940F9A.png[/IMG]