preface
This Tencent map demo can do: 1. Locate the location 2. Plan the starting and ending routes 3. 4. Animation simulates the smooth movement of the car on the planned route.
Implementation steps
Let’s take a look at the implementation:
Start a new Android project and create an Activity named DrivingRouteActivity. First draw the UI layout. The layout is relatively simple, including a Map component MapView under Tencent SDK package and two input boxes for entering the starting position. It consists of two buttons for confirming route planning, an ImageView for locating the current position and a TextView for displaying itinerary information. The layout code is only for the convenience of displaying the implementation function, so the layout code is directly posted below:
<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"
tools:context=".activity.DrivingRouteActivity">
<com.tencent.tencentmap.mapsdk.maps.MapView
android:id="@+id/mapview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="bottom"
android:paddingTop="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/from_et"
android:hint="Where do you get on?"
android:layout_weight="1"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:layout_height="50dp"></EditText>
<ImageButton
android:id="@+id/location_ib"
android:background="@drawable/sendtocar_balloon"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:layout_width="50dp"
android:layout_height="50dp"></ImageButton>
</LinearLayout>
<EditText
android:id="@+id/to_et"
android:hint="Where are you going?"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"></EditText>
<TextView
android:id="@+id/orderdesc_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"></TextView>
<Button
android:id="@+id/confirm_btn"
android:text="Sure"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:visibility="gone"></Button>
<Button
android:id="@+id/order_btn"
android:text="Reservation express"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"></Button>
</LinearLayout>
</LinearLayout>
Copy the code
1. Register and configure the account
Before development, we need to register an account on the official website of Tencent Location serviceEnter the console after registrationSelect Key Management, click Create New Key, and submit application according to application information
Configure the key under the application TAB as follows (replace value with your own key)
<meta-data
android:name="TencentMapSDK"
android:value="XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX" />
Copy the code
2. Introduce Tencent Android Map SDK
Enter the Android Map SDK and download the 3D map SDK packageAfter downloading, open the compressed package, copy the JAR package from the libs folder to the libs directory of the app, right-click the JAR package and select Add as Library to add it as a dependency, and create a directory named jniLibs under the project app\ SRC \main path. Put all packages in the libs/jniLibs/strip folder into the jniLibs directoryAfter introduction, configure related permissions in the androidmanifest.xml file
<! Access the map service -->
<uses-permission android:name="android.permission.INTERNET" />
<! Check network availability -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<! -- Access WiFi status -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<! Need external storage write permission to save map cache -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<! Get device ID -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Copy the code
3. Initialize the map
In order to make the implementation logic clearer, I have separated the business logic code and view rendering code into two packages. In addition to the DrivingRouteActivity package, I have created a present package. And create a DrivingRoutePresent class under the package, respectively by DrivingRouteActivity is responsible for UI component view rendering, by DrivingRoutePresent class is responsible for business logic. I also create a new contract package and create a DrivingRouteContract interface that interacts with DrivingRoutePresent and DrivingRouteActivity using methods defined by the interface. In the DrivingRouteContract interface, we define two interfaces, a View interface for DrivingRouteActivity and a Presenter interface for DrivingRoutePresent, and define initialization methods
public interface DrivingRouteContract {
interface View{
void initView(a);// Initialize the View
void initOnClick(a);// Initialize OnClickListener
void setOrderDescTV(String content);// Render order itinerary information
EditText getFromET(a);
}
interface Presenter{
void attachView(DrivingRouteContract.View view);/ / bind the View}}Copy the code
Then let DrivingRouteActivity DrivingRouteContract. View interface initialized and statement in the UI components
public class DrivingRouteActivity extends Activity implements DrivingRouteContract.View.View.OnClickListener {
private MapView mapView;
private TencentMap mMap;
private Button confirmBtn;
private Button orderBtn;
private ImageButton locationIB;
private EditText fromET;
private EditText toET;
private TextView orderDescTV;
private DrivingRoutePresent drivingRoutePresent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_driving_route);
initView();
initOnClick();
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.order_btn:
// Implement itinerary planning
break;
case R.id.confirm_btn:
// Enable animation movement
break;
case R.id.location_ib:
// Locate the current location
break; }}/** * MapView lifecycle management */
@Override
protected void onStart(a) {
super.onStart();
mapView.onStart();
}
@Override
protected void onResume(a) {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause(a) {
super.onPause();
mapView.onPause();
}
@Override
protected void onStop(a) {
super.onStop();
mapView.onStop();
}
@Override
protected void onDestroy(a) {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onRestart(a) {
super.onRestart();
mapView.onRestart();
}
@Override
public void initView(a) {
mapView = findViewById(R.id.mapview);
confirmBtn = findViewById(R.id.confirm_btn);
orderBtn = findViewById(R.id.order_btn);
locationIB = findViewById(R.id.location_ib);
fromET = findViewById(R.id.from_et);
toET = findViewById(R.id.to_et);
orderDescTV = findViewById(R.id.orderdesc_tv);
mMap = mapView.getMap();
drivingRoutePresent = new DrivingRoutePresent();
drivingRoutePresent.attachView(this);
}
@Override
public void initOnClick(a) {
orderBtn.setOnClickListener(this);
confirmBtn.setOnClickListener(this);
locationIB.setOnClickListener(this);
}
@Override
public void setOrderDescTV(String content) {
orderDescTV.setText(content);
}
@Override
public EditText getFromET(a) {
returnfromET; }}Copy the code
DrivingRoutePresent DrivingRouteContract. Presenter interface
public class DrivingRoutePresent implements DrivingRouteContract.Presenter {
private DrivingRouteContract.View drinvingRouteView;
@Override
public void attachView(DrivingRouteContract.View view) { drinvingRouteView = view; }}Copy the code
Since we will need the current application context in multiple places later, we need to write a GlobalApplication context utility class to help us get the context, create an util package, and create a GlobalApplication class
public class GlobalApplication extends Application {
private static Context context;
@Override
public void onCreate(a) {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext(a){
returncontext; }}Copy the code
Also, add the following properties to the Application TAB of the Android class file to load the GlobalApplication at startup
android:name=".util.GlobalApplication"
Copy the code
Here, we have completed the basic design of the interface and business code, run the APP, you can see the basic map information displayed. Now let’s implement the function of route planning. Tencent’s official Android map SDK development document has a more detailed description of route planning services and address resolution. An invocation sample Demo is also provided. If you are not sure how to call it, you can refer to the official Demo or the code below.
4. Address resolution and route planning
First we in DrivingRouteContract. The Presenter interface declares a for by address lookup the latitude and longitude geocoder method and a routePlan method for route planning
public interface DrivingRouteContract {
interface View{
void initView(a);// Initialize the View
void initOnClick(a);// Initialize OnClickListener
void setOrderDescTV(String content);// Render order itinerary information
EditText getFromET(a);
}
interface Presenter{
void attachView(DrivingRouteContract.View view);/ / bind the View
void geocoder(String address, Integer type);// Address decode, convert latitude and longitude
void routePlan(a);// Implement route planning}}Copy the code
By tencent Android SDK map route planning services for the development of the document, we know that to get the planning route need to get starting point and end point of latitude and longitude, and in general business scenario, we will not allow the user to manually enter the latitude and longitude, almost use address resolution services, so I need here by typing Chinese address to obtain the latitude and longitude, Then through the latitude and longitude planning route (but in the actual business it is best to add keywords input prompt service, convenient for users to find the location of the input).
Implement both methods in the DrivinGroup Present class
public static final Integer FROM_TYPE = 0x100; // Get the starting coordinates
public static final Integer TO_TYPE = 0x101; // Get the destination coordinates
private LatLng fromLatLng;
private LatLng toLatLng;
/** * address decoding *@paramAddress Passes in the address to be decoded *@paramType Indicates the address type, including the start location and destination location */
@Override
public void geocoder(String address, final Integer type) {
TencentSearch tencentSearch = new TencentSearch(GlobalApplication.getContext());
Address2GeoParam address2GeoParam =
new Address2GeoParam(address);
tencentSearch.address2geo(address2GeoParam, new HttpResponseListener<BaseObject>() {
@Override
public void onSuccess(int arg0, BaseObject arg1) {
if (arg1 == null) {
return;
}
Address2GeoResultObject obj = (Address2GeoResultObject)arg1;
if(obj.result.latLng ! =null) {
if (type==FROM_TYPE)
fromLatLng = obj.result.latLng;
else if(type==TO_TYPE) toLatLng = obj.result.latLng; routePlan(); }}@Override
public void onFailure(int arg0, String arg1, Throwable arg2) {
Log.e("test"."error code:" + arg0 + ", msg:"+ arg1); }}); }private TencentSearch tencentSearch = new TencentSearch(GlobalApplication.getContext());
private StringBuffer lineStringBuilder = new StringBuffer();// Route coordinates
private Double taxiFare = 0d;// Estimate the taxi fare
private Float distance = 0f;// Estimated mileage
/** ** route planning */
@Override
public void routePlan(a) {
if(fromLatLng! =null&&toLatLng! =null){
Toast.makeText(GlobalApplication.getContext(), "I'm planning your route.", Toast.LENGTH_SHORT).show();
DrivingParam drivingParam = new DrivingParam(fromLatLng, toLatLng);
drivingParam.policy(DrivingParam.Policy.TRIP);// Driving route planning strategy, ride-hailing scenarios, passenger drop-off
drivingParam.setCarNumber("Guangdong A00001");// Fill in the license plate number to avoid the license plate restricted area during route planning
tencentSearch.getRoutePlan(drivingParam, new HttpResponseListener<DrivingResultObject>() {
@Override
public void onSuccess(int i, DrivingResultObject drivingResultObject) {
for (DrivingResultObject.Route route : drivingResultObject.result.routes){
for (LatLng latLng : route.polyline){
lineStringBuilder.append(latLng.latitude + "," + latLng.longitude);
lineStringBuilder.append(",");
}
distance += route.distance;
taxiFare += route.taxi_fare.fare;
}
drinvingRouteView.setOrderDescTV("Approximately" + distance + "M, is expected to selections" + taxiFare + "Yuan");
// Clear itinerary, mileage, and cost information
lineStringBuilder = new StringBuffer();
distance = 0f;
taxiFare = 0d;
}
@Override
public void onFailure(int i, String s, Throwable throwable) {
Log.d("DrivingRouteActivity"."onSuccess: "+ s + i); }}); fromLatLng=null;
toLatLng=null; }}Copy the code
The geocoder method is used to obtain the coordinates of the starting position we input (where to get on the bus) and the longitude and latitude of the destination position we input (where to get off the bus). After recording the longitude and latitude of the position, the routePlan method is called to request the route planning interface and record the mileage and cost information. The longitude and latitude of the points passed during the course of the route (used to realize car movement behind).
In addition to the common parameters of the route planning interface, there are many other interface parameters. For details, see the official interface document and add parameters as required
Refer to the official interface documentation: lbs.qq.com/AndroidDocs…
5. Vehicle driving animation
Now that you have a route planning method, add an implementation to the “Book an express” button
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.order_btn:
drivingRoutePresent.geocoder(fromET.getText().toString(), DrivingRoutePresent.FROM_TYPE);
drivingRoutePresent.geocoder(toET.getText().toString(), DrivingRoutePresent.TO_TYPE);
confirmBtn.setVisibility(View.VISIBLE);
orderBtn.setVisibility(View.GONE);
break;
case R.id.confirm_btn:
// Enable animation movement
break;
case R.id.location_ib:
// Locate the current location
break; }}Copy the code
At this point, run the APP and input the starting point and end point to get the planned route for driving
Next, we will realize the function of driving the car according to the planned route on the effect map
In DrivingRouteContract. The View in the interface to join the car animation initAnimation initialization method
public interface DrivingRouteContract {
interface Model{}interface View{
void initView(a);// Initialize the View
void initOnClick(a);// Initialize OnClickListener
void setOrderDescTV(String content);// Render order itinerary information
EditText getFromET(a);
void initAnimation(String line);// Initialize the car movement animation
}
interface Presenter{
void attachView(DrivingRouteContract.View view);/ / bind the View
void startLocation(boolean single);
void stopLocation(a);
void geocoder(String address, Integer type);// Address decode, convert latitude and longitude
void routePlan(a);// Implement route planning}}Copy the code
Implement initAnimation method and refer to the interface documentation above for other parameters of Marker
private Marker mCarMarker;
private LatLng[] mCarLatLngArray;
private MarkerTranslateAnimator mAnimator;
@Override
public void initAnimation(String line) {
// Get the latitude and longitude array
String[] linePointsStr = line.split(",");
mCarLatLngArray = new LatLng[linePointsStr.length / 2];
for (int i = 0; i < mCarLatLngArray.length; i++) {
double latitude = Double.parseDouble(linePointsStr[i * 2]);
double longitude = Double.parseDouble(linePointsStr[i * 2 + 1]);
mCarLatLngArray[i] = new LatLng(latitude, longitude);
}
// Add a car route
mMap.addPolyline(new PolylineOptions().add(mCarLatLngArray)
.color(R.color.colorLine));// This color is a custom color in colors.xml
// Add car
LatLng carLatLng = mCarLatLngArray[0];
mCarMarker = mMap.addMarker(
new MarkerOptions(carLatLng)
.anchor(0.5 f.0.5 f)
.icon(BitmapDescriptorFactory.fromResource(R.mipmap.taxi_t))// Car icon
.flat(true)
.clockwise(false));
// Create a moving animation
mAnimator = new MarkerTranslateAnimator(mCarMarker, 50 * 1000, mCarLatLngArray, true);
// Adjust the best view
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
LatLngBounds.builder().include(Arrays.asList(mCarLatLngArray)).build(), 50));
}
Copy the code
This method is called in the routePlan method, passing in the route string
// Initialize the car animation
drinvingRouteView.initAnimation(lineStringBuilder.substring(0, lineStringBuilder.length()-1));
Copy the code
Complete code Reference
/** ** route planning */
@Override
public void routePlan(a) {
if(fromLatLng! =null&&toLatLng! =null){
Toast.makeText(GlobalApplication.getContext(), "I'm planning your route.", Toast.LENGTH_SHORT).show();
DrivingParam drivingParam = new DrivingParam(fromLatLng, toLatLng);
drivingParam.policy(DrivingParam.Policy.TRIP);// Driving route planning strategy, ride-hailing scenarios, passenger drop-off
drivingParam.setCarNumber("Guangdong A00001");// Fill in the license plate number to avoid the license plate restricted area during route planning
tencentSearch.getRoutePlan(drivingParam, new HttpResponseListener<DrivingResultObject>() {
@Override
public void onSuccess(int i, DrivingResultObject drivingResultObject) {
for (DrivingResultObject.Route route : drivingResultObject.result.routes){
for (LatLng latLng : route.polyline){
lineStringBuilder.append(latLng.latitude + "," + latLng.longitude);
lineStringBuilder.append(",");
}
distance += route.distance;
taxiFare += route.taxi_fare.fare;
}
// Initialize the car animation
drinvingRouteView.initAnimation(lineStringBuilder.substring(0, lineStringBuilder.length()-1));
drinvingRouteView.setOrderDescTV("Approximately" + distance + "M, is expected to selections" + taxiFare + "Yuan");
// Clear itinerary, mileage, and cost information
lineStringBuilder = new StringBuffer();
distance = 0f;
taxiFare = 0d;
}
@Override
public void onFailure(int i, String s, Throwable throwable) {
Log.d("DrivingRouteActivity"."onSuccess: "+ s + i); }}); fromLatLng=null;
toLatLng=null; }}Copy the code
Finally we call the startAnimation method of MarkerTranslateAnimator on the ok button click event to start the animation
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.order_btn:
drivingRoutePresent.geocoder(fromET.getText().toString(), DrivingRoutePresent.FROM_TYPE);
drivingRoutePresent.geocoder(toET.getText().toString(), DrivingRoutePresent.TO_TYPE);
confirmBtn.setVisibility(View.VISIBLE);
orderBtn.setVisibility(View.GONE);
break;
case R.id.confirm_btn:
// Enable animation movement
mAnimator.startAnimation();
orderBtn.setVisibility(View.VISIBLE);
confirmBtn.setVisibility(View.GONE);
break;
case R.id.location_ib:
// Locate the current location
break; }}Copy the code
6. Introduce Tencent Android positioning SDK
The basic effect is complete, now there is one last location function, to implement the location function will need to introduce another SDK (Android location SDK)
Let’s open the Android location SDK development documentation and download the latest SDKPut the JAR package in the compressed package into the LIBS package of the APP and add it as a dependencyThen copy each SO file under the liBS folder of the compressed package to the corresponding directory of project jniLibsOpen the androidmanifest.xml file and add the following permissions
<! -- Get accurate position by GPS -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<! -- Get rough location through network -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<! -- Access the network. Some location information needs to be retrieved from the web server -->
<uses-permission android:name="android.permission.INTERNET" />
<! -- Access WiFi status. Need WiFi information for network location -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<! -- Change the WiFi status. To initiate WiFi scan, WiFi information is required for network location -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<! Access network status and check network availability. Network operator information is required for network location -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<! Network access changes, need some information for network location -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<! Device ID is required for network location -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<! -- Support A-GPS assisted positioning -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<! -- Log -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Copy the code
7. The current location is displayed
After configuration is complete, we in DrivingRouteContract. The Presenter interface to add a start location startLocation and an end to locate stopLocation method
void startLocation(boolean single);
void stopLocation(a);
Copy the code
Let’s implement the drivingroup present method again
private boolean IS_SINGLE_LOCATION_MODE = false;// Whether to continuously locate
private TencentLocationManager mLocationManager = TencentLocationManager.getInstance(GlobalApplication.getContext());
private TencentLocationRequest locationRequest;
@Override
public void startLocation(boolean single) {
IS_SINGLE_LOCATION_MODE = single;// Because we only need to locate once, we add a parameter
locationRequest = TencentLocationRequest.create();
locationRequest.setInterval(5000);// Locate the interval
REQUEST_LEVEL_ADMIN_AREA: specifies the latitude, longitude, and administrative division of the location in mainland China based on the level of detail obtained by the user
locationRequest.setRequestLevel(TencentLocationRequest.REQUEST_LEVEL_ADMIN_AREA);
locationRequest.setAllowGPS(true);// Whether GPS positioning is allowed
mLocationManager.requestLocationUpdates(locationRequest, this);// continuous positioning
}
@Override
public void stopLocation(a) {
mLocationManager.removeUpdates(this);
}
Copy the code
In addition, to get the location information for the location, we also need to have DrivingRoutePresent implement the TencentLocationListener interface in addition, Implement the onLocationChanged (for receiving location results) and onStatusUpdate (for receiving GPS,WiFi,Cell status codes) methods.
@Override
public void onLocationChanged(TencentLocation tencentLocation, int i, String s) {
if (IS_SINGLE_LOCATION_MODE)
stopLocation();
switch (i){
case TencentLocation.ERROR_OK:
// The location is successful
drinvingRouteView.setLocation(tencentLocation);
// Render location information
if(drinvingRouteView.getFromET()! =null&&drinvingRouteView.getFromET().getText().toString().trim().equals(""))
drinvingRouteView.getFromET().setText(tencentLocation.getAddress());
/ / Toast. MakeText (GlobalApplication getContext (), "positioning" success, Toast. LENGTH_SHORT), show ();
break;
case TencentLocation.ERROR_NETWORK:
Toast.makeText(GlobalApplication.getContext(), "Location failure due to network problems.", Toast.LENGTH_SHORT).show();
break;
case TencentLocation.ERROR_BAD_JSON:
Toast.makeText(GlobalApplication.getContext(), "Location failure due to GPS, Wi-Fi or base station error.", Toast.LENGTH_SHORT).show();
break;
case TencentLocation.ERROR_WGS84:
Toast.makeText(GlobalApplication.getContext(), "Location failure while unable to convert WGS84 coordinates to GCJ-02 coordinates.", Toast.LENGTH_SHORT).show();
break;
case TencentLocation.ERROR_UNKNOWN:
Toast.makeText(GlobalApplication.getContext(), "Location failure due to unknown cause.", Toast.LENGTH_SHORT).show();
break; }}@Override
public void onStatusUpdate(String s, int i, String s1) {
/ / TencentLocationListener correction of this method is introduced to GPS, WiFi, Cell status code, specific status code to check the position of the Android SDK documentation development
}
Copy the code
Finally, we add an implementation of the click event that is bound to the location button, and call the startLocation and stopLocation methods on onResume and onPause to automatically position the app when it starts or switches back to the current Activity
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.order_btn:
drivingRoutePresent.geocoder(fromET.getText().toString(), DrivingRoutePresent.FROM_TYPE);
drivingRoutePresent.geocoder(toET.getText().toString(), DrivingRoutePresent.TO_TYPE);
confirmBtn.setVisibility(View.VISIBLE);
orderBtn.setVisibility(View.GONE);
break;
case R.id.confirm_btn:
// Enable animation movement
mAnimator.startAnimation();
orderBtn.setVisibility(View.VISIBLE);
confirmBtn.setVisibility(View.GONE);
break;
case R.id.location_ib:
// Position once
drivingRoutePresent.startLocation(true);
break; }}@Override
protected void onResume(a) {
super.onResume();
mapView.onResume();
drivingRoutePresent.startLocation(true);
}
@Override
protected void onPause(a) {
super.onPause();
mapView.onPause();
drivingRoutePresent.stopLocation();
}
Copy the code
At the end
Write here, all the functions on the renderings are basically completed, in general, the function is still very powerful, for enterprises with relevant needs to develop very time saving effort. In addition, the development documentation and interface documentation are also more detailed. Due to limited time, we have only experienced a few of these services for the time being. Students with more needs can explore the official website by themselves.