├── 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 extends Parcelable> 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 extends Parcelable> 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 |
--------------------------------------------------------------------------------