Merge pull request #1099 from yemel/feature/android-uri-handling

Implement bitcoin URI Handling on Android
This commit is contained in:
Matias Alejo Garcia 2014-08-16 00:11:38 -04:00
commit 8975b70530
6 changed files with 326 additions and 1 deletions

View File

@ -13,4 +13,19 @@ function onDeviceReady() {
if (menu.offsetParent) menu.click(); if (menu.offsetParent) menu.click();
}, false); }, false);
function handleBitcoinURI(url) {
if (!url) return;
var body = document.getElementsByTagName('nav')[0];
var $rootScope = angular.element(body).scope();
$rootScope.pendingPayment = new bitcore.BIP21(url);
// Redirect or reload controller (if already there)
window.location = ($rootScope.wallet ? '#!/send' : '#!/open') + '?r=' + Math.random();
}
window.plugins.webintent.getUri(handleBitcoinURI);
window.plugins.webintent.onNewIntent(handleBitcoinURI);
} }

View File

@ -5,11 +5,19 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name"> <application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/app_name" android:launchMode="singleTop" android:name="Copay" android:theme="@android:style/Theme.Black.NoTitleBar"> <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/app_name" android:launchMode="singleTask" android:name="Copay" android:theme="@android:style/Theme.Black.NoTitleBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter android:label="Send Bitcoins">
<data android:scheme="bitcoin"/>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> </activity>
<activity android:clearTaskOnLaunch="true" android:configChanges="orientation|keyboardHidden" android:exported="false" android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden"> <activity android:clearTaskOnLaunch="true" android:configChanges="orientation|keyboardHidden" android:exported="false" android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter> <intent-filter>

View File

@ -38,6 +38,13 @@ module.exports = [
{ {
"file": "plugins/nl.x-services.plugins.toast/test/tests.js", "file": "plugins/nl.x-services.plugins.toast/test/tests.js",
"id": "nl.x-services.plugins.toast.tests" "id": "nl.x-services.plugins.toast.tests"
},
{
"file": "plugins/com.borismus.webintent/www/webintent.js",
"id": "com.borismus.webintent.WebIntent",
"clobbers": [
"WebIntent"
]
} }
]; ];
module.exports.metadata = module.exports.metadata =
@ -48,6 +55,7 @@ module.exports.metadata =
"org.apache.cordova.splashscreen": "0.3.0", "org.apache.cordova.splashscreen": "0.3.0",
"com.verso.cordova.clipboard": "0.1.0", "com.verso.cordova.clipboard": "0.1.0",
"nl.x-services.plugins.toast": "2.0" "nl.x-services.plugins.toast": "2.0"
"com.borismus.webintent": "1.0.0"
} }
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });

View File

@ -0,0 +1,75 @@
cordova.define("com.borismus.webintent.WebIntent", function(require, exports, module) { /**
* cordova Web Intent plugin
* Copyright (c) Boris Smus 2010
*
*/
(function(cordova){
var WebIntent = function() {
};
WebIntent.prototype.ACTION_SEND = "android.intent.action.SEND";
WebIntent.prototype.ACTION_VIEW= "android.intent.action.VIEW";
WebIntent.prototype.EXTRA_TEXT = "android.intent.extra.TEXT";
WebIntent.prototype.EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
WebIntent.prototype.EXTRA_STREAM = "android.intent.extra.STREAM";
WebIntent.prototype.EXTRA_EMAIL = "android.intent.extra.EMAIL";
WebIntent.prototype.ACTION_CALL = "android.intent.action.CALL";
WebIntent.prototype.ACTION_SENDTO = "android.intent.action.SENDTO";
WebIntent.prototype.startActivity = function(params, success, fail) {
return cordova.exec(function(args) {
success(args);
}, function(args) {
fail(args);
}, 'WebIntent', 'startActivity', [params]);
};
WebIntent.prototype.hasExtra = function(params, success, fail) {
return cordova.exec(function(args) {
success(args);
}, function(args) {
fail(args);
}, 'WebIntent', 'hasExtra', [params]);
};
WebIntent.prototype.getUri = function(success, fail) {
return cordova.exec(function(args) {
success(args);
}, function(args) {
fail(args);
}, 'WebIntent', 'getUri', []);
};
WebIntent.prototype.getExtra = function(params, success, fail) {
return cordova.exec(function(args) {
success(args);
}, function(args) {
fail(args);
}, 'WebIntent', 'getExtra', [params]);
};
WebIntent.prototype.onNewIntent = function(callback) {
return cordova.exec(function(args) {
callback(args);
}, function(args) {
}, 'WebIntent', 'onNewIntent', []);
};
WebIntent.prototype.sendBroadcast = function(params, success, fail) {
return cordova.exec(function(args) {
success(args);
}, function(args) {
fail(args);
}, 'WebIntent', 'sendBroadcast', [params]);
};
window.webintent = new WebIntent();
// backwards compatibility
window.plugins = window.plugins || {};
window.plugins.webintent = window.webintent;
})(window.PhoneGap || window.Cordova || window.cordova);
});

View File

@ -33,5 +33,7 @@
</feature> </feature>
<feature name="Toast"> <feature name="Toast">
<param name="android-package" value="nl.xservices.plugins.Toast" /> <param name="android-package" value="nl.xservices.plugins.Toast" />
<feature name="WebIntent">
<param name="android-package" value="com.borismus.webintent.WebIntent" />
</feature> </feature>
</widget> </widget>

View File

