[
  {
    "path": ".gitignore",
    "content": "node_modules/\npackage-lock.json\n"
  },
  {
    "path": ".npmrc",
    "content": "registry=https://registry.npmjs.org/\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2016-present Drifty Co.\nhttp://drifty.com/\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Community Maintained\n\nThis plugin is being maintained by the Ionic community. Interested in helping? Message `max` on ionic worldwide slack.\n\nAnother great solution for deep links for Ionic is the Branch Metrics plugin: [https://github.com/BranchMetrics/cordova-ionic-phonegap-branch-deep-linking](https://github.com/BranchMetrics/cordova-ionic-phonegap-branch-deep-linking)\n\nIf you used to handle URI schemes with the help of this plugin and have migrated to Branch Metrics, you can make use of a plugin such as [https://github.com/EddyVerbruggen/Custom-URL-scheme](https://github.com/EddyVerbruggen/Custom-URL-scheme) to facilitate custom URL schemes.\n\nIonic Deeplinks Plugin\n======\n\nThis plugin makes it easy to respond to deeplinks through custom URL schemes\nand Universal/App Links on iOS and Android.\n\nFor example, you can have your app open through a link to https://yoursite.com/product/cool-beans and then navigate\nto display the Cool Beans in your app (cool beans!).\n\nAdditionally, on Android iOS, your app can be opened through a custom URL scheme, like `coolbeans://product/cool-beans`.\n\nSince Custom URL scheme behavior has changed quite a bit in iOS 9.2 for the case where the app isn't installed, you'll want to start using [Universal Links](#ios-configuration) as it's clear custom URL schemes are on the way out.\n\n*Note: this plugin may clash with existing Custom URL Scheme and Universal Links Plugins. Please let\nus know if you encounter compatibility issues. Also, try removing them and using this one on its own.*\n\nThank you to the [Cordova Universal Links Plugin](https://github.com/nordnet/cordova-universal-links-plugin) and the [Custom URL Scheme](https://github.com/EddyVerbruggen/Custom-URL-scheme) plugin that this plugin is inspired and borrows from.\n\n## Installation\n\n```bash\ncordova plugin add ionic-plugin-deeplinks\n--variable URL_SCHEME=myapp --variable DEEPLINK_SCHEME=https --variable DEEPLINK_HOST=example.com\n--variable ANDROID_PATH_PREFIX=/\n```\n\nFill in the appropriate values as shown below:\n\n * `URL_SCHEME` - the custom URL scheme you'd like to use for your app. This lets your app respond to links like `myapp://blah`\n * `DEEPLINK_SCHEME` - the scheme to use for universal/app links. Defaults to 'https' in 1.0.13. 99% of the time you'll use `https` here as iOS and Android require SSL for app links domains.\n * `DEEPLINK_HOST` - the host that will respond to deeplinks. For example, if we want `example.com/product/cool-beans` to open in our app, we'd use `example.com` here.\n * `ANDROID_PATH_PREFIX` - (optional): specify which path prefix our Android app should open from [more info](https://developer.android.com/guide/topics/manifest/data-element.html)\n\n(New in 1.0.13): If you'd like to support multiple hosts for Android, you can also set the variables `DEEPLINK_2_SCHEME`, `DEEPLINK_2_HOST`, `ANDROID_2_PATH_PREFIX` and optionally substitue `2` with 3, 4, and 5 to set more.\n\n## Handling Deeplinks in JavaScript\n\n#### Ionic/Angular 2\n\n*note: make sure to call IonicDeeplink from a platform.ready or `deviceready` event*\n\nUsing [Ionic Native](https://github.com/ionic-team/ionic-native) (available in 1.2.4 or greater):\n\n```javascript\nimport { Platform, NavController } from 'ionic-angular';\nimport { Deeplinks } from '@ionic-native/deeplinks/ngx';\n\nexport class MyApp {\n  constructor(\n    protected platform: Platform\n    , protected navController: NavController\n    , protected deeplinks: Deeplinks\n    ) {\n    this.platform.ready().then(() => {\n      this.deeplinks.route({\n        '/about-us': HomePage,\n        '/products/:productId': HelpPage\n      }).subscribe((match) => {\n        // match.$route - the route we matched, which is the matched entry from the arguments to route()\n        // match.$args - the args passed in the link\n        // match.$link - the full link data\n        console.log('Successfully matched route', match);\n      },\n      (nomatch) => {\n        // nomatch.$link - the full link data\n        console.error('Got a deeplink that didn\\'t match', nomatch);\n      });\n    });\n  }\n}\n\n// Note: routeWithNavController returns an observable from Ionic Native so it *must* be subscribed to first in order to trigger.\n```\n\nIf you're using Ionic 2, there is a convenience method to route automatically (see the simple [Ionic 2 Deeplinks](https://github.com/ionic-team/ionic2-deeplinks-demo/blob/master/app/app.ts) demo for an example):\n\n```javascript\nimport { Platform, NavController } from 'ionic-angular';\nimport { Deeplinks } from '@ionic-native/deeplinks/ngx';\n\nexport class MyApp {\n  constructor(\n    protected platform: Platform\n    , protected navController: NavController\n    , protected deeplinks: Deeplinks\n    ) {\n    this.platform.ready().then(() => {\n      this.deeplinks.routeWithNavController(this.navController, {\n        '/about-us': HomePage,\n        '/products/:productId': HelpPage\n      }).subscribe((match) => {\n        // match.$route - the route we matched, which is the matched entry from the arguments to route()\n        // match.$args - the args passed in the link\n        // match.$link - the full link data\n        console.log('Successfully matched route', match);\n      },\n      (nomatch) => {\n        // nomatch.$link - the full link data\n        console.error('Got a deeplink that didn\\'t match', nomatch);\n      });\n    });\n  }\n}\n\n// Note: routeWithNavController returns an observable from Ionic Native so it *must* be subscribed to first in order to trigger.\n```\n\n#### Ionic/Angular 1\n\nFor Ionic 1 and Angular 1 apps using Ionic Native, there are many ways we can handle deeplinks. However,\nwe need to make sure we set up a history stack for the user, we can't navigate directly to our page\nbecause Ionic 1's navigation system won't properly build the navigation stack (to show a back button, for example).\n\nThis is all fine because deeplinks should provide the user with a designed experience for what the back button\nshould do, as we are putting them deep into the app and need to provide a natural way back to the main flow:\n\n(See a simple [demo](https://github.com/ionic-team/ionic1-deeplinks-demo) of v1 deeplinking).\n\n```javascript\nangular.module('myApp', ['ionic', 'ionic.native'])\n\n.run(['$ionicPlatform', '$cordovaDeeplinks', '$state', '$timeout', function($ionicPlatform, $cordovaDeeplinks, $state, $timeout) {\n  $ionicPlatform.ready(function() {\n    // Note: route's first argument can take any kind of object as its data,\n    // and will send along the matching object if the route matches the deeplink\n    $cordovaDeeplinks.route({\n      '/product/:productId': {\n        target: 'product',\n        parent: 'products'\n      }\n    }).subscribe(function(match) {\n      // One of our routes matched, we will quickly navigate to our parent\n      // view to give the user a natural back button flow\n      $timeout(function() {\n        $state.go(match.$route.parent, match.$args);\n\n        // Finally, we will navigate to the deeplink page. Now the user has\n        // the 'product' view visibile, and the back button goes back to the\n        // 'products' view.\n        $timeout(function() {\n          $state.go(match.$route.target, match.$args);\n        }, 800);\n      }, 100); // Timeouts can be tweaked to customize the feel of the deeplink\n    }, function(nomatch) {\n      console.warn('No match', nomatch);\n    });\n  });\n}])\n```\n\n#### Non-Ionic/angular\n\nIonic Native works with non-Ionic/Angular projects and can be accessed at `window.IonicNative` if imported.\n\nIf you don't want to use Ionic Native, the plugin is available on `window.IonicDeeplink` with a similar API minus the observable callback:\n\n```javascript\nwindow.addEventListener('deviceready', function() {\n  IonicDeeplink.route({\n    '/product/:productId': {\n      target: 'product',\n      parent: 'products'\n    }\n  }, function(match) {\n  }, function(nomatch) {\n  });\n})\n```\n\n## iOS Configuration\n\nAs of iOS 9.2, Universal Links *must* be enabled in order to deep link to your app. Custom URL schemes are no longer supported.\n\nFollow the official [Universal Links](https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html) guide on the Apple Developer docs\nto set up your domain to allow Universal Links.\n\n### How to set up top-level domains (TLD's)\n#### Set up Associated Domains\nFirst you must enable the `Associated Domains` capability in your [provisioning profile](https://developer.apple.com/account/resources/profiles/list).\nAfter that you must enable it in the Xcode project, too.\nFor automated builds you can do it easily by adding this to your `config.xml`.\n\n    <config-file target=\"*-Debug.plist\" parent=\"com.apple.developer.associated-domains\">\n        <array>\n            <string>applinks:example.org</string>\n        </array>\n    </config-file>\n\n    <config-file target=\"*-Release.plist\" parent=\"com.apple.developer.associated-domains\">\n        <array>\n            <string>applinks:example.org</string>\n        </array>\n    </config-file>\n\nInstead of `applinks` only you could use `<string>webcredentials:example.org</string>` or `<string>activitycontinuation:example.org</string>`, too.\n\n#### Set up Apple App Site Association (AASA)\nYour website (i.e. `example.org`) must provide this both files.\n\n* /apple-app-site-association\n* /.well-known/apple-app-site-association\n\nThe content should contain your app.\n\n    {\n      \"applinks\": {\n        \"apps\": [],\n        \"details\": [\n          {\n            \"appID\": \"1A234BCD56.org.example\",\n            \"paths\": [\n              \"NOT \\/api\\/*\",\n              \"NOT \\/\",\n              \"*\"\n            ]\n          }\n        ]\n      }\n    }\n\nThis means that all your requests - except /api and / - will be redirected to your app.\nPlease replace `1A234BCD56` with your TEAM ID and `org.example` with your Bundle-ID. (the `id=\"\"` of your `<widget />`)\n\n## Android Configuration\n\nAndroid supports Custom URL Scheme links, and as of Android 6.0 supports a similar feature to iOS' Universal Links called App Links.\n\nFollow the App Links documentation on [Declaring Website Associations](https://developer.android.com/training/app-links/index.html#web-assoc) to enable your domain to\ndeeplink to your Android app.\n\nTo prevent Android from creating multiple app instances when opening deeplinks, you can add the following preference in Cordova config.xml file:\n\n```xml\n <preference name=\"AndroidLaunchMode\" value=\"singleTask\" />\n```\n\n### How to set up top-level domains (TLD's)\n#### Set up [Android App Links](https://developer.android.com/training/app-links)\nYour website (i.e. `example.org`) must provide this file.\n\n* /.well-known/assetlinks.json\n\nThe content should contain your app.\n\n    [\n      {\n        \"relation\": [\n          \"delegate_permission\\/common.handle_all_urls\"\n        ],\n        \"target\": {\n          \"namespace\": \"android_app\",\n          \"package_name\": \"org.example\",\n          \"sha256_cert_fingerprints\": [\n            \"12:A3:BC:D4:56:E7:89:F0:12:34:5A:B6:78:90:C1:23:45:DE:67:FA:89:01:2B:C3:45:67:8D:9E:0F:1A:2B:C3\"\n          ]\n        }\n      }\n    ]\n\nReplace `org.example` with your app package. (the `id=\"\"` of your `<widget />`)\nThe fingerprints you can get via `$ keytool -list -v -keystore my-release-key.keystore`.\nYou can test it via https://developers.google.com/digital-asset-links/tools/generator.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"ionic-plugin-deeplinks\",\n  \"version\": \"1.0.24\",\n  \"cordova\": {\n    \"id\": \"ionic-plugin-deeplinks\",\n    \"platforms\": [\n      \"android\",\n      \"ios\"\n    ]\n  },\n  \"description\": \"Ionic Deeplinks Plugin\",\n  \"repository\": \"https://github.com/ionic-team/ionic-plugin-deeplinks.git\",\n  \"issue\": \"https://github.com/ionic-team/ionic-plugin-deeplinks/issues\",\n  \"keywords\": [\n    \"ionic\",\n    \"cordova\",\n    \"deeplink\",\n    \"deeplinks\",\n    \"mobile\",\n    \"hybrid\",\n    \"ecosystem:cordova\",\n    \"cordova-android\",\n    \"cordova-ios\"\n  ],\n  \"dependencies\": {\n    \"mkpath\": \">=1.0.0\",\n    \"xml2js\": \">=0.4\",\n    \"node-version-compare\": \">=1.0.1\",\n    \"plist\": \">=1.2.0\"\n  },\n  \"author\": \"Max Lynch <max@ionic.io>\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "plugin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<plugin xmlns=\"http://apache.org/cordova/ns/plugins/1.0\" id=\"ionic-plugin-deeplinks\" version=\"1.0.22\">\n\t<name>Ionic Deeplink Plugin</name>\n\t<description>Ionic Deeplink Plugin</description>\n\t<license>MIT</license>\n\t<keywords>Ionic,deeplinks,deeplinking</keywords>\n\t<repo>https://github.com/ionic-team/ionic-plugin-deeplink.git</repo>\n\t<issue>https://github.com/ionic-team/ionic-plugin-deeplink/issues</issue>\n\n\t<preference name=\"URL_SCHEME\" />\n\t<preference name=\"DEEPLINK_SCHEME\" default=\"https\" />\n\t<preference name=\"DEEPLINK_HOST\" default=\"\" />\n\n\t<!-- android -->\n\t<platform name=\"android\">\n\t\t<preference name=\"ANDROID_PATH_PREFIX\" default=\"/\" />\n\t\t<preference name=\"ANDROID_2_PATH_PREFIX\" default=\"/\" />\n\t\t<preference name=\"ANDROID_3_PATH_PREFIX\" default=\"/\" />\n\t\t<preference name=\"ANDROID_4_PATH_PREFIX\" default=\"/\" />\n\t\t<preference name=\"ANDROID_5_PATH_PREFIX\" default=\"/\" />\n\n\t\t<preference name=\"DEEPLINK_2_SCHEME\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_2_HOST\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_3_SCHEME\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_3_HOST\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_4_SCHEME\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_4_HOST\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_5_SCHEME\" default=\" \" />\n\t\t<preference name=\"DEEPLINK_5_HOST\" default=\" \" />\n\n\t\t<js-module src=\"www/deeplink.js\" name=\"deeplink\">\n\t\t\t<runs/>\n\t\t\t<clobbers target=\"IonicDeeplink\" />\n\t\t</js-module>\n\t\t<config-file target=\"res/xml/config.xml\" parent=\"/*\">\n\t\t\t<feature name=\"IonicDeeplinkPlugin\">\n\t\t\t\t<param name=\"android-package\" value=\"io.ionic.links.IonicDeeplink\" />\n\t\t\t\t<param name=\"onload\" value=\"true\" />\n\t\t\t</feature>\n\t\t</config-file>\n\n\n\t\t<config-file target=\"AndroidManifest.xml\" parent=\"/manifest/application/activity\">\n\t\t\t<intent-filter>\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:scheme=\"$URL_SCHEME\" />\n\t\t\t</intent-filter>\n\t\t\t<intent-filter android:autoVerify=\"true\">\n\t\t\t\t<action android:name=\"android.intent.action.VIEW\" />\n\t\t\t\t<category android:name=\"android.intent.category.DEFAULT\" />\n\t\t\t\t<category android:name=\"android.intent.category.BROWSABLE\" />\n\t\t\t\t<data android:scheme=\"$DEEPLINK_SCHEME\" android:host=\"$DEEPLINK_HOST\" android:pathPrefix=\"$ANDROID_PATH_PREFIX\" />\n\t\t\t\t<data android:scheme=\"$DEEPLINK_2_SCHEME\" android:host=\"$DEEPLINK_2_HOST\" android:pathPrefix=\"$ANDROID_2_PATH_PREFIX\" />\n\t\t\t\t<data android:scheme=\"$DEEPLINK_3_SCHEME\" android:host=\"$DEEPLINK_3_HOST\" android:pathPrefix=\"$ANDROID_3_PATH_PREFIX\" />\n\t\t\t\t<data android:scheme=\"$DEEPLINK_4_SCHEME\" android:host=\"$DEEPLINK_4_HOST\" android:pathPrefix=\"$ANDROID_4_PATH_PREFIX\" />\n\t\t\t\t<data android:scheme=\"$DEEPLINK_5_SCHEME\" android:host=\"$DEEPLINK_5_HOST\" android:pathPrefix=\"$ANDROID_5_PATH_PREFIX\" />\n\t\t\t</intent-filter>\n\t\t</config-file>\n\n\t\t<source-file src=\"src/android/io/ionic/links/IonicDeeplink.java\" target-dir=\"src/io/ionic/deeplink\" />\n\t</platform>\n\n\t<!-- ios -->\n\t<platform name=\"ios\">\n\t\t<js-module src=\"www/deeplink.js\" name=\"deeplink\">\n\t\t\t<runs/>\n\t\t\t<clobbers target=\"IonicDeeplink\" />\n\t\t</js-module>\n\t\t<config-file target=\"config.xml\" parent=\"/*\">\n\t\t\t<feature name=\"IonicDeeplinkPlugin\">\n\t\t\t\t<param name=\"ios-package\" value=\"IonicDeeplinkPlugin\" onload=\"true\" />\n\t\t\t</feature>\n\t\t\t<preference name=\"URL_SCHEME\" value=\"$URL_SCHEME\" />\n\t\t\t<preference name=\"DEEPLINK_SCHEME\" value=\"$DEEPLINK_SCHEME\" />\n\t\t\t<preference name=\"DEEPLINK_HOST\" value=\"$DEEPLINK_HOST\" />\n\t\t</config-file>\n\n\t\t<config-file target=\"*-Info.plist\" parent=\"CFBundleURLTypes\">\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t\t\t<array>\n\t\t\t\t\t\t<string>$URL_SCHEME</string>\n\t\t\t\t\t</array>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</config-file>\n\n\t\t<source-file src=\"src/ios/AppDelegate+IonicDeeplink.m\" />\n\t\t<header-file src=\"src/ios/IonicDeeplinkPlugin.h\" />\n\t\t<source-file src=\"src/ios/IonicDeeplinkPlugin.m\" />\n\t</platform>\n\n\t<platform name=\"browser\">\n\t\t<js-module src=\"www/deeplink.js\" name=\"deeplink\">\n\t\t\t<runs/>\n\t\t\t<clobbers target=\"IonicDeeplink\" />\n\t\t</js-module>\n\t\t<js-module src=\"src/browser/DeeplinkProxy.js\" name=\"IonicDeeplinkProxy\">\n\t\t\t<runs />\n\t\t</js-module>\n\t</platform>\n</plugin>\n"
  },
  {
    "path": "src/android/io/ionic/links/IonicDeeplink.java",
    "content": "/**\n * Ionic Deeplinks Plugin.\n * License: MIT\n *\n * Thanks to Eddy Verbruggen and nordnet for the great custom URl scheme and\n * unviversal links plugins this plugin was inspired by.\n *\n * https://github.com/EddyVerbruggen/Custom-URL-scheme\n * https://github.com/nordnet/cordova-universal-links-plugin\n */\npackage io.ionic.links;\n\nimport org.apache.cordova.CallbackContext;\nimport org.apache.cordova.CordovaInterface;\nimport org.apache.cordova.CordovaPlugin;\nimport org.apache.cordova.CordovaWebView;\nimport org.apache.cordova.PluginResult;\nimport org.apache.cordova.PluginResult.Status;\nimport org.json.JSONObject;\nimport org.json.JSONArray;\nimport org.json.JSONException;\n\nimport java.util.Date;\nimport java.util.TimeZone;\n\nimport android.util.Log;\nimport android.content.Intent;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.util.DisplayMetrics;\nimport android.view.View;\nimport android.view.ViewTreeObserver.OnGlobalLayoutListener;\nimport android.view.inputmethod.InputMethodManager;\nimport android.telephony.TelephonyManager;\nimport android.net.Uri;\nimport android.content.pm.PackageManager;\nimport android.os.Bundle;\nimport android.provider.Settings;\n\nimport java.util.ArrayList;\nimport java.util.Set;\nimport java.util.Collection;\nimport java.util.Map;\n\npublic class IonicDeeplink extends CordovaPlugin {\n  private static final String TAG = \"IonicDeeplinkPlugin\";\n\n  private JSONObject lastEvent;\n\n  private ArrayList<CallbackContext> _handlers = new ArrayList<CallbackContext>();\n\n  public void initialize(CordovaInterface cordova, CordovaWebView webView) {\n    super.initialize(cordova, webView);\n    Log.d(TAG, \"IonicDeepLinkPlugin: firing up...\");\n\n    handleIntent(cordova.getActivity().getIntent());\n  }\n\n  @Override\n  public void onNewIntent(Intent intent) {\n    handleIntent(intent);\n  }\n\n  public void handleIntent(Intent intent) {\n    final String intentString = intent.getDataString();\n\n    // read intent\n    String action = intent.getAction();\n    Uri url = intent.getData();\n    JSONObject bundleData = this._bundleToJson(intent.getExtras());\n    Log.d(TAG, \"Got a new intent: \" + intentString + \" \" + intent.getScheme() + \" \" + action + \" \" + url);\n\n    // if app was not launched by the url - ignore\n    if (!Intent.ACTION_VIEW.equals(action) || url == null) {\n      return;\n    }\n\n    // store message and try to consume it\n    try {\n      lastEvent = new JSONObject();\n      lastEvent.put(\"url\", url.toString());\n      lastEvent.put(\"path\", url.getPath());\n      lastEvent.put(\"queryString\", url.getQuery());\n      lastEvent.put(\"scheme\", url.getScheme());\n      lastEvent.put(\"host\", url.getHost());\n      lastEvent.put(\"fragment\", url.getFragment());\n      lastEvent.put(\"extra\", bundleData);\n      consumeEvents();\n    } catch(JSONException ex) {\n      Log.e(TAG, \"Unable to process URL scheme deeplink\", ex);\n    }\n  }\n\n  public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {\n    if(action.equals(\"onDeepLink\")) {\n      addHandler(args, callbackContext);\n    } else if(action.equals(\"canOpenApp\")) {\n      String uri = args.getString(0);\n      canOpenApp(uri, callbackContext);\n    } else if(action.equals(\"getHardwareInfo\")) {\n      getHardwareInfo(args, callbackContext);\n    }\n    return true;\n  }\n\n  /**\n   * Try to consume any waiting intent events by sending them to our plugin\n   * handlers. We will only do this if we have active handlers so the message isn't lost.\n   */\n  private void consumeEvents() {\n    if(this._handlers.size() == 0 || lastEvent == null) {\n      return;\n    }\n\n    for(CallbackContext callback : this._handlers) {\n      sendToJs(lastEvent, callback);\n    }\n    lastEvent = null;\n  }\n\n  private void sendToJs(JSONObject event, CallbackContext callback) {\n    final PluginResult result = new PluginResult(PluginResult.Status.OK, event);\n    result.setKeepCallback(true);\n    callback.sendPluginResult(result);\n  }\n\n  private void addHandler(JSONArray args, final CallbackContext callbackContext) {\n    this._handlers.add(callbackContext);\n    this.consumeEvents();\n  }\n\n  private JSONObject _bundleToJson(Bundle bundle) {\n    if(bundle == null) {\n      return new JSONObject();\n    }\n\n    JSONObject j = new JSONObject();\n    Set<String> keys = bundle.keySet();\n    for(String key : keys) {\n      try {\n        Class<?> jsonClass = j.getClass();\n        Class[] cArg = new Class[1];\n        cArg[0] = String.class;\n        //Workaround for API < 19\n        try{\n          if(jsonClass.getDeclaredMethod(\"wrap\", cArg) != null){\n            j.put(key, JSONObject.wrap(bundle.get(key)));\n          }\n        }\n        catch(NoSuchMethodException e) {\n          j.put(key, this._wrap(bundle.get(key)));\n        }\n      } catch(JSONException ex) {}\n    }\n\n    return j;\n  }\n  //Wrap method not available in JSONObject API < 19\n  private Object _wrap(Object o){\n    if (o == null) {\n      return null;\n    }\n    if (o instanceof JSONArray || o instanceof JSONObject) {\n      return o;\n    }\n    if (o.equals(null)) {\n      return o;\n    }\n    try {\n      if (o instanceof Collection) {\n        return new JSONArray((Collection) o);\n      } else if (o.getClass().isArray()) {\n        return new JSONArray(o);\n      }\n      if (o instanceof Map) {\n        return new JSONObject((Map) o);\n      }\n      if (o instanceof Boolean ||\n          o instanceof Byte ||\n          o instanceof Character ||\n          o instanceof Double ||\n          o instanceof Float ||\n          o instanceof Integer ||\n          o instanceof Long ||\n          o instanceof Short ||\n          o instanceof String) {\n        return o;\n      }\n      if (o.getClass().getPackage().getName().startsWith(\"java.\")) {\n        return o.toString();\n      }\n    } catch (Exception ignored) {}\n    return null;\n  }\n\n  /**\n   * Check if we can open an app with a given URI scheme.\n   *\n   * Thanks to https://github.com/ohh2ahh/AppAvailability/blob/master/src/android/AppAvailability.java\n   */\n  private void canOpenApp(String uri, final CallbackContext callbackContext) {\n    Context ctx = this.cordova.getActivity().getApplicationContext();\n    final PackageManager pm = ctx.getPackageManager();\n\n    try {\n      pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);\n      callbackContext.success();\n    } catch(PackageManager.NameNotFoundException e) {}\n\n    callbackContext.error(\"\");\n  }\n\n  private void getHardwareInfo(JSONArray args, final CallbackContext callbackContext) {\n    String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);\n\n    JSONObject j = new JSONObject();\n    try {\n      j.put(\"uuid\", uuid);\n      j.put(\"platform\", this.getPlatform());\n      j.put(\"tz\", this.getTimeZoneID());\n      j.put(\"tz_offset\", this.getTimeZoneOffset());\n      j.put(\"os_version\", this.getOSVersion());\n      j.put(\"sdk_version\", this.getSDKVersion());\n    } catch(JSONException ex) {}\n\n    final PluginResult result = new PluginResult(PluginResult.Status.OK, j);\n    callbackContext.sendPluginResult(result);\n  }\n\n  private boolean isAmazonDevice() {\n    if (android.os.Build.MANUFACTURER.equals(\"Amazon\")) {\n      return true;\n    }\n    return false;\n  }\n  private String getTimeZoneID() {\n    TimeZone tz = TimeZone.getDefault();\n    return (tz.getID());\n  }\n\n  private int getTimeZoneOffset() {\n    TimeZone tz = TimeZone.getDefault();\n    return tz.getOffset(new Date().getTime()) / 1000 / 60;\n  }\n\n  private String getSDKVersion() {\n    @SuppressWarnings(\"deprecation\")\n    String sdkversion = android.os.Build.VERSION.SDK;\n    return sdkversion;\n  }\n  private String getOSVersion() {\n    String osversion = android.os.Build.VERSION.RELEASE;\n    return osversion;\n  }\n  private String getPlatform() {\n    String platform;\n    if (isAmazonDevice()) {\n      platform = \"amazon-fireos\";\n    } else {\n      platform = \"android\";\n    }\n    return platform;\n  }\n}\n"
  },
  {
    "path": "src/browser/DeeplinkProxy.js",
    "content": "function parseSchemeFromUrl (url) {\n\tvar _sep = url.indexOf(':');\n\tif (_sep > -1) {\n\t\treturn url.slice(0, _sep + 1);\n\t}\n\n\treturn undefined;\n}\n\nfunction parseQueryStringFromUrl(url) {\n\tvar qs = url.indexOf('?');\n\n\tif (qs > -1) {\n\t\treturn url.slice(qs + 1);\n\t}\n\n\treturn undefined;\n}\n\nfunction locationToData(l) {\n  return {\n    url: l.href,\n    path: l.pathname,\n    host: l.hostname,\n    fragment: l.hash,\n\t\tscheme: parseSchemeFromUrl(l.href),\n\t\tqueryString: parseQueryStringFromUrl(l.href)\n  }\n}\n\nmodule.exports = {\n  canOpenApp: function() {\n    // We can't infer this from the browser environment\n    return false;\n  },\n\n  onDeepLink: function(callback) {\n    // Try the first deeplink route\n    setTimeout(function() {\n      callback && callback(locationToData(window.location), {\n        keepCallback: true\n      });\n    })\n\n    return window.addEventListener('hashchange', function(e) {\n      callback && callback(locationToData(window.location), {\n        keepCallback: true\n      });\n    }, false);\n  },\n\n  getHardwareInfo: function(callback) {\n    return {};\n  }\n};\n\nrequire(\"cordova/exec/proxy\").add(\"IonicDeeplinkPlugin\", module.exports);\n"
  },
  {
    "path": "src/ios/AppDelegate+IonicDeeplink.m",
    "content": "#import \"AppDelegate.h\"\n#import \"IonicDeeplinkPlugin.h\"\n\nstatic NSString *const PLUGIN_NAME = @\"IonicDeeplinkPlugin\";\n\n/**\n *  Category for the AppDelegate that overrides application:continueUserActivity:restorationHandler method,\n *  so we could handle application launch when user clicks on the link in the browser.\n */\n@interface AppDelegate (IonicDeeplinkPlugin)\n\n- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options;\n- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;\n- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler;\n- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;\n\n@end\n\n@implementation AppDelegate (IonicDeeplinkPlugin)\n\n- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {\n    NSMutableString *sourceApp = [[NSMutableString alloc] init];\n    NSMutableString *annotation = [[NSMutableString alloc] init];\n    \n    if([options objectForKey:UIApplicationOpenURLOptionsSourceApplicationKey]) {\n        sourceApp = [options objectForKey:UIApplicationOpenURLOptionsSourceApplicationKey];\n    }\n\n    if([options objectForKey:UIApplicationOpenURLOptionsAnnotationKey]) {\n        annotation = [options objectForKey:UIApplicationOpenURLOptionsAnnotationKey];\n    }\n\n    return [self application:app openURL:url sourceApplication:sourceApp annotation:annotation];\n}\n\n- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {\n    IonicDeeplinkPlugin *plugin = [self.viewController getCommandInstance:PLUGIN_NAME];\n\n    if(plugin == nil) {\n      NSLog(@\"Unable to get instance of command plugin\");\n      return NO;\n    }\n\n    BOOL handled = [plugin handleLink:url];\n\n    if(!handled) {\n      // Pass event through to Cordova\n      NSMutableDictionary * openURLData = [[NSMutableDictionary alloc] init];\n\n      [openURLData setValue:url forKey:@\"url\"];\n\n      if (sourceApplication) {\n          [openURLData setValue:sourceApplication forKey:@\"sourceApplication\"];\n      }\n\n      if (annotation) {\n          [openURLData setValue:annotation forKey:@\"annotation\"];\n      }\n\n      [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];\n      [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLWithAppSourceAndAnnotationNotification object:openURLData]];\n\n      // Send notice to the rest of our plugin that we didn't handle this URL\n      [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@\"IonicLinksUnhandledURL\" object:[url absoluteString]]];\n    }\n\n    return YES;\n}\n\n- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {\n    // Pass it off to our plugin\n    IonicDeeplinkPlugin *plugin = [self.viewController getCommandInstance:PLUGIN_NAME];\n\n    if(plugin == nil) {\n      return NO;\n    }\n\n    BOOL handled = [plugin handleContinueUserActivity:userActivity];\n\n    if(!handled) {\n        // Continue sending the openURL request through\n    }\n    return YES;\n}\n\n- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {\n    // Pass the push notification to the plugin\n    if([userInfo objectForKey:@\"uri\"] == nil) {\n      return;\n    }\n\n    if(application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground) {\n      IonicDeeplinkPlugin *plugin = [self.viewController getCommandInstance:PLUGIN_NAME];\n\n      if(plugin == nil) {\n        NSLog(@\"Unable to get instance of command plugin\");\n        return;\n      }\n\n      NSURL *url = [NSURL URLWithString:[userInfo objectForKey:@\"uri\"]];\n      [plugin handleLink:url];\n    }\n}\n\n@end\n"
  },
  {
    "path": "src/ios/IonicDeeplinkPlugin.h",
    "content": "#import <Cordova/CDVPlugin.h>\n\n@interface IonicDeeplinkPlugin : CDVPlugin {\n  // Handlers for URL events\n  NSMutableArray *_handlers;\n  CDVPluginResult *_lastEvent;\n}\n\n// User-plugin command handler\n- (void)canOpenApp:(CDVInvokedUrlCommand *)command;\n- (void)onDeepLink:(CDVInvokedUrlCommand *)command;\n- (void)getHardwareInfo:(CDVInvokedUrlCommand *)command;\n\n// Internal deeplink and CUA handlers\n- (BOOL)handleLink:(NSURL *)url;\n- (BOOL)handleContinueUserActivity:(NSUserActivity *)userActivity;\n\n- (void)sendToJs;\n\n- (CDVPluginResult*)createResult:(NSURL *)url;\n\n@end\n"
  },
  {
    "path": "src/ios/IonicDeeplinkPlugin.m",
    "content": "#import \"IonicDeeplinkPlugin.h\"\n\n#import <Cordova/CDVAvailability.h>\n\n@implementation IonicDeeplinkPlugin\n\n- (void)pluginInitialize {\n  _handlers = [[NSMutableArray alloc] init];\n}\n\n/* ------------------------------------------------------------- */\n\n- (void)onAppTerminate {\n  _handlers = nil;\n  [super onAppTerminate];\n}\n\n- (void)canOpenApp:(CDVInvokedUrlCommand *)command {\n  CDVPluginResult* result = nil;\n\n  NSString* scheme = [command.arguments objectAtIndex:0];\n\n  if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:scheme]]) {\n    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:(true)];\n  } else {\n    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsBool:(false)];\n  }\n\n  [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];\n}\n\n- (void)onDeepLink:(CDVInvokedUrlCommand *)command {\n  [_handlers addObject:command.callbackId];\n  // Try to consume any events we got before we were listening\n  [self sendToJs];\n}\n\n- (BOOL)handleLink:(NSURL *)url {\n  NSLog(@\"IonicDeepLinkPlugin: Handle link (internal) %@\", url);\n  \n  if(![self checkUrl:url]) {\n    return NO;\n  }\n\n  _lastEvent = [self createResult:url];\n\n  [self sendToJs];\n\n  return YES;\n}\n\n- (BOOL)checkUrl:(NSURL *)url {\n  if(url == nil) return NO;\n    \n  NSString* urlScheme = [[self.commandDelegate settings] objectForKey:@\"url_scheme\"];\n    \n  if(urlScheme == nil) return NO;\n    \n  NSLog(@\"url scheme:%@\",[url scheme]);\n  NSLog(@\"url host:%@\",[url host]);\n\n  if([[url scheme] isEqualToString:urlScheme]) {\n    return YES;\n  }\n    \n  NSString* deeplinkScheme = [[self.commandDelegate settings] objectForKey:@\"deeplink_scheme\"];\n  NSString* deeplinkHost = [[self.commandDelegate settings] objectForKey:@\"deeplink_host\"];\n    \n  if(deeplinkScheme!=nil && deeplinkHost != nil) {\n    if([[url scheme] isEqualToString:deeplinkScheme]&&[[url host] isEqualToString:deeplinkHost]) {\n      return YES;\n    }\n  }\n  \n  return NO;\n}\n\n- (BOOL)handleContinueUserActivity:(NSUserActivity *)userActivity {\n\n  if (![userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb] || userActivity.webpageURL == nil) {\n    return NO;\n  }\n\n  NSURL *url = userActivity.webpageURL;\n  _lastEvent = [self createResult:url];\n  NSLog(@\"IonicDeepLinkPlugin: Handle continueUserActivity (internal) %@\", url);\n\n  [self sendToJs];\n\n  return NO;\n}\n\n- (void) sendToJs {\n  // Send the last event to JS if we have one\n  if (_handlers.count == 0 || _lastEvent == nil) {\n    return;\n  }\n\n  // Iterate our handlers and send the event\n  for (id callbackID in _handlers) {\n    [self.commandDelegate sendPluginResult:_lastEvent callbackId:callbackID];\n  }\n\n  // Clear out the last event\n  _lastEvent = nil;\n}\n\n- (CDVPluginResult *)createResult:(NSURL *)url {\n  NSDictionary* data = @{\n    @\"url\": [url absoluteString] ?: @\"\",\n    @\"path\": [url path] ?: @\"\",\n    @\"queryString\": [url query] ?: @\"\",\n    @\"scheme\": [url scheme] ?: @\"\",\n    @\"host\": [url host] ?: @\"\",\n    @\"fragment\": [url fragment] ?: @\"\"\n  };\n\n  CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:data];\n  [result setKeepCallbackAsBool:YES];\n  return result;\n}\n\n- (void)getHardwareInfo:(CDVInvokedUrlCommand *)command {\n  NSMutableDictionary *info = [[NSMutableDictionary alloc] init];\n\n\n  // Removing part where advertisingIdentifier is being used to keep the functional part working.\n\n  NSString *uuid = [[UIDevice currentDevice].identifierForVendor UUIDString];\n\n  if(uuid && [uuid length] > 0) {\n    [info setObject:uuid forKey:@\"uuid\"];\n  }\n\n  CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info];\n  [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];\n}\n\n@end\n"
  },
  {
    "path": "www/deeplink.js",
    "content": "var argscheck = require('cordova/argscheck'),\n  utils = require('cordova/utils'),\n  exec = require('cordova/exec');\n\nvar PLUGIN_NAME = 'IonicDeeplinkPlugin';\n\nvar extend = function (out) {\n  out = out || {};\n\n  for (var i = 1; i < arguments.length; i++) {\n    if (!arguments[i]) {\n      continue;\n    }\n    for (var key in arguments[i]) {\n      if (arguments[i].hasOwnProperty(key)) {\n        out[key] = arguments[i][key];\n      }\n    }\n  }\n  return out;\n};\n\n\nvar IonicDeeplink = {\n\n  /**\n   * How long to wait after a deeplink match before navigating.\n   * Default is 800ms which gives the app time to get back and then\n   * smoothly animate.\n   */\n  NAVIGATION_DELAY: 800,\n\n  canOpenApp: function (app, cb) {\n    exec(cb, null, PLUGIN_NAME, 'canOpenApp', []);\n  },\n\n  route: function (paths, success, error) {\n    var self = this;\n    this.paths = paths;\n\n    this.onDeepLink(function (data) {\n      var realPath = self._getRealPath(data);\n\n      var args = self._queryToObject(data.queryString);\n\n      var matched = false;\n      var finalArgs;\n      var pathData;\n\n      for (var targetPath in paths) {\n        pathData = paths[targetPath];\n\n        var matchedParams = self.routeMatch(targetPath, realPath);\n\n        if (matchedParams !== false) {\n          matched = true;\n          finalArgs = extend({}, matchedParams, args);\n\n          break;\n        }\n      }\n\n      if (matched === true) {\n        console.log('Match found', realPath);\n\n        if (typeof (success) === 'function') {\n          success({\n            $route: pathData,\n            $args: finalArgs,\n            $link: data,\n          });\n        }\n\n        return;\n      }\n\n      if (typeof (error) === 'function') {\n        console.log('No Match found');\n        error({ $link: data });\n      }\n    })\n  },\n\n  routeWithNavController: function (navController, paths, options, success, error) {\n    var self = this;\n\n    var defaultOptions = {\n      root: false,\n    };\n\n    if (typeof options !== 'function') {\n      options = extend(defaultOptions, options);\n    } else {\n      success = options;\n      error = success;\n      options = defaultOptions;\n    }\n\n    this.route(paths, function (match) {\n      // Defer this to ensure animations run\n      setTimeout(function () {\n        if (options.root === true) {\n          navController.setRoot(match.$route, match.$args);\n        } else {\n          navController.push(match.$route, match.$args);\n        }\n      }, self.NAVIGATION_DELAY);\n\n      if (typeof (success) === 'function') {\n        success(match);\n      }\n    }, function (nomatch) {\n      if (typeof (error) === 'function') {\n        error(nomatch);\n      }\n    });\n  },\n\n  /**\n   * Check if the path matches the route.\n   */\n  routeMatch: function (route, path) {\n    if (route === path) {\n      return {};\n    }\n\n    var parts = path.split('/');\n    var routeParts = route.split('/');\n\n    // Our aggregated route params that matched our route path.\n    // This is used for things like /post/:id\n    var routeParams = {};\n\n    if (parts.length !== routeParts.length) {\n      // Can't possibly match if the lengths are different\n      return false;\n    }\n\n    // Otherwise, we need to check each part\n\n    var rp,\n      pp;\n\n    for (var i = 0; i < parts.length; i++) {\n      pp = parts[i];\n      rp = routeParts[i];\n\n      if (rp[0] == ':') {\n        // We have a route param, store it in our\n        // route params without the colon\n        routeParams[rp.slice(1)] = pp;\n      } else if (pp !== rp) {\n        return false;\n      }\n    }\n    return routeParams;\n  },\n\n  _queryToObject: function (q) {\n    if (!q) return {};\n\n    var qIndex = q.indexOf('?');\n\n    if (qIndex > -1) {\n      q = q.slice(qIndex + 1);\n    }\n\n    var i \t\t\t= 0,\n      retObj \t= {},\n      pair \t\t= null,\n      qArr \t\t= q.split('&');\n\n    for (; i < qArr.length; i++) {\n      if (!qArr[i]) {\n        continue;\n      }\n\n      pair = qArr[i].split('=');\n      retObj[pair[0]] = pair[1];\n    }\n\n    return retObj;\n  },\n\n  _stripFragmentLeadingHash: function (fragment) {\n    var hs = fragment.indexOf('#');\n\n    if (hs > -1) {\n      fragment.slice(0, hs);\n    }\n\n    return fragment;\n  },\n\n  /**\n   * We're fairly flexible when it comes to matching a URL. We support\n   * host-less custom URL scheme matches like ionic://camera?blah but also support\n   * and match against fragments.\n   *\n   * This method tries to infer what the proper \"path\" is from the URL\n   */\n  _getRealPath: function (data) {\n\n    // 1. Let's just do the obvious and return the parsed 'path' first, if available.\n    if (!!data.path && data.path !== \"\") {\n      return data.path;\n    }\n\n    // 2. Now, are we using a non-standard scheme?\n    var isCustomScheme = data.scheme.indexOf('http') === -1;\n\n    // 3. Nope so we'll go fragment first if available as that should be what comes after\n    if (!isCustomScheme) {\n      if (!!data.fragment) {\n        return this._stripFragmentLeadingHash(data.fragment);\n      }\n    }\n\n    // 4. Now fall back to host / authority\n    if (!!data.host) {\n      if (data.host.charAt(0) != '/') {\n        data.host = '/' + data.host;\n      }\n\n      return data.host;\n    }\n\n    // 5. We'll use fragment next if we're in a custom scheme, though this might need a little more thought\n    if (isCustomScheme && !!data.fragment) {\n      return this._stripFragmentLeadingHash(data.fragment);\n    }\n\n    // 6. Last resort - no obvious path, fragment or host, so we\n    // slice out the scheme and any query string or fragment from the full url.\n    var restOfUrl = data.url;\n    var separator = data.url.indexOf('://');\n\n    if (separator !== -1) {\n      restOfUrl = data.url.slice(separator + 3);\n    } else {\n      separator = data.url.indexOf(':/');\n      if (separator !== -1) {\n        restOfUrl = data.url.slice(separator + 2);\n      }\n    }\n\n    var qs = restOfUrl.indexOf('?');\n    if (qs > -1) {\n      restOfUrl = restOfUrl.slice(0, qs);\n    }\n\n    var hs = restOfUrl.indexOf('#');\n    if (hs > -1) {\n      restOfUrl = restOfUrl.slice(0, hs);\n    }\n\n    return restOfUrl;\n  },\n\n  onDeepLink: function (callback) {\n    var innerCB = function (data) {\n      callback(data);\n    };\n    exec(innerCB, null, PLUGIN_NAME, 'onDeepLink', []);\n  },\n\n  getHardwareInfo: function (callback) {\n    exec(callback, null, PLUGIN_NAME, 'getHardwareInfo', []);\n  },\n};\n\nmodule.exports = IonicDeeplink;\n"
  }
]