├── LICENSE ├── README.md ├── package.json ├── plugin.xml ├── src ├── android │ ├── Launcher.java │ └── ParseTypes.java └── ios │ ├── Launcher.h │ └── Launcher.m └── www └── Launcher.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nicholas Hutchind 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cordova-plugin-app-launcher 2 | =========================== 3 | 4 | Simple Cordova plugin to see if other apps are installed and launch them. 5 | 6 | ## 0. Index 7 | 1. [Description](#1-description) 8 | 2. [Installation](#2-installation) 9 | 3. [Usage](#3-usage) 10 | 4. [Changelog](#4-changelog) 11 | 5. [Credits](#5-credits) 12 | 6. [License](#6-license) 13 | 14 | ## 1. Description 15 | 16 | This plugin allows you to check if an app is installed that can handle a specific uri and launch an app via uri on iOS and Android. Additionally, you may open an Android app using its package id. 17 | * (iOS, Android) Check if any apps are installed that can launch via a specified uri. 18 | * (iOS, Android) Launch an app via a specified uri. 19 | * (Android) Check if an app is installed via its package id. 20 | * (Android) Check if an app is installed for an action name. 21 | * (Android) Launch an app via its package id. 22 | * (Android) Launch an app for an action name. 23 | * (Android) Launch an app with extras included. 24 | * (Android) Return results from a launched app once it is finished. 25 | 26 | ## 2. Installation 27 | 28 | ### Automatically (CLI / Plugman) 29 | 30 | ``` 31 | $ cordova plugin add https://github.com/nchutchind/cordova-plugin-app-launcher.git 32 | ``` 33 | and then (this step will modify your project): 34 | ``` 35 | $ cordova prepare 36 | ``` 37 | 38 | 1\. Add the following xml to your `config.xml`: 39 | ```xml 40 | 41 | 42 | 43 | 44 | 49 | ``` 50 | ```xml 51 | 52 | 53 | 54 | 55 | ``` 56 | 57 | 2\. Add `www/Launcher.js` to your project and reference it in `index.html`: 58 | ```html 59 | 60 | ``` 61 | 62 | 3\. Copy the files in `src/` for iOS and/or Android into your project. 63 | 64 | iOS: Copy `Launcher.h` and `Launcher.m` to `platforms/ios//Plugins` 65 | 66 | Android: Copy `Launcher.java` to `platforms/android/src/com/hutchind/cordova/plugins` (you will probably need to create this path) 67 | 68 | ### PhoneGap Build (possibly outdated) 69 | 70 | Add the following xml to your `config.xml` to always use the latest version of this plugin: 71 | ```xml 72 | 73 | ``` 74 | or to use a specific version: 75 | ```xml 76 | 77 | ``` 78 | For iOS 9+, the following may need to be added so that the URLs used to launch apps can be whitelisted (in this example, customSchemeName:// and fb:// would have been the URLs registered to the apps we want to be able to launch): 79 | ```xml 80 | 81 | 82 | customSchemeName 83 | fb 84 | 85 | 86 | ``` 87 | 88 | ## 3. Usage 89 | ```javascript 90 | // Default handlers 91 | var successCallback = function(data) { 92 | alert("Success!"); 93 | // if calling canLaunch() with getAppList:true, data will contain an array named "appList" with the package names of applications that can handle the uri specified. 94 | }; 95 | var errorCallback = function(errMsg) { 96 | alert("Error! " + errMsg); 97 | } 98 | ``` 99 | 100 | Check to see if Facebook can be launched via uri (**iOS** and **Android**) 101 | ```javascript 102 | window.plugins.launcher.canLaunch({uri:'fb://'}, successCallback, errorCallback); 103 | ``` 104 | 105 | Check to see if Facebook is installed (**Android**) 106 | ```javascript 107 | window.plugins.launcher.canLaunch({packageName:'com.facebook.katana'}, successCallback, errorCallback); 108 | ``` 109 | 110 | Launch Facebook to the logged in user's profile (**iOS** and **Android**) 111 | ```javascript 112 | window.plugins.launcher.launch({uri:'fb://profile'}, successCallback, errorCallback); 113 | ``` 114 | 115 | Launch Facebook via package id (**Android**) 116 | ```javascript 117 | window.plugins.launcher.launch({packageName:'com.facebook.katana'}, successCallback, errorCallback); 118 | ``` 119 | 120 | Check to see if an app is installed that can play NASA TV (**Android**) 121 | ```javascript 122 | window.plugins.launcher.canLaunch({ 123 | uri:'http://nasatv-lh.akamaihd.net/i/NASA_101@319270/master.m3u8', 124 | dataType:'application/x-mpegURL' 125 | }, successCallback, errorCallback); 126 | ``` 127 | 128 | Get a list of installed app packages that can play NASA TV (**Android**) 129 | ```javascript 130 | window.plugins.launcher.canLaunch({ 131 | uri:'http://nasatv-lh.akamaihd.net/i/NASA_101@319270/master.m3u8', 132 | dataType:'application/x-mpegURL', 133 | getAppList: true 134 | }, successCallback, errorCallback); 135 | ``` 136 | 137 | Launch NASA TV video stream in MxPlayer Free (**Android**) 138 | ```javascript 139 | window.plugins.launcher.launch({ 140 | packageName:'com.mxtech.videoplayer.ad', 141 | uri:'http://nasatv-lh.akamaihd.net/i/NASA_101@319270/master.m3u8', 142 | dataType:'application/x-mpegURL' 143 | }, successCallback, errorCallback); 144 | ``` 145 | 146 | Launch MxPlayer Free with Extras for specific videos from the sdcard, specific titles, and starting at 3 seconds in (**Android**) 147 | ```javascript 148 | var sdcard = "file:///sdcard/"; 149 | var file1 = sdcard + "video1.mp4", file2 = sdcard + "video2.mp4"; 150 | 151 | window.plugins.launcher.launch({ 152 | packageName:'com.mxtech.videoplayer.ad', 153 | uri:file1, 154 | dataType:'video/*' 155 | extras: [ 156 | {"name":"video_list", "value":[file1,file2], "dataType":"ParcelableArray", "paType":"Uri"}, 157 | {"name":"video_list.name", "value":["Awesome Video","Sweet Title"], "dataType":"StringArray"}, 158 | {"name":"position", "value":3000, "dataType":"int"} 159 | ] 160 | }, successCallback, errorCallback); 161 | ``` 162 | Launch MxPlayer Free with Extras for a specific video with title and return results (**Android**) 163 | ```javascript 164 | var filename = "file:///sdcard/video.mp4"; 165 | 166 | window.plugins.launcher.launch({ 167 | packageName:'com.mxtech.videoplayer.ad', 168 | uri:filename, 169 | dataType:'video/*', 170 | extras: [ 171 | {"name":"video_list", "value":[filename], "dataType":"ParcelableArray", "paType":"Uri"}, 172 | {"name":"video_list.name", "value":["Whatever Title You Want"], "dataType":"StringArray"}, 173 | {"name":"return_result", "value":true, "dataType":"Boolean"} 174 | ], 175 | successCallback: function(json) { 176 | if (json.isActivityDone) { 177 | if (json.extras && json.extras.end_by) { 178 | if (json.data) { 179 | alert("MxPlayer stopped while on video: " + json.data); 180 | } 181 | if (json.extras.end_by == "user") { 182 | // MxPlayer stopped because the User quit 183 | alert("User watched " + json.extras.position + " of " + json.extras.duration + " before quitting."); 184 | } else { 185 | alert("MxPlayer finished playing video without user quitting."); 186 | } 187 | } else { 188 | alert("Playback finished, but we have no results from MxPlayer."); 189 | } 190 | } else { 191 | console.log("MxPlayer launched"); 192 | } 193 | }, 194 | errorCallback: function(err) { 195 | alert("There was an error launching MxPlayer.") 196 | } 197 | }); 198 | ``` 199 | 200 | Check to see if Peek Acuity can be launched via an Action Name (**Android**) 201 | 202 | AndroidManifest.xml: 203 | ```xml 204 | 206 | 207 | 208 | 209 | 210 | 211 | ``` 212 | 213 | Typescript: 214 | ```typescript 215 | let actionName = 'org.peekvision.intent.action.TEST_ACUITY'; 216 | 217 | window["plugins"].launcher.canLaunch({actionName: actionName}, 218 | data => console.log("Peek Acuity can be launched"), 219 | errMsg => console.log("Peek Acuity not installed! " + errMsg) 220 | ); 221 | ``` 222 | 223 | Launch Peek Acuity via an Action Name with Extras and return results (**Android**) 224 | ```typescript 225 | let actionName = 'org.peekvision.intent.action.TEST_ACUITY'; 226 | 227 | let extras = [ 228 | {"name":"progressionLogMarArray", "value":[1.0,0.8,0.6,0.3,0.1],"dataType":"DoubleArray"}, 229 | {"name":"instructions", "value":"none", "dataType":"String"}, 230 | {"name":"eye", "value":"left", "dataType":"String"}, 231 | {"name":"beyondOpto", "value":true, "dataType":"Boolean"}, 232 | {"name":"testDistance", "value":"4m", "dataType":"String"}, 233 | {"name":"displayResult", "value":false, "dataType":"Boolean"}, 234 | {"name":"return_result", "value":true, "dataType":"Boolean"} 235 | ]; 236 | 237 | window["plugins"].launcher.launch({actionName: actionName, extras: extras}, 238 | json => { 239 | if (json.isActivityDone) { 240 | if (json.data) { 241 | console.log("data=" + json.data); 242 | } 243 | if (json.extras) { 244 | if (json.extras.logMar) { 245 | console.log("logMar=" + json.extras.logMar); 246 | } 247 | if (json.extras.averageLux) { 248 | console.log("averageLux=" + json.extras.averageLux); 249 | } 250 | } else { 251 | console.log("Peek Acuity done but no results"); 252 | } 253 | } else { 254 | console.log("Peek Acuity launched"); 255 | } 256 | }, 257 | errMsg => console.log("Peek Acuity error launching: " + errMsg) 258 | ); 259 | ``` 260 | 261 | # Extras Data Types 262 | 263 | Most datatypes that can be put into an Android Bundle are able to be passed in. You must provide the datatype to convert to. 264 | Only Uri Parcelables are supported currently. 265 | ```javascript 266 | extras: [ 267 | {"name":"myByte", "value":1, "dataType":"Byte"}, 268 | {"name":"myByteArray", "value":[1,0,2,3], "dataType":"ByteArray"}, 269 | {"name":"myShort", "value":5, "dataType":"Short"}, 270 | {"name":"myShortArray", "value":[1,2,3,4], "dataType":"ShortArray"}, 271 | {"name":"myInt", "value":2000, "dataType":"Int"}, 272 | {"name":"myIntArray", "value":[12,34,56], "dataType":"IntArray"}, 273 | {"name":"myIntArrayList", "value":[123,456,789], "dataType":"IntArrayList"}, 274 | {"name":"myLong", "value":123456789101112, "dataType":"Long"}, 275 | {"name":"myLongArray", "value":[123456789101112,121110987654321], "dataType":"LongArray"}, 276 | {"name":"myFloat", "value":12.34, "dataType":"Float"}, 277 | {"name":"myFloatArray", "value":[12.34,56.78], "dataType":"FloatArray"}, 278 | {"name":"myDouble", "value":12.3456789, "dataType":"Double"}, 279 | {"name":"myDoubleArray", "value":[12.3456789, 98.7654321], "dataType":"DoubleArray"}, 280 | {"name":"myBoolean", "value":false, "dataType":"Boolean"}, 281 | {"name":"myBooleanArray", "value":[true,false,true], "dataType":"BooleanArray"}, 282 | {"name":"myString", "value":"this is a test", "dataType":"String"}, 283 | {"name":"myStringArray", "value":["this","is", "a", "test"], "dataType":"StringArray"}, 284 | {"name":"myStringArrayList", "value":["this","is","a","test"], "dataType":"StringArrayList"}, 285 | {"name":"myChar", "value":"T", "dataType":"Char"}, 286 | {"name":"myCharArray", "value":"this is a test", "dataType":"CharArray"}, 287 | {"name":"myCharSequence", "value":"this is a test", "dataType":"CharSequence"}, 288 | {"name":"myCharSequenceArray", "value":["this","is a", "test"], "dataType":"CharSequenceArray"}, 289 | {"name":"myCharSequenceArrayList", "value":["this","is a", "test"], "dataType":"CharSequenceArrayList"}, 290 | {"name":"myParcelable", "value":"http://foo", "dataType":"Parcelable", "paType":"Uri"}, 291 | {"name":"myParcelableArray", "value":["http://foo","http://bar"], "dataType":"ParcelableArray", "paType":"Uri"}, 292 | {"name":"myParcelableArrayList", "value":["http://foo","http://bar"], "dataType":"ParcelableArrayList", "paType":"Uri"}, 293 | {"name":"mySparseParcelableArray", "value":{"10":"http://foo", "-25":"http://bar"}, "dataType":"SparseParcelableArray", "paType":"Uri"}, 294 | ] 295 | ``` 296 | 297 | #### Launcher.canLaunch Success Callback 298 | No data is passed. 299 | 300 | #### Launcher.canLaunch Error Callback 301 | Passes a string containing an error message. 302 | 303 | #### Launcher.launch Success Callback Data 304 | Passes a JSON object with varying parts. 305 | 306 | Activity launched 307 | ```javascript 308 | { 309 | isLaunched: true 310 | } 311 | ``` 312 | 313 | Activity finished 314 | ```javascript 315 | { 316 | isActivityDone: true 317 | } 318 | ``` 319 | 320 | Activity launched and data returned 321 | ```javascript 322 | { 323 | isActivityDone: true, 324 | data: , 325 | extras 326 | } 327 | ``` 328 | 329 | #### Launcher.launch Error Callback Data 330 | Passes an error message as a string. 331 | 332 | ## 4. Changelog 333 | 0.4.0: Android: Added ability to launch with intent. Thanks to [@mmey3k] for the code. 334 | 335 | 0.2.0: Android: Added ability to launch activity with extras and receive data back from launched app when it is finished. 336 | 337 | 0.1.2: Added ability to check if any apps are installed that can handle a certain datatype on Android. 338 | 339 | 0.1.1: Added ability to launch a package with a data uri and datatype on Android. 340 | 341 | 0.1.0: initial version supporting Android and iOS 342 | 343 | ## 5. Credits 344 | Special thanks to [@michael1t](https://github.com/michael1t) for sponsoring the development of the Extras portion of this plugin. 345 | 346 | ## 6. License 347 | 348 | [The MIT License (MIT)](http://www.opensource.org/licenses/mit-license.html) 349 | 350 | Permission is hereby granted, free of charge, to any person obtaining a copy 351 | of this software and associated documentation files (the "Software"), to deal 352 | in the Software without restriction, including without limitation the rights 353 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 354 | copies of the Software, and to permit persons to whom the Software is 355 | furnished to do so, subject to the following conditions: 356 | 357 | The above copyright notice and this permission notice shall be included in 358 | all copies or substantial portions of the Software. 359 | 360 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 361 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 362 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 363 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 364 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 365 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 366 | THE SOFTWARE. 367 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-app-launcher", 3 | "version": "0.4.0", 4 | "description": "This plugin allows you to see if other apps are installed and launch them. On Android, you can send extras to the launched Activity and receive result data (if available), as well as retrieve a list of applications that can open a specified uri.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/nchutchind/cordova-plugin-app-launcher.git" 8 | }, 9 | "keywords": [ 10 | "cordova", 11 | "launcher", 12 | "ecosystem:cordova", 13 | "cordova-android", 14 | "cordova-ios" 15 | ], 16 | "author": "Nicholas Hutchind", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/nchutchind/cordova-plugin-app-launcher/issues" 20 | }, 21 | "homepage": "https://github.com/nchutchind/cordova-plugin-app-launcher#readme" 22 | } 23 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | Launcher 9 | 10 | 11 | This plugin allows you to see if other apps are installed and launch them. On Android, you can send extras to the launched Activity and receive result data (if available), as well as retrieve a list of applications that can open a specified uri. 12 | 13 | 14 | MIT 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/android/Launcher.java: -------------------------------------------------------------------------------- 1 | package com.hutchind.cordova.plugins.launcher; 2 | 3 | import org.apache.cordova.CordovaInterface; 4 | import org.apache.cordova.CallbackContext; 5 | import org.apache.cordova.CordovaPlugin; 6 | import org.apache.cordova.PluginResult; 7 | 8 | import org.json.JSONArray; 9 | import org.json.JSONException; 10 | import org.json.JSONObject; 11 | 12 | import android.app.Activity; 13 | import android.content.ActivityNotFoundException; 14 | import android.content.Intent; 15 | import android.net.Uri; 16 | import android.util.Log; 17 | import android.content.pm.ActivityInfo; 18 | import android.content.pm.ApplicationInfo; 19 | import android.content.pm.PackageManager; 20 | import android.content.pm.PackageManager.NameNotFoundException; 21 | import android.content.pm.ResolveInfo; 22 | import android.os.Bundle; 23 | import android.os.Parcelable; 24 | import android.os.Build; 25 | 26 | import java.lang.reflect.Array; 27 | import java.util.List; 28 | import java.util.Arrays; 29 | import java.util.ArrayList; 30 | import java.util.Collection; 31 | import java.util.Collections; 32 | import java.util.Map; 33 | import java.util.Set; 34 | import java.util.HashSet; 35 | 36 | public class Launcher extends CordovaPlugin { 37 | public static final String TAG = "Launcher Plugin"; 38 | public static final String ACTION_CAN_LAUNCH = "canLaunch"; 39 | public static final String ACTION_LAUNCH = "launch"; 40 | public static final int LAUNCH_REQUEST = 0; 41 | 42 | private CallbackContext callback; 43 | 44 | private abstract class LauncherRunnable implements Runnable { 45 | public CallbackContext callbackContext; 46 | LauncherRunnable(CallbackContext cb) { 47 | this.callbackContext = cb; 48 | } 49 | } 50 | 51 | @Override 52 | public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { 53 | callback = callbackContext; 54 | if (ACTION_CAN_LAUNCH.equals(action)) { 55 | return canLaunch(args); 56 | } else if (ACTION_LAUNCH.equals(action)) { 57 | return launch(args); 58 | } 59 | return false; 60 | } 61 | 62 | private boolean canLaunch(JSONArray args) throws JSONException { 63 | final JSONObject options = args.getJSONObject(0); 64 | final CordovaInterface mycordova = cordova; 65 | final CordovaPlugin plugin = this; 66 | 67 | if (options.has("packageName")) { 68 | final String appPackageName = options.getString("packageName"); 69 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 70 | public void run() { 71 | final Intent intent = new Intent(Intent.ACTION_VIEW); 72 | String packageName = appPackageName; 73 | String passedActivityName = null; 74 | if (packageName.contains("/")) { 75 | String[] items = appPackageName.split("/"); 76 | packageName = items[0]; 77 | passedActivityName = items[1]; 78 | } 79 | final ActivityInfo appInfo = getAppInfo(intent, packageName); 80 | 81 | if (appInfo != null) { 82 | Log.d(TAG, "App Info found for " + packageName); 83 | callbackContext.success(); 84 | } else { 85 | final PackageManager pm = plugin.webView.getContext().getPackageManager(); 86 | final Intent launchIntent = pm.getLaunchIntentForPackage(packageName); 87 | if (launchIntent != null) { 88 | Log.d(TAG, "Launch Intent for " + packageName + " found."); 89 | callbackContext.success(); 90 | } else { 91 | Log.d(TAG, "Could not find launch intent for package: " + packageName); 92 | callbackContext.error("Application is not installed."); 93 | } 94 | } 95 | 96 | } 97 | }); 98 | } else if (options.has("uri")) { 99 | final String uri = options.getString("uri"); 100 | final String dataType = options.has("dataType") ? options.getString("dataType") : null; 101 | 102 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 103 | public void run() { 104 | final PackageManager pm = plugin.webView.getContext().getPackageManager(); 105 | final Intent intent = new Intent(Intent.ACTION_VIEW); 106 | if (dataType != null) { 107 | intent.setDataAndType(Uri.parse(uri), dataType); 108 | } else { 109 | intent.setData(Uri.parse(uri)); 110 | } 111 | 112 | List resInfos = pm.queryIntentActivities(intent, 0); 113 | if (resInfos.size() > 0) { 114 | Log.d(TAG, "Found Activities that handle uri: " + uri); 115 | 116 | boolean shouldGetAppList = false; 117 | try { 118 | shouldGetAppList = options.has("getAppList") && options.getBoolean("getAppList") == true; 119 | } catch (JSONException e) {} 120 | 121 | if (shouldGetAppList) { 122 | JSONObject obj = new JSONObject(); 123 | JSONArray appList = new JSONArray(); 124 | 125 | for(ResolveInfo resolveInfo : resInfos) { 126 | try { 127 | appList.put(resolveInfo.activityInfo.packageName); 128 | } catch (Exception e) { 129 | //Do Nothing 130 | } 131 | } 132 | 133 | try { 134 | obj.put("appList", wrap(appList)); 135 | } catch(Exception e) { 136 | 137 | } 138 | callbackContext.success(obj); 139 | } else { 140 | callbackContext.success(); 141 | } 142 | } else { 143 | Log.d(TAG, "No Activities found that handle uri: " + uri); 144 | callbackContext.error("No application found."); 145 | } 146 | } 147 | }); 148 | } else if (options.has("actionName")) { 149 | canLaunchAction(options.getString("actionName")); 150 | } 151 | 152 | return true; 153 | } 154 | 155 | private void canLaunchAction(final String actionName) { 156 | final CordovaPlugin plugin = this; 157 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 158 | public void run() { 159 | final PackageManager pm = plugin.webView.getContext().getPackageManager(); 160 | final Intent intent = new Intent(actionName); 161 | List resInfos = pm.queryIntentActivities(intent, 0); 162 | if (resInfos.size() > 0) { 163 | Log.d(TAG, "Found Activity that handles action: " + actionName); 164 | callbackContext.success(); 165 | } else { 166 | Log.d(TAG, "No Activity found that handles action: " + actionName); 167 | callbackContext.error("No application found."); 168 | } 169 | } 170 | }); 171 | } 172 | 173 | private ActivityInfo getAppInfo(final Intent intent, final String appPackageName) { 174 | final PackageManager pm = webView.getContext().getPackageManager(); 175 | try { 176 | Log.d(TAG, pm.getApplicationInfo(appPackageName, 0) + ""); 177 | }catch (NameNotFoundException e) { 178 | Log.i(TAG, "No info found for package: " + appPackageName); 179 | } 180 | return null; 181 | } 182 | 183 | private boolean launch(JSONArray args) throws JSONException { 184 | final JSONObject options = args.getJSONObject(0); 185 | Bundle extras = null; 186 | if (options.has("extras")) { 187 | extras = createExtras(options.getJSONArray("extras")); 188 | } else { 189 | extras = new Bundle(); 190 | } 191 | int flags = 0; 192 | if (options.has("flags")) { 193 | flags = options.getInt("flags"); 194 | } 195 | 196 | if (options.has("uri") && (options.has("packageName") || options.has("dataType"))) { 197 | String dataType = null; 198 | String packageName = null; 199 | if (options.has("packageName")) { 200 | packageName = options.getString("packageName"); 201 | } 202 | if (options.has("dataType")) { 203 | dataType = options.getString("dataType"); 204 | } 205 | launchAppWithData(packageName, options.getString("uri"), dataType, extras); 206 | return true; 207 | } else if (options.has("packageName")) { 208 | launchApp(options.getString("packageName"), extras); 209 | return true; 210 | } else if (options.has("uri")) { 211 | launchIntent(options.getString("uri"), extras, flags); 212 | return true; 213 | } else if (options.has("actionName")) { 214 | launchAction(options.getString("actionName"), extras); 215 | return true; 216 | } 217 | return false; 218 | } 219 | 220 | private Bundle createExtras(JSONArray extrasObj) throws JSONException { 221 | Bundle extras = new Bundle(); 222 | for (int i = 0, size = extrasObj.length(); i < size; i++) { 223 | JSONObject extra = extrasObj.getJSONObject(i); 224 | if (extra.has("name") && extra.has("value") && extra.has("dataType")) { 225 | String extraName = extra.getString("name"); 226 | String dataType = extra.getString("dataType"); 227 | try { 228 | if (dataType.equalsIgnoreCase("Byte")) { 229 | try { 230 | extras.putByte(extraName, ((byte) extra.getInt("value"))); 231 | } catch (Exception e) { 232 | Log.e(TAG, "Error converting to byte for extra: " + extraName); 233 | e.printStackTrace(); 234 | throw e; 235 | } 236 | } else if (dataType.equalsIgnoreCase("ByteArray")) { 237 | try { 238 | extras.putByteArray(extraName, ParseTypes.toByteArray(extra.getJSONArray("value"))); 239 | } catch (Exception e) { 240 | Log.e(TAG, "Error converting to byte for extra: " + extraName); 241 | e.printStackTrace(); 242 | throw e; 243 | } 244 | } else if (dataType.equalsIgnoreCase("Short")) { 245 | try { 246 | extras.putShort(extraName, ((short) extra.getInt("value"))); 247 | } catch (Exception e) { 248 | Log.e(TAG, "Error converting to short for extra: " + extraName); 249 | e.printStackTrace(); 250 | throw e; 251 | } 252 | } else if (dataType.equalsIgnoreCase("ShortArray")) { 253 | extras.putShortArray(extraName, ParseTypes.toShortArray(extra.getJSONArray("value"))); 254 | } else if (dataType.equalsIgnoreCase("Int")) { 255 | extras.putInt(extraName, extra.getInt("value")); 256 | } else if (dataType.equalsIgnoreCase("IntArray")) { 257 | extras.putIntArray(extraName, ParseTypes.toIntArray(extra.getJSONArray("value"))); 258 | } else if (dataType.equalsIgnoreCase("IntArrayList")) { 259 | extras.putIntegerArrayList(extraName, ParseTypes.toIntegerArrayList(extra.getJSONArray("value"))); 260 | } else if (dataType.equalsIgnoreCase("Long")) { 261 | extras.putLong(extraName, extra.getLong("value")); 262 | } else if (dataType.equalsIgnoreCase("LongArray")) { 263 | extras.putLongArray(extraName, ParseTypes.toLongArray(extra.getJSONArray("value"))); 264 | } else if (dataType.equalsIgnoreCase("Float")) { 265 | try { 266 | extras.putFloat(extraName, Float.parseFloat(extra.getString("value"))); 267 | } catch (Exception e) { 268 | Log.e(TAG, "Error parsing float for extra: " + extraName); 269 | e.printStackTrace(); 270 | throw e; 271 | } 272 | } else if (dataType.equalsIgnoreCase("FloatArray")) { 273 | try { 274 | extras.putFloatArray(extraName, ParseTypes.toFloatArray(extra.getJSONArray("value"))); 275 | } catch (Exception e) { 276 | Log.e(TAG, "Error parsing float for extra: " + extraName); 277 | e.printStackTrace(); 278 | throw e; 279 | } 280 | } else if (dataType.equalsIgnoreCase("Double")) { 281 | extras.putDouble(extraName, extra.getDouble("value")); 282 | } else if (dataType.equalsIgnoreCase("DoubleArray")) { 283 | extras.putDoubleArray(extraName, ParseTypes.toDoubleArray(extra.getJSONArray("value"))); 284 | } else if (dataType.equalsIgnoreCase("Boolean")) { 285 | extras.putBoolean(extraName, extra.getBoolean("value")); 286 | } else if (dataType.equalsIgnoreCase("BooleanArray")) { 287 | extras.putBooleanArray(extraName, ParseTypes.toBooleanArray(extra.getJSONArray("value"))); 288 | } else if (dataType.equalsIgnoreCase("String")) { 289 | extras.putString(extraName, extra.getString("value")); 290 | } else if (dataType.equalsIgnoreCase("StringArray")) { 291 | extras.putStringArray(extraName, ParseTypes.toStringArray(extra.getJSONArray("value"))); 292 | } else if (dataType.equalsIgnoreCase("StringArrayList")) { 293 | extras.putStringArrayList(extraName, ParseTypes.toStringArrayList(extra.getJSONArray("value"))); 294 | } else if (dataType.equalsIgnoreCase("Char")) { 295 | extras.putChar(extraName, ParseTypes.toChar(extra.getString("value"))); 296 | } else if (dataType.equalsIgnoreCase("CharArray")) { 297 | extras.putCharArray(extraName, ParseTypes.toCharArray(extra.getString("value"))); 298 | } else if (dataType.equalsIgnoreCase("CharSequence")) { 299 | extras.putCharSequence(extraName, extra.getString("value")); 300 | } else if (dataType.equalsIgnoreCase("CharSequenceArray")) { 301 | extras.putCharSequenceArray(extraName, ParseTypes.toCharSequenceArray(extra.getJSONArray("value"))); 302 | } else if (dataType.equalsIgnoreCase("CharSequenceArrayList")) { 303 | extras.putCharSequenceArrayList(extraName, ParseTypes.toCharSequenceArrayList(extra.getJSONArray("value"))); 304 | /* 305 | } else if (dataType.equalsIgnoreCase("Size") && Build.VERSION.SDK_INT >= 21) { 306 | extras.putSize(extraName, extra.getJSONObject("value")); 307 | } else if (dataType.equalsIgnoreCase("SizeF") && Build.VERSION.SDK_INT >= 21) { 308 | extras.putSizeF(extraName, extra.getJSONObject("value")); 309 | */ 310 | } else if (dataType.toLowerCase().contains("parcelable")) { 311 | if (!extra.has("paType")) { 312 | Log.e(TAG, "Property 'paType' must be provided if dataType is " + dataType + "."); 313 | throw new Exception("Missing property paType."); 314 | } else { 315 | String paType = extra.getString("paType").toUpperCase(); 316 | if (ParseTypes.SUPPORTED_PA_TYPES.contains(paType)) { 317 | if (dataType.equalsIgnoreCase("Parcelable")) { 318 | extras.putParcelable(extraName, ParseTypes.toParcelable(extra.getString("value"), paType)); 319 | } else if (dataType.equalsIgnoreCase("ParcelableArray")) { 320 | extras.putParcelableArray(extraName, ParseTypes.toParcelableArray(extra.getJSONArray("value"), paType)); 321 | } else if (dataType.equalsIgnoreCase("ParcelableArrayList")) { 322 | extras.putParcelableArrayList(extraName, ParseTypes.toParcelableArrayList(extra.getJSONArray("value"), paType)); 323 | } else if (dataType.equalsIgnoreCase("SparseParcelableArray")) { 324 | extras.putSparseParcelableArray(extraName, ParseTypes.toSparseParcelableArray(extra.getJSONObject("value"), paType)); 325 | } 326 | } else { 327 | Log.e(TAG, "ParcelableArray type '" + paType + "' is not currently supported."); 328 | throw new Exception("Provided parcelable array type not supported."); 329 | } 330 | } 331 | } 332 | } catch (Exception e) { 333 | Log.e(TAG, "Error processing extra. Skipping: " + extraName); 334 | } 335 | } else { 336 | Log.e(TAG, "Extras must have a name, value, and datatype."); 337 | } 338 | } 339 | 340 | Log.d(TAG, "EXTRAS"); 341 | Log.d(TAG, "" + extras); 342 | 343 | return extras; 344 | } 345 | 346 | private void launchAppWithData(final String packageName, final String uri, final String dataType, final Bundle extras) throws JSONException { 347 | final CordovaInterface mycordova = cordova; 348 | final CordovaPlugin plugin = this; 349 | final CallbackContext callbackContext = this.callback; 350 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 351 | public void run() { 352 | Intent intent = new Intent(Intent.ACTION_VIEW); 353 | if (dataType != null) { 354 | intent.setDataAndType(Uri.parse(uri), dataType); 355 | } else { 356 | intent.setData(Uri.parse(uri)); 357 | } 358 | 359 | if (packageName != null && !packageName.equals("")) { 360 | intent.setPackage(packageName); 361 | } 362 | 363 | intent.putExtras(extras); 364 | 365 | try { 366 | mycordova.startActivityForResult(plugin, intent, LAUNCH_REQUEST); 367 | ((Launcher) plugin).callbackLaunched(); 368 | } catch(ActivityNotFoundException e) { 369 | Log.e(TAG, "Error: No applications installed that can handle uri " + uri); 370 | e.printStackTrace(); 371 | callbackContext.error("Application not found for uri."); 372 | } 373 | 374 | } 375 | }); 376 | } 377 | 378 | private void launchApp(final String packageName, final Bundle extras) { 379 | final CordovaInterface mycordova = cordova; 380 | final CordovaPlugin plugin = this; 381 | Log.i(TAG, "Trying to launch app: " + packageName); 382 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 383 | public void run() { 384 | final PackageManager pm = plugin.webView.getContext().getPackageManager(); 385 | final Intent launchIntent = pm.getLaunchIntentForPackage(packageName); 386 | boolean appNotFound = launchIntent == null; 387 | 388 | if (!appNotFound) { 389 | try { 390 | launchIntent.putExtras(extras); 391 | mycordova.startActivityForResult(plugin, launchIntent, LAUNCH_REQUEST); 392 | ((Launcher) plugin).callbackLaunched(); 393 | } catch (ActivityNotFoundException e) { 394 | Log.e(TAG, "Error: Activity for package" + packageName + " was not found."); 395 | e.printStackTrace(); 396 | callbackContext.error("Activity not found for package name."); 397 | } 398 | } else { 399 | callbackContext.error("Activity not found for package name."); 400 | } 401 | } 402 | }); 403 | } 404 | 405 | private void launchIntent(final String uri, final Bundle extras, final int flags) { 406 | final CordovaInterface mycordova = cordova; 407 | final CordovaPlugin plugin = this; 408 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 409 | public void run() { 410 | Intent intent = new Intent(Intent.ACTION_VIEW); 411 | intent.setData(Uri.parse(uri)); 412 | if (flags != 0) { 413 | intent.setFlags(flags); 414 | } 415 | try { 416 | intent.putExtras(extras); 417 | mycordova.startActivityForResult(plugin, intent, LAUNCH_REQUEST); 418 | ((Launcher) plugin).callbackLaunched(); 419 | } catch (ActivityNotFoundException e) { 420 | Log.e(TAG, "Error: Activity for " + uri + " was not found."); 421 | e.printStackTrace(); 422 | callbackContext.error("Activity not found for uri."); 423 | } 424 | } 425 | }); 426 | } 427 | 428 | private void launchAction(final String actionName, final Bundle extras) { 429 | final CordovaInterface mycordova = cordova; 430 | final CordovaPlugin plugin = this; 431 | cordova.getThreadPool().execute(new LauncherRunnable(this.callback) { 432 | public void run() { 433 | Intent intent = new Intent(actionName); 434 | try { 435 | intent.putExtras(extras); 436 | mycordova.startActivityForResult(plugin, intent, LAUNCH_REQUEST); 437 | ((Launcher) plugin).callbackLaunched(); 438 | } catch (ActivityNotFoundException e) { 439 | Log.e(TAG, "Error: Activity for " + actionName + " was not found."); 440 | e.printStackTrace(); 441 | callbackContext.error("Activity not found for action name."); 442 | } 443 | } 444 | }); 445 | } 446 | 447 | @Override 448 | public void onActivityResult(int requestCode, int resultCode, Intent intent) { 449 | super.onActivityResult(requestCode, resultCode, intent); 450 | if (requestCode == LAUNCH_REQUEST) { 451 | if (resultCode == Activity.RESULT_OK || resultCode == Activity.RESULT_CANCELED) { 452 | JSONObject json = new JSONObject(); 453 | try { 454 | json.put("isActivityDone", true); 455 | } catch(JSONException ignored) {} 456 | if (intent != null) { 457 | Bundle extras = intent.getExtras(); 458 | if (extras != null) { 459 | JSONObject jsonExtras = new JSONObject(); 460 | Set keys = extras.keySet(); 461 | for (String key : keys) { 462 | try { 463 | jsonExtras.put(key, wrap(extras.get(key))); 464 | } catch(JSONException ignored) {} 465 | } 466 | try { 467 | json.put("extras", jsonExtras); 468 | } catch(JSONException ignored) {} 469 | } 470 | 471 | try { 472 | json.put("data", intent.getDataString()); 473 | } catch(JSONException ignored) {} 474 | } 475 | callback.success(json); 476 | } else { 477 | callback.error("Activity failed (" + resultCode + ")."); 478 | } 479 | } 480 | } 481 | 482 | public void callbackLaunched() { 483 | try { 484 | JSONObject json = new JSONObject(); 485 | json.put("isLaunched", true); 486 | PluginResult result = new PluginResult(PluginResult.Status.OK, json); 487 | result.setKeepCallback(true); 488 | callback.sendPluginResult(result); 489 | } catch (JSONException e) { 490 | PluginResult result = new PluginResult(PluginResult.Status.OK, "{'isLaunched':true}"); 491 | result.setKeepCallback(true); 492 | callback.sendPluginResult(result); 493 | } 494 | } 495 | 496 | private Object wrap(Object o) { 497 | if (o == null) { 498 | return JSONObject.NULL; 499 | } 500 | if (o instanceof JSONArray || o instanceof JSONObject) { 501 | return o; 502 | } 503 | if (o.equals(JSONObject.NULL)) { 504 | return o; 505 | } 506 | try { 507 | if (o instanceof Collection) { 508 | return new JSONArray((Collection) o); 509 | } else if (o.getClass().isArray()) { 510 | JSONArray jsa = new JSONArray(); 511 | int length = Array.getLength(o); 512 | for (int i = 0; i < length; i += 1) { 513 | jsa.put(wrap(Array.get(o, i))); 514 | } 515 | return jsa; 516 | } 517 | if (o instanceof Map) { 518 | return new JSONObject((Map) o); 519 | } 520 | if (o instanceof Boolean || 521 | o instanceof Byte || 522 | o instanceof Character || 523 | o instanceof Double || 524 | o instanceof Float || 525 | o instanceof Integer || 526 | o instanceof Long || 527 | o instanceof Short || 528 | o instanceof String) { 529 | return o; 530 | } 531 | if (o.getClass().getPackage().getName().startsWith("java.")) { 532 | return o.toString(); 533 | } 534 | } catch (Exception ignored) { 535 | } 536 | return null; 537 | } 538 | } -------------------------------------------------------------------------------- /src/android/ParseTypes.java: -------------------------------------------------------------------------------- 1 | package com.hutchind.cordova.plugins.launcher; 2 | 3 | import android.net.Uri; 4 | import android.os.Parcelable; 5 | import android.util.SparseArray; 6 | //import android.annotation.TargetApi; 7 | 8 | import org.json.JSONObject; 9 | import org.json.JSONArray; 10 | import org.json.JSONException; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import java.util.Collections; 16 | import java.util.Iterator; 17 | 18 | public class ParseTypes { 19 | public static final List SUPPORTED_PA_TYPES = Collections.unmodifiableList(Arrays.asList("URI")); 20 | 21 | public static byte[] toByteArray(JSONArray arr) throws JSONException { 22 | int jsize = arr.length(); 23 | byte[] exVal = new byte[jsize]; 24 | for(int j=0; j < jsize; j++) { 25 | exVal[j] = (byte) arr.getInt(j); 26 | } 27 | return exVal; 28 | } 29 | public static short[] toShortArray(JSONArray arr) throws JSONException { 30 | int jsize = arr.length(); 31 | short[] exVal = new short[jsize]; 32 | for(int j=0; j < jsize; j++) { 33 | exVal[j] = (short) arr.getInt(j); 34 | } 35 | return exVal; 36 | } 37 | public static int[] toIntArray(JSONArray arr) throws JSONException { 38 | int jsize = arr.length(); 39 | int[] exVal = new int[jsize]; 40 | for(int j=0; j < jsize; j++) { 41 | exVal[j] = arr.getInt(j); 42 | } 43 | return exVal; 44 | } 45 | public static ArrayList toIntegerArrayList(JSONArray arr) throws JSONException { 46 | int[] ints = ParseTypes.toIntArray(arr); 47 | ArrayList exVal = new ArrayList(); 48 | for (int i = 0; i < ints.length; i++) { 49 | exVal.add(ints[i]); 50 | } 51 | return exVal; 52 | } 53 | public static long[] toLongArray(JSONArray arr) throws JSONException { 54 | int jsize = arr.length(); 55 | long[] exVal = new long[jsize]; 56 | for(int j=0; j < jsize; j++) { 57 | exVal[j] = arr.getLong(j); 58 | } 59 | return exVal; 60 | } 61 | public static float[] toFloatArray(JSONArray arr) throws JSONException, NumberFormatException { 62 | int jsize = arr.length(); 63 | float[] exVal = new float[jsize]; 64 | for(int j=0; j < jsize; j++) { 65 | exVal[j] = Float.parseFloat(arr.getString(j)); 66 | } 67 | return exVal; 68 | } 69 | public static double[] toDoubleArray(JSONArray arr) throws JSONException { 70 | int jsize = arr.length(); 71 | double[] exVal = new double[jsize]; 72 | for(int j=0; j < jsize; j++) { 73 | exVal[j] = arr.getDouble(j); 74 | } 75 | return exVal; 76 | } 77 | public static boolean[] toBooleanArray(JSONArray arr) throws JSONException { 78 | int jsize = arr.length(); 79 | boolean[] exVal = new boolean[jsize]; 80 | for(int j=0; j < jsize; j++) { 81 | exVal[j] = arr.getBoolean(j); 82 | } 83 | return exVal; 84 | } 85 | public static String[] toStringArray(JSONArray arr) throws JSONException { 86 | int jsize = arr.length(); 87 | String[] exVal = new String[jsize]; 88 | for(int j=0; j < jsize; j++) { 89 | exVal[j] = arr.getString(j); 90 | } 91 | return exVal; 92 | } 93 | public static ArrayList toStringArrayList(JSONArray arr) throws JSONException { 94 | String[] strs = ParseTypes.toStringArray(arr); 95 | ArrayList exVal = new ArrayList(); 96 | for (int i = 0; i < strs.length; i++) { 97 | exVal.add(strs[i]); 98 | } 99 | return exVal; 100 | } 101 | public static char toChar(String str) { 102 | if (str == null) { 103 | return (char) 0; 104 | } else { 105 | return str.charAt(0); 106 | } 107 | } 108 | public static char[] toCharArray(String str) { 109 | if (str == null) { 110 | return new char[0]; 111 | } else { 112 | return str.toCharArray(); 113 | } 114 | } 115 | public static CharSequence[] toCharSequenceArray(JSONArray arr) throws JSONException { 116 | return ParseTypes.toStringArray(arr); 117 | } 118 | public static ArrayList toCharSequenceArrayList(JSONArray arr) throws JSONException { 119 | CharSequence[] strs = ParseTypes.toCharSequenceArray(arr); 120 | ArrayList exVal = new ArrayList(); 121 | for (int i = 0; i < strs.length; i++) { 122 | exVal.add(strs[i]); 123 | } 124 | return exVal; 125 | } 126 | /*@TargetApi(21) 127 | public static android.util.Size toSize(JSONObject o) throws JSONException { 128 | int width = 0, height = 0; 129 | try { 130 | width = o.getInt("width"); 131 | height = o.getInt("height"); 132 | } catch (JSONException e) { 133 | width = o.getInt("w"); 134 | height = o.getInt("h"); 135 | } 136 | return new android.util.Size(width, height); 137 | } 138 | @TargetApi(21) 139 | public static android.util.SizeF toSizeF(JSONObject o) throws JSONException { 140 | float width = 0, height = 0; 141 | try { 142 | width = Float.parseFloat(o.getString("width")); 143 | height = Float.parseFloat(o.getString("height")); 144 | } catch (JSONException e) { 145 | width = Float.parseFloat(o.getString("w")); 146 | height = Float.parseFloat(o.getString("h")); 147 | } 148 | return new android.util.SizeF(width, height); 149 | }*/ 150 | public static SparseArray toSparseParcelableArray(JSONObject o, String pType) throws Exception, NullPointerException, JSONException { 151 | SparseArray sa = new SparseArray(); 152 | for(Iterator iter = o.keys(); iter.hasNext();) { 153 | String jsonKey = iter.next(); 154 | Integer saKey = Integer.parseInt(jsonKey); 155 | sa.put(saKey, ParseTypes.toParcelable(o.getString(jsonKey), pType)); 156 | } 157 | return sa; 158 | } 159 | public static Parcelable toParcelable(String str, String pType) throws Exception, NullPointerException { 160 | if (pType.equalsIgnoreCase("URI")) { 161 | return Uri.parse(str); 162 | } 163 | throw new Exception("Parcelable type " + pType + " is not supported."); 164 | } 165 | public static Parcelable[] toParcelableArray(JSONArray arr, String pType) throws Exception, NullPointerException, JSONException { 166 | ArrayList items = (ArrayList)ParseTypes.toParcelableArrayList(arr, pType); 167 | return items.toArray(new Parcelable[items.size()]); 168 | } 169 | public static ArrayList toParcelableArrayList(JSONArray arr, String pType) throws Exception, NullPointerException, JSONException { 170 | int jsize = arr.length(); 171 | ArrayList items = new ArrayList(); 172 | for(int j=0; j < jsize; j++) { 173 | items.add(ParseTypes.toParcelable(arr.getString(j), pType)); 174 | } 175 | 176 | return items; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/ios/Launcher.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface Launcher : CDVPlugin 4 | 5 | - (void)canLaunch:(CDVInvokedUrlCommand*)command; 6 | - (void)launch:(CDVInvokedUrlCommand*)command; 7 | 8 | @end -------------------------------------------------------------------------------- /src/ios/Launcher.m: -------------------------------------------------------------------------------- 1 | #import "Launcher.h" 2 | #import 3 | 4 | @implementation Launcher 5 | 6 | - (void)canLaunch:(CDVInvokedUrlCommand*)command { 7 | NSDictionary* options = [command.arguments objectAtIndex:0]; 8 | CDVPluginResult * pluginResult = nil; 9 | 10 | if ([options objectForKey:@"uri"]) { 11 | NSString *uri = [options objectForKey:@"uri"]; 12 | if ([[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:uri]]) { 13 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; 14 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 15 | } else { 16 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No app installed that can handle that uri."]; 17 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 18 | } 19 | } else { 20 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Missing option: 'uri' required."]; 21 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 22 | } 23 | } 24 | 25 | - (void)launch:(CDVInvokedUrlCommand*)command { 26 | NSDictionary* options = [command.arguments objectAtIndex:0]; 27 | CDVPluginResult * pluginResult = nil; 28 | 29 | if ([options objectForKey:@"uri"]) { 30 | NSString *uri = [options objectForKey:@"uri"]; 31 | if ([[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:uri]]) { 32 | NSURL *launchURL = [NSURL URLWithString:uri]; 33 | [[UIApplication sharedApplication] openURL: launchURL]; 34 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; 35 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 36 | } else { 37 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No app installed that can handle that uri."]; 38 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 39 | } 40 | } else { 41 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Missing option: 'uri' required."]; 42 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; 43 | } 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /www/Launcher.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function Launcher() {} 3 | 4 | Launcher.prototype.FLAG_ACTIVITY_NEW_TASK = 0x10000000; 5 | 6 | Launcher.prototype.canLaunch = function (options, successCallback, errorCallback) { 7 | options = options || {}; 8 | options.successCallback = options.successCallback || successCallback; 9 | options.errorCallback = options.errorCallback || errorCallback; 10 | cordova.exec(options.successCallback || null, options.errorCallback || null, "Launcher", "canLaunch", [options]); 11 | }; 12 | 13 | Launcher.prototype.launch = function(options, successCallback, errorCallback) { 14 | options = options || {}; 15 | options.successCallback = options.successCallback || successCallback; 16 | options.errorCallback = options.errorCallback || errorCallback; 17 | cordova.exec(options.successCallback || null, options.errorCallback || null, "Launcher", "launch", [options]); 18 | }; 19 | 20 | Launcher.install = function () { 21 | if (!window.plugins) { 22 | window.plugins = {}; 23 | } 24 | 25 | window.plugins.launcher = new Launcher(); 26 | return window.plugins.launcher; 27 | }; 28 | 29 | cordova.addConstructor(Launcher.install); 30 | --------------------------------------------------------------------------------