diff --git a/Bolts/src/bolts/HtmlAppLinkResolver.java b/Bolts/src/bolts/HtmlAppLinkResolver.java new file mode 100644 index 0000000..9855e8f --- /dev/null +++ b/Bolts/src/bolts/HtmlAppLinkResolver.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +package bolts; + +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.Editable; +import android.text.Html; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/** + * A simple implementation for an App Link resolver that parses the HTML containing App Link metadata + * using {@link android.text.Html#fromHtml}. + */ +public class HtmlAppLinkResolver implements AppLinkResolver { + /** + * Creates an {@code HtmlAppLinkResolver}. + */ + public HtmlAppLinkResolver() { + } + + private static final String META_TAG = "meta"; + private static final String META_TAG_PREFIX = "al:"; + private static final String META_ATTR_PROPERTY = "property"; + private static final String META_ATTR_CONTENT = "content"; + + @Override + public Task getAppLinkFromUrlInBackground(final Uri url) { + return ResolverUtils.fetchUrl(url) + .onSuccess(new Continuation() { + @Override + public JSONArray then(Task task) throws Exception { + return parseMetaTags(task.getResult().getContent()); + } + }).onSuccess(new Continuation() { + @Override + public AppLink then(Task task) throws Exception { + return ResolverUtils.parseAppLinkFromAlData(task.getResult(), url); + } + }); + } + + private static JSONArray parseMetaTags(String html) throws Exception { + final MetaTagHandler metaTagHandler = new MetaTagHandler(); + Html.fromHtml(html, metaTagHandler, metaTagHandler); + + return metaTagHandler.getJsonArray(); + } + + private static class MetaTagHandler extends DefaultHandler implements Html.TagHandler, Html.ImageGetter { + + private JSONArray jsonArray = new JSONArray(); + + @Override + public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { + xmlReader.setContentHandler(this); + } + + @Override + public Drawable getDrawable(String source) { + return null; + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (META_TAG.equalsIgnoreCase(localName)) { + addMetaElement(attributes); + } + } + + public JSONArray getJsonArray() { + return jsonArray; + } + + private void addMetaElement(Attributes attributes) throws SAXException { + String property = attributes.getValue(META_ATTR_PROPERTY); + + if (property != null && property.startsWith(META_TAG_PREFIX)) { + String content = attributes.getValue(META_ATTR_CONTENT); + + JSONObject metaObject = new JSONObject(); + try { + metaObject.put(META_ATTR_PROPERTY, property); + if (content != null) { + metaObject.put(META_ATTR_CONTENT, content); + } + } catch (JSONException e) { + throw new SAXException(e); + } + + jsonArray.put(metaObject); + } + } + + } +} diff --git a/Bolts/src/bolts/MeasurementEvent.java b/Bolts/src/bolts/MeasurementEvent.java index 6447ef8..6f49956 100644 --- a/Bolts/src/bolts/MeasurementEvent.java +++ b/Bolts/src/bolts/MeasurementEvent.java @@ -7,7 +7,6 @@ * of patent rights can be found in the PATENTS file in the same directory. * */ - package bolts; import android.content.ComponentName; diff --git a/Bolts/src/bolts/ResolverUtils.java b/Bolts/src/bolts/ResolverUtils.java new file mode 100644 index 0000000..e20e899 --- /dev/null +++ b/Bolts/src/bolts/ResolverUtils.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +package bolts; + +import android.net.Uri; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +public class ResolverUtils { + + private static final String PREFER_HEADER = "Prefer-Html-Meta-Tags"; + private static final String META_TAG_PREFIX = "al"; + + private static final String KEY_AL_VALUE = "value"; + private static final String KEY_APP_NAME = "app_name"; + private static final String KEY_CLASS = "class"; + private static final String KEY_PACKAGE = "package"; + private static final String KEY_URL = "url"; + private static final String KEY_SHOULD_FALLBACK = "should_fallback"; + private static final String KEY_WEB_URL = "url"; + private static final String KEY_WEB = "web"; + private static final String KEY_ANDROID = "android"; + + private ResolverUtils() { + } + + /** + * Fetch the content from the specified URL. + * + * @param url the URL to fetch the content from. + * @return the page content as a {@code StringEntity}. + */ + public static Task fetchUrl(final Uri url) { + return Task.callInBackground(new Callable() { + @Override + public StringEntity call() throws Exception { + URL currentURL = new URL(url.toString()); + URLConnection connection = null; + + while (currentURL != null) { + // Fetch the content at the given URL. + connection = currentURL.openConnection(); + if (connection instanceof HttpURLConnection) { + // Unfortunately, this doesn't actually follow redirects if they go from http->https, + // so we have to do that manually. + ((HttpURLConnection) connection).setInstanceFollowRedirects(true); + } + connection.setRequestProperty(PREFER_HEADER, META_TAG_PREFIX); + connection.connect(); + + if (connection instanceof HttpURLConnection) { + HttpURLConnection httpConnection = (HttpURLConnection) connection; + if (httpConnection.getResponseCode() >= 300 && httpConnection.getResponseCode() < 400) { + currentURL = new URL(httpConnection.getHeaderField("Location")); + httpConnection.disconnect(); + } else { + currentURL = null; + } + } else { + currentURL = null; + } + } + + try { + String content = readFromConnection(connection); + String contentType = connection.getContentType(); + return new StringEntity(content, contentType); + } finally { + if (connection instanceof HttpURLConnection) { + ((HttpURLConnection) connection).disconnect(); + } + } + } + }); + } + + /** + * Parse an AppLink from the specified al data specified in a JSON array. + * + * @param dataArray the al meta tag data. + * @param url the URL of the AppLink. + * @return the AppLink. + * @throws JSONException if there is an error parsing the JSON array. + */ + public static AppLink parseAppLinkFromAlData(JSONArray dataArray, Uri url) throws JSONException { + Map alData = parseAlData(dataArray); + return makeAppLinkFromAlData(alData, url); + } + + /** + * Builds up a data structure filled with the app link data from the meta tags on a page. + * The structure of this object is a dictionary where each key holds an array of app link + * data dictionaries. Values are stored in a key called "_value". + */ + private static Map parseAlData(JSONArray dataArray) throws JSONException { + HashMap al = new HashMap(); + for (int i = 0; i < dataArray.length(); i++) { + JSONObject tag = dataArray.getJSONObject(i); + String name = tag.getString("property"); + String[] nameComponents = name.split(":"); + if (!nameComponents[0].equals(META_TAG_PREFIX)) { + continue; + } + Map root = al; + for (int j = 1; j < nameComponents.length; j++) { + @SuppressWarnings("unchecked") + List> children = + (List>) root.get(nameComponents[j]); + if (children == null) { + children = new ArrayList>(); + root.put(nameComponents[j], children); + } + Map child = children.size() > 0 ? children.get(children.size() - 1) : null; + if (child == null || j == nameComponents.length - 1) { + child = new HashMap(); + children.add(child); + } + root = child; + } + if (tag.has("content")) { + if (tag.isNull("content")) { + root.put(KEY_AL_VALUE, null); + } else { + root.put(KEY_AL_VALUE, tag.getString("content")); + } + } + } + return al; + } + + @SuppressWarnings("unchecked") + private static List> getAlList(Map map, String key) { + List> result = (List>) map.get(key); + if (result == null) { + return Collections.emptyList(); + } + return result; + } + + @SuppressWarnings("unchecked") + private static AppLink makeAppLinkFromAlData(Map appLinkDict, Uri destination) { + List targets = new ArrayList(); + List> platformMapList = + (List>) appLinkDict.get(KEY_ANDROID); + if (platformMapList == null) { + platformMapList = Collections.emptyList(); + } + for (Map platformMap : platformMapList) { + // The schema requires a single url/package/app name/class, but we could find multiple + // of them. We'll make a best effort to interpret this data. + List> urls = getAlList(platformMap, KEY_URL); + List> packages = getAlList(platformMap, KEY_PACKAGE); + List> classes = getAlList(platformMap, KEY_CLASS); + List> appNames = getAlList(platformMap, KEY_APP_NAME); + + int maxCount = Math.max(urls.size(), + Math.max(packages.size(), Math.max(classes.size(), appNames.size()))); + for (int i = 0; i < maxCount; i++) { + String urlString = (String) (urls.size() > i ? + urls.get(i).get(KEY_AL_VALUE) : null); + Uri url = tryCreateUrl(urlString); + String packageName = (String) (packages.size() > i ? + packages.get(i).get(KEY_AL_VALUE) : null); + String className = (String) (classes.size() > i ? + classes.get(i).get(KEY_AL_VALUE) : null); + String appName = (String) (appNames.size() > i ? + appNames.get(i).get(KEY_AL_VALUE) : null); + AppLink.Target target = new AppLink.Target(packageName, className, url, appName); + targets.add(target); + } + } + + Uri webUrl = destination; + List> webMapList = (List>) appLinkDict.get(KEY_WEB); + if (webMapList != null && webMapList.size() > 0) { + Map webMap = webMapList.get(0); + List> urls = (List>) webMap.get(KEY_WEB_URL); + List> shouldFallbacks = + (List>) webMap.get(KEY_SHOULD_FALLBACK); + if (shouldFallbacks != null && shouldFallbacks.size() > 0) { + String shouldFallbackString = (String) shouldFallbacks.get(0).get(KEY_AL_VALUE); + if (Arrays.asList("no", "false", "0").contains(shouldFallbackString.toLowerCase())) { + webUrl = null; + } + } + if (webUrl != null && urls != null && urls.size() > 0) { + String webUrlString = (String) urls.get(0).get(KEY_AL_VALUE); + webUrl = tryCreateUrl(webUrlString); + } + } + return new AppLink(destination, targets, webUrl); + } + + private static Uri tryCreateUrl(String urlString) { + if (urlString == null) { + return null; + } + return Uri.parse(urlString); + } + + private static String readFromConnection(URLConnection connection) throws IOException { + InputStream stream; + if (connection instanceof HttpURLConnection) { + HttpURLConnection httpConnection = (HttpURLConnection) connection; + try { + stream = connection.getInputStream(); + } catch (Exception e) { + stream = httpConnection.getErrorStream(); + } + } else { + stream = connection.getInputStream(); + } + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int read; + while ((read = stream.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + String charset = connection.getContentEncoding(); + if (charset == null) { + String mimeType = connection.getContentType(); + String[] parts = mimeType.split(";"); + for (String part : parts) { + part = part.trim(); + if (part.startsWith("charset=")) { + charset = part.substring("charset=".length()); + break; + } + } + if (charset == null) { + charset = "UTF-8"; + } + } + + return new String(output.toByteArray(), charset); + } finally { + stream.close(); + } + } + + /** + * Represents an HTML entity (content) as a string. + */ + public static class StringEntity { + + private String content; + private String contentType; + + /** + * Create a new {@code StringEntity}. + * @param content the content, as a UTF-8 string. + * @param contentType the content type. + */ + public StringEntity(String content, String contentType) { + this.content = content; + this.contentType = contentType; + } + + /** + * @return the content as a UTF-8 string. + */ + public String getContent() { + return content; + } + + /** + * @return the content type of the content. + */ + public String getContentType() { + return contentType; + } + } + +} diff --git a/Bolts/src/bolts/WebViewAppLinkResolver.java b/Bolts/src/bolts/WebViewAppLinkResolver.java index 6a40576..ebb2d87 100644 --- a/Bolts/src/bolts/WebViewAppLinkResolver.java +++ b/Bolts/src/bolts/WebViewAppLinkResolver.java @@ -17,21 +17,6 @@ import org.json.JSONArray; import org.json.JSONException; -import org.json.JSONObject; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; /** * A reference implementation for an App Link resolver that uses a hidden @@ -65,65 +50,15 @@ public WebViewAppLinkResolver(Context context) { " }" + " return JSON.stringify(results);" + "})())"; - private static final String PREFER_HEADER = "Prefer-Html-Meta-Tags"; - private static final String META_TAG_PREFIX = "al"; - - private static final String KEY_AL_VALUE = "value"; - private static final String KEY_APP_NAME = "app_name"; - private static final String KEY_CLASS = "class"; - private static final String KEY_PACKAGE = "package"; - private static final String KEY_URL = "url"; - private static final String KEY_SHOULD_FALLBACK = "should_fallback"; - private static final String KEY_WEB_URL = "url"; - private static final String KEY_WEB = "web"; - private static final String KEY_ANDROID = "android"; @Override public Task getAppLinkFromUrlInBackground(final Uri url) { - final Capture content = new Capture(); - final Capture contentType = new Capture(); - return Task.callInBackground(new Callable() { - @Override - public Void call() throws Exception { - URL currentURL = new URL(url.toString()); - URLConnection connection = null; - while (currentURL != null) { - // Fetch the content at the given URL. - connection = currentURL.openConnection(); - if (connection instanceof HttpURLConnection) { - // Unfortunately, this doesn't actually follow redirects if they go from http->https, - // so we have to do that manually. - ((HttpURLConnection) connection).setInstanceFollowRedirects(true); - } - connection.setRequestProperty(PREFER_HEADER, META_TAG_PREFIX); - connection.connect(); - - if (connection instanceof HttpURLConnection) { - HttpURLConnection httpConnection = (HttpURLConnection) connection; - if (httpConnection.getResponseCode() >= 300 && httpConnection.getResponseCode() < 400) { - currentURL = new URL(httpConnection.getHeaderField("Location")); - httpConnection.disconnect(); - } else { - currentURL = null; - } - } else { - currentURL = null; - } - } - - try { - content.set(readFromConnection(connection)); - contentType.set(connection.getContentType()); - } finally { - if (connection instanceof HttpURLConnection) { - ((HttpURLConnection) connection).disconnect(); - } - } - return null; - } - }).onSuccessTask(new Continuation>() { + //final Capture content = new Capture(); + //final Capture contentType = new Capture(); + return ResolverUtils.fetchUrl(url) + .onSuccessTask(new Continuation>() { @Override - public Task then(Task task) throws Exception { + public Task then(Task task) throws Exception { // Load the content in a WebView and use JavaScript to extract the meta tags. final Task.TaskCompletionSource tcs = Task.create(); final WebView webView = new WebView(context); @@ -164,179 +99,25 @@ public void setValue(String value) { } } }, "boltsWebViewAppLinkResolverResult"); + String inferredContentType = null; - if (contentType.get() != null) { - inferredContentType = contentType.get().split(";")[0]; + String contentType = task.getResult().getContentType(); + if (contentType != null) { + inferredContentType = contentType.split(";")[0]; } + String content = task.getResult().getContent(); webView.loadDataWithBaseURL(url.toString(), - content.get(), - inferredContentType, - null, - null); + content, + inferredContentType, + null, + null); return tcs.getTask(); } }, Task.UI_THREAD_EXECUTOR).onSuccess(new Continuation() { @Override public AppLink then(Task task) throws Exception { - Map alData = parseAlData(task.getResult()); - AppLink appLink = makeAppLinkFromAlData(alData, url); - return appLink; + return ResolverUtils.parseAppLinkFromAlData(task.getResult(), url); } }); } - - /** - * Builds up a data structure filled with the app link data from the meta tags on a page. - * The structure of this object is a dictionary where each key holds an array of app link - * data dictionaries. Values are stored in a key called "_value". - */ - private static Map parseAlData(JSONArray dataArray) throws JSONException { - HashMap al = new HashMap(); - for (int i = 0; i < dataArray.length(); i++) { - JSONObject tag = dataArray.getJSONObject(i); - String name = tag.getString("property"); - String[] nameComponents = name.split(":"); - if (!nameComponents[0].equals(META_TAG_PREFIX)) { - continue; - } - Map root = al; - for (int j = 1; j < nameComponents.length; j++) { - @SuppressWarnings("unchecked") - List> children = - (List>) root.get(nameComponents[j]); - if (children == null) { - children = new ArrayList>(); - root.put(nameComponents[j], children); - } - Map child = children.size() > 0 ? children.get(children.size() - 1) : null; - if (child == null || j == nameComponents.length - 1) { - child = new HashMap(); - children.add(child); - } - root = child; - } - if (tag.has("content")) { - if (tag.isNull("content")) { - root.put(KEY_AL_VALUE, null); - } else { - root.put(KEY_AL_VALUE, tag.getString("content")); - } - } - } - return al; - } - - @SuppressWarnings("unchecked") - private static List> getAlList(Map map, String key) { - List> result = (List>) map.get(key); - if (result == null) { - return Collections.emptyList(); - } - return result; - } - - @SuppressWarnings("unchecked") - private static AppLink makeAppLinkFromAlData(Map appLinkDict, Uri destination) { - List targets = new ArrayList(); - List> platformMapList = - (List>) appLinkDict.get(KEY_ANDROID); - if (platformMapList == null) { - platformMapList = Collections.emptyList(); - } - for (Map platformMap : platformMapList) { - // The schema requires a single url/package/app name/class, but we could find multiple - // of them. We'll make a best effort to interpret this data. - List> urls = getAlList(platformMap, KEY_URL); - List> packages = getAlList(platformMap, KEY_PACKAGE); - List> classes = getAlList(platformMap, KEY_CLASS); - List> appNames = getAlList(platformMap, KEY_APP_NAME); - - int maxCount = Math.max(urls.size(), - Math.max(packages.size(), Math.max(classes.size(), appNames.size()))); - for (int i = 0; i < maxCount; i++) { - String urlString = (String) (urls.size() > i ? - urls.get(i).get(KEY_AL_VALUE) : null); - Uri url = tryCreateUrl(urlString); - String packageName = (String) (packages.size() > i ? - packages.get(i).get(KEY_AL_VALUE) : null); - String className = (String) (classes.size() > i ? - classes.get(i).get(KEY_AL_VALUE) : null); - String appName = (String) (appNames.size() > i ? - appNames.get(i).get(KEY_AL_VALUE) : null); - AppLink.Target target = new AppLink.Target(packageName, className, url, appName); - targets.add(target); - } - } - - Uri webUrl = destination; - List> webMapList = (List>) appLinkDict.get(KEY_WEB); - if (webMapList != null && webMapList.size() > 0) { - Map webMap = webMapList.get(0); - List> urls = (List>) webMap.get(KEY_WEB_URL); - List> shouldFallbacks = - (List>) webMap.get(KEY_SHOULD_FALLBACK); - if (shouldFallbacks != null && shouldFallbacks.size() > 0) { - String shouldFallbackString = (String) shouldFallbacks.get(0).get(KEY_AL_VALUE); - if (Arrays.asList("no", "false", "0").contains(shouldFallbackString.toLowerCase())) { - webUrl = null; - } - } - if (webUrl != null && urls != null && urls.size() > 0) { - String webUrlString = (String) urls.get(0).get(KEY_AL_VALUE); - webUrl = tryCreateUrl(webUrlString); - } - } - return new AppLink(destination, targets, webUrl); - } - - private static Uri tryCreateUrl(String urlString) { - if (urlString == null) { - return null; - } - return Uri.parse(urlString); - } - - /** - * Gets a string with the proper encoding (including using the charset specified in the MIME type - * of the request) from a URLConnection. - */ - private static String readFromConnection(URLConnection connection) throws IOException { - InputStream stream; - if (connection instanceof HttpURLConnection) { - HttpURLConnection httpConnection = (HttpURLConnection) connection; - try { - stream = connection.getInputStream(); - } catch (Exception e) { - stream = httpConnection.getErrorStream(); - } - } else { - stream = connection.getInputStream(); - } - try { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int read = 0; - while ((read = stream.read(buffer)) != -1) { - output.write(buffer, 0, read); - } - String charset = connection.getContentEncoding(); - if (charset == null) { - String mimeType = connection.getContentType(); - String[] parts = mimeType.split(";"); - for (String part : parts) { - part = part.trim(); - if (part.startsWith("charset=")) { - charset = part.substring("charset=".length()); - break; - } - } - if (charset == null) { - charset = "UTF-8"; - } - } - return new String(output.toByteArray(), charset); - } finally { - stream.close(); - } - } } diff --git a/BoltsTest/src/bolts/AppLinkNavigationTest.java b/BoltsTest/src/bolts/AppLinkNavigationTest.java new file mode 100644 index 0000000..7ecc94f --- /dev/null +++ b/BoltsTest/src/bolts/AppLinkNavigationTest.java @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +package bolts; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.test.InstrumentationTestCase; + +import junit.framework.Assert; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import bolts.utils.TestUtils; + +public class AppLinkNavigationTest extends InstrumentationTestCase { + + private List openedIntents; + private Context activityInterceptor; + + @Override + protected void setUp() throws Exception { + super.setUp(); + openedIntents = new ArrayList<>(); + activityInterceptor = new ContextWrapper(getInstrumentation().getTargetContext()) { + @Override + public void startActivity(Intent intent) { + openedIntents.add(intent); + } + }; + } + + public void testSimpleAppLinkNavigationExplicit() throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity", + Uri.parse("bolts://"), + "Bolts"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target), + Uri.parse("http://www.example.com/path")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testSimpleAppLinkNavigationImplicit() throws Exception { + // Don't provide a class name so that implicit resolution occurs. + AppLink.Target target = new AppLink.Target("bolts.android", + null, + Uri.parse("bolts://"), + "Bolts"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target), + Uri.parse("http://www.example.com/path")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testSimpleAppLinkNavigationWithExtras() throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity", + Uri.parse("bolts://"), + "Bolts"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target), + Uri.parse("http://www.example.com/path")); + + Bundle extras = new Bundle(); + extras.putString("foo", "bar"); + + AppLinkNavigation navigation = new AppLinkNavigation(appLink, extras, null); + + AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + Assert.assertEquals("bar", AppLinks.getAppLinkExtras(openedIntent).getString("foo")); + } + + public void testSimpleAppLinkNavigationWithAppLinkData() throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity", + Uri.parse("bolts://"), + "Bolts"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target), + Uri.parse("http://www.example.com/path")); + + Bundle appLinkData = new Bundle(); + appLinkData.putString("foo", "bar"); + + AppLinkNavigation navigation = new AppLinkNavigation(appLink, null, appLinkData); + + AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + Assert.assertEquals("bar", AppLinks.getAppLinkData(openedIntent).getString("foo")); + } + + public void testSimpleAppLinkNavigationWithExtrasAndAppLinkData() throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity", + Uri.parse("bolts://"), + "Bolts"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target), + Uri.parse("http://www.example.com/path")); + + Bundle extras = new Bundle(); + extras.putString("foo", "bar1"); + + Bundle appLinkData = new Bundle(); + appLinkData.putString("foo", "bar2"); + + AppLinkNavigation navigation = new AppLinkNavigation(appLink, extras, appLinkData); + + AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + Assert.assertEquals("bar1", AppLinks.getAppLinkExtras(openedIntent).getString("foo")); + Assert.assertEquals("bar2", AppLinks.getAppLinkData(openedIntent).getString("foo")); + } + + public void testSimpleAppLinkNavigationWithExtrasAndAppLinkDataFallBackToWeb() + throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity3", + Uri.parse("bolts3://"), + "Bolts"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target), + Uri.parse("http://www.example.com/path")); + + Bundle extras = new Bundle(); + extras.putString("foo", "bar1"); + + Bundle appLinkData = new Bundle(); + appLinkData.putString("foo", "bar2"); + + AppLinkNavigation navigation = new AppLinkNavigation(appLink, extras, appLinkData); + + AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertTrue(openedIntent.getDataString().startsWith("http://www.example.com/path")); + + String appLinkDataString = openedIntent.getData().getQueryParameter("al_applink_data"); + JSONObject appLinkDataJSON = new JSONObject(appLinkDataString); + JSONObject appLinkExtrasJSON = appLinkDataJSON.getJSONObject("extras"); + Assert.assertEquals("bar1", appLinkExtrasJSON.getString("foo")); + Assert.assertEquals("bar2", appLinkData.getString("foo")); + } + + public void testAppLinkNavigationMultipleTargetsNoFallbackExplicit() throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity", + Uri.parse("bolts://"), + "Bolts"); + AppLink.Target target2 = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity2", + Uri.parse("bolts2://"), + "Bolts 2"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target, target2), + Uri.parse("http://www.example.com/path")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testAppLinkNavigationMultipleTargetsNoFallbackImplicit() throws Exception { + // Remove the class name to make it implicit + AppLink.Target target = new AppLink.Target("bolts.android", + null, + Uri.parse("bolts://"), + "Bolts"); + AppLink.Target target2 = new AppLink.Target("bolts.android", + null, + Uri.parse("bolts2://"), + "Bolts 2"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target, target2), + Uri.parse("http://www.example.com/path")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testAppLinkNavigationMultipleTargetsWithFallbackExplicit() throws Exception { + AppLink.Target target = new AppLink.Target("bolts.android", + "bolts.utils.InvalidActivity", + Uri.parse("bolts://"), + "Bolts"); + AppLink.Target target2 = new AppLink.Target("bolts.android", + "bolts.utils.BoltsActivity2", + Uri.parse("bolts2://"), + "Bolts 2"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target, target2), + Uri.parse("http://www.example.com/path")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); + } + + public void testAppLinkNavigationMultipleTargetsWithFallbackImplicit() throws Exception { + // Remove the class name to make it implicit + AppLink.Target target = new AppLink.Target("bolts.android", + null, + Uri.parse("invalid://"), + "Bolts"); + AppLink.Target target2 = new AppLink.Target("bolts.android", + null, + Uri.parse("bolts2://"), + "Bolts 2"); + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + Arrays.asList(target, target2), + Uri.parse("http://www.example.com/path")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(Uri.parse("http://www.example.com/path"), + AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); + } + + public void testAppLinkNavigationNoTargets() throws Exception { + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + null, + Uri.parse("http://www.example.com/path2")); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertTrue(openedIntent.getDataString().startsWith("http://www.example.com/path2")); + } + + public void testAppLinkNavigationFailure() throws Exception { + AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), + null, + null); + + AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( + activityInterceptor, appLink); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.FAILED, navigationType); + Assert.assertEquals(0, openedIntents.size()); + } + + public void testSimpleAppLinkURLNavigationExplicit() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts://", + "al:android:class", "bolts.utils.BoltsActivity", + "al:android:app_name", "Bolts"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testSimpleAppLinkURLNavigationImplicit() throws Exception { + // Don't provide a class name so that implicit resolution occurs. + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts://", + "al:android:app_name", "Bolts"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testAppLinkURLNavigationMultipleTargetsNoFallbackExplicit() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts://", + "al:android:class", "bolts.utils.BoltsActivity", + "al:android:app_name", "Bolts", + + "al:android", + "al:android:package", "bolts.android", + "al:android:url", "bolts2://", + "al:android:class", "bolts.utils.BoltsActivity2", + "al:android:app_name", "Bolts 2"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testAppLinkURLNavigationMultipleTargetsNoFallbackImplicit() throws Exception { + // Remove the class name to make it implicit + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + + "al:android", + "al:android:package", "bolts.android", + "al:android:url", "bolts2://", + "al:android:app_name", "Bolts 2"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts", openedIntent.getData().getScheme()); + } + + public void testAppLinkURLNavigationMultipleTargetsWithFallbackExplicit() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts://", + "al:android:class", "bolts.utils.InvalidActivity", + "al:android:app_name", "Bolts", + + "al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts2://", + "al:android:class", "bolts.utils.BoltsActivity2", + "al:android:app_name", "Bolts 2"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); + } + + public void testAppLinkURLNavigationMultipleTargetsWithFallbackImplicit() throws Exception { + // Remove the class name to make it implicit + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "invalid://", + "al:android:app_name", "Bolts", + + "al:android", null, + "al:android:package", "bolts.android", + "al:android:url", "bolts2://", + "al:android:app_name", "Bolts 2"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); + Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); + } + + public void testAppLinkURLNavigationNoTargets() throws Exception { + String html = TestUtils.getHtmlWithMetaTags(); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertTrue(openedIntent.getDataString().startsWith(uri.toString())); + } + + public void testAppLinkURLNavigationWebOnly() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:web:url", "http://www.example.com/path"); + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); + Assert.assertEquals(1, openedIntents.size()); + + Intent openedIntent = openedIntents.get(0); + Assert.assertTrue(openedIntent.getDataString().startsWith("http://www.example.com/path")); + } + + public void testAppLinkURLNavigationFailure() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:web:should_fallback", "No"); // case insensitive + Uri uri = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + + Task task = AppLinkNavigation.navigateInBackground( + activityInterceptor, uri); + TestUtils.waitForTask(task); + AppLinkNavigation.NavigationResult navigationType = task.getResult(); + + Assert.assertEquals(AppLinkNavigation.NavigationResult.FAILED, navigationType); + Assert.assertEquals(0, openedIntents.size()); + } + +} diff --git a/BoltsTest/src/bolts/AppLinkTest.java b/BoltsTest/src/bolts/AppLinkTest.java index 7ba7e74..aee3f39 100644 --- a/BoltsTest/src/bolts/AppLinkTest.java +++ b/BoltsTest/src/bolts/AppLinkTest.java @@ -11,83 +11,19 @@ import android.content.BroadcastReceiver; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.test.InstrumentationTestCase; + import junit.framework.Assert; -import org.json.JSONArray; -import org.json.JSONObject; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class AppLinkTest extends InstrumentationTestCase { - private List openedIntents; - private Context activityInterceptor; - - @Override - protected void setUp() throws Exception { - super.setUp(); - openedIntents = new ArrayList(); - activityInterceptor = new ContextWrapper(getInstrumentation().getTargetContext()) { - @Override - public void startActivity(Intent intent) { - openedIntents.add(intent); - } - }; - } - - private void waitForTask(Task t) throws InterruptedException { - t.waitForCompletion(); - if (t.isFaulted()) { - throw new RuntimeException(t.getError()); - } - } - - /** - * A helper method to get an HTML string with pre-populated meta tags. - * values should contain pairs of "property" and "content" values to inject into - * the meta tags. - */ - private String getHtmlWithMetaTags(String... values) { - StringBuilder sb = new StringBuilder(""); - for (int i = 0; i < values.length; i += 2) { - sb.append(""); - } - sb.append("Hello, world!"); - return sb.toString(); - } - - private Uri getURLForData(String data) throws IOException { - File result = File.createTempFile("temp", - ".html", - getInstrumentation().getTargetContext().getCacheDir()); - PrintWriter writer = new PrintWriter(result); - writer.write(data); - writer.close(); - result.deleteOnExit(); - return Uri.parse(result.toURI().toString()); - } public void testSimpleIntent() throws Exception { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); @@ -113,46 +49,6 @@ public void testSimpleIntentWithAppLink() throws Exception { Assert.assertEquals("bar", AppLinks.getAppLinkExtras(i).getString("foo")); } - public void testGeneralMeasurementEventsBroadcast() throws Exception { - Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); - i.putExtra("foo", "bar"); - ArrayList arr = new ArrayList(); - arr.add("foo2"); - arr.add("bar2"); - i.putExtra("foobar", arr); - Map other = new HashMap(); - other.put("yetAnotherFoo", "yetAnotherBar"); - - final CountDownLatch lock = new CountDownLatch(1); - final String[] receivedStrings = new String[5]; - LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getInstrumentation().getTargetContext()); - manager.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String eventName = intent.getStringExtra("event_name"); - Bundle eventArgs = intent.getBundleExtra("event_args"); - receivedStrings[0] = eventName; - receivedStrings[1] = eventArgs.getString("foo"); - receivedStrings[2] = eventArgs.getString("foobar"); - receivedStrings[3] = eventArgs.getString("yetAnotherFoo"); - receivedStrings[4] = eventArgs.getString("intentData"); - lock.countDown(); - } - }, - new IntentFilter("com.parse.bolts.measurement_event") - ); - - MeasurementEvent.sendBroadcastEvent(getInstrumentation().getTargetContext(), "myEventName", i, other); - lock.await(2000, TimeUnit.MILLISECONDS); - - assertEquals("myEventName", receivedStrings[0]); - assertEquals("bar", receivedStrings[1]); - assertEquals((new JSONArray(arr)).toString(), receivedStrings[2]); - assertEquals("yetAnotherBar", receivedStrings[3]); - assertEquals("http://www.example.com", receivedStrings[4]); - } - public void testAppLinkNavInEventBroadcast() throws Exception { Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); Bundle appLinkData = new Bundle(); @@ -200,704 +96,4 @@ public void onReceive(Context context, Intent intent) { assertEquals("a_token", receivedStrings[5]); assertEquals("com.bolts.referrer", receivedStrings[6]); } - - public void testWebViewSimpleAppLinkParsing() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts", - "al:android:class", "com.bolts.BoltsActivity"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(1, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); - Assert.assertEquals("Bolts", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); - Assert.assertEquals("com.bolts", target.getPackageName()); - Assert.assertEquals(url, link.getWebUrl()); - } - - public void testWebViewAppLinkParsingFailure() throws Exception { - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(Uri.parse("http://badurl")); - task.waitForCompletion(); - Assert.assertNotNull(task.getError()); - } - - public void testWebViewSimpleAppLinkParsingZeroShouldFallback() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts", - "al:android:class", "com.bolts.BoltsActivity", - "al:web:should_fallback", "0"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(1, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); - Assert.assertEquals("Bolts", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); - Assert.assertEquals("com.bolts", target.getPackageName()); - Assert.assertNull(link.getWebUrl()); - } - - public void testWebViewSimpleAppLinkParsingFalseShouldFallback() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts", - "al:android:class", "com.bolts.BoltsActivity", - "al:web:should_fallback", "fAlse"); // case insensitive - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(1, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); - Assert.assertEquals("Bolts", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); - Assert.assertEquals("com.bolts", target.getPackageName()); - Assert.assertNull(link.getWebUrl()); - } - - public void testWebViewSimpleAppLinkParsingWithWebUrl() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts", - "al:android:class", "com.bolts.BoltsActivity", - "al:web:url", "http://www.example.com"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(1, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); - Assert.assertEquals("Bolts", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); - Assert.assertEquals("com.bolts", target.getPackageName()); - Assert.assertEquals(Uri.parse("http://www.example.com"), link.getWebUrl()); - } - - public void testWebViewVersionedAppLinkParsing() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts", - "al:android:class", "com.bolts.BoltsActivity", - - "al:android", null, - "al:android:url", "bolts2://", - "al:android:app_name", "Bolts2", - "al:android:package", "com.bolts2", - "al:android:class", "com.bolts.BoltsActivity2"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(2, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); - Assert.assertEquals("Bolts", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); - Assert.assertEquals("com.bolts", target.getPackageName()); - - target = link.getTargets().get(1); - Assert.assertEquals(Uri.parse("bolts2://"), target.getUrl()); - Assert.assertEquals("Bolts2", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity2", target.getClassName()); - Assert.assertEquals("com.bolts2", target.getPackageName()); - Assert.assertEquals(url, link.getWebUrl()); - } - - public void testWebViewVersionedAppLinkParsingOnlyPackages() throws Exception { - String html = getHtmlWithMetaTags("al:android:package", "com.bolts", - "al:android:package", "com.bolts2"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(2, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals("com.bolts", target.getPackageName()); - - target = link.getTargets().get(1); - Assert.assertEquals("com.bolts2", target.getPackageName()); - Assert.assertEquals(url, link.getWebUrl()); - } - - public void testWebViewVersionedAppLinkParsingPackagesAndNames() throws Exception { - String html = getHtmlWithMetaTags("al:android:package", "com.bolts", - "al:android:package", "com.bolts2", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts3", - "al:android:app_name", "Bolts2"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(3, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals("com.bolts", target.getPackageName()); - Assert.assertEquals("Bolts", target.getAppName()); - - target = link.getTargets().get(1); - Assert.assertEquals("com.bolts2", target.getPackageName()); - Assert.assertEquals("Bolts2", target.getAppName()); - Assert.assertEquals(url, link.getWebUrl()); - - target = link.getTargets().get(2); - Assert.assertEquals("com.bolts3", target.getPackageName()); - Assert.assertNull(target.getAppName()); - Assert.assertEquals(url, link.getWebUrl()); - } - - public void testWebViewPlatformFiltering() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - "al:android:package", "com.bolts", - "al:android:class", "com.bolts.BoltsActivity", - - "al:ios", null, - "al:ios:url", "bolts://iphone", - "al:ios:app_name", "Bolts", - "al:ios:app_store_id", "123456", - - "al:android", null, - "al:android:url", "bolts2://", - "al:android:app_name", "Bolts2", - "al:android:package", "com.bolts2", - "al:android:class", "com.bolts.BoltsActivity2"); - Uri url = getURLForData(html); - Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) - .getAppLinkFromUrlInBackground(url); - waitForTask(task); - AppLink link = task.getResult(); - - Assert.assertEquals(2, link.getTargets().size()); - - AppLink.Target target = link.getTargets().get(0); - Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); - Assert.assertEquals("Bolts", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); - Assert.assertEquals("com.bolts", target.getPackageName()); - - target = link.getTargets().get(1); - Assert.assertEquals(Uri.parse("bolts2://"), target.getUrl()); - Assert.assertEquals("Bolts2", target.getAppName()); - Assert.assertEquals("com.bolts.BoltsActivity2", target.getClassName()); - Assert.assertEquals("com.bolts2", target.getPackageName()); - Assert.assertEquals(url, link.getWebUrl()); - } - - public void testSimpleAppLinkNavigationExplicit() throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity", - Uri.parse("bolts://"), - "Bolts"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target), - Uri.parse("http://www.example.com/path")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testSimpleAppLinkNavigationImplicit() throws Exception { - // Don't provide a class name so that implicit resolution occurs. - AppLink.Target target = new AppLink.Target("bolts.android", - null, - Uri.parse("bolts://"), - "Bolts"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target), - Uri.parse("http://www.example.com/path")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testSimpleAppLinkNavigationWithExtras() throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity", - Uri.parse("bolts://"), - "Bolts"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target), - Uri.parse("http://www.example.com/path")); - - Bundle extras = new Bundle(); - extras.putString("foo", "bar"); - - AppLinkNavigation navigation = new AppLinkNavigation(appLink, extras, null); - - AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - Assert.assertEquals("bar", AppLinks.getAppLinkExtras(openedIntent).getString("foo")); - } - - public void testSimpleAppLinkNavigationWithAppLinkData() throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity", - Uri.parse("bolts://"), - "Bolts"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target), - Uri.parse("http://www.example.com/path")); - - Bundle appLinkData = new Bundle(); - appLinkData.putString("foo", "bar"); - - AppLinkNavigation navigation = new AppLinkNavigation(appLink, null, appLinkData); - - AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - Assert.assertEquals("bar", AppLinks.getAppLinkData(openedIntent).getString("foo")); - } - - public void testSimpleAppLinkNavigationWithExtrasAndAppLinkData() throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity", - Uri.parse("bolts://"), - "Bolts"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target), - Uri.parse("http://www.example.com/path")); - - Bundle extras = new Bundle(); - extras.putString("foo", "bar1"); - - Bundle appLinkData = new Bundle(); - appLinkData.putString("foo", "bar2"); - - AppLinkNavigation navigation = new AppLinkNavigation(appLink, extras, appLinkData); - - AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - Assert.assertEquals("bar1", AppLinks.getAppLinkExtras(openedIntent).getString("foo")); - Assert.assertEquals("bar2", AppLinks.getAppLinkData(openedIntent).getString("foo")); - } - - public void testSimpleAppLinkNavigationWithExtrasAndAppLinkDataFallBackToWeb() - throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity3", - Uri.parse("bolts3://"), - "Bolts"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target), - Uri.parse("http://www.example.com/path")); - - Bundle extras = new Bundle(); - extras.putString("foo", "bar1"); - - Bundle appLinkData = new Bundle(); - appLinkData.putString("foo", "bar2"); - - AppLinkNavigation navigation = new AppLinkNavigation(appLink, extras, appLinkData); - - AppLinkNavigation.NavigationResult navigationType = navigation.navigate(activityInterceptor); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertTrue(openedIntent.getDataString().startsWith("http://www.example.com/path")); - - String appLinkDataString = openedIntent.getData().getQueryParameter("al_applink_data"); - JSONObject appLinkDataJSON = new JSONObject(appLinkDataString); - JSONObject appLinkExtrasJSON = appLinkDataJSON.getJSONObject("extras"); - Assert.assertEquals("bar1", appLinkExtrasJSON.getString("foo")); - Assert.assertEquals("bar2", appLinkData.getString("foo")); - } - - public void testAppLinkNavigationMultipleTargetsNoFallbackExplicit() throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity", - Uri.parse("bolts://"), - "Bolts"); - AppLink.Target target2 = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity2", - Uri.parse("bolts2://"), - "Bolts 2"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target, target2), - Uri.parse("http://www.example.com/path")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testAppLinkNavigationMultipleTargetsNoFallbackImplicit() throws Exception { - // Remove the class name to make it implicit - AppLink.Target target = new AppLink.Target("bolts.android", - null, - Uri.parse("bolts://"), - "Bolts"); - AppLink.Target target2 = new AppLink.Target("bolts.android", - null, - Uri.parse("bolts2://"), - "Bolts 2"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target, target2), - Uri.parse("http://www.example.com/path")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testAppLinkNavigationMultipleTargetsWithFallbackExplicit() throws Exception { - AppLink.Target target = new AppLink.Target("bolts.android", - "bolts.utils.InvalidActivity", - Uri.parse("bolts://"), - "Bolts"); - AppLink.Target target2 = new AppLink.Target("bolts.android", - "bolts.utils.BoltsActivity2", - Uri.parse("bolts2://"), - "Bolts 2"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target, target2), - Uri.parse("http://www.example.com/path")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); - } - - public void testAppLinkNavigationMultipleTargetsWithFallbackImplicit() throws Exception { - // Remove the class name to make it implicit - AppLink.Target target = new AppLink.Target("bolts.android", - null, - Uri.parse("invalid://"), - "Bolts"); - AppLink.Target target2 = new AppLink.Target("bolts.android", - null, - Uri.parse("bolts2://"), - "Bolts 2"); - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - Arrays.asList(target, target2), - Uri.parse("http://www.example.com/path")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(Uri.parse("http://www.example.com/path"), - AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); - } - - public void testAppLinkNavigationNoTargets() throws Exception { - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - null, - Uri.parse("http://www.example.com/path2")); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertTrue(openedIntent.getDataString().startsWith("http://www.example.com/path2")); - } - - public void testAppLinkNavigationFailure() throws Exception { - AppLink appLink = new AppLink(Uri.parse("http://www.example.com/path"), - null, - null); - - AppLinkNavigation.NavigationResult navigationType = AppLinkNavigation.navigate( - activityInterceptor, appLink); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.FAILED, navigationType); - Assert.assertEquals(0, openedIntents.size()); - } - - public void testSimpleAppLinkURLNavigationExplicit() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts://", - "al:android:class", "bolts.utils.BoltsActivity", - "al:android:app_name", "Bolts"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testSimpleAppLinkURLNavigationImplicit() throws Exception { - // Don't provide a class name so that implicit resolution occurs. - String html = getHtmlWithMetaTags("al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts://", - "al:android:app_name", "Bolts"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testAppLinkURLNavigationMultipleTargetsNoFallbackExplicit() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts://", - "al:android:class", "bolts.utils.BoltsActivity", - "al:android:app_name", "Bolts", - - "al:android", - "al:android:package", "bolts.android", - "al:android:url", "bolts2://", - "al:android:class", "bolts.utils.BoltsActivity2", - "al:android:app_name", "Bolts 2"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testAppLinkURLNavigationMultipleTargetsNoFallbackImplicit() throws Exception { - // Remove the class name to make it implicit - String html = getHtmlWithMetaTags("al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts://", - "al:android:app_name", "Bolts", - - "al:android", - "al:android:package", "bolts.android", - "al:android:url", "bolts2://", - "al:android:app_name", "Bolts 2"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts", openedIntent.getData().getScheme()); - } - - public void testAppLinkURLNavigationMultipleTargetsWithFallbackExplicit() throws Exception { - String html = getHtmlWithMetaTags("al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts://", - "al:android:class", "bolts.utils.InvalidActivity", - "al:android:app_name", "Bolts", - - "al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts2://", - "al:android:class", "bolts.utils.BoltsActivity2", - "al:android:app_name", "Bolts 2"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); - } - - public void testAppLinkURLNavigationMultipleTargetsWithFallbackImplicit() throws Exception { - // Remove the class name to make it implicit - String html = getHtmlWithMetaTags("al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "invalid://", - "al:android:app_name", "Bolts", - - "al:android", null, - "al:android:package", "bolts.android", - "al:android:url", "bolts2://", - "al:android:app_name", "Bolts 2"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.APP, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertEquals(uri, AppLinks.getTargetUrl(openedIntent)); - Assert.assertEquals("bolts2", openedIntent.getData().getScheme()); - } - - public void testAppLinkURLNavigationNoTargets() throws Exception { - String html = getHtmlWithMetaTags(); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertTrue(openedIntent.getDataString().startsWith(uri.toString())); - } - - public void testAppLinkURLNavigationWebOnly() throws Exception { - String html = getHtmlWithMetaTags("al:web:url", "http://www.example.com/path"); - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.WEB, navigationType); - Assert.assertEquals(1, openedIntents.size()); - - Intent openedIntent = openedIntents.get(0); - Assert.assertTrue(openedIntent.getDataString().startsWith("http://www.example.com/path")); - } - - public void testAppLinkURLNavigationFailure() throws Exception { - String html = getHtmlWithMetaTags("al:web:should_fallback", "No"); // case insensitive - Uri uri = getURLForData(html); - - Task task = AppLinkNavigation.navigateInBackground( - activityInterceptor, uri); - waitForTask(task); - AppLinkNavigation.NavigationResult navigationType = task.getResult(); - - Assert.assertEquals(AppLinkNavigation.NavigationResult.FAILED, navigationType); - Assert.assertEquals(0, openedIntents.size()); - } - } diff --git a/BoltsTest/src/bolts/HtmlAppLinkResolverTest.java b/BoltsTest/src/bolts/HtmlAppLinkResolverTest.java new file mode 100644 index 0000000..4373778 --- /dev/null +++ b/BoltsTest/src/bolts/HtmlAppLinkResolverTest.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +package bolts; + +import android.net.Uri; +import android.test.InstrumentationTestCase; + +import junit.framework.Assert; + +import bolts.utils.TestUtils; + +public class HtmlAppLinkResolverTest extends InstrumentationTestCase { + + public void testHtmlSimpleAppLinkParsing() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testHtmlAppLinkParsingFailure() throws Exception { + Task task = new HtmlAppLinkResolver() + .getAppLinkFromUrlInBackground(Uri.parse("http://badurl")); + task.waitForCompletion(); + Assert.assertNotNull(task.getError()); + } + + public void testHtmlSimpleAppLinkParsingZeroShouldFallback() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + "al:web:should_fallback", "0"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertNull(link.getWebUrl()); + } + + public void testHtmlSimpleAppLinkParsingFalseShouldFallback() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + "al:web:should_fallback", "fAlse"); // case insensitive + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertNull(link.getWebUrl()); + } + + public void testHtmlSimpleAppLinkParsingWithWebUrl() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + "al:web:url", "http://www.example.com"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertEquals(Uri.parse("http://www.example.com"), link.getWebUrl()); + } + + public void testHtmlVersionedAppLinkParsing() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + + "al:android", null, + "al:android:url", "bolts2://", + "al:android:app_name", "Bolts2", + "al:android:package", "com.bolts2", + "al:android:class", "com.bolts.BoltsActivity2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(2, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + + target = link.getTargets().get(1); + Assert.assertEquals(Uri.parse("bolts2://"), target.getUrl()); + Assert.assertEquals("Bolts2", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity2", target.getClassName()); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testHtmlVersionedAppLinkParsingOnlyPackages() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android:package", "com.bolts", + "al:android:package", "com.bolts2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(2, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals("com.bolts", target.getPackageName()); + + target = link.getTargets().get(1); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testHtmlVersionedAppLinkParsingPackagesAndNames() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android:package", "com.bolts", + "al:android:package", "com.bolts2", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts3", + "al:android:app_name", "Bolts2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(3, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertEquals("Bolts", target.getAppName()); + + target = link.getTargets().get(1); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals("Bolts2", target.getAppName()); + Assert.assertEquals(url, link.getWebUrl()); + + target = link.getTargets().get(2); + Assert.assertEquals("com.bolts3", target.getPackageName()); + Assert.assertNull(target.getAppName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testHtmlPlatformFiltering() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + + "al:ios", null, + "al:ios:url", "bolts://iphone", + "al:ios:app_name", "Bolts", + "al:ios:app_store_id", "123456", + + "al:android", null, + "al:android:url", "bolts2://", + "al:android:app_name", "Bolts2", + "al:android:package", "com.bolts2", + "al:android:class", "com.bolts.BoltsActivity2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new HtmlAppLinkResolver().getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(2, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + + target = link.getTargets().get(1); + Assert.assertEquals(Uri.parse("bolts2://"), target.getUrl()); + Assert.assertEquals("Bolts2", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity2", target.getClassName()); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } +} diff --git a/BoltsTest/src/bolts/MeasurementEventTest.java b/BoltsTest/src/bolts/MeasurementEventTest.java new file mode 100644 index 0000000..cf539f5 --- /dev/null +++ b/BoltsTest/src/bolts/MeasurementEventTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +package bolts; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.LocalBroadcastManager; +import android.test.InstrumentationTestCase; + +import junit.framework.Assert; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public class MeasurementEventTest extends InstrumentationTestCase { + + public void testGeneralMeasurementEventsBroadcast() throws Exception { + Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); + i.putExtra("foo", "bar"); + ArrayList arr = new ArrayList(); + arr.add("foo2"); + arr.add("bar2"); + i.putExtra("foobar", arr); + Map other = new HashMap(); + other.put("yetAnotherFoo", "yetAnotherBar"); + + final CountDownLatch lock = new CountDownLatch(1); + final String[] receivedStrings = new String[5]; + LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getInstrumentation().getTargetContext()); + manager.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String eventName = intent.getStringExtra("event_name"); + Bundle eventArgs = intent.getBundleExtra("event_args"); + receivedStrings[0] = eventName; + receivedStrings[1] = eventArgs.getString("foo"); + receivedStrings[2] = eventArgs.getString("foobar"); + receivedStrings[3] = eventArgs.getString("yetAnotherFoo"); + receivedStrings[4] = eventArgs.getString("intentData"); + lock.countDown(); + } + }, + new IntentFilter("com.parse.bolts.measurement_event") + ); + + MeasurementEvent.sendBroadcastEvent(getInstrumentation().getTargetContext(), "myEventName", i, other); + lock.await(2000, TimeUnit.MILLISECONDS); + + assertEquals("myEventName", receivedStrings[0]); + assertEquals("bar", receivedStrings[1]); + assertEquals((new JSONArray(arr)).toString(), receivedStrings[2]); + assertEquals("yetAnotherBar", receivedStrings[3]); + assertEquals("http://www.example.com", receivedStrings[4]); + } + +} diff --git a/BoltsTest/src/bolts/WebViewAppLinkResolverTest.java b/BoltsTest/src/bolts/WebViewAppLinkResolverTest.java new file mode 100644 index 0000000..d1df81c --- /dev/null +++ b/BoltsTest/src/bolts/WebViewAppLinkResolverTest.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ +package bolts; + +import android.net.Uri; +import android.test.InstrumentationTestCase; + +import junit.framework.Assert; + +import bolts.utils.TestUtils; + +public class WebViewAppLinkResolverTest extends InstrumentationTestCase { + + public void testWebViewSimpleAppLinkParsing() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testWebViewAppLinkParsingFailure() throws Exception { + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(Uri.parse("http://badurl")); + task.waitForCompletion(); + Assert.assertNotNull(task.getError()); + } + + public void testWebViewSimpleAppLinkParsingZeroShouldFallback() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + "al:web:should_fallback", "0"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertNull(link.getWebUrl()); + } + + public void testWebViewSimpleAppLinkParsingFalseShouldFallback() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + "al:web:should_fallback", "fAlse"); // case insensitive + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertNull(link.getWebUrl()); + } + + public void testWebViewSimpleAppLinkParsingWithWebUrl() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + "al:web:url", "http://www.example.com"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(1, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertEquals(Uri.parse("http://www.example.com"), link.getWebUrl()); + } + + public void testWebViewVersionedAppLinkParsing() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + + "al:android", null, + "al:android:url", "bolts2://", + "al:android:app_name", "Bolts2", + "al:android:package", "com.bolts2", + "al:android:class", "com.bolts.BoltsActivity2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(2, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + + target = link.getTargets().get(1); + Assert.assertEquals(Uri.parse("bolts2://"), target.getUrl()); + Assert.assertEquals("Bolts2", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity2", target.getClassName()); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testWebViewVersionedAppLinkParsingOnlyPackages() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android:package", "com.bolts", + "al:android:package", "com.bolts2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(2, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals("com.bolts", target.getPackageName()); + + target = link.getTargets().get(1); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testWebViewVersionedAppLinkParsingPackagesAndNames() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android:package", "com.bolts", + "al:android:package", "com.bolts2", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts3", + "al:android:app_name", "Bolts2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(3, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals("com.bolts", target.getPackageName()); + Assert.assertEquals("Bolts", target.getAppName()); + + target = link.getTargets().get(1); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals("Bolts2", target.getAppName()); + Assert.assertEquals(url, link.getWebUrl()); + + target = link.getTargets().get(2); + Assert.assertEquals("com.bolts3", target.getPackageName()); + Assert.assertNull(target.getAppName()); + Assert.assertEquals(url, link.getWebUrl()); + } + + public void testWebViewPlatformFiltering() throws Exception { + String html = TestUtils.getHtmlWithMetaTags("al:android", null, + "al:android:url", "bolts://", + "al:android:app_name", "Bolts", + "al:android:package", "com.bolts", + "al:android:class", "com.bolts.BoltsActivity", + + "al:ios", null, + "al:ios:url", "bolts://iphone", + "al:ios:app_name", "Bolts", + "al:ios:app_store_id", "123456", + + "al:android", null, + "al:android:url", "bolts2://", + "al:android:app_name", "Bolts2", + "al:android:package", "com.bolts2", + "al:android:class", "com.bolts.BoltsActivity2"); + Uri url = TestUtils.getURLForData(getInstrumentation().getTargetContext(), html); + Task task = new WebViewAppLinkResolver(getInstrumentation().getTargetContext()) + .getAppLinkFromUrlInBackground(url); + TestUtils.waitForTask(task); + AppLink link = task.getResult(); + + Assert.assertEquals(2, link.getTargets().size()); + + AppLink.Target target = link.getTargets().get(0); + Assert.assertEquals(Uri.parse("bolts://"), target.getUrl()); + Assert.assertEquals("Bolts", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity", target.getClassName()); + Assert.assertEquals("com.bolts", target.getPackageName()); + + target = link.getTargets().get(1); + Assert.assertEquals(Uri.parse("bolts2://"), target.getUrl()); + Assert.assertEquals("Bolts2", target.getAppName()); + Assert.assertEquals("com.bolts.BoltsActivity2", target.getClassName()); + Assert.assertEquals("com.bolts2", target.getPackageName()); + Assert.assertEquals(url, link.getWebUrl()); + } +} diff --git a/BoltsTest/src/bolts/utils/TestUtils.java b/BoltsTest/src/bolts/utils/TestUtils.java new file mode 100644 index 0000000..c8d8453 --- /dev/null +++ b/BoltsTest/src/bolts/utils/TestUtils.java @@ -0,0 +1,60 @@ +package bolts.utils; + +import android.content.Context; +import android.net.Uri; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; + +import bolts.Task; + +public class TestUtils { + + private TestUtils() { + } + + public static void waitForTask(Task t) throws InterruptedException { + t.waitForCompletion(); + if (t.isFaulted()) { + throw new RuntimeException(t.getError()); + } + } + + /** + * A helper method to get an HTML string with pre-populated meta tags. + * values should contain pairs of "property" and "content" values to inject into + * the meta tags. + */ + public static String getHtmlWithMetaTags(String... values) { + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < values.length; i += 2) { + sb.append(""); + } + sb.append("Hello, world!"); + return sb.toString(); + } + + /** + * Gets a Uri for the specified data by writing it to a temporary file. + */ + public static Uri getURLForData(Context context, String data) throws IOException { + File result = File.createTempFile("temp", + ".html", + context.getCacheDir()); + PrintWriter writer = new PrintWriter(result); + writer.write(data); + writer.close(); + result.deleteOnExit(); + return Uri.parse(result.toURI().toString()); + } + +}