@ -0,0 +1,217 @@
package com.borismus.webintent;
import java.util.HashMap;
import java.util.Map;
import org.apache.cordova.CordovaActivity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Intent;
import android.net.Uri;
import android.text.Html;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.PluginResult;
/**
* WebIntent is a PhoneGap plugin that bridges Android intents and web
* applications:
*
* 1. web apps can spawn intents that call native Android applications. 2.
* (after setting up correct intent filters for PhoneGap applications), Android
* intents can be handled by PhoneGap web applications.
*
* @author boris@borismus.com
*
*/
public class WebIntent extends CordovaPlugin {
private CallbackContext onNewIntentCallbackContext = null;
//public boolean execute(String action, JSONArray args, String callbackId) {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
try {
if (action.equals("startActivity")) {
if (args.length() != 1) {
//return new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
}
// Parse the arguments
final CordovaResourceApi resourceApi = webView.getResourceApi();
JSONObject obj = args.getJSONObject(0);
String type = obj.has("type") ? obj.getString("type") : null;
Uri uri = obj.has("url") ? resourceApi.remapUri(Uri.parse(obj.getString("url"))) : null;
JSONObject extras = obj.has("extras") ? obj.getJSONObject("extras") : null;
Map<String, String> extrasMap = new HashMap<String, String>();
// Populate the extras if any exist
if (extras != null) {
JSONArray extraNames = extras.names();
for (int i = 0; i < extraNames.length(); i++) {
String key = extraNames.getString(i);
String value = extras.getString(key);
extrasMap.put(key, value);
}
}
startActivity(obj.getString("action"), uri, type, extrasMap);
//return new PluginResult(PluginResult.Status.OK);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
return true;
} else if (action.equals("hasExtra")) {
if (args.length() != 1) {
//return new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
}
Intent i = ((CordovaActivity)this.cordova.getActivity()).getIntent();
String extraName = args.getString(0);
//return new PluginResult(PluginResult.Status.OK, i.hasExtra(extraName));
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i.hasExtra(extraName)));
return true;
} else if (action.equals("getExtra")) {
if (args.length() != 1) {
//return new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
}
Intent i = ((CordovaActivity)this.cordova.getActivity()).getIntent();
String extraName = args.getString(0);
if (i.hasExtra(extraName)) {
//return new PluginResult(PluginResult.Status.OK, i.getStringExtra(extraName));
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i.getStringExtra(extraName)));
return true;
} else {
//return new PluginResult(PluginResult.Status.ERROR);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR));
return false;
}
} else if (action.equals("getUri")) {
if (args.length() != 0) {
//return new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
}
Intent i = ((CordovaActivity)this.cordova.getActivity()).getIntent();
String uri = i.getDataString();
//return new PluginResult(PluginResult.Status.OK, uri);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, uri));
return true;
} else if (action.equals("onNewIntent")) {
//save reference to the callback; will be called on "new intent" events
this.onNewIntentCallbackContext = callbackContext;
if (args.length() != 0) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
}
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
result.setKeepCallback(true); //re-use the callback on intent events
callbackContext.sendPluginResult(result);
return true;
//return result;
} else if (action.equals("sendBroadcast"))
{
if (args.length() != 1) {
//return new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
}
// Parse the arguments
JSONObject obj = args.getJSONObject(0);
JSONObject extras = obj.has("extras") ? obj.getJSONObject("extras") : null;
Map<String, String> extrasMap = new HashMap<String, String>();
// Populate the extras if any exist
if (extras != null) {
JSONArray extraNames = extras.names();
for (int i = 0; i < extraNames.length(); i++) {
String key = extraNames.getString(i);
String value = extras.getString(key);
extrasMap.put(key, value);
}
}
sendBroadcast(obj.getString("action"), extrasMap);
//return new PluginResult(PluginResult.Status.OK);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
return true;
}
//return new PluginResult(PluginResult.Status.INVALID_ACTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
return false;
} catch (JSONException e) {
e.printStackTrace();
String errorMessage=e.getMessage();
//return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION,errorMessage));
return false;
}
}
@Override
public void onNewIntent(Intent intent) {
if (this.onNewIntentCallbackContext != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, intent.getDataString());
result.setKeepCallback(true);
this.onNewIntentCallbackContext.sendPluginResult(result);
}
}
void startActivity(String action, Uri uri, String type, Map<String, String> extras) {
Intent i = (uri != null ? new Intent(action, uri) : new Intent(action));
if (type != null && uri != null) {
i.setDataAndType(uri, type); //Fix the crash problem with android 2.3.6
} else {
if (type != null) {
i.setType(type);
}
}
for (String key : extras.keySet()) {
String value = extras.get(key);
// If type is text html, the extra text must sent as HTML
if (key.equals(Intent.EXTRA_TEXT) && type.equals("text/html")) {
i.putExtra(key, Html.fromHtml(value));
} else if (key.equals(Intent.EXTRA_STREAM)) {
// allowes sharing of images as attachments.
// value in this case should be a URI of a file
final CordovaResourceApi resourceApi = webView.getResourceApi();
i.putExtra(key, resourceApi.remapUri(Uri.parse(value)));
} else if (key.equals(Intent.EXTRA_EMAIL)) {
// allows to add the email address of the receiver
i.putExtra(Intent.EXTRA_EMAIL, new String[] { value });
} else {
i.putExtra(key, value);
}
}
((CordovaActivity)this.cordova.getActivity()).startActivity(i);
}
void sendBroadcast(String action, Map<String, String> extras) {
Intent intent = new Intent();
intent.setAction(action);
for (String key : extras.keySet()) {
String value = extras.get(key);
intent.putExtra(key, value);
}
((CordovaActivity)this.cordova.getActivity()).sendBroadcast(intent);
}
}