Deep linking

To allow an application to be deep linkable, aside the mandatory additions to AndroidManifest.xml, as seen on quickstart and setting up the SDK, it needs to map routes. These routes should follow the convention static/path/:variable/:another_variable.

Route mapping

Annotations

To map routes with HOKO through annotations you should add @DeeplinkRoute, @DeeplinkDefaultRoute @DeeplinkFragmentActivity annotations to your Activities and Fragments.

If an application which has a wishlist activity it should somehow map a product route by adding @DeeplinkRoute to your WishlistActivity.

@DeeplinkRoute("wishlist")
public class WishlistActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Hoko.deeplinking().inject(this);
  }
}

This will map a wishlist route to the WishlistActivity. This WishlistActivity will always be started when a deep link matching the route is opened in the user’s device. (e.g. opening hoko://wishlist).

Injection

HOKO works by injecting deep link parameters into Activity or Fragment variables. This happens on the inject(...) method on the deep linking module.

DeeplinkRoute

On classes annotated with @DeeplinkRoute, the injection will look for variables annotated @DeeplinkRouteParameter, @DeeplinkQueryParameter and a JSONObject annotated with @DeeplinkMetadata.

@DeeplinkRoute("products/:product_id")
public class ProductActivity extends Activity {
  @DeeplinkRouteParameter("product_id")
  private int mProductId;
  @DeeplinkQueryParameter("referrer")
  public String mReferrer;
  @DeeplinkMetadata
  public JSONObject metadata;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (!Hoko.deeplinking().inject(this)) {
      // This code will be executed if the activity was not started by Hoko
      mProductId = getIntent().getIntExtra("product_id", 0);
    } else {
      String coupon = metadata.optString("coupon");
      useCoupon(coupon);
    }
  }
}

When inject(...) is called, and the ProductActivity was started from a deep link such as hoko://products/42?referrer=hokolinks.com, it will set the mProductId as 42 and the mReferrer as "hokolinks.com".

The difference between route parameters and query parameters is how they appear in the actual deep link and if they are required or not for a route to be triggered.

Route parameters must always be matched and as such, if an Activity was injected with a deep link, a @DeeplinkRouteParameter variable will always have a value.

Query parameters are completly optional and may be null or 0, even if the Activity was injected with a given deep link.

Additionally we provide you with Metadata which consists on invisible information transfered from the Smartlink creation onto the actual opening of the contained deep link. This object is represented by a JSONObject and may contain whatever information you provided beforehand. This type of data is especially useful to guarantee data integrity (e.g. coupons, rewards, discounts) in order to avoid harmful users from exploiting visible deeplinking mechanics in your app.

DeeplinkFragmentActivity

In order to be able to deep link into fragments, HOKO has a @DeeplinkFragmentActivity. This annotation serves the purpose of identifying which View to replace with which possible DeeplinkRoute annotated Fragments.

@DeeplinkFragmentActivity(id = R.id.frame_layout, fragments = {WishlistFragment.class, ProductFragment.class})
public class MainActivity extends FragmentActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Hoko.deeplinking().inject(this);
  }
}

In which ProductFragment and WishlistFragment classes are annotated with @DeeplinkRoute.

@DeeplinkRoute("products/:product_id")
public class ProductFragment extends Fragment {
  @DeeplinkRouteParameter("product_id")
  String mProductId;
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_product, container, false);
    if (!Hoko.deeplinking().inject(this)) {
      // This code will be executed if the activity was not started by Hoko
      mProductId = getArguments().getInt("product_id", 0);
    }
    return rootView;
  }
}
Default route

In case you want to provide a given behavior for when an unmapped deep linking opens the app you can do so by using the @DeeplinkDefaultRoute annotation. Only deep links that do not match any existing routes will trigger the default route.

@DeeplinkDefaultRoute()
public class SplashActivity extends Activity {
  @DeeplinkQueryParameter("referrer")
  String mReferrer;
  @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      Hoko.deeplinking().inject(this);
    }
}

DeeplinkCallback

If you want to manually manage the deeplinking logic, all you have to do is map a route format to a DeeplinkCallback object after your Hoko.setup() call in your Application subclass.

Hoko.deeplinking().mapRoute("invite/:user_id", new DeeplinkCallback() {
  @Override
  public void deeplinkOpened(Deeplink deeplink) {
    String userId = deeplink.getRouteParameters().get("user_id");
    String inviteMessage = deeplink.getQueryParameters().get("invite_message");
    String friendCoupon = deeplink.getMetadata().optString("coupon_code")
    Social.getInstance().showInvitePopup(userId, inviteMessage, friendCoupon);
  }
})

This code will be executed on the HokoActivity class, if you want to create show something to the user you have to start an Activity on the deeplinkOpened() method.

Handlers

In order to execute code that is common to any incoming deep link (e.g. analytics tracking, referrer tracking, etc), the deeplinking module allows delegating and block execution of handlers.

If a class object implements the Handler interface and that object is added to the handler list, its handle(deeplink) method will be executed. p

// Analytics.java
public class Analytics implements Handler {
  ...
  @Override
  public void handle(Deeplink deeplink) {
    trackEvent("deeplink", deeplink.getRoute());
  }
}

If your class implements the interface you can then add it to the deep linking module after your Hoko.setup(...) method call.

Hoko.deeplinking().addHandler(Analytics.getInstance());

You can also use Handler as an anonymous class.

Hoko.deeplinking().addHandler(new Handler() {
  @Override
  public void handle(HokoDeeplink deeplink) {
    Analytics.getInstance().trackEvent("deeplink", deeplink.getRoute());
  }
});

To save time integrating HOKO in an application, HOKO does not require delegation of actual deep links, as long as HokoActivity is added to the AndroidManifest.xml with a URL scheme. In case you want a better control of how HOKO should operate, you can manually delegate deep links through the deep linking module.

Hoko.deeplinking().openURL(deeplink);

Smart links may be created on the dashboard or through the HOKO SDK, in order to allow users to share platform independent links directly to the relevant content.

To generate Smart links through templates, the application needs a route format, the corresponding route parameters and optional query parameters.

From @DeeplinkRoute

To generate Smart links from a @DeeplinkRoute annotated Activity or Fragment you can pass the object to the generateSmartlink(...) call.

Hoko.deeplinking().generateSmartlink(this, new LinkGenerationListener() {
  @Override
  public void onLinkGenerated(String smartlink) {
    Social.getInstance().shareProduct(mProduct.getName(), smartlink);
  }
  // Share product link in case smart link fails
  @Override
  public void onError(Exception exception) {
    Social.getInstance().shareProduct(mProduct.getName(), mProduct.getLink());
  }
});

Another way is to generate Smart links from a Deeplink object.

HashMap routeParameters = new HashMap();
routeParameters.put("product_id", mProduct.getId());
HashMap queryParameters = new HashMap();
queryParameters.put("referrer", "app");
JSONObject metadata = new JSONObject();
try {
  metadata.putOpt("coupon_code", "hokosale30");
} catch (JSONException e) {
  e.printStackTrace();
}
Deeplink deeplink = Deeplink.deeplink("products/:product_id", routeParameters, queryParameters, metadata);
Hoko.deeplinking().generateSmartlink(deeplink, new LinkGenerationListener() {
  @Override
  public void onLinkGenerated(String smartlink) {
    Social.getInstance().shareProduct(mProduct.getName(), smartlink);
  }
  @Override
  public void onError(Exception e) {
    Social.getInstance().shareProduct(mProduct.getName(), mProduct.getLink());
  }
});

To allow applications to link to content without the use of templates, the HOKO SDK allows the creation of Deeplink objects and the manual addition of platform dependent URLs.

HashMap routeParameters = new HashMap();
routeParameters.put("product_id", mProduct.getId());
HashMap queryParameters = new HashMap();
queryParameters.put("referrer", "app");
Deeplink deeplink = Deeplink.deeplink("products/:product_id", routeParameters, queryParameters);
deeplink.addURL("http://awesomeapp.com/the_perfect_product", DeeplinkPlatform.WEB);
deeplink.addURL("http://awesomeapp.com/no_ios_app_yet", DeeplinkPlatform.IOS);
Hoko.deeplinking().generateSmartlink(deeplink, new LinkGenerationListener() {
  @Override
  public void onLinkGenerated(String smartlink) {
    Social.getInstance().shareProduct(mProduct.getName(), smartlink);
  }
  @Override
  public void onError(Exception e) {
    Social.getInstance().shareProduct(mProduct.getName(), mProduct.getLink());
  }
});

Should you want to open a Smartlink (e.g. https://yourapp.hoko.link/example) and have it work as a deeplink in regards to the routing of Activities and Fragments, you can call openSmartlink(smartlink, smartlinkResolveListener).

Hoko.deeplinking().openSmartlink("https://yourapp.hoko.link/example", new SmartlinkResolveListener() {
  @Override
  public void onLinkResolved(String deeplink) {
    // deeplink was opened
  }
  @Override
  public void onError(Exception e) {
    // could not resolve Smartlink
  }
})