Filling in addresses is a task that users of lifestyle apps and mini programs that provide services such as group buying, takeout, package delivery, housekeeping, logistics, and moving services often have to perform. Generally, this requires users to manually fill in their address information, for example, selecting California, Los Angeles, and Hollywood Blvd in sequence using several drop-down list boxes and then manually entering their names and phone numbers. This process usually takes some time and is prone to input error.
Wouldn't it be handy if there was an automatic way for users to fill in addresses quickly and accurately? With HMS Core Location Kit's fused location and geocoding capabilities, a lifestyle app can automatically pinpoint the current location of a user or obtain the street address of a map location, and fill that information in the address box. Thanks to this, users are freed from the hassle of having to manually enter addresses, as well preventing human error. In this article, I will explain how you can easily integrate this feature into your app and provide you with sample code.
Demo
{
"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"
}
Development ProcedurePrepare for the development1. Sign in to AppGallery Connect and click My projects. Find your project, go to Project settings > Manage APIs, and toggle on the Location Kit switch. Then, click the app for which you need to configure the signing certificate fingerprint, and go to Project settings > General information. In the App information area, click Add next to SHA-256 certificate fingerprint, and enter the SHA-256 certificate fingerprint.
2. Go to Project settings > General information. In the App information area, click agconnect-services.json to download the configuration file. Then, copy the configuration file to the app's root directory.
3. Configure the project-level build.gradle file.
Java:
buildscript {
repositories {
google()
jcenter()
maven { url 'https://developer.huawei.com/repo/' }
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
allprojects {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
mavenCentral()
}
}
Configure the app-level build.gradle file.
Java:
plugins {
id 'com.android.application'
id 'com.huawei.agconnect'
}
Add the following build dependency in the dependencies block in the app-level build.gradle file:
Java:
implementation 'com.huawei.hms:location:6.3.0.300'
Check permissions1. Declare the ACCESS_COARSE_LOCATION (approximate location permission), ACCESS_FINE_LOCATION (precise location permission), and ACCESS_BACKGROUND_LOCATION permissions in the AndroidManifest.xml file.
Java:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
2. Dynamically apply for related location permissions (according to requirements for dangerous permissions in Android 6.0 or later).
Java:
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "android sdk < 28 Q");
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
Obtain the location result1. Set location parameters, including the location update interval and location type
Java:
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
mSettingsClient = LocationServices.getSettingsClient(this);
mLocationRequest = new LocationRequest();
// Set the location update interval, in milliseconds.
mLocationRequest.setInterval(5000);
// Set the priority.
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
2. Call the getSettingsClient() method to obtain a SettingsClient instance, and call checkLocationSettings() to check the device location settings.
Java:
LocationSettingsRequest locationSettingsRequest = builder.build();
// Before requesting location updates, call checkLocationSettings to check the device location settings.
Task<LocationSettingsResponse> locationSettingsResponseTask =
mSettingsClient.checkLocationSettings(locationSettingsRequest);
After checking that the device location function is enabled, call requestLocationUpdates() to request location updates.
Java:
locationSettingsResponseTask.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
Log.i(TAG, "check location settings success");
mFusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "requestLocationUpdatesWithCallback onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "requestLocationUpdatesWithCallback onFailure:" + e.getMessage());
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "checkLocationSetting onFailure:" + e.getMessage());
int statusCode = 0;
if (e instanceof ApiException) {
statusCode = ((ApiException) e).getStatusCode();
}
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
// When startResolutionForResult is called, a popup will
// appear, asking the user to grant relevant permissions.
if (e instanceof ResolvableApiException) {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(MainActivity.this, 0);
}
} catch (IntentSender.SendIntentException sie) {
Log.e(TAG, "PendingIntent unable to execute request.");
}
break;
default:
break;
}
}
});
Obtain the address of the current location through reverse geocodingAfter obtaining the longitude and latitude of a location, pass them to the geocoding service (GeocoderService) to obtain a geocoding request object. Then, call the getFromLocation method and set request (GetFromLocationRequest) parameters to obtain the address of the location.
Java:
if (null == mLocationCallback) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
List<Location> locations = locationResult.getLocations();
if (!locations.isEmpty()) {
ExecutorUtil.getInstance().execute(new Runnable() {
@Override
public void run() {
Locale locale = new Locale("zh", "CN");
GeocoderService geocoderService = LocationServices.getGeocoderService(MainActivity.this, locale);
GetFromLocationRequest getFromLocationRequest = new GetFromLocationRequest(locations.get(0).getLatitude(), locations.get(0).getLongitude(), 1);
geocoderService.getFromLocation(getFromLocationRequest)
.addOnSuccessListener(new OnSuccessListener<List<HWLocation>>() {
@Override
public void onSuccess(List<HWLocation> hwLocation) {
printGeocoderResult(hwLocation);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.i(TAG, e.getMessage());
}
});
}
});
}
}
}
@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (locationAvailability != null) {
boolean flag = locationAvailability.isLocationAvailable();
Log.i(TAG, "onLocationAvailability isLocationAvailable:" + flag);
}
}
};
}
Finally, display the obtained address on the screen to complete the implementation.
References>> Location Kit official website
>> Location Kit Development Guide
>> Reddit to join developer discussions
>> GitHub to download the sample code
>> Stack Overflow to solve integration problems
Related
This article is originally from HUAWEI Developer Forum
Forum link: https://forums.developer.huawei.com/forumPortal/en/home
As Flutter grows, we would get lot of plugins . If your project needs a specific dependency that does not exist on flutter you can define it in native code (IOS and android) and create a communication link between your flutter code and native code.
Contents
· Download Flutter SDK
· Set up flutter in Android studio
· Understanding the structure
· Running the demo project
· Understanding UI Components
· Threading concepts in flutter
· Integrating HMS to a flutter app
· Using hot reload
· Flutter inspector
Download Flutter SDK
Visit the following url to download the latest flutter SDK for your operating system.
https://flutter.dev/docs/get-started/install
Open Android studio and go to Plugin market place and search flutter and download the flutter plugin and restart your ID to show changes to your IDE, which enables us to create and manage a Flutter project.
{
"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"
}
Set up flutter in Android studio
To create a new Flutter application select File->New Flutter project and follow the steps mentioned below.
Specify the path of your Flutter SDK that you downloaded in your Flutter SDK path as shown below and click next.
Understanding the structure
Pubspec.yaml : Here you will find the app’s dependencies. This includes configuration files, images and other assets. Enter the package name and the version under the dependencies: section of the pubspec.yaml. Run the command flutter packages get to install these packages, enter the package name and version under the dependencies section.
Main.dart : main.dart is the entry point of any flutter application, this is found under lib folder as shown above, this contains all the dart classes. During creation the main.dart file comes with a simple pre written code. Connect your device to the system or start the emulator and run the project to see the result of the sample project.
Adding images in flutter:
Create an assets folder in the directory that contains the pubspec.yaml file and move all your Images to this folder.
Use pub.get to sync all the files and use the images.
All native flutter dependencies are also added to pubspec.yaml file as shown below
Running Demo project
When we create a new flutter project, we get a simple app with floating action button and a counter, if you change text from the sample project above the counter text and click save it is immediately shown on the screen, this feature is called Hot reload and will be used extensively during flutter development.
We will be creating a simple UI with a click event which helps us to send messages to our native code.
UI Components in Flutter
Scaffold
Container
Material app
Stateful and stateless widget
Scaffold: The foundation for most projects on Android are an App bar, a body which holds our app design and a floating action button as highlighted below.
Create a structure for the design remains simple with a scaffold as most of the commonly used components are defined and supported by Scaffold.
As shown in the program below we have the following properties
· Appbar with a title
· Body with a text at the center of the screen
· Floating action button with onPressed click event
Code:
Widget build(BuildContext context){
Return Scaffold(
appBar:AppBar(
title: const Text(‘Sample Code’),
),
Body: Center(child: Text(‘You have pressed the button $_count times.’)),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _count++),
tooltip: ‘Increment Counter’,
child: const Icon(Icons.add),
),
);
}}
Container: allows us to add background padding and design to different elements available on flutter. The diagram shows an example on using Container where we have a child text and color.
MaterialApp: MaterialApp is required when we use Material components and follow material design in your app
Stateful widget: Stateful widgets are mutable and are used when the UI can change dynamically, flutter creates a state object during build process of a stateful widget, this is the place where mutable state for that widget is saved.
Stateless widget: A stateless widget has no internal state to manage. Icon, IconButton, and Text are examples of stateless widgets, which are a subclass of StatelessWidget and are expected to remain static.
Threading concepts in Flutter
Async: function is asynchronous and you might need to wait a bit to get its result.
Await: wait until this function is finished and you will get its return value.
Future: is a type that ‘comes from the future’ and returns value from your asynchronous function. It can complete with success (.then) or with an error(.catchError).
Then ((value){…}): is a callback that’s called when future completes successfully(with a value).
Difference between Const and final in Flutter
Const: it is initialized during the compilation. Whenever you compile the program the value of PI will be initialized and memory will be allocated. It does not matter if you are using this variable or not.
Final: If you use the final variable in your program only then the value is initialized and memory location is allocated. And if you never use the final variable the value will never be initialized in your program.
Let us use the design and threading concepts to create project which updates location on a click event.
Integrating HMS to a Flutter app
Add a channel name in MyHomePage stateful widget, which will be added to our native code for message passing.
Code:
static const Locationplatform = const MethodChannel("samples.flutter.dev/Location");
We will be adding an Icon button and adding a click event to create a class which extends stateful widget as shown
Code:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
static const Locationplatform = const MethodChannel("samples.flutter.dev/Location");
@override
Widget build(BuildContext context) {
}
}
We have a stateful widget which will hold our click event to pass the message to native code using method channel. The program for the design of the button and click event is defined in the overridden method build in MyHomePageState class.
We have a text element which will be placed below the icon and all of this would be at the center of the screen. We define a Scaffold with app bar and body which would eb the important material design components of our app.
We define tooltip and icon button and center align them and create a text at the top of the screen.
Here is the complete main.dart file
Code:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
static const Locationplatform = const MethodChannel("samples.flutter.dev/Location");
@override
Widget build(BuildContext context) {
}
}
Using hot reload
One of the best feature of flutter is hot reload, make changes to your UI and click save while your device is connected to pc and click Ctrl + S on windows and see changes instantly on your screen.
Hot reload helps you see code and user interface changes instantly on your virtual device or mobile device. This is an extremely useful feature for the developer as it saves development time.
To see the changes instantly on your device change the title of your material app as shown above and click ctrl+S on windows and cmd+S on Mac.
It is important to know that any editing that will be performed in gradle or your JAVA/Kotlin files have to be opened for editing in a new window as editing it in the same files could show errors, which cannot be corrected.
After selecting open for editing in android studio as show below, we can follow the mentioned steps to add HMS location kit dependency, the process remains the same as adding any other kit to your project.
Please follow these steps
Creating an AGC Application.
Creating an Android Studio Project.
Generating a signature certificate.
Generating a signature certificate fingerprint.
Configuring the signature certificate fingerprint.
Adding the application package name and save the configuration file.
Configure the Maven address and AGC gradle plug-in.
Configure the signature file in Android Studio.
For more information please check the following link:
https://developer.huawei.com/consumer/en/codelab/HMSLocationKit/index.html#0
Define the Method channel in MainActivity file as shown below and this name should match the channel name in your main.dart file
Code:
private static final String ShowLocationchannel = "samples.flutter.dev/Location";
Extend your MainActivity class with FlutterActivity and override configureFlutterEngine to handle click event.
Code:
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), ShowLocationchannel)
.setMethodCallHandler(
(call, result) -> {
if(call.method.equals("ShowLocation"))
{
ShowLocation();
result.success(0);
}
}
);
}
Create a method ShowLocation() and create an Intent to move to LocationActivity class
Code:
private int ShowLocation() {
startActivity(new Intent(MainActivity.this, LocationActivity.class));
return 0;
} Create LocationActivity class that uses FusedLocationProviderClient object to requestLocationUpdates and settingsClient object to call checkLocationSettings method to check device settings.
public class LocationActivity extends AppCompatActivity implements View.OnClickListener {
public static final String TAG = "LocationUpdatesCallback";
// the callback of the request
LocationCallback mLocationCallback;
LocationRequest mLocationRequest;
private FusedLocationProviderClient fusedLocationProviderClient;
private SettingsClient settingsClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_location);
findViewById(R.id.location_requestLocationUpdatesWithCallback).setOnClickListener(this);
findViewById(R.id.location_removeLocationUpdatesWithCallback).setOnClickListener(this);
// create fusedLocationProviderClient
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
// create settingsClient
settingsClient = LocationServices.getSettingsClient(this);
mLocationRequest = new LocationRequest();
// Set the interval for location updates, in milliseconds.
mLocationRequest.setInterval(10000);
// set the priority of the request
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (null == mLocationCallback) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
List<Location> locations = locationResult.getLocations();
if (!locations.isEmpty()) {
for (Location location : locations) {
Toast.makeText(LocationActivity.this,
"onLocationResult location[Longitude,Latitude,Accuracy]:" + location.getLongitude()
+ "," + location.getLatitude() + "," + location.getAccuracy(),Toast.LENGTH_LONG);
}
}
}
}
@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (locationAvailability != null) {
boolean flag = locationAvailability.isLocationAvailable();
Log.i(TAG, "onLocationAvailability isLocationAvailable:" + flag);
}
}
};
}
// check location permisiion
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "sdk < 28 Q");
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
}
private void requestLocationUpdatesWithCallback() {
try {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// check devices settings before request location updates.
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
Log.i(TAG, "check location settings success");
// request location updates
fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "requestLocationUpdatesWithCallback onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG,
"requestLocationUpdatesWithCallback onFailure:" + e.getMessage());
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "checkLocationSetting onFailure:" + e.getMessage());
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(LocationActivity.this, 0);
} catch (IntentSender.SendIntentException sie) {
Log.e(TAG, "PendingIntent unable to execute request.");
}
break;
}
}
});
} catch (Exception e) {
Log.e(TAG, "requestLocationUpdatesWithCallback exception:" + e.getMessage());
}
}
@Override
protected void onDestroy() {
// don't need to receive callback
removeLocationUpdatesWithCallback();
super.onDestroy();
}
/**
* remove the request with callback
*/
private void removeLocationUpdatesWithCallback() {
try {
fusedLocationProviderClient.removeLocationUpdates(mLocationCallback)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "removeLocationUpdatesWithCallback onSuccess");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
Log.e(TAG, "removeLocationUpdatesWithCallback onFailure:" + e.getMessage());
}
});
} catch (Exception e) {
Log.e(TAG, "removeLocationUpdatesWithCallback exception:" + e.getMessage());
}
}
@Override
public void onClick(View v) {
try {
switch (v.getId()) {
case R.id.location_requestLocationUpdatesWithCallback:
requestLocationUpdatesWithCallback();
break;
case R.id.location_removeLocationUpdatesWithCallback:
removeLocationUpdatesWithCallback();
break;
default:
break;
}
} catch (Exception e) {
Log.e(TAG, "RequestLocationUpdatesWithCallbackActivity Exception:" + e);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed");
}
}
if (requestCode == 2) {
if (grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful");
} else {
Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed");
}
}
}
}
Code:
<span style="font-family: arial , helvetica , sans-serif;font-size: 18.0px;"><strong>Please add the following permissions to your manifest file to request Location updates</strong></span><strong>
</strong>
Code:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
Flutter inspector
We can use the Flutter inspector to find the nesting of all your widgets making it very easy to edit or find the widget you are looking for, improving design or changes to your UI is extremely simple in flutter using Enable select widget module.
Click select widget module to use he feature as shown below, after you click the select widget module, click on any widget on your screen and you will see changes to your flutter inspector widget tab. The widget selected on your device is highlighted and changes can be made this makes it simpler to find the widget if you a lot of widgets in your widget tree.
Click on the find Icon as shown in the figure to repeat the process again to find a widget.
Conclusion
An existing HMS project can be linked to a flutter screen using a method channel.
This completes the flutter tutorial, please comment like and share.
Stay tuned for more updates
More articles like this, you can visit HUAWEI Developer Forum.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Introduction
Now days, most of the application integrate an Maps. This article I will give you the process on how to do. Millions of users look up directions, plan their commutes, catch a ride.as well as touch on the many things available through maps to enhance the user experience in mobile apps.
Let’s Start how to Integrate Map:
Step1: create a new project in Android studio.
Step 2: Configure your app into AGC.
Step 3: Enable required Api & add SHA-256.
Step 4: Download the agconnect-services.json from AGC. Paste into app directory.
Step 5: Add the below dependency in app.gradle file.
Code:
implementation 'com.huawei.hms:maps:4.0.0.301'
Step 6: Add the below dependency in root.gradle file
Code:
maven { url 'http://developer.huawei.com/repo/' }
Step 7: Add appId & permissions in AndoridManifest.xml file
Code:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="appid=*******" />
Step 8: Sync your Project
Let’s Discuss functionality:
1.OnMapReady()
2.OnMapClick()
3.OnMarkerClick()
4.Create Circle
5.Create Custom Marker
1. OnMapReady: This Callback interface for when map is ready to be used
Code:
@Override
public void onMapReady(HuaweiMap map) {
mHuaweiMap = map;
enableUiSettings();
mHuaweiMap.setMaxZoomPreference(15);
mHuaweiMap.setMinZoomPreference(2);
}
2. OnMapClick : This callback interface when the user makes tap on the map.
Code:
@Override
public void onMapClick(LatLng latLng) {
try {
createMarker(latLng);
} catch (IOException e) {
e.printStackTrace();
}
}
3. OnMarkerClick : This callback interface when a marker is clicked
Code:
@Override
public boolean onMarkerClick(Marker marker) {
marker.showInfoWindow();
return true;
}
4. How to create circle on map:
Code:
private void addCircleToCurrentLocation() {
mHuaweiMap.addCircle(new CircleOptions()
.center(new LatLng(12.9716, 77.5946))
.radius(1000)
.strokeWidth(10)
.strokeColor(Color.GREEN)
.fillColor(Color.argb(128, 255, 0, 0))
.clickable(true));
}
5. How to create marker:
Code:
private void createMarker(LatLng latLng) throws IOException {
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.snippet("Address : " + featchAddress(latLng))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location));
mHuaweiMap.addMarker(markerOptions);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng) // Sets the center of the map to location user
.zoom(20) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(40) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
mHuaweiMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
mHuaweiMap.setOnMarkerClickListener(this);
}
In this article I covered few basics callbacks. below is the final code
Code:
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback, HuaweiMap.OnMapClickListener, HuaweiMap.OnMarkerClickListener {
private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";
private static final int REQUEST_CODE = 100;
private static final LatLng LAT_LNG = new LatLng(12.9716, 77.5946);
private HuaweiMap mHuaweiMap;
private MapView mMapView;
private Button btnCustom;
private static final String[] RUNTIME_PERMISSIONS = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET};
private Marker marker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnCustom = findViewById(R.id.btn_custom);
if (!hasPermissions(this, RUNTIME_PERMISSIONS)) {
ActivityCompat.requestPermissions(this, RUNTIME_PERMISSIONS, REQUEST_CODE);
}
mMapView = findViewById(R.id.mapView);
Bundle mapViewBundle = null;
if (savedInstanceState != null) {
mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
}
init(mapViewBundle);
btnCustom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mHuaweiMap.setOnMapClickListener(MainActivity.this);
}
});
}
private void init(Bundle mapViewBundle) {
mMapView.onCreate(mapViewBundle);
mMapView.getMapAsync(this);
}
@Override
public void onMapReady(HuaweiMap map) {
mHuaweiMap = map;
enableUiSettings();
mHuaweiMap.setMaxZoomPreference(15);
mHuaweiMap.setMinZoomPreference(2);
addCircleToCurrentLocation();
}
/*
Enable Ui Settings
*/
private void enableUiSettings() {
mHuaweiMap.setMyLocationEnabled(true);
mHuaweiMap.getUiSettings().setMyLocationButtonEnabled(true);
mHuaweiMap.getUiSettings().setCompassEnabled(true);
mHuaweiMap.getUiSettings().setZoomControlsEnabled(true);
mHuaweiMap.getUiSettings().setMyLocationButtonEnabled(true);
}
/*
Create Circle to current location
*/
private void addCircleToCurrentLocation() {
mHuaweiMap.addCircle(new CircleOptions()
.center(new LatLng(12.9716, 77.5946))
.radius(1000)
.strokeWidth(10)
.strokeColor(Color.GREEN)
.fillColor(Color.argb(128, 255, 0, 0))
.clickable(true));
}
/*
Create Marker when you click on map
*/
private void createMarker(LatLng latLng) throws IOException {
MarkerOptions markerOptions = new MarkerOptions()
.position(latLng)
.snippet("Address : " + featchAddress(latLng))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_location));
mHuaweiMap.addMarker(markerOptions);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng) // Sets the center of the map to location user
.zoom(20) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(40) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
mHuaweiMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
mHuaweiMap.setOnMarkerClickListener(this);
}
/*
Convert from latlong to Address
*/
private String featchAddress(LatLng latLng) throws IOException {
Geocoder geocoder = new Geocoder(this, Locale.ENGLISH);
List<Address> addresses = geocoder.getFromLocation(latLng.latitude, latLng.latitude, 1);
Toast.makeText(this, addresses.get(0).getLocality() + ", "
+ addresses.get(0).getAdminArea() + ", "
+ addresses.get(0).getCountryName(), Toast.LENGTH_SHORT).show();
return addresses.get(0).getLocality() + ", "
+ addresses.get(0).getAdminArea() + ", "
+ addresses.get(0).getCountryName();
}
private static boolean hasPermissions(Context context, String... permissions) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
@Override
public void onMapClick(LatLng latLng) {
try {
createMarker(latLng);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean onMarkerClick(Marker marker) {
marker.showInfoWindow();
return true;
}
Output:
Conclusion:
In this article you’ve learned how to create custom markers, how callbacks will work, as well as new ways for users to interact with the map.
https://developer.huawei.com/consumer/en/doc/development/HMS-References/hms-map-cameraupdate
Reference:
Introduction
Online food ordering is process to deliver food from restaurants. In this article will do how to draw polyline, Integrate Location kit in food applications.
In this article, will guide you to selected hotel locations on Huawei map and shows direction from current location to destination.
Steps
1. Create App in Android.
2. Configure App in AGC.
3. Integrate the SDK in our new Android project.
4. Integrate the dependencies.
5. Sync project.
Location Module
Huawei location kit combines the GNSS, Wi-Fi, base station location helps you to obtain users location quickly. It will support different types of functionalities like Fused location, activity identification, geo-fence.
Function Restrictions
{
"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"
}
Use Case
1. Fused location: Quickly obtain the user location.
2. Activity Identification: Identifies user status using sensor, cellular network and magnetometer helps to adjust your app based on user behavior.
3. Geofence: This helps you to set boundary for specific area.
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:location:4.0.3.301'
implementation 'com.squareup.okhttp3:okhttp:3.14.2'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.google.code.gson:gson:2.8.6'
Root level gradle dependencies
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
Add the below permissions in Android Manifest file
Code:
<manifest xlmns:android>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
</manifest>
1.Create xml layout class define below snippet
Code:
<com.huawei.hms.maps.MapView
android:layout_marginTop="?actionBarSize"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraZoom="8.5"
map:mapType="normal"
map:uiCompass="true"1)
map:uiZoomControls="true"/>
2. Create Instance for FuesdLocationProviderClient and SettingsClient.
3.Check location permissions dynamically.
Code:
public void RequestPermission() {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions((Activity) this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions((Activity) this, strings, 2);
}
}
}
4. Checking the location settings, if the location permission disabled then we Cannot obtain the location details.
Code:
try {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(locationSettingsResponse -> fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(v -> Toast.makeText(getApplicationContext(), "APi Fail1", Toast.LENGTH_SHORT).show())
.addOnFailureListener(e -> {
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(LocationActivity.this, 0);
} catch (IntentSender.SendIntentException sie) {
}
break;
}
}));
} catch (Exception e) {
}
5. Create latlngModel object.
Code:
private latlngModel getLatLangModel() {
latlngModel latlngModel = new latlngModel();
latlngModel.Origin origin = new latlngModel.Origin();
origin.setLat(latlngcurrent.latitude);
origin.setLng(latlngcurrent.longitude);
latlngModel.Destination des = new latlngModel.Destination();
des.setLat(deslatLng.latitude);
des.setLng(deslatLng.longitude);
latlngModel.setDestination(des);
latlngModel.setOrigin(origin);
return latlngModel;
}
6. LocationRequest() LocationCallback class to obtain the LocationResult object.
Code:
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (null == mLocationCallback) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult != null) {
List<Location> locations = locationResult.getLocations();
if (!locations.isEmpty()) {
for (Location location : locations) {
Toast.makeText(getApplicationContext(), String.valueOf(location.getTime()), Toast.LENGTH_SHORT).show();
latlngcurrent = new LatLng(location.getLatitude(), location.getLongitude());
}
}
}
}
@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (locationAvailability != null) {
Toast.makeText(getApplicationContext(), "APi Fail", Toast.LENGTH_SHORT).show();
}
}
};
}
Map Kit Draw Polyline
Polyline to draw the directions from origin to destination, this will help to show directions in three ways such as Driving, Bicycle and walking.
1. Create Retrofit interface class.
Code:
public interface Api {
@POST("driving?key=" + Constants.API_KEY)
Call<DirectionResponse> getDriveDistanceDuration(@Body latlngModel bodys);
@POST("walking?key=" + Constants.API_KEY)
Call<DirectionResponse> getWalkDistanceDuration(@Body latlngModel bodys);
@POST("bicycling?key=" + Constants.API_KEY)
Call<DirectionResponse> getBicycleDistanceDuration(@Body latlngModel bodys);
}
2. Create Retrofit Client class.
Code:
private static Retrofit retrofit;
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
3. Call Direction Api this will return Direction Response.
Code:
private void callDirectionApi(Call<DirectionResponse> responseCall) {
responseCall.enqueue(new Callback<DirectionResponse>() {
@Override
public void onResponse(Call<DirectionResponse> call, Response<DirectionResponse> response) {
if (response.isSuccessful()) {
showDirection(response.body());
}
}
@Override
public void onFailure(Call<DirectionResponse> call, Throwable t) {
Toast.makeText(getApplicationContext(), "Api Failure", Toast.LENGTH_SHORT).show();
}
});
}
4. Using Direction Response draw polyline.
Code:
private void showDirection(DirectionResponse response) {
ArrayList<LatLng> pathList = new ArrayList<>();
if (response.getRoutes() != null) {
if (response.getRoutes().get(0).getPaths() != null) {
for (int i = 0; i < response.getRoutes().get(0).getPaths().size(); i++) {
if (response.getRoutes().get(0).getPaths().get(i).getSteps() != null) {
for (int j = 0; j < response.getRoutes().get(0).getPaths().get(i).getSteps().size(); j++) {
if (response.getRoutes().get(0).getPaths().get(i).getSteps().get(j).polyline != null) {
for (int k = 0; k < response.getRoutes().get(0).getPaths().get(i).getSteps().get(j).polyline.size(); k++) {
pathList.add(new LatLng(response.getRoutes().get(0).getPaths().get(i).getSteps().get(j).polyline.get(k).getLat(),
response.getRoutes().get(0).getPaths().get(i).getSteps().get(j).polyline.get(k).getLng()));
}
}
}
}
}
}
}
if (response.getRoutes() != null) {
curentPolyline = hMap.addPolyline(new PolylineOptions()
.addAll(pathList)
.width(4)
.color(getResources().getColor(R.color.colorAccent)));
}
}
Result:
Conclusion
In this Article, I have explained how to integrate Location Kit on food application and draw route between origin to destination.
Reference:
Map kit: https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides/android-sdk-introduction-0000001050158633
Location kit: https://developer.huawei.com/consumer/en/doc/development/HMSCore-Guides-V5/introduction-0000001050706106-V5
Nice article. Can we enable mock location using location kit ?
What about links to each restaurant, to place the orders and schedule pick-ups ?
Real-time locating is a core function for many apps, allowing them to quickly and accurately locate users' real time locations.
HUAWEI Location Kit enables apps to quickly obtain precise user locations and build up global locating capabilities, helping you implement personalized map display and interaction, as well as improve overall location-based service experience.
This article demonstrates how to use HUAWEI Location Kit and Map Kit to implement the real-time locating capability in an app.
ExpectationsAn app can obtain and display a user's real-time location on the map, especially when the app is launched for the first time. The map display changes in accordance to the user's actual location.
{
"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"
}
Involved CapabilitiesLocation Kit: basic locating
Map Kit: map display
Implementation PrincipleAn app uses Location Kit to obtain a user's real-time location and uses Map Kit to display the My Location button on the in-app map that the user can tap to determine their real-time location.
PreparationsRegister as a developer and create a project in AppGallery Connect.
Click here to register as a developer.
Create an app, add the SHA-256 signing certificate fingerprint, enable Map Kit and Site Kit, and download the agconnect-services.json file of the app. For detailed instructions, please visit the official website of HUAWEI Developers.
Configure the Android Studio project.
Copy the agconnect-services.json file to the app directory of the project.
Go to allprojects > repositories and configure the Maven repository address for the HMS Core SDK.
Go to buildscript > repositories and configure the Maven repository address for the HMS Core SDK.
If the agconnect-services.json file has been added to the app, go to buildscript > dependencies and add the AppGallery Connect plugin configuration.
Code:
buildscript {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.huawei.agconnect:agcp:1.3.1.300'
}
}
allprojects {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
google()
jcenter()
}
}
Add build dependencies in the dependencies block.
Code:
dependencies {
implementation 'com.huawei.hms:maps:{version}'
implementation 'com.huawei.hms:location:{version}'
}
Add the following configuration to the file header.
Code:
apply plugin: 'com.huawei.agconnect'
Copy the signing certificate generated in Generating a Signing Certificate to the app directory of your project, and configure the signing certificate in android in the build.gradle file.
Code:
signingConfigs {
release {
// Signing certificate.
storeFile file("**.**")
// KeyStore password.
storePassword "******"
// Key alias.
keyAlias "******"
// Key password.
keyPassword "******"
v2SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
debuggable true
}
debug {
debuggable true
}
}
Key Code Implementation(1) Compile a service to obtain a user's real-time location.
Java:
public class LocationService extends Service {
private final String TAG = this.getClass().getSimpleName();
List<ILocationChangedLister> locationChangedList = new ArrayList<>();
// Location
private FusedLocationProviderClient fusedLocationProviderClient;
private LocationRequest mLocationRequest;
private final LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
locationResult.getLocations();
Log.d(TAG, "onLocationResult: " + locationResult);
Location location = locationResult.getLocations().get(0);
Log.w(TAG, "onLocationResult:Latitude " + location.getLatitude());
Log.w(TAG, "onLocationResult:Longitude " + location.getLongitude());
for (ILocationChangedLister locationChanged : locationChangedList) {
locationChanged.locationChanged(new LatLng(location.getLatitude(), location.getLongitude()));
}
}
@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
super.onLocationAvailability(locationAvailability);
Log.d(TAG, "onLocationAvailability: " + locationAvailability.toString());
}
};
private final MyBinder binder = new MyBinder();
private final Random generator = new Random();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
Log.i("DemoLog", "TestService -> onCreate, Thread: " + Thread.currentThread().getName());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("DemoLog",
"TestService -> onStartCommand, startId: " + startId + ", Thread: " + Thread.currentThread().getName());
return START_NOT_STICKY;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("DemoLog", "TestService -> onUnbind, from:" + intent.getStringExtra("from"));
return false;
}
@Override
public void onDestroy() {
Log.i("DemoLog", "TestService -> onDestroy, Thread: " + Thread.currentThread().getName());
super.onDestroy();
}
public int getRandomNumber() {
return generator.nextInt();
}
public void addLocationChangedlister(ILocationChangedLister iLocationChangedLister) {
locationChangedList.add(iLocationChangedLister);
}
public void getMyLoction() {
Log.d(TAG, "getMyLoction: ");
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
mLocationRequest = new LocationRequest();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Location setting
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(locationSettingsResponse -> fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(aVoid -> Log.d(TAG, "onSuccess: " + aVoid)))
.addOnFailureListener(Throwable::printStackTrace);
}
public class MyBinder extends Binder {
public LocationService getService() {
return LocationService.this;
}
}
public interface ILocationChangedLister {
/**
* Update the location information
*
* @param latLng The new location information
*/
public void locationChanged(LatLng latLng);
}
}
(2) Add a map in the activity to monitor a user's real-time location.
Add a map using the XML layout file:
XML:
<com.huawei.hms.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Add a map in the activity:
XML:
mapView.onCreate(null);
mapView.getMapAsync(this);
Tap My Location button to display the current location on the map:
XML:
@Override
public void onMapReady(HuaweiMap huaweiMap) {
hMap = huaweiMap;
hMap.setMyLocationEnabled(true);
}
Bind Location Kit to listen to location changing events:
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBound = true;
if (binder instanceof LocationService.MyBinder) {
LocationService.MyBinder myBinder = (LocationService.MyBinder) binder;
locationService = myBinder.getService();
Log.i(TAG, "ActivityA onServiceConnected");
locationService.addLocationChangedlister(iLocationChangedLister);
locationService.getMyLoction();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
locationService = null;
Log.i(TAG, "ActivityA onServiceDisconnected");
}
};
Bind the activity to LocationService:
private void bindLocationService() {
Intent intent = new Intent(mActivity, LocationService.class);
intent.putExtra("from", "ActivityA");
Log.i(TAG, "-------------------------------------------------------------");
Log.i(TAG, "bindService to ActivityA");
mActivity.bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
Process the location changing events in the location changing listener:
LocationService.ILocationChangedLister iLocationChangedLister = new LocationService.ILocationChangedLister() {
@Override
public void locationChanged(LatLng latLng) {
Log.d(TAG, "locationChanged: " + latLng.latitude);
Log.d(TAG, "locationChanged: " + latLng.longitude);
updateLocation(latLng);
}
};
Update map view:
private void updateLocation(LatLng latLng) {
mLatLng = latLng;
hMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 1));
}
Testing the AppYou can use a mock location app to change your current location and see how the map view and My Location button alter accordingly.
To learn more, please visit:
HUAWEI Developers official website
Development Guide
Reddit to join developer discussions
GitHub or Gitee to download the demo and sample code
Stack Overflow to solve integration problems
Follow our official account for the latest HMS Core-related news and updates.
Original Source
Check this out: Are you bending like this at your desk?
{
"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"
}
Well, I am, and if you're like me, maybe we should get up and move around for a little while.
Joking aside, I know COVID-19 forced many of you to work from home. As a result, many of us have started to live a sedentary lifestyle. After reading a bunch of posts shared by my family describing how harmful sitting too long is, I decided to change this habit by developing a function that reminds me to move around, like this:
Development OverviewTo develop such a function, I turned to the mobile context-awareness capabilities from HMS Core Awareness Kit. I used a time awareness capability and behavior awareness capability to create a time barrier and behavior detection barrier respectively, as well as a combination of the barriers.
More specifically, these included:
Time awareness capability: TimeBarrier.duringTimePeriod(long startTimeStamp, long endSecondsMillis); is used to define a time barrier. If the current time is within the range from startTimeStamp to endSecondsMillis, the barrier status is true. Otherwise, it is false.
Behavior awareness capability: BehaviorBarrier.keeping(BehaviorBarrier.BEHAVIOR_STILL); is used to define a behavior detection barrier. If the status of a user is still, the barrier status is true; if the status of a user changes — from being stationary to moving, for example — then the barrier will be triggered, and its status will be false.
Barrier combination: Use and to combine the above two barriers into AwarenessBarrier.and(keepStillBarrier, timePeriodBarrier). When the current time of a user is within the specified time segment, and their status is still, the barrier status will be true. Otherwise, it is false.
It's quite straightforward, right? Let's take a deeper look into how the function is developed.
Development ProcedureMaking Preparations1. Create an Android Studio project. Put agconnect-services.json and the app signing certificate to the app's root directory. If you need to know where to obtain the two files, you can check the References section to get more information.
2. Configure a Maven repository address and import a plugin.
Code:
buildscript {
repositories {
maven { url 'http://szxy1.artifactory.cd-cloud-artifact.tools.huawei.com/artifactory/sz-maven-public/' }
maven { url 'http://dgg.maven.repo.cmc.tools.huawei.com/artifactory/Product-CloudTest-snapshot/' }
maven { url 'http://dgg.maven.repo.cmc.tools.huawei.com/artifactory/Product-cloudserviceSDK-release/' }
maven { url 'http://artifactory.cde.huawei.com/artifactory/Product-Binary-Release/' }
maven { url 'http://language.cloudartifact.dgg.dragon.tools.huawei.com/artifactory/product_maven/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.3'
classpath 'com.huawei.agconnect:agcp:1.0.0.300'
}
}
allprojects {
repositories {
maven { url 'http://szxy1.artifactory.cd-cloud-artifact.tools.huawei.com/artifactory/sz-maven-public/' }
maven { url 'http://dgg.maven.repo.cmc.tools.huawei.com/artifactory/Product-CloudTest-snapshot/' }
maven { url 'http://dgg.maven.repo.cmc.tools.huawei.com/artifactory/Product-cloudserviceSDK-release/' }
maven { url 'http://artifactory.cde.huawei.com/artifactory/Product-Binary-Release/' }
maven { url 'http://language.cloudartifact.dgg.dragon.tools.huawei.com/artifactory/product_maven/' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
3. Open the app-level build.gradle file, add the plugin, configure the signing certificate parameters, and add necessary building dependencies.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
android {
compileSdkVersion 31
buildToolsVersion "31.0.0"
defaultConfig {
applicationId "com.huawei.smartlifeassistant"
minSdkVersion 26
targetSdkVersion 31
versionCode 2
versionName "2.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
storeFile file('Awareness.jks')
keyAlias 'testKey'
keyPassword 'lhw123456'
storePassword 'lhw123456'
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.release
debuggable true
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.huawei.agconnect:agconnect-core:1.5.2.300'
implementation 'com.huawei.hms:awareness:3.1.0.301'
}
4. Make sure that the app package names in agconnect-services.json and the project are the same. Then, compile the project.
Requesting Dynamic Permissions
Code:
private static final int PERMISSION_REQUEST_CODE = 940;
private final String[] mPermissionsOnHigherVersion = new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION,
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.BLUETOOTH_CONNECT};
private final String[] mPermissionsOnLowerVersion = new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
"com.huawei.hms.permission.ACTIVITY_RECOGNITION"};
private void checkAndRequestPermissions() {
List<String> permissionsDoNotGrant = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
for (String permission : mPermissionsOnHigherVersion) {
if (ActivityCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED) {
permissionsDoNotGrant.add(permission);
}
}
} else {
for (String permission : mPermissionsOnLowerVersion) {
if (ActivityCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED) {
permissionsDoNotGrant.add(permission);
}
}
}
if (permissionsDoNotGrant.size() > 0) {
ActivityCompat.requestPermissions(this,
permissionsDoNotGrant.toArray(new String[0]), PERMISSION_REQUEST_CODE);
}
}
Check whether the dynamic permissions are granted in onCreate of the activity.
Code:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sedentary_reminder);
setTitle(getString(R.string.life_assistant));
// Check whether the dynamic permissions are granted.
checkAndRequestPermissions();
//...
}
private void checkAndRequestPermissions() {
List<String> permissionsDoNotGrant = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
for (String permission : mPermissionsOnHigherVersion) {
if (ActivityCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED) {
permissionsDoNotGrant.add(permission);
}
}
} else {
for (String permission : mPermissionsOnLowerVersion) {
if (ActivityCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED) {
permissionsDoNotGrant.add(permission);
}
}
}
if (permissionsDoNotGrant.size() > 0) {
ActivityCompat.requestPermissions(this,
permissionsDoNotGrant.toArray(new String[0]), PERMISSION_REQUEST_CODE);
}
}
Using the Broadcast Message to Create PendingIntent Which Is Triggered When the Barrier Status Changes, and Registering a Broadcast Receiver
Code:
final String barrierReceiverAction = getApplication().getPackageName() + "COMBINED_BARRIER_RECEIVER_ACTION";
Intent intent = new Intent(barrierReceiverAction);
// Also, we can use getActivity() or getService() to create PendingIntent.
// This depends on what action you want to be triggered when the barrier status changes.
mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
| PendingIntent.FLAG_MUTABLE);
// Register a broadcast receiver to receive the broadcast when the barrier status changes.
mBarrierReceiver = new CombinedBarrierReceiver();
registerReceiver(mBarrierReceiver, new IntentFilter(barrierReceiverAction));
final class CombinedBarrierReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String label = barrierStatus.getBarrierLabel();
int barrierPresentStatus = barrierStatus.getPresentStatus();
if (label == null) {
return;
}
switch (label) {
case COMBINED_BEHAVIOR_TIME_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.FALSE) {
if (System.currentTimeMillis() - lastTime >= tenSecondsMillis) {
alert.show();
}
updateTimeAwarenessBarrier();
}
break;
default:
break;
}
}
}
Registering or Deleting the Barrier CombinationUse a switch on the UI to register or delete the barrier combination.
Code:
automaticAdjustSwitch = findViewById(R.id.sedentary_reminder_switch);
automaticAdjustSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
startAutomaticAdust(isChecked);
}
});
private void startAutomaticAdust(boolean isChecked) {
if (isChecked) {
addBarriers();
} else {
deleteBarriers();
}
}
private void addBarriers() {
keepStillBarrier = BehaviorBarrier.keeping(BehaviorBarrier.BEHAVIOR_STILL);
updateTimeAwarenessBarrier();
}
@NonNull
private void updateTimeAwarenessBarrier() {
long currentTimeStamp = System.currentTimeMillis();
lastTime = currentTimeStamp;
AwarenessBarrier timePeriodBarrier = TimeBarrier.duringTimePeriod(currentTimeStamp, currentTimeStamp + tenSecondsMillis);
AwarenessBarrier combinedTimeBluetoothBarrier = AwarenessBarrier.and(keepStillBarrier, timePeriodBarrier);
Utils.addBarrier(this, COMBINED_BEHAVIOR_TIME_BARRIER_LABEL,
combinedTimeBluetoothBarrier, mPendingIntent);
}
private void deleteBarriers() {
Utils.deleteBarrier(this, mPendingIntent);
}
Showing the Reminding InformationUse an AlertDialog to remind a user.
Code:
// Initialize Builder.
builder = new AlertDialog.Builder(this);
// Load and configure the custom view.
final LayoutInflater inflater = getLayoutInflater();
View view_custom = inflater.inflate(R.layout.view_dialog_custom, null, false);
builder.setView(view_custom);
builder.setCancelable(false);
alert = builder.create();
view_custom.findViewById(R.id.i_kown).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alert.dismiss();
}
});
final class CombinedBarrierReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BarrierStatus barrierStatus = BarrierStatus.extract(intent);
String label = barrierStatus.getBarrierLabel();
int barrierPresentStatus = barrierStatus.getPresentStatus();
if (label == null) {
return;
}
switch (label) {
case COMBINED_BEHAVIOR_TIME_BARRIER_LABEL:
if (barrierPresentStatus == BarrierStatus.FALSE) {
if (System.currentTimeMillis() - lastTime >= tenSecondsMillis) {
alert.show();
}
updateTimeAwarenessBarrier();
}
break;
default:
break;
}
}
}
And just like that, the standing up reminder function is created.
In fact, I've got some more ideas for using mobile context-awareness capabilities, such as developing a sleep reminder using the ambient light awareness capability and the time awareness capability. This reminder can notify users when it is bedtime based on a specified time and when the ambient brightness is lower than a specified value.
A schedule reminder also sounds like a good idea, which uses the time awareness capability to tell a user their schedule for a day at a specified time.
These are just some of my ideas. If you've got some other interesting inspirations for using the context-awareness capabilities, please share them in the comments section below and see how our ideas overlap.
References>>The dangers of sitting
>>What are the risks of sitting too much?[z1]
>>Obtaining agconnect-services.json
>>Obtaining a signing certificate