App Linking allows you to create links that work across multiple platforms including Android, iOS, and web. When an Android or iOS device user taps a link created with App Linking, they will be redirected to the specific in-app content. If a user has not yet installed the app, they will be redirected to their local app store to download the app. After downloading and opening the app, the user will be taken to the in-app content.
The following shows you how to share in-app images with App Linking.
Service SetupTo integrate the service, perform the following steps:
Create a project in AppGallery Connect.
Request a URL prefix.
Integrate the service SDK into your app and create a link using an API.
Configure your Android app to receive and process the link of App linking.
To identify and load an image to be shared, you need to add the PhotoID parameter to the deep link of App Linking, and parse the deep link to obtain the parameter value when receiving the link.
Creating a Project in AppGallery Connect
Sign in to AppGallery Connect and click My projects.
Click + to add a project on the displayed page. Enter a project name and click OK.
Add an app to the project, and configure the same package name as that set in your Android 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"
}
Requesting a URL Prefix
Go to Grow > App Linking.
On the App Linking page, click Use now to enable the service.
Under the URL prefixes tab page, click New URL prefix to add a unique URL prefix. AppGallery Connect automatically checks whether the URL prefix is unique.
In this example, the URL prefix is configured as follows.
Integrating the Service SDK and Creating a Link Using an APIGo to Project setting > General information, download the agconnect-service.json file and add it to the App directory of your project.
Open the project-level gradle file, and add the Maven repository address and AppGallery Connect plugin to the file.
Code:
buildscript {
repositories {
google()
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
// Note: Configure your app dependencies
// in the app-level build.gradle file.
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
}
Apply the AppGallery Connect plugin in the app-level gradle file, and add the service SDK to the file.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
android{…}
dependencies {
….
implementation 'com.huawei.agconnect:agconnect-applinking:1.6.0.300'
}
Call an API to create a link of App Linking based on the photo ID.
Define the UserName and ImageURL input parameters for the preview page of the deep link, and encapsulate the PhotoID parameter into the deep link.
Code:
private static final String DOMAIN_URI_PREFIX = "https://photoplaza.drcn.agconnect.link";
private static final String DEEP_LINK = "https://photoplaza-agc.dra.agchosting.link";
private static final String SHARE_DEEP_LINK = "photo://photoplaza.share.com";
/**
* Call AppLinking.Builder to create a link of App Linking in your app.
*
* @param UserName input UserName
* @param PhotoID input PhotoID
* @param ImageUrl input ImageUrl
* @param icallback input icallback
*/
public void createShareLinking(String UserName, String PhotoID, String ImageUrl, Icallback icallback) {
String newDeep_Link = SHARE_DEEP_LINK + "?PhotoID=" + PhotoID;
AppLinking.Builder builder = AppLinking.newBuilder()
.setUriPrefix(DOMAIN_URI_PREFIX)
.setDeepLink(Uri.parse(DEEP_LINK))
.setAndroidLinkInfo(AppLinking.AndroidLinkInfo.newBuilder()
.setAndroidDeepLink(newDeep_Link)
.build())
.setSocialCardInfo(AppLinking.SocialCardInfo.newBuilder()
.setTitle("It is a beautiful Photo")
.setImageUrl(ImageUrl)
.setDescription(UserName + " share a Photo to you")
.build())
.setCampaignInfo(AppLinking.CampaignInfo.newBuilder()
.setName("UserSharePhoto")
.setSource("ShareInApp")
.setMedium("WeChat")
.build());
builder.buildShortAppLinking().addOnSuccessListener(shortAppLinking -> {
shortLink = shortAppLinking.getShortUrl().toString();
try {
icallback.onSuccess(shortLink, "Success");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}).addOnFailureListener(e -> icallback.onFailure(e.getMessage()));
}
Receiving a Link of App LinkingFirst of all, create an activity on the ImageDetail page for receiving the deep link of App Linking, to which a user will be redirected after the link is tapped.
Add an intent filter for receiving the deep link to ImageDetailActivity in the AndroidManifest file
Code:
<activity android:name=".ImageDetailActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ImageListActivity" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Enter your custom domain name. -->
<data android:host="photoplaza.share.com" android:scheme="photo" />
</intent-filter>
</activity>
Under onCreate() of the ImageDetail file, add the following code for receiving and parsing a link of App Linking, and for obtaining the data of the deep link.
Code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDarkStatusIcon();
setContentView(R.layout.image_detail);
AGConnectAppLinking.getInstance().getAppLinking(ImageDetailActivity.this)
.addOnSuccessListener(resolvedLinkData - > {
// The user has installed and used your app and the app is opened.
if (resolvedLinkData != null) {
Uri deepLink = resolvedLinkData.getDeepLink();
Log.i(TAG, "Open From AppLinking:" + deepLink);
// Obtain the image details.
getSharePicDetails(deepLink.toString());
}
}).addOnFailureListener(e - > {
Bundle data = getIntent().getExtras();
if (data != null) {
if (data.getBoolean("firstLink")) {
// The user has just installed your app and the app is opened for the first time.
getSharePicDetails(data.getString("deepLink"));
} else {
// Open the ImageDetail page.
initView();
}
}
});
}
/**
* Receive the image details from the link of App Linking, which is shared by the user.
*
* @param link input a deepLink of App Linking
*/
private void getSharePicDetails(String link) {
ToastUtils.showToast(this, getResources().getString(R.string.loading_photo));
sharePhotoID = parseLink(link).get("PhotoID");
Log.i("AppLinking", "sharePhotoID: " + sharePhotoID);
}
/**
* Obtain the data of the deep link of App Linking.
*
* @param url input the received deepLink
* @return myMap output the param
*/
public static Map < String, String > parseLink(String url) {
Map < String, String > myMap = new HashMap < String, String > ();
String[] urlParts = url.split("\\?");
String[] params = urlParts[1].split("&");
for (String param: params) {
String[] keyValue = param.split("=");
myMap.put(keyValue[0], keyValue[1]);
}
return myMap;
}
Additional ConfigurationsIf a user has not installed your app, perform the following operations:
If a user has not installed your app, they will be redirected to the app details page on AppGallery or another local app store, to download your app, based on the app package name of your app.
When the user downloads and launches your app, the link of App Linking can still take effect and parameter values can be passed. However, the user will be redirected to the home page upon the first launch your app, as the intent filter configured in the last step does not take effect. Therefore, you need to configure the home screen for receiving the link of App Linking.
So for example set an activity for getAppLinking, for example, LoginActivity.
Code:
AGConnectAppLinking.getInstance().getAppLinking(LoginActivity.this).addOnSuccessListener(resolvedLinkData -> {
// Your app is launched for the first time through the link of App Linking.
Log.i(TAG,"StartUp From AppLinking");
if (resolvedLinkData!= null) {
String deepLink = resolvedLinkData.getDeepLink().toString();
Log.i("AppLinking", "deepLink:" + deepLink);
Bundle bundle = new Bundle();
bundle.putBoolean("firstLink", true);
bundle.putString("deepLink", deepLink);
Intent intent;
intent = new Intent(LoginActivity.this, ImageDetailActivity.class);
intent.putExtras(bundle);
startActivity(intent);
finish();
}
}).addOnFailureListener(e-> {
// Your app is launched normally.
Log.i(TAG,"Normal StartUp");
initView();
});
References
Service introduction of App Linking
Get Started tutorials
App Linking Videos: Troubleshooting
Related
More information like this, you can visit HUAWEI Developer Forum
Introduction
Huawei Cab Application is to explore HMS Kits in real time scenario, use this app as reference to CP during HMS integration and understand easily about HMS function.
Login Module
The user can access Huawei Cab Application in Login page by HUAWEI ID or Email ID Login or Mobile Login and Google Sign In.
You can use AGC Auth Service to integrate one or more of the following authentication methods into your app for achieving easy and efficient user registration, and sign-in.
AGC Auth service is not providing user google mail id. So, you can retrieve your mail Id directly from third party google server by ID token, we have covered that one also in this article.
Huawei Cab Application needs user authentication to access application using following method:
Huawei Cab Authentication type:
1. Huawei Account Kit
2. Huawei Auth Service
App Screen
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Huawei Account Kit
HUAWEI Account Kit provides developers with simple, secure, and quick sign-in, and authorization functions. Instead of entering accounts and passwords, and authorization waiting, users can click the Sign In with Huawei ID button to quickly and securely sign in to app.
HUAWEI Auth Service
Auth Service provides backend services and an SDK to authenticate users to your app. It supports multiple authentication providers such as Phone Number, Google Sign-In, Email ID and more, and report authentication credentials to the AppGallery Connect.
When a user signs in to an app again, the app can obtain the users personal information and other data protected by security rules in other server less functions from Auth Service.
Auth Service can greatly reduce your investment and costs in building an authentication system and its O&M.
Integration Preparations
To integrate HUAWEI Account Kit, you must complete the following preparations:
Create an app in AppGallery Connect.
Create a project in Android Studio.
Generate a signing certificate.
Generate a signing certificate fingerprint.
Configure the signing certificate fingerprint.
Add the app package name and save the configuration file.
Add the AppGallery Connect plug-in and the Maven repository in the project-level build.gradle file.
Configure the signature file in Android Studio.
Configuring the Development Environment
Enabling HUAWEI Account Kit and Auth Service
1. Sign in to AppGallery Connect, select My apps and click an App. Choose Develop > Overview > Manage APIs.
Enabling Authentication Modes
1. Sign in to AppGallery Connect, select My apps, and click your App. Choose Develop > Build > Auth Service. If it is the first time that you use Auth Service, click Enable now in the upper right corner.
2. Click Enable in the row of each authentication mode to be enabled. In this codelab, click Enable for Huawei account, Huawei game, and Anonymous account.
3. In the displayed dialog box, configure app information. Required information can be obtained as follows:
Huawei account: Obtain the app ID and secret by referring to Querying App Information.
Huawei game: Sign in to AppGallery Connect, select My apps, click your game. Choose Develop > Build > Google Sign In, and obtain the client id and client secret id.
Integrating the Account Kit and Auth Service SDK
If you are using Android Studio, you can integrate the App Linking SDK by using the Maven repository into your Android Studio project before development.
1. Sign in to AppGallery Connect, select My apps and click your App. Choose Develop > Overview.
2. Click agconnect-services.json to download the configuration file.
3. Copy agconnect-services.json file to the app's root directory.
4. Open the build.gradle file in the root directory of your Android Studio project.
5. Configure the following information in the build.gradle file:
Code:
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.huawei.agconnect:agcp:1.3.2.301'
}
}
6. Open the build.gradle file in the app directory.
7. Configure the HUAWEI Account Kit service address, Auth Service SDK address, and HUAWEI Game Service address.
Code:
// Apply the APM plug-in.
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation 'com.huawei.hms:hwid:4.0.1.300'
implementation 'com.huawei.agconnect:agconnect-auth:1.4.0.300'
implementation 'net.openid:appauth:0.7.1'
}
8. Click Sync Now to synchronize the configuration.
9. Add into your Manifest.
Code:
<activity android:name="net.openid.appauth.RedirectUriReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="Your package name"/>
</intent-filter>
</activity>
10. Add your app client Id from Google Developer Console for Google Sign In.
Code:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- android-client id-->
<string name="google_client_ids">Your Client ID</string>
<string name="redirect_uri">"your_package_name:/oauth2callback"</string>
</resources>
Code Snipped
Code:
private var mAuthManager: HuaweiIdAuthService? = null
private var mAuthParam: HuaweiIdAuthParams? = null
fun initHuaweiID() {
mAuthParam = HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM)
.setEmail()
.setUid()
.setProfile()
.setMobileNumber()
.setIdToken()
.setAccessToken()
.createParams()
mAuthManager = HuaweiIdAuthManager.getService(this, mAuthParam)
}
fun onLoginButtonClick(view : View) {
if (!isConnected) {
toast("No network connectivity")
} else {
startActivityForResult(mAuthManager?.signInIntent, RC_SIGN_IN)
}
}
fun signInGoogle() {
val serviceConfiguration =
AuthorizationServiceConfiguration(
Uri.parse("https://accounts.google.com/o/oauth2/auth"), // authorization endpoint
Uri.parse("https://oauth2.googleapis.com/token")
)
val authorizationService = AuthorizationService(this)
val clientId = getString(R.string.google_client_ids)
val redirectUri = Uri.parse(getString(R.string.redirect_uri))
val builder = AuthorizationRequest.Builder(
serviceConfiguration,
clientId,
ResponseTypeValues.CODE,
redirectUri
)
builder.setScopes("openid email profile")
val request = builder.build()
val intent = authorizationService.getAuthorizationRequestIntent(request)
startActivityForResult(
intent,
RC_GOOGLE_SIGN_IN
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
//login success
//get user message by parseAuthResultFromIntent
val authHuaweiIdTask =
HuaweiIdAuthManager.parseAuthResultFromIntent(data)
if (authHuaweiIdTask.isSuccessful) {
val huaweiAccount = authHuaweiIdTask.result
Log.i(
TAG,
huaweiAccount.uid + " signIn success "
)
Log.i(
TAG,
"AccessToken: " + huaweiAccount.accessToken
)
displayInfo(huaweiAccount)
} else {
Log.i(
TAG,
"signIn failed: " + (authHuaweiIdTask.exception as ApiException).statusCode
)
}
} else if (requestCode == RC_GOOGLE_SIGN_IN) {
val response = AuthorizationResponse.fromIntent(data!!)
val error = AuthorizationException.fromIntent(data)
val authState =
AuthState(response, error)
if (response != null) {
Log.i(
TAG,
String.format("Handled Authorization Response %s ", authState.toString())
)
val service = AuthorizationService(this)
service.performTokenRequest(
response.createTokenExchangeRequest()
) { tokenResponse, exception ->
if (exception != null) {
Log.w(
TAG,
"Token Exchange failed",
exception
)
} else {
if (tokenResponse != null) {
val suffixUrl = "tokeninfo?id_token=${tokenResponse.idToken}"
viewModel.getGmailMailID(suffixUrl)
viewModel.googleMailResponse.observe([email protected], Observer {
if (it != null) {
if (it.error.toString().contentEquals("invalid_token")) {
Toast.makeText([email protected], "${it.error_description}", Toast.LENGTH_LONG).show()
} else{
GoogleMailID = "${it.email}"
Log.d("GoogleMail_ID ", "${it.email}")
tokenResponse.idToken?.let { agcAuthWithGoogle(it) }
}
} else {
Toast.makeText([email protected], "Somethings error. Please try again later", Toast.LENGTH_LONG).show()
}
})
}
}
}
}
}
else {
mCallbackManager!!.onActivityResult(requestCode, resultCode, data)
}
}
fun getGmailMailID(url: String) {
val googleUserModel: GoogleUserModel
viewModelScope.launch {
try {
MyGoogleApi
.invoke()
.getGoogleMailID(url)
.enqueue(object : Callback<GoogleUserModel> {
override fun onFailure(call: Call<GoogleUserModel>, t: Throwable) {
Log.e(TAG, "onFailure: "+t.localizedMessage )
googleMailResponse.value = null
}
override fun onResponse(
call: Call<GoogleUserModel>,
response: Response<GoogleUserModel>
) {
if (response.isSuccessful) {
googleMailResponse.value = response.body()
} else {
googleMailResponse.value = null
}
}
})
} catch (e: Exception) {
e.stackTrace
}
}
}
private fun createAccount(email: String, password: String) {
val settings = VerifyCodeSettings.newBuilder()
.action(VerifyCodeSettings.ACTION_REGISTER_LOGIN) //ACTION_REGISTER_LOGIN/ACTION_RESET_PASSWORD
.sendInterval(30) // Minimum sending interval, ranging from 30s to 120s.
.locale(Locale.getDefault()) // Language in which a verification code is sent, which is optional. The default value is Locale.getDefault.
.build()
val task =
EmailAuthProvider.requestVerifyCode(email, settings)
task.addOnSuccessListener(
TaskExecutors.uiThread(),
OnSuccessListener {
Log.d("Email Auth", " Success")
val inflater = layoutInflater
val alertLayout: View =
inflater.inflate(R.layout.dialog_verification_code, null)
val verifyBtn =
alertLayout.findViewById<Button>(R.id.verifyBtn)
val edtverifyBtn = alertLayout.findViewById<EditText>(R.id.smsCodeEt)
val alert =
AlertDialog.Builder(this)
alert.setTitle("Verifying code")
// this is set the view from XML inside AlertDialog
alert.setView(alertLayout)
// disallow cancel of AlertDialog on click of back button and outside touch
alert.setCancelable(false)
val dialog = alert.create()
verifyBtn.setOnClickListener {
if (!edtverifyBtn.text.toString().isEmpty()) {
dialog.dismiss()
verifyEmailWithCode(
edtverifyBtn.text.toString(),
email,
password
)
} else {
Toast.makeText(
[email protected],
"Email Code must not be empty!",
Toast.LENGTH_LONG
).show()
}
}
dialog.show()
}).addOnFailureListener(
TaskExecutors.uiThread(),
OnFailureListener { e -> Log.d("Email Auth", " Failed " + e.message.toString()) })
}
private fun verifyEmailWithCode(
code: String,
email: String,
password: String
) {
val emailUser = EmailUser.Builder()
.setEmail(email)
.setVerifyCode(code)
.setPassword(password) // Optional. If this parameter is set, the current user has created a password and can use the password to sign in.
// If this parameter is not set, the user can only sign in using a verification code.
.build()
viewModel.AGCCreateUser_EmailVerificationCode(emailUser)
}
private fun startPhoneNumberVerification(phoneNumber: String) {
val settings = VerifyCodeSettings.newBuilder()
.action(VerifyCodeSettings.ACTION_REGISTER_LOGIN) //ACTION_REGISTER_LOGIN/ACTION_RESET_PASSWORD
.sendInterval(30) // Minimum sending interval, which ranges from 30s to 120s.
.locale(Locale.getDefault()) // Optional. It indicates the language for sending a verification code. The value of locale must contain the language and country/region information. The defualt value is Locale.getDefault.
.build()
PhoneAuthProvider.verifyPhoneCode(
"91", // Country Code
phoneNumber, // Phone Num
settings,
object : VerifyCodeSettings.OnVerifyCodeCallBack {
override fun onVerifySuccess(
shortestInterval: String,
validityPeriod: String
) {
Log.d("Phone Auth", " Success")
}
override fun onVerifyFailure(e: Exception) {
Log.d("Phone Auth", " Failed " + e.message.toString())
}
})
}
private fun verifyPhoneNumberWithCode(
code: String,
phoneNumber: String
) {
val phoneUser = PhoneUser.Builder()
.setCountryCode("+91")
.setPhoneNumber(phoneNumber) // The value of phoneNumber must contains the country/region code and mobile number.
.setVerifyCode(code)
.setPassword("Your password") // Mandatory. If this parameter is set, a password has been created for the current user by default and the user can sign in using the password.
// Otherwise, the user can only sign in using a verification code.
.build()
viewModel.AGCCreateUser_VerificationCode(phoneUser)
}
private fun signin_withverificationcode() {
val credential = PhoneAuthProvider.credentialWithVerifyCode(
"+91",
field_phone_number!!.text.toString(),
"Your password",
field_verification_code1!!.text.toString()
)
viewModel.AGCGoogleSignIn(credential)
}
private fun validatePhoneNumber(): Boolean {
val phoneNumber = field_phone_number!!.text.toString()
if (TextUtils.isEmpty(phoneNumber)) {
field_phone_number!!.error = "Invalid phone number."
return false
}
return true
}
Video Demo for Login with Mobile No
Reference URL:
Account Kit: https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/account-introduction-v4
AGC Auth Service: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-auth-service-introduction
Hi, how do I verify an anonymous account?
HUAWEI AppGallery Connect Cloud Storage provides maintenance-free cloud storage functions.
In this post, we’ll walk you through the steps required for integrating this service in Unity. You can access the Cloud Storage sample code on GitHub.
1. Test Environment
SDK Version: agconnect-storage:1.3.1.100
Platform:Unity 2019.4.17f1c1
Test Device:HONOR Magic 2
AppGallery Connect:
https://developer.huawei.com/consumer/en/service/josp/agc/index.html
2. Applying for Cloud Storage
Currently, Cloud Storage is still in beta status. To use the service, you need to send an application by email. For details, check:
https://developer.huawei.com/consum...Gallery-connect-Guides/agc-cloudstorage-apply
3. Importing the Unity Package
Unity materials:
https://docs.unity.cn/cn/Packages-cn/[email protected]/manual/cloudstorage.html
1. Download Unity Hub here and install Unity Editor.
2. Configure the Android environment.
{
"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"
}
3. Import the HuaweiServices package. Download the package from Unity Assets Store. In Unity Editor, choose Assets > Import package.
Select the required package and click Import.
4. Completing Configurations in AppGallery Connect
1. Sign in to AppGallery Connect, and click the created app.
2. Go to My projects > Build > Cloud Storage and click Enable now. Set the default storage instance as required.
To permit Cloud Storage to read and write data without authorization, add the following code:
XML:
agc.cloud.storage[
match: /{bucket}/{path=**} {
allow read, write: if true;
}
]
3. Go to Project settings > General information and download the latest agconnect-services.json file.
4. Save the file to the Assets\Plugins\Android directory of your Unity project.
If the downloaded JSON file does not contain the default_storage parameter under cloudstorage, you need to add it manually. Set its value to the storage instance you just configured.
5. Completing Project Information in Unity Editor
1. Choose Edit > Project Settings > Player > Publish Settings. In the Build area, select the items for Android according to your requirements.
2. In Unity Editor, choose Edit > Project Settings > Player > Other Settings, and set the package name to match the package name you set in AppGallery Connect.
3. Add the following code to the project-level baseProjectTmeplate.gradle file in the Assets\Plugins\Android directory:
XML:
allprojects {
buildscript {
repositories {
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.huawei.agconnect:agcp:1.4.2.301'
**BUILD_SCRIPT_DEPS**
}
}
repositories {
maven { url 'https://developer.huawei.com/repo/' }
}
}
4. Add the following code to the app-level LauncherTmeplate.gradle file in the Assets\Plugins\Android directory:
XML:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation project(':unityLibrary')
implementation "com.huawei.agconnect:agconnect-storage:1.3.1.100"
implementation 'com.huawei.agconnect:agconnect-auth:1.4.2.301'
5. Configure the manifest file to add the corresponding permissions.
XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="false"
android:requestLegacyExternalStorage="true" >
6. Using Cloud Storage
1. Configure the UI layout.
In Unity Editor, choose GameObject > UI > Button and create a button. Click the button, click Add Component on the right to create a script file, and then create the corresponding method.
2. Initialize the storage instance and apply for the read and write permissions.
C#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiService;
using HuaweiService.CloudStorage;
using System;
public delegate void SuccessCallBack<T>(T o);
public class HmsSuccessListener<T>:OnSuccessListener{
public SuccessCallBack<T> CallBack;
public HmsSuccessListener(SuccessCallBack<T> c){
CallBack = c;
}
public void onSuccess(T arg0)
{
Debug.Log("OnSuccessListener onSuccess");
if(CallBack != null)
{
CallBack.Invoke(arg0);
}
}
public override void onSuccess(AndroidJavaObject arg0){
Debug.Log("OnSuccessListener onSuccess");
if(CallBack !=null)
{
Type type = typeof(T);
IHmsBase ret = (IHmsBase)Activator.CreateInstance(type);
ret.obj = arg0;
CallBack.Invoke((T)ret);
}
}
}
public class testStorageDemo : MonoBehaviour
{
private AGCStorageManagement mAGCStorageManagement;
private string[] permissions =
{
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.READ_EXTERNAL_STORAGE",
};
// Start is called before the first frame update
void Start()
{
AndroidJavaClass javaUnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = javaUnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
Activity aaa = HmsUtil.GetHmsBase<Activity>(currentActivity);
ActivityCompat.requestPermissions(aaa, permissions, 1);
}
// Update is called once per frame
void Update()
{
}
public void initAGCStorageManagement() {
mAGCStorageManagement = AGCStorageManagement.getInstance("9105385871708601205-ffeon");
Debug.Log("Instance is: "+ mAGCStorageManagement);
}
public class MySuccessListener : OnSuccessListener
{
private string m_name;
public MySuccessListener(string name)
{
m_name = name;
}
public MySuccessListener()
{
m_name = "default";
}
public override void onSuccess(AndroidJavaObject ex)
{
Debug.Log("download success: " + m_name);
}
}
}
3. Upload a file.
C#:
public void uploadFile() {
if (mAGCStorageManagement == null){
initAGCStorageManagement();
}
string fileName = "testUnity.jpg";
StorageReference reference = mAGCStorageManagement.getStorageReference(fileName);
string FileFolder = "/storage/emulated/0/AGCSdk/";
string FilePath = FileFolder + fileName;
Debug.Log("FilePath = " + FilePath);
File file = new File(FilePath);
Debug.Log("UploadFile = " + file);
UploadTask task = reference.putFile(file);
task.addOnSuccessListener(new MySuccessListener());
Debug.Log("UploadFile done:");
}
4. Download a file.
C#:
public void downloadFile() {
if (mAGCStorageManagement == null){
initAGCStorageManagement();
}
StorageReference reference = mAGCStorageManagement.getStorageReference("test.jpg");
string FileFolder = "/storage/emulated/0/AGCSdk/";
string FilePath = FileFolder + "test.jpg";
Debug.Log("FilePath = " + FilePath);
File file = new File(FilePath);
Debug.Log("File = " + file);
DownloadTask task = reference.getFile(file);
task.addOnSuccessListener(new MySuccessListener("NormalListener"));
Debug.Log("DownloadTask Result:");
}
5. Delete a file.
C#:
public void deleteFile() {
if (mAGCStorageManagement == null){
initAGCStorageManagement();
}
StorageReference reference = mAGCStorageManagement.getStorageReference("testUnity.jpg");
reference.delete();
Debug.Log("DeleteFileTest success.");
}
6. Package and test the app.
After you have packaged and installed the test app, you can tap each button, and view related logs in the Android Studio Logcat.
You can view the files you upload to or download from AppGallery Connect.
7. Summary
You can use Cloud Storage to store your Unity game data on the cloud without the hassle of server building and O&M. With AppGallery Connect, a web console, you can easily manage your files on the cloud side.
In addition to file upload, download, and deletion, Cloud Storage also offers a metadata setting function.
For reference:
Cloud Storage development guide:
https://developer.huawei.com/consum...-connect-Guides/agc-cloudstorage-introduction
Unity materials:
https://docs.unity.cn/cn/Packages-cn/[email protected]/manual/cloudstorage.html
Cloud Storage codelab:
https://github.com/AppGalleryConnect/agc-demos/tree/main/Android/agc-cloudstorage-demo-java
Introduction
This article is based on Multiple HMS services application. I have created Hotel Booking application using HMS Kits. We need mobile app for reservation hotels when we are traveling from one place to another place.
In this article, I am going to implement HMS Location Kit & Shared Preferences.
{
"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"
}
Flutter setup
Refer this URL to setup Flutter.
Software Requirements
1. Android Studio 3.X
2. JDK 1.8 and later
3. SDK Platform 19 and later
4. Gradle 4.6 and later
Steps to integrate service
1. We need to register as a developer account in AppGallery Connect.
2. Create an app by referring to Creating a Project and Creating an App in the Project
3. Set the data storage location based on current location.
4. Enabling Required Services: Location Kit.
5. Generating a Signing Certificate Fingerprint.
6. Configuring the Signing Certificate Fingerprint.
7. Get your agconnect-services.json file to the app root directory.
Important: While adding app, the package name you enter should be the same as your Flutter project’s package name.
Note: Before you download agconnect-services.json file, make sure the required kits are enabled.
Development Process
Create Application in Android Studio.
1. Create Flutter project.
2. App level gradle dependencies. Choose inside project Android > app > build.gradle.
Code:
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
Root level gradle dependencies
Code:
maven {url 'https://developer.huawei.com/repo/'}
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
Add the below permissions in Android Manifest file.
Code:
<manifest xlmns:android...>
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="com.huawei.hms.permission.ACTIVITY_RECOGNITION" />
<application ...
</manifest>
3. Refer below URL for cross-platform plugins. Download required plugins.
https://developer.huawei.com/consum...y-V1/flutter-sdk-download-0000001050304074-V1
4. After completing all the steps above, you need to add the required kits’ Flutter plugins as dependencies to pubspec.yaml file. You can find all the plugins in pub.dev with the latest versions.
Code:
dependencies:
flutter:
sdk: flutter
shared_preferences: ^0.5.12+4
bottom_navy_bar: ^5.6.0
cupertino_icons: ^1.0.0
provider: ^4.3.3
huawei_location:
path: ../huawei_location/
flutter:
uses-material-design: true
assets:
- assets/images/
5. After adding them, run flutter pub get command. Now all the plugins are ready to use.
6. Open main.dart file to create UI and business logics.
Location kit
HUAWEI Location Kit assists developers in enabling their apps to get quick and accurate user locations and expand global positioning capabilities using GPS, Wi-Fi, and base station locations.
Fused location: Provides a set of simple and easy-to-use APIs for you to quickly obtain the device location based on the GPS, Wi-Fi, and base station location data.
Activity identification: Identifies user motion status through the acceleration sensor, cellular network information, and magnetometer, helping you adjust your app based on user behaviour.
Geofence: Allows you to set an interested area through an API so that your app can receive a notification when a specified action (such as leaving, entering, or lingering in the area) occurs.
Integration
Permissions
First of all we need permissions to access location and physical data.
Create a PermissionHandler instance,add initState() for initialize.
Code:
final PermissionHandler permissionHandler;
@override
void initState() {
permissionHandler = PermissionHandler(); super.initState();
}
Check Permissions
We need to check device has permission or not using hasLocationPermission() method.
Code:
void hasPermission() async {
try {
final bool status = await permissionHandler.hasLocationPermission();
if(status == true){
showToast("Has permission: $status");
}else{
requestPermission();
}
} on PlatformException catch (e) {
showToast(e.toString());
}
}
If device don’t have permission,then request for Permission to use requestLocationPermission() method.
Code:
void requestPermission() async {
try {
final bool status = await permissionHandler.requestLocationPermission();
showToast("Is permission granted");
} on PlatformException catch (e) {
showToast(e.toString());
}
}
Fused Location
Create FusedLocationPrvoiderClient instance using the init() method and use the instance to call location APIs.
Code:
final FusedLocationProviderClient locationService
@override
void initState() {
locationService = FusedLocationProviderClient(); super.initState();
}
getLastLocation()
Code:
void getLastLocation() async {
try {
Location location = await locationService.getLastLocation();
setState(() {
lastlocation = location.toString();
print("print: " + lastlocation);
});
} catch (e) {
setState(() {
print("error: " + e.toString());
});
}
}
getLastLocationWithAddress()
Create LocationRequest instance and set required parameters.
Code:
final LocationRequest locationRequest;
locationRequest = LocationRequest()
..needAddress = true
..interval = 5000;
void _getLastLocationWithAddress() async {
try {
HWLocation location =
await locationService.getLastLocationWithAddress(locationRequest);
setState(() {
String street = location.street;
String city = location.city;
String countryname = location.countryName;
currentAddress = '$street' + ',' + '$city' + ' , ' + '$countryname';
print("res: $location");
});
showToast(currentAddress);
} on PlatformException catch (e) {
showToast(e.toString());
}
}
Location Update using Call back
Create LocationCallback instance and create callback functions in initstate().
Code:
LocationCallback locationCallback;
@override
void initState() {
locationCallback = LocationCallback(
onLocationResult: _onCallbackResult,
onLocationAvailability: _onCallbackResult,
);
super.initState();
}
void requestLocationUpdatesCallback() async {
if (_callbackId == null) {
try {
final int callbackId = await locationService.requestLocationUpdatesExCb(
locationRequest, locationCallback);
_callbackId = callbackId;
} on PlatformException catch (e) {
showToast(e.toString());
}
} else {
showToast("Already requested location updates.");
}
}
void onCallbackResult(result) {
print(result.toString());
showToast(result.toString());
}
I have created Helper class to store user login information in locally using shared Preferences class.
Code:
class StorageUtil {
static StorageUtil _storageUtil;
static SharedPreferences _preferences;
static Future<StorageUtil> getInstance() async {
if (_storageUtil == null) {
var secureStorage = StorageUtil._();
await secureStorage._init();
_storageUtil = secureStorage;
}
return _storageUtil;
}
StorageUtil._();
Future _init() async {
_preferences = await SharedPreferences.getInstance();
}
// get string
static String getString(String key) {
if (_preferences == null) return null;
String result = _preferences.getString(key) ?? null;
print('result,$result');
return result;
}
// put string
static Future<void> putString(String key, String value) {
if (_preferences == null) return null;
print('result $value');
return _preferences.setString(key, value);
}
}
Result
Tips & Tricks
1. Download latest HMS Flutter plugin.
2. To work with mock location we need to add permissions in Manifest.XML.
3. Whenever you updated plugins, click on pug get.
Conclusion
We implemented simple hotel booking application using Location kit in this article. We have learned how to get Lastlocation, getLocationWithAddress and how to use callback method, in flutter how to store data into Shared Preferences in applications.
Thank you for reading and if you have enjoyed this article, I would suggest you to implement this and provide your experience.
Reference
Location Kit URL
Shared Preferences URL
Read full article
Goodjob
Thank you
I thought huawei doesn't support flutter. I guess it should as it is Android only.
good
Wow
Nice.
I thought its not doable
Interesting.
Like
This document describes how to integrate Ads Kit using the official Unity asset. After the integration, your app can use the services of this Kit on HMS mobile phones.
For details about Ads Kit, please visit HUAWEI Developers.
1.1 Restrictions
1.1.1 Ads Supported by the Official Unity Asset
The official asset of version 1.3.4 in Unity supports interstitial ads and rewarded ads of Ads Kit.
Note: To support other types of ads, you can use the Android native integration mode. This document will take the banner ad as an example to describe such integration.
1.1.2 Supported Devices and Unity Versions
{
"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"
}
Note: If the version is earlier than 2018.4.25, you can manually import assets. If there are any unknown build errors that are not caused by the AfterBuildToDo file, upgrade your Unity.
1.1 Preparations
1.1.1 Prerequisites
l HMS Core (APK) 4.0.0.300 or later has been installed on the device. Otherwise, the APIs of the HUAWEI Ads SDK, which depend on HMS Core (APK) 4.0.0.300 or later, cannot be used.
l You have registered as a Huawei developer and completed identity verification on HUAWEI Developers. For details, please refer to Registration and Verification.
You have created a project and add an app to the project in AppGallery Connect by referring to Creating an AppGallery Connect Project and Adding an App to the Project.
For details about applying for a formal ad slot, please visit HUAWEI Developers.
1.1.2 Importing Unity Assets
1. Open Asset Store in Unity.
Go to Window > Asset Store in Unity.
2. Search for the Huawei HMS AGC Services asset. Download and then import it.
3. Import the asset to My Assets, with all services selected.
4. Change the package name.
Go to Edit > Project Settings> Player > Android > Other Settings in Unity, and then set Package Name.
The default package name is com.${Company Name}.${Product Name}. You need to change the package name, and the app will be released to AppGallery with the new name.
1.2.3 Generating .gradle Files1. Enable project gradle.
Go to Edit > Project Settings > Player in Unity, click the Android icon, and go to Publishing Settings > Build.
Enable Custom Main Manifest.
Enable Custom Main Gradle Template.
Enable Custom Launcher Gradle Template.
Enable Custom Base Gradle Template.
2. Generate a signature.
You can use an existing keystore file or create a new one to sign your app.
Go to Edit > Project Settings > Player in Unity, click the Android icon, and go to Publishing Settings > Keystore Manager.
Then, go to Keystore... > Create New.
Enter the password when you open Unity. Otherwise, you cannot build the APK.
1.1.1 Configuring .gradle Files1. Configure the BaseProjectTemplate.gradle file.
Configure the Maven repository address.
Code:
<p style="line-height: 1.5em;">buildscript {
repositories {**ARTIFACTORYREPOSITORY**
google()
jcenter()
maven { url 'https://developer.huawei.com/repo/' }
}
repositories {**ARTIFACTORYREPOSITORY**
google()
jcenter()
maven { url 'https://developer.huawei.com/repo/' }
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
}
2. Configure the launcherTemplate.gradle file.
Add dependencies.
apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation project(':unityLibrary')
implementation 'com.huawei.hms:ads-lite:13.4.29.303'
}</p>
1.2 App Development with the Official Asset1.2.1 Rewarded AdsRewarded ads are full-screen video ads that allow users to view in exchange for in-app rewards.
1.2.1.1 Sample Code
Code:
<p style="line-height: 1.5em;">using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HuaweiHms;
namespace Demo
{
public class RewardDemo : MonoBehaviour
{
public void LoadRewardAds()
{
// Create a RewardAd object.
// "testx9dtjwj8hp" is the test ad slot. You can use it to perform a test. Replace the test slot with a formal one for official release.
RewardAd rewardAd = new RewardAd(new Context(), "testx9dtjwj8hp");
AdParam.Builder builder = new AdParam.Builder();
AdParam adParam = builder.build();
// Load the ad.
rewardAd.loadAd(adParam, new MRewardLoadListener(rewardAd));
}
}
// Listen for ad events.
public class MRewardLoadListener:RewardAdLoadListener
{
private RewardAd ad;
public rewardLoadListener(RewardAd _ad)
{
ad = _ad;
}
public override void onRewardAdFailedToLoad(int arg0)
{
}
public override void onRewardedLoaded()
{
ad.show(new Context(),new RewardAdStatusListener());
}
}
}</p>
1.3.1.2 Testing the APKGo to File > Build Settings > Android, click Switch Platform and then Build And Run.
1.3.2 Interstitial Ads1.3.2.1 Sample Code
Code:
<p style="line-height: 1.5em;">using UnityEngine;
using HuaweiService;
using HuaweiService.ads;
namespace Demo
{
public class interstitialDemo : MonoBehaviour
{
public void LoadImageAds()
{
// Create an ad object.
// "testb4znbuh3n2" and "teste9ih9j0rc3" are test ad slots. You can use them to perform a test. Replace the test slots with formal ones for official release.
InterstitialAd ad = new InterstitialAd(new Context());
ad.setAdId("teste9ih9j0rc3");
ad.setAdListener(new MAdListener(ad));
AdParam.Builder builder = new AdParam.Builder();
AdParam adParam = builder.build();
// Load the ad.
ad.loadAd(adParam);
}
public void LoadVideoAds()
{
InterstitialAd ad = new InterstitialAd(new Context());
ad.setAdId("testb4znbuh3n2");
ad.setAdListener(new MAdListener(ad));
AdParam.Builder builder = new AdParam.Builder();
ad.loadAd(builder.build());
}
public class MAdListener : AdListener
{
private InterstitialAd ad;
public MAdListener(InterstitialAd _ad) : base()
{
ad = _ad;
}
public override void onAdLoaded()
{
// Display the ad if it is successfully loaded.
ad.show();
}
public override void onAdFailed(int arg0)
{
}
public override void onAdOpened()
{
}
public override void onAdClicked()
{
}
public override void onAdLeave()
{
}
public override void onAdClosed()
{
}
}
}
}</p>
1.4 App Development with Android Studio1.4.1 Banner Ads1.4.1.1 Loading Banner Ads on a Page as RequiredAndroidJavaClass javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayerActivity");
javaClass.CallStatic("loadBannerAds");
1.4.1.2 Exporting Your Project from UnityGo to File > Build Settings > Android and click Switch Platform. Then, click Export Project, select your project, and click Export.
1.4.1.3 Integrating the Banner Ad Function in Android StudioOpen the exported project in Android Studio.
Add implementation 'com.huawei.hms:ads-lite:13.4.29.303' to build.gradle in the src directory.
1. Add code related to banner ads to UnityPlayerActivity.
a. Define the static variable bannerView.
Code:
<p style="line-height: 1.5em;">private static BannerView
bannerView
;</p>
b. Add the initialization of bannerView to the onCreate method.
Code:
<p style="line-height: 1.5em;">bannerView = new BannerView(this);
bannerView.setAdId("testw6vs28auh3");
bannerView.setBannerAdSize(BannerAdSize.BANNER_SIZE_360_57);
mUnityPlayer.addView(bannerView);</p>
c. Add the following static method for loading ads in the Android Studio project, and then build and run the project.
Code:
<p style="line-height: 1.5em;">public static void loadBannerAds()
{ // "testw6vs28auh3" is a dedicated test ad slot ID. Before releasing your app, replace the test ad slot ID with the formal one.
AdParam adParam = new AdParam.Builder().build();
bannerView.loadAd(adParam);
}</p>
d. If banner ads need to be placed at the bottom of the page, refer to the following code:
Code:
<p style="line-height: 1.5em;">// Set up activity layout.
@Override protected void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));
getIntent().putExtra("unity", cmdLine);
RelativeLayout relativeLayout = new RelativeLayout(this);
mUnityPlayer = new UnityPlayer(this, this);
setContentView(relativeLayout);
relativeLayout.addView(mUnityPlayer);
mUnityPlayer.requestFocus();
bannerView = new BannerView(this);
bannerView.setAdId("testw6vs28auh3");
bannerView.setBannerAdSize(BannerAdSize.BANNER_SIZE_360_57);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT); layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); relativeLayout.addView(bannerView,layoutParams);
}
public static void LoadBannerAd() {
// "testw6vs28auh3" is a dedicated test ad slot ID. Before releasing your app, replace the test ad slot ID with the formal one.
AdParam adParam = new AdParam.Builder().build();
bannerView.loadAd(adParam);
bannerView.setAdListener(new AdListener()
{
@Override
public void onAdFailed(int errorCode)
{
Log.d("BannerAds" ,"error" + errorCode);
}
});
}</p>
1.5 FAQs1. If an error indicating invalid path is reported when you export a project from Unity, change the export path to Downloads or Desktop.
2. Unity of a version earlier than 2018.4.25 does not support asset download from Asset Store. You can download the asset using Unity of 2018.4.25 or a later version, export it, and then import it to Unity of an earlier version.
More Information
To join in on developer discussion forums
To download the demo app and sample code
For solutions to integration-related issues
Checkout in forum
Precise messaging is an important way for mobile apps to retain users and is usually achieved by segmenting users into different groups according to their preferences and then adopting different messaging policies for each user segment. However, if you want to push messages to users based on their precise locations, in-depth customization is usually required since most available third-party messaging services cannot narrow the target audience down to a specific business area or a small area. With geofences, this issue can be effectively resolved. A geofence is a set of virtual boundaries that define a given area on a map. When a user's device enters or leaves the geofence, or stays in the geofence for a specific amount of time, messages and notifications can be automatically sent to an app on the user's device. Geofence and messaging capabilities can work together to precisely send messages to target audiences in a specified area.
For example, suppose that a travel app wants to promote its ticket booking service in Paris. To do so, the app can create geofences for popular scenic spots in Paris. When a target user arrives at a scenic spot during a specified time range, the app will send a promotion message such as "You have received a coupon for the XXX. Tap here to claim the coupon." to the user, increasing their willingness to buy a ticket.
ImplementationYou can carry out precise messaging to specified target audiences by using the geofence capability HMS Core Location Kit in conjunction with the message pushing capability of HMS Core Push Kit. By creating a geofence for a specified area, the app can detect the user's status, for example, when they enter, leave, or stay in this area. Once the messaging condition is met, the app on the user's device will receive a push message in real time. The push message can be sent to and displayed on the user's device even when the app is not running in the background, achieving a delivery rate as high as 99%.
Demo1. Install the demo app on the test device.
2. Start the demo app, tap Add Geofence on the GeoFence screen, and set relevant parameters to create a geofence.
3. Wait for the geofence to be triggered.
4. Check the received message.
{
"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 Procedure1. Configure the Maven repository address for the SDK.
(The procedure for configuring the Maven repository address in Android Studio is different for Gradle plugin versions earlier than 7.0, Gradle plugin 7.0, and Gradle plugin 7.1 or later versions. Here, the procedure for Gradle plugin 7.1 is used as an example.)
a) Go to buildscript > dependencies and add AppGallery Connect plugin configurations.
Code:
buildscript {
dependencies {
...
// Add the AppGallery Connect plugin configuration. You are advised to use the latest plugin version.
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
b) Open the project-level settings.gradle file and configure the Maven repository address for the SDK.
Code:
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
// Configure the Maven repository address for the SDK.
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencyResolutionManagement {
...
repositories {
google()
mavenCentral()
// Configure the Maven repository address for the SDK.
maven { url 'https://developer.huawei.com/repo/' }
}
}
2. Add build dependencies in the dependencies block.
Code:
// Configure the app-level build.gradle file.
dependencies {
implementation 'com.huawei.hms:location: 6.4.0.300'
implementation 'com.huawei.hms:push: 6.3.0.304'
}
3. Declare system permissions in the AndroidManifest.xml file.
The Location SDK incorporates the GNSS, Wi-Fi, and base station location functions into an app to build up precise global positioning capabilities. Therefore, it requires the network, precise location, and coarse location permissions to function correctly. If the app needs to continuously obtain user locations when running in the background, the ACCESS_BACKGROUND_LOCATION permission also needs to be declared in the AndroidManifest.xml file.
Code:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARES_LOCATION" />
Note: The ACCESS_FINE_LOCATION, WRITE_EXTERNAL_STORAGE, and READ_EXTERNAL_STORAGE permissions are dangerous system permissions, so they must be dynamically applied for. If the app does not have the permissions, Location Kit will be unable to provide services for the app.
Key CodeCode file: com.huawei.hmssample2.geofence\GeoFenceActivity.java
If you want to integrate the geofence service and implement message pushing in your app, you only need to add relevant code in GeoFenceActivity.java to your app project.
1. Configure geofences.
a) Create geofences and geofence groups as needed, and set relevant parameters, such as the geofence radius and triggering time.
Code:
if (checkStyle(geofences, data.uniqueId) == false) {
LocationLog.d("GeoFenceActivity", "not unique ID!");
LocationLog.i("GeoFenceActivity", "addGeofence failed!");
return;
}
geoBuild.setRoundArea(data.latitude, data.longitude, data.radius);
geoBuild.setUniqueId(data.uniqueId);
geoBuild.setConversions(data.conversions);
geoBuild.setValidContinueTime(data.validContinueTime);
geoBuild.setDwellDelayTime(data.dwellDelayTime);
geoBuild.setNotificationInterval(data.notificationInterval);
geofences.add(geoBuild.build());
LocationLog.i("GeoFenceActivity", "addGeofence success!");
b) Register a broadcast using the intent.
Code:
GeofenceRequest.Builder geofenceRequest = new GeofenceRequest.Builder();
geofenceRequest.createGeofenceList(GeoFenceData.returnList());
if (trigger.getText() != null) {
int trigGer = Integer.parseInt(trigger.getText().toString());
geofenceRequest.setInitConversions(trigGer);
LocationLog.d(TAG, "trigger is " + trigGer);
} else {
geofenceRequest.setInitConversions(5);
LocationLog.d(TAG, "default trigger is 5");
}
final PendingIntent pendingIntent = getPendingIntent();
try {
geofenceService.createGeofenceList(geofenceRequest.build(), pendingIntent)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
LocationLog.i(TAG, "add geofence success! ");
setList(pendingIntent, GeoFenceData.getRequestCode(), GeoFenceData.returnList());
GeoFenceData.createNewList();
} else {
// Get the status code for the error and log it using a user-friendly message.
LocationLog.w(TAG, "add geofence failed : " + task.getException().getMessage());
}
}
});
} catch (Exception e) {
LocationLog.i(TAG, "add geofence error:" + e.getMessage());
}
private PendingIntent getPendingIntent() {
Intent intent = new Intent(this, GeoFenceBroadcastReceiver.class);
intent.setAction(GeoFenceBroadcastReceiver.ACTION_PROCESS_LOCATION);
Log.d(TAG, "new request");
GeoFenceData.newRequest();
return PendingIntent.getBroadcast(this, GeoFenceData.getRequestCode(), intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
2. Trigger message pushing. Send a push message when onReceive of GeoFenceBroadcastReceiver detects that the geofence is triggered successfully. The message will be displayed in the notification panel on the device.
Code:
GeofenceData geofenceData = GeofenceData.getDataFromIntent(intent);
if (geofenceData != null) {
int errorCode = geofenceData.getErrorCode();
int conversion = geofenceData.getConversion();
ArrayList<Geofence> list = (ArrayList<Geofence>) geofenceData.getConvertingGeofenceList();
Location myLocation = geofenceData.getConvertingLocation();
boolean status = geofenceData.isSuccess();
sb.append("errorcode: " + errorCode + next);
sb.append("conversion: " + conversion + next);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
sb.append("geoFence id :" + list.get(i).getUniqueId() + next);
}
}
if (myLocation != null) {
sb.append("location is :" + myLocation.getLongitude() + " " + myLocation.getLatitude() + next);
}
sb.append("is successful :" + status);
LocationLog.i(TAG, sb.toString());
Toast.makeText(context, "" + sb.toString(), Toast.LENGTH_LONG).show();
//
new PushSendUtils().netSendMsg(sb.toString());
}
Note: The geofence created using the sample code will trigger two callbacks for conversion types 1 and 4. One is triggered when a user enters the geofence and the other when a user stays in the geofence. If Trigger is set to 7 in the code, callbacks will be configured for all scenarios, including entering, staying, and leaving the geofence.
Once you have performed the preceding steps, you will have enabled geofence-based message pushing for your app and can send messages to target audiences in specific areas, achieving precise marketing.
ReferencesLocation Kit official website
Location Kit development documentation