Intermediate: Integration of App Linking in Harmony OS App

Manoj Kumar
6 min readSep 24, 2021

Overview

In this article, I will create a demo app along with the integration of App Linking which is based on Harmony OS. I will provide the use case of AppLink in Harmony OS application.

HMS App Linking Introduction

HMS App Linking allows you to create cross-platform links that can work as defined. When a user taps the link on Harmony device, the user will be redirected to the specified in-app content. If a user taps the link in a browser, the user will be redirected to the same content of the web version.

To identify the source of a user, you can set tracing parameters for various channels when creating a link of App Linking to trace traffic sources. By analyzing the link performance of each traffic source based on the tracing parameters, you can find the platform that can achieve better promotion effect for your app:

  1. Deferred deep link: Directs a user who has not installed your app to AppGallery to download your app first and then navigate to the link in-app content directly, without requiring the user to tap the link again.
  2. Link display in card form: Uses a social Meta tag to display a link of App Linking as a card, which will attract more users from social media.
  3. Statistics: Records the data of all link-related events, such as numbers of link taps, first app launches, and non-first app launches for you to conduct analysis.

API Overview

1. (Mandatory) Call AppLinking.Builder to create a Builder object.

2. (Mandatory) Call AppLinking.Builder.setUriPrefix to set the URL prefix that has been requested.

3. (Mandatory) Call AppLinking.Builder.setDeepLink to set a deep link.

4. Call AppLinking.Builder.setHarmonyLinkInfo to set HarmonyOS app parameters. In this method, HarmonyOS app parameters are contained in an AppLinking.HarmonyLinkInfo instance, which can be built by calling AppLinking.HarmonyLinkInfo.Builder. If this method is not called, the link will be opened in the browser by default.

5. Call AppLinking.Builder.setIOSLinkInfo to set iOS app parameters. In this method, iOS app parameters are contained in an AppLinking.IOSLinkInfo instance, which can be built by calling AppLinking.IOSLinkInfo.Builder. If this method is not called, the link will be opened in the browser by default.

6. Call AppLinking.IOSLinkInfo.Builder.setITunesConnectCampaignInfo to set App Store Connect campaign parameters. In this method, App Store Connect campaign parameters are contained in an AppLinking.ITunesConnectCampaignInfo instance, which can be built by calling AppLinking.ITunesConnectCampaignInfo.Builder.

7. Call AppLinking.Builder.setPreviewType to set the link preview type. If this method is not called, the preview page with app information is displayed by default.

8. Call AppLinking.Builder.setSocialCardInfo to set social meta tags. In this method, social meta tags are contained in an AppLinking.SocialCardInfo instance, which can be built by calling AppLinking.SocialCardInfo.Builder. If this method is not called, links will not be displayed as cards during social sharing.

9. Call AppLinking.Builder.setCampaignInfo to set ad tracing parameters. In this method, campaign parameters are contained in an AppLinking.CampaignInfo instance, which can be built by calling AppLinking.CampaignInfo.Builder.

Key Concepts

URL prefix

The URL prefix is the domain name contained in a link, which is in https://Domain name format. You can use the domain name provided by AppGallery Connect for free.

Long link

A long link is a link of App Linking in its entirety. Follows this format:

URL prefix+Deep link+Android app parameters+iOS app parameters+[Preview type]+[Social meta tag]+[Tracing parameters]+[Landing page display parameter]+[Site ID].

An example of a long link is as follows:

https://yourapp.drcn.agconnect.link/deeplink=https%3A%2F%2Fyourdeeplink.com&android_deeplink=https%3A%2F%2Fyourdeeplink.com&android_open_type=1&android_package_name=tiantian.huawei&campaign_channel=huawei&campaign_medium=pic&campaign_name=%E4%BF%83%E9%94%80&ios_link=https%3A%2F%2Fyourdeeplink.com&ios_bundle_id=aapp.huawei&at=1234&pt=212&ct=1234&mt=1&preview_type=2&social_title=%E6%8E%A5%E5%85%A5%E4%BF%83%E9%94%80&landing_page_type=1&&region_id=0

Short link

If a long link is too long, it can be converted to a short link. A short link follows this format:

URL prefix+Random suffix of the string type

Prerequisite

  1. Harmony OS phone.
  2. Java JDK.
  3. DevEco Studio.
  4. AppGallery Account

App Development

  1. Create a New Project.
  2. Configure Project Gradle.
  3. Configure App Gradle.
  4. Configure Config.json.
  5. Create Ability class with XML UI.

MainAbility.java:

package com.hos.applinkharmony;import com.hos.applinkharmony.slice.MainAbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
}
}

MainAbilitySlice.java:

package com.hos.applinkharmony.slice;import com.hos.applinkharmony.ResourceTable;
import com.hos.applinkharmony.utils.DoubleLineListItemFactory;
import com.hos.applinkharmony.utils.RichTextFactory;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Image;
import ohos.agp.components.Text;
import ohos.agp.components.ScrollView;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ElementScatter;
import ohos.agp.text.RichText;
import ohos.bundle.AbilityInfo;
import ohos.global.configuration.Configuration;
import ohos.utils.net.Uri;
/**
* MainAbilitySlice
*/
public class MainAbilitySlice extends AbilitySlice {
private static final int OVER_SCROLL_PERCENT = 20;
private static final float OVER_SCROLL_RATE = 1.0f;
private static final int REMAIN_VISIBLE_PERCENT = 20;
private static final int ITEM_NUM = 3;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
int orientation = getResourceManager().getConfiguration().direction;
if (orientation == Configuration.DIRECTION_HORIZONTAL) {
super.setUIContent(ResourceTable.Layout_ability_main_landscape);
} else {
super.setUIContent(ResourceTable.Layout_ability_main);
initScrollView();
initItems();
}
initRichText();
initAppBar();
}
@Override
protected void onOrientationChanged(AbilityInfo.DisplayOrientation displayOrientation) {
if (displayOrientation == AbilityInfo.DisplayOrientation.LANDSCAPE) {
setUIContent(ResourceTable.Layout_ability_main_landscape);
} else if (displayOrientation == AbilityInfo.DisplayOrientation.PORTRAIT) {
setUIContent(ResourceTable.Layout_ability_main);
initScrollView();
initItems();
}
initRichText();
initAppBar();
}
private void shareAppLink(){
AppLinking.Builder builder = AppLinking.newBuilder()
.setUriPrefix("https://example.xxx.agconnect.link")
.setDeepLink(Uri.parse("https://www.example.com/detail?id=123"))
.setHarmonyLinkInfo(AppLinking.HarmonyLinkInfo.newBuilder()
.setHarmonyDeepLink("agckit://www.example.com/detail?id=123")
.build())
.setIOSLinkInfo(AppLinking.IOSLinkInfo.newBuilder()
.setIOSDeepLink("agckit://example/detail")
.setBundleId("com.example.ios")
.setITunesConnectCampaignInfo(AppLinking.ITunesConnectCampaignInfo.newBuilder()
.setMediaType("MediaType")
.setProviderToken("ProviderToken")
.setAffiliateToken("AffiliateToken")
.build())
.build())
.setSocialCardInfo(AppLinking.SocialCardInfo.newBuilder()
.setTitle("Title")
.setImageUrl("https://example.com/1.png")
.setDescription("Description").build())
.setCampaignInfo(AppLinking.CampaignInfo.newBuilder()
.setName("name")
.setSource("AGC")
.setMedium("App")
.build())
.setPreviewType(AppLinking.LinkingPreviewType.AppInfo);
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
private void initAppBar() {
DirectionalLayout backButton = (DirectionalLayout)
findComponentById(ResourceTable.Id_appBar_backButton_touchTarget);
Image backButtonImage = (Image) findComponentById(ResourceTable.Id_appBar_backButton);
if (backButtonImage.getLayoutDirectionResolved() == Component.LayoutDirection.RTL) {
Element buttonImage = ElementScatter.getInstance(this).parse(ResourceTable.Graphic_ic_back_mirror);
backButtonImage.setImageElement(buttonImage);
}
backButton.setClickedListener(component -> onBackPressed());
}
private void initRichText() {
RichTextFactory richTextFactory = new RichTextFactory(getContext());
richTextFactory.addClickableText("XXXX XXXX XXXXXXXX");
RichText openSourceText = richTextFactory.getRichText();
Text openSourceTextContainer = (Text) findComponentById(ResourceTable.Id_openSourceNoticeText);
openSourceTextContainer.setRichText(openSourceText);
richTextFactory.clean();
richTextFactory.addClickableText("XXXXX XXXX XXXXXXXX");
richTextFactory.addNormalText(" XXX ");
richTextFactory.addClickableText("XXXXX XXXX XXXXX XXX XXXXXX");
RichText protocolPrivacyText = richTextFactory.getRichText();
Text protocolPrivacyTextContainer = (Text) findComponentById(ResourceTable.Id_protocolPrivacyText);
protocolPrivacyTextContainer.setRichText(protocolPrivacyText);
}
private void initScrollView() {
ScrollView scrollView = (ScrollView) findComponentById(ResourceTable.Id_aboutPageScrollView);
scrollView.setReboundEffectParams(OVER_SCROLL_PERCENT, OVER_SCROLL_RATE, REMAIN_VISIBLE_PERCENT);
scrollView.setReboundEffect(true);
}
private void initItems() {
DoubleLineListItemFactory doubleLineListItemFactory = new DoubleLineListItemFactory(getContext());
DirectionalLayout aboutPageList = (DirectionalLayout) findComponentById(ResourceTable.Id_aboutPageLowerPart);
aboutPageList.removeAllComponents();
// Add ITEM_NUM - 1 Components, manually hide the last component's divider
for (int i = 0; i < ITEM_NUM - 1; i++) {
aboutPageList.addComponent(doubleLineListItemFactory
.getDoubleLineList("XXXXXXX XXXXXX", "XXXXX.XXXXX.com"));
}
DirectionalLayout lastItem = doubleLineListItemFactory
.getDoubleLineList("XXXXXXX XXXXXX", "XXXXX.XXXXX.com");
lastItem.findComponentById(ResourceTable.Id_divider).setVisibility(Component.INVISIBLE);
aboutPageList.addComponent(lastItem);
}
}

main_ability.xml:

<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:id="$+id:ability_main"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<DirectionalLayout
ohos:id="$+id:appBar"
ohos:height="$float:height_appBar"
ohos:width="match_parent"
ohos:layout_direction="locale"
ohos:orientation="horizontal">
<DirectionalLayout
ohos:id="$+id:appBar_backButton_touchTarget"
ohos:height="$float:height_appBar_backButton_touchTarget"
ohos:width="$float:width_appBar_backButton_touchTarget"
ohos:alignment="center"
ohos:layout_direction="locale"
ohos:start_margin="$float:leftMargin_appBar_backButton_touchTarget">
<Image
ohos:id="$+id:appBar_backButton"
ohos:height="$float:size_appBar_backButton"
ohos:width="$float:size_appBar_backButton"
ohos:image_src="$graphic:back"
ohos:layout_direction="locale"/>
</DirectionalLayout>
<Text
ohos:id="$+id:appBar_title"
ohos:height="match_parent"
ohos:width="match_content"
ohos:start_margin="$float:leftMargin_appBar_title"
ohos:text="$string:title"
ohos:text_size="$float:textSize_title"/>
</DirectionalLayout>
<DirectionalLayout
ohos:id="$+id:bottom"
ohos:height="match_content"
ohos:width="match_parent"
ohos:center_in_parent="true"
ohos:horizontal_center="true"
ohos:orientation="vertical">
<DirectionalLayout
ohos:height="1vp"
ohos:width="match_parent"
ohos:end_margin="8vp"
ohos:top_margin="20vp"
ohos:start_margin="8vp"/>
<Button
ohos:id="$+id:loginHuaweiButton"
ohos:height="40vp"
ohos:width="match_parent"
ohos:background_element="$graphic:background_login"
ohos:text="Share Link"
ohos:text_alignment="center"
ohos:text_color="#F2FFFFFF"
ohos:top_margin="20vp"/>
</DirectionalLayout>
<ScrollView
ohos:id="$+id:aboutPageScrollView"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:below="$id:appBar">
<DependentLayout
ohos:id="$+id:aboutPageMain"
ohos:height="match_content"
ohos:width="match_parent"
ohos:min_height="$float:aboutPage_minHeight"
ohos:orientation="vertical">
<DirectionalLayout
ohos:id="$+id:aboutPageUpperPart"
ohos:height="$float:height_aboutPage_upperPart"
ohos:width="match_parent"
ohos:align_parent_top="true"
ohos:alignment="horizontal_center"
ohos:orientation="vertical">
<!-- TODO: Set the app icon here-->
<Image
ohos:id="$+id:aboutPageIcon"
ohos:height="$float:size_aboutPage_iconBackground"
ohos:width="$float:size_aboutPage_iconBackground"
ohos:alignment="center"
ohos:image_src="$media:icon"
ohos:top_margin="$float:topMargin_aboutPage_iconBackground"/>
<Text
ohos:id="$+id:aboutPageTitlePrimary"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="$string:aboutPage_title_primary"
ohos:text_color="$color:color_aboutPage_title_primary"
ohos:text_size="$float:size_aboutPage_title_primary"
ohos:top_margin="$float:topMargin_aboutPage_title_primary"/>
<Text
ohos:id="$+id:aboutPageTitleSecondary"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="$string:aboutPage_title_secondary"
ohos:text_color="$color:color_aboutPage_title_secondary"
ohos:text_size="$float:size_aboutPage_title_secondary"/>
</DirectionalLayout>
<DirectionalLayout
ohos:id="$+id:aboutPageLowerPart"
ohos:height="match_content"
ohos:width="match_parent"
ohos:background_element="$graphic:stacklayout_background"
ohos:below="$id:aboutPageUpperPart"
ohos:end_margin="$float:card_margin_end"
ohos:orientation="vertical"
ohos:start_margin="$float:card_margin_start"/>
<DirectionalLayout
ohos:id="$+id:aboutPageBottomPart"
ohos:height="match_content"
ohos:width="match_parent"
ohos:align_parent_bottom="true"
ohos:alignment="horizontal_center"
ohos:below="$+id:aboutPageLowerPart"
ohos:bottom_padding="$float:default_padding_bottom_fixed"
ohos:end_padding="$float:maxPadding_end"
ohos:orientation="vertical"
ohos:start_padding="$float:maxPadding_start"
ohos:top_padding="$float:default_padding_top_fixed">
<Text
ohos:id="$+id:openSourceNoticeText"
ohos:height="match_content"
ohos:width="match_parent"
ohos:layout_direction="locale"
ohos:text_alignment="center"
ohos:text_size="$float:textSize_body3"/>
<Text
ohos:id="$+id:protocolPrivacyText"
ohos:height="match_content"
ohos:width="match_parent"
ohos:layout_direction="locale"
ohos:multiple_lines="true"
ohos:text_alignment="center"
ohos:text_size="$float:textSize_body3"/>
<Text
ohos:id="$+id:copyrightText"
ohos:height="match_content"
ohos:width="match_parent"
ohos:layout_direction="locale"
ohos:text="$string:copyright_text"
ohos:text_alignment="center"
ohos:text_color="$color:textColor_secondary"
ohos:text_size="$float:textSize_body3"/>
<Text
ohos:id="$+id:technicalSupportText"
ohos:height="match_content"
ohos:width="match_parent"
ohos:layout_direction="locale"
ohos:text="$string:technicalSupport_text"
ohos:text_alignment="center"
ohos:text_color="$color:textColor_secondary"
ohos:text_size="$float:textSize_body3"/>
</DirectionalLayout>
</DependentLayout>
</ScrollView>
</DependentLayout>

App Gallery Integration process

  1. Sign In and Create or Choose a project on AppGallery Connect portal.
  2. Navigate to Project settings and download the configuration file.
  3. Navigate to General Information, and then provide Data Storage location.
  4. Navigate to Manage APIs and enable APIs which is required by application.
  5. Navigate to AppLinking and Enable.
  6. Add New link.
  7. Navigate to App Linking and select Set short url.
  8. Copy Domain Name and add in your project.

App Build Result

Tips and Tricks

Huawei strictly conforms to the General Data Protection Regulation (GDPR) in providing services and is dedicated to helping developers achieve business success under the principles of the GDPR. The GDPR stipulates the obligations of the data controller and data processor. When using our service, you act as the data controller, and Huawei is the data processor. Huawei solely processes data within the scope of the data processor’s obligations and rights, and you must assume all obligations of the data controller as specified by the GDPR.

Conclusion

In this article, we have learned how to integrate AppLinking in Harmony OS application. In this application, I have explained that how to deep link our application with URL.

Thanks for reading this article. Be sure to like and comments to this article, if you found it helpful. It means a lot to me.

References

HMS AppLinking Doc:

https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-applinking-createlinks-bysdk-harmonyos-0000001139959088

--

--