package com.hokolinks.deeplinking; import android.app.Activity; import android.content.Context; import android.support.v4.app.Fragment; import com.hokolinks.deeplinking.listeners.LinkGenerationListener; import com.hokolinks.deeplinking.listeners.SmartlinkResolveListener; import com.hokolinks.model.Deeplink; import com.hokolinks.model.DeeplinkCallback; import com.hokolinks.model.FilterCallback; import com.hokolinks.model.exceptions.LinkGenerationException; import com.hokolinks.utils.log.HokoLog; import com.hokolinks.utils.networking.Networking; import com.hokolinks.utils.networking.async.HttpRequest; import org.json.JSONException; import org.json.JSONObject; import java.lang.reflect.Field; import java.util.HashMap; /** * The Deeplinking module provides all the necessary APIs to map, handle and generate deeplinks. * Different APIs as provided in order to be as versatile as your application requires them to be. */ public class Deeplinking { private static final String INSTALL_PATH = "installs/android"; private Routing mRouting; private Handling mHandling; private Filtering mFiltering; private LinkGenerator mLinkGenerator; private Resolver mResolver; private String mToken; public Deeplinking(String token, Context context) { mToken = token; mHandling = new Handling(); mFiltering = new Filtering(); mRouting = new Routing(token, context, mHandling, mFiltering); mLinkGenerator = new LinkGenerator(token); mResolver = new Resolver(token, context); } // Map Routes /** * Maps a route to an activity class and its fields as route parameters or query parameters. * * @param route The route in route format. * @param activityClassName The activity's class name. * @param routeParameters The route parameters mapped from Strings to Fields. * @param queryParameters The query parameters mapped from Strings to Fields. */ void mapRoute(String route, String activityClassName, HashMap<String, Field> routeParameters, HashMap<String, Field> queryParameters) { mRouting.mapRoute(route, activityClassName, routeParameters, queryParameters); } /** * Maps a route with a specific callback that will be executed whenever the mapped deep link * is opened. * * @param route The route in route format. * @param callback The callback that will be executed when the mapped deep link is opened. */ public void mapRoute(String route, DeeplinkCallback callback) { mRouting.mapRoute(route, callback); } /** * Maps an activity class as a default route and its fields as query parameters. * * @param activityClassName The activity's class name. * @param queryParameters The query parameters mapped from Strings to Fields. */ void mapDefaultRoute(String activityClassName, HashMap<String, Field> queryParameters) { mapRoute(null, activityClassName, null, queryParameters); } /** * Maps the default route with a specific callback. Only deep links that do not match any * existing routes will trigger the default route. * * @param callback The callback that will be executed when the opened deep link does not match * any existing routes. */ public void mapDefaultRoute(DeeplinkCallback callback) { mapRoute(null, callback); } /** * inject(activity) should be called on your DeeplinkRoute activities' onCreate(...) method. * It will try to map the current deeplink to annotated DeeplinkRouteParameters or * DeeplinkQueryParameters, on that particular activity instance. * In case it is not possible to map a deeplink or one does not exist, this function will return * false. * <pre>{@code * Hoko.deeplinking().inject(this); * }</pre> * * @param activity Your activity instance. * @return true in case of success, false in case of failure or non-existent deeplink. */ public boolean inject(Activity activity) { return mRouting.inject(activity); } /** * inject(fragment) should be called on your DeeplinkRoute fragment' onCreateView(...) * method. It will try to map the current deeplink to annotated DeeplinkRouteParameters or * DeeplinkQueryParameters, on that particular fragment instance. * In case it is not possible to map a deeplink or one does not exist, this function will return * false. * <pre>{@code * Hoko.deeplinking().inject(this); * }</pre> * * @param fragment Your fragment instance. * @return true in case of success, false in case of failure or non-existent deeplink. */ public boolean inject(android.app.Fragment fragment) { return mRouting.inject(fragment); } /** * inject(fragment) should be called on your DeeplinkRoute fragment' onCreateView(...) * method. It will try to map the current deeplink to annotated DeeplinkRouteParameters or * DeeplinkQueryParameters, on that particular fragment instance. * In case it is not possible to map a deeplink or one does not exist, this function will return * false. * <pre>{@code * Hoko.deeplinking().inject(this); * }</pre> * * @param fragment Your fragment instance. * @return true in case of success, false in case of failure or non-existent deeplink. */ public boolean inject(Fragment fragment) { return mRouting.inject(fragment); } /** * openURL(urlString) is called when HokoActivity receives a deeplink Intent from the Android * OS. * It returns true or false depending on whether it has such a deeplink mapped. * * @param urlString The url passed on the intent. * @return true in case of success, false in case of failure or non-existent deeplink. */ public boolean openURL(String urlString) { return openURL(urlString, null); } /** * openURL(urlString) is called when HokoActivity receives a deeplink Intent from the Android * OS. * It returns true or false depending on whether it has such a deeplink mapped. * * @param urlString The url passed on the intent. * @param metadata The metadata in JSON format which was passed when the smartlink was created. * @return true in case of success, false in case of failure or non-existent deeplink. */ public boolean openURL(String urlString, JSONObject metadata) { return mRouting.openURL(urlString, metadata, false); } /** * openDeferredURL(urlString) is called when DeferredDeeplinkingBroadcastReceiver receives a * deeplink Intent from Google Play. * * @param urlString The url passed on the intent. */ void openDeferredURL(String urlString) { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("deeplink", urlString); } catch (JSONException e) { HokoLog.e(e); } Networking.getNetworking().addRequest( new HttpRequest(HttpRequest.HokoNetworkOperationType.POST, HttpRequest.getURLFromPath(INSTALL_PATH), mToken, jsonObject.toString())); mRouting.openURL(urlString, null, true); } /** * openSmartlink(smartlink) should be called when a Smartlink needs to be resolved into a * deeplink to open the correct view. e.g. Opening a Smartlink from a push notification. * * @param smartlink A smartlink string. */ public void openSmartlink(String smartlink) { openSmartlink(smartlink, null); } /** * openSmartlink(smartlink) should be called when a Smartlink needs to be resolved into a * deeplink to open the correct view. e.g. Opening a Smartlink from a push notification. * * @param smartlink A smartlink string. * @param smartlinkResolveListener A link resolved listener for lifecycle purposes, called * before opening the deeplink. */ public void openSmartlink(String smartlink, final SmartlinkResolveListener smartlinkResolveListener) { mResolver.resolveSmartlink(smartlink, new SmartlinkResolveListener() { @Override public void onLinkResolved(String deeplink, JSONObject metadata) { if (smartlinkResolveListener != null) { smartlinkResolveListener.onLinkResolved(deeplink, metadata); } openURL(deeplink, metadata); } @Override public void onError(Exception e) { if (smartlinkResolveListener != null) { smartlinkResolveListener.onError(e); } } }); } /** * This method will return the current the last deep link that was processed (whether it was * sucessfully opened, or not, due to filters) by the HOKO SDK. If no deep links were processed * at the time of the call, this will return null. * * @return The last deeplink object that was processed by the SDK. */ public Deeplink getCurrentDeeplink() { return mRouting.getCurrentDeeplink(); } /** * This method will try to open the last deep link that was processed (whether it was * sucessfully opened, or not, due to filters) by calling the route that is currently mapping * this deeplink and the handlers. * If the deeplink object is not nil and was opened during this call, the method will return * true, otherwise false. * * @return Returns true if the current deeplink object was sucessfully opened, false otherwise. */ public boolean openCurrentDeeplink() { return mRouting.openCurrentDeeplink(); } /** * This method will try to open the deeplink object given in the parameters, * by calling the route that is currently mapping that deeplink and the handlers. * If the deeplink was opened during this call, the method will return true, otherwise false * * @param deeplink The deeplink object that will be opened * @return true if the deeplink object given in the parameters was successfully opened, false * otherwise. */ public boolean openDeeplink(Deeplink deeplink) { return mRouting.openDeeplink(deeplink); } // Handlers /** * With addHandler() you can add an object which implements the DeeplinkCallback interface to be * called every time your application opens a deeplink. This allows you to track incoming * deeplinks outside of the deeplinking targets. * <pre>{@code * Hoko.deeplinking().addHandler(new DeeplinkCallback() { * public void deeplinkOpened(Deeplink deeplink) { * Log.d("HOKO", deeplink.toString()); * }}); * }</pre> * * @param callback An object which implements the DeeplinkCallback interface. */ public void addHandler(DeeplinkCallback callback) { mHandling.addHandler(callback); } /** * With removeHandler() you can remove a previously added DeeplinkCallback object. * <pre>{@code * Hoko.deeplinking().removeHandler(analyticsHandler); * }</pre> * * @param callback An object which implements the DeeplinkCallback interface. * @return true if the handler was removed, false otherwise. */ public boolean removeHandler(DeeplinkCallback callback) { return mHandling.removeHandler(callback); } // Filters /** * With addFilter() you can add an object which implements the FilterCallback interface to be * called every time your application opens a deeplink. This allows to filter out deeplinks on * your application. * <pre>{@code * Hoko.deeplinking().addFilter(new FilterCallback() { * public boolean openDeeplink(Deeplink deeplink) { * return user.isLoggedIn(); * }}); * }</pre> * * @param filterCallback An object which implements the FilterCallback interface. */ public void addFilter(FilterCallback filterCallback) { mFiltering.addFilter(filterCallback); } /** * With removeFilter() you can remove a previously added FilterCallback object. * <pre>{@code * Hoko.deeplinking().removeFilter(userLoggedInFilter); * }</pre> * * @param filterCallback An object which implements the DeeplinkCallback interface. * @return true if the handler was removed, false otherwise. */ public boolean removeFilter(FilterCallback filterCallback) { return mFiltering.removeFilter(filterCallback); } // Link Generation /** * generateSmartlink(deeplink, listener) allows the app to generate Smartlinks for the * user to share with other users, independent of the platform, users will be redirected to the * corresponding view. A user generated Deeplink object may be passed along to generate the * deeplinks for all available platforms. In case the request is successful, the onLinkGenerated * function will be called receiving an Smartlink (e.g. http://hoko.io/XmPle). Otherwise it will * return the cause of failure in the onError function. * <pre>{@code * HashMap<String, String> routeParameters = new HashMap<String, String>(); * routeParameters.put("id", "30"); * Hoko.deeplinking().generateSmartlink(Deeplink * .deeplink("products/:id", routeParameters, null), new LinkGenerationListener() { * public void onLinkGenerated(String smartlink) { * shareLink(smartlink); * } * public void onError(Exception exception) { * exception.printStackTrace(); * }}); * }</pre> * * @param deeplink A Deeplink object. * @param listener A HokoLinkGenerationLister instance. */ public void generateSmartlink(Deeplink deeplink, LinkGenerationListener listener) { mLinkGenerator.generateSmartlink(deeplink, listener); } /** * generateSmartlink(activity, listener) allows the app to generate Hoko Smartlinks for the * user to share with other users, independent of the platform users will be redirected to the * corresponding view. An activity annotated with DeeplinkRoute may be passed along to * generate the deeplinks for all available platforms. In case the request is successful, the * onLinkGenerated function will be called receiving an smartlink (e.g. http://hoko.io/XmPle). * Otherwise it will return the cause of failure in the onError function. * <pre>{@code * Hoko.deeplinking().generateSmartlink(this, new LinkGenerationListener() { * public void onLinkGenerated(String smartlink) { * shareLink(smartlink); * } * public void onError(Exception exception) { * exception.printStackTrace(); * }}); * }</pre> * * @param activity An activity annotated with DeeplinkRoute. * @param listener A LinkGenerationLister instance. */ public void generateSmartlink(Activity activity, LinkGenerationListener listener) { Deeplink deeplink = AnnotationParser.deeplinkFromActivity(activity); if (deeplink != null) { generateSmartlink(deeplink, listener); } else { listener.onError(new LinkGenerationException()); } } /** * generateSmartlink(fragment, listener) allows the app to generate Smartlinks for the * user to share with other users, independent of the platform users will be redirected to the * corresponding view. A fragment annotated with DeeplinkRoute may be passed along to * generate the deeplinks for all available platforms. In case the request is successful, the * onLinkGenerated function will be called receiving an smartlink (e.g. http://hoko.io/XmPle). * Otherwise it will return the cause of failure in the onError function. * <pre>{@code * Hoko.deeplinking().generateSmartlink(this, new LinkGenerationListener() { * public void onLinkGenerated(String smartlink) { * shareLink(smartlink); * } * public void onError(Exception exception) { * exception.printStackTrace(); * }}); * }</pre> * * @param fragment A fragment annotated with DeeplinkRoute. * @param listener A HokoLinkGenerationLister instance. */ public void generateSmartlink(Fragment fragment, LinkGenerationListener listener) { Deeplink deeplink = AnnotationParser.deeplinkFromFragment(fragment); if (deeplink != null) { generateSmartlink(deeplink, listener); } else { listener.onError(new LinkGenerationException()); } } /** * generateSmartlink(fragment, listener) allows the app to generate Smartlinks for the * user to share with other users, independent of the platform users will be redirected to the * corresponding view. A fragment annotated with DeeplinkRoute may be passed along to * generate the deeplinks for all available platforms. In case the request is successful, the * onLinkGenerated function will be called receiving an smartlink (e.g. http://hoko.io/XmPle). * Otherwise it will return the cause of failure in the onError function. * <pre>{@code * Hoko.deeplinking().generateSmartlink(this, new LinkGenerationListener() { * public void onLinkGenerated(String smartlink) { * shareLink(smartlink); * } * public void onError(Exception exception) { * exception.printStackTrace(); * }}); * }</pre> * * @param fragment A fragment annotated with DeeplinkRoute. * @param listener A HokoLinkGenerationLister instance. */ public void generateSmartlink(android.app.Fragment fragment, LinkGenerationListener listener) { Deeplink deeplink = AnnotationParser.deeplinkFromFragment(fragment); if (deeplink != null) { generateSmartlink(deeplink, listener); } else { listener.onError(new LinkGenerationException()); } } /** * generateLazySmartlink(deeplink, domain) allows the app to generate lazy Smartlinks for the * user to share with other users, independent of the platform, users will be redirected to the * corresponding view. A user generated Deeplink object may be passed along to generate the * deeplinks for all available platforms. In case the translation is possible, the method will * return a lazy Smartlink (e.g. http://yourapp.hoko.link/lazy?uri=%2Fproduct%2F0 ). * Where the uri query parameter will be the url encoded version of the translated deep link. * * @param deeplink A Deeplink object. * @param domain The domain to which HOKO should generate a lazy Smartlink. * (e.g. yourapp.hoko.link or yourapp.customdomain.com). */ public String generateLazySmartlink(Deeplink deeplink, String domain) { return mLinkGenerator.generateLazySmartlink(deeplink, domain); } /** * generateLazySmartlink(fragment, domain) allows the app to generate lazy Smartlinks for the * user to share with other users, independent of the platform, users will be redirected to the * corresponding view. A fragment annotated with DeeplinkRoute may be passed along to generate * the deeplinks for all available platforms. In case the translation is possible, the method * will return a lazy Smartlink (e.g. http://yourapp.hoko.link/lazy?uri=%2Fproduct%2F0 ). * Where the uri query parameter will be the url encoded version of the translated deep link. * * @param fragment A fragment annotated with DeeplinkRoute. * @param domain The domain to which HOKO should generate a lazy Smartlink. * (e.g. yourapp.hoko.link or yourapp.customdomain.com). */ public String generateLazySmartlink(android.app.Fragment fragment, String domain) { return generateLazySmartlink(AnnotationParser.deeplinkFromFragment(fragment), domain); } /** * generateLazySmartlink(fragment, domain) allows the app to generate lazy Smartlinks for the * user to share with other users, independent of the platform, users will be redirected to the * corresponding view. A fragment annotated with DeeplinkRoute may be passed along to generate * the deeplinks for all available platforms. In case the translation is possible, the method * will return a lazy Smartlink (e.g. http://yourapp.hoko.link/lazy?uri=%2Fproduct%2F0 ). * Where the uri query parameter will be the url encoded version of the translated deep link. * * @param fragment A fragment annotated with DeeplinkRoute. * @param domain The domain to which HOKO should generate a lazy Smartlink. * (e.g. yourapp.hoko.link or yourapp.customdomain.com). */ public String generateLazySmartlink(Fragment fragment, String domain) { return generateLazySmartlink(AnnotationParser.deeplinkFromFragment(fragment), domain); } /** * generateLazySmartlink(fragment, domain) allows the app to generate lazy Smartlinks for the * user to share with other users, independent of the platform, users will be redirected to the * corresponding view. An activity annotated with DeeplinkRoute may be passed along to generate * the deeplinks for all available platforms. In case the translation is possible, the method * will return a lazy Smartlink (e.g. http://yourapp.hoko.link/lazy?uri=%2Fproduct%2F0 ). * Where the uri query parameter will be the url encoded version of the translated deep link. * * @param activity An activity annotated with DeeplinkRoute. * @param domain The domain to which HOKO should generate a lazy Smartlink. * (e.g. yourapp.hoko.link or yourapp.customdomain.com). */ public String generateLazySmartlink(Activity activity, String domain) { return generateLazySmartlink(AnnotationParser.deeplinkFromActivity(activity), domain); } public Routing routing() { return mRouting; } Handling handling() { return mHandling; } }