Connect web service with Android app.

Build interactive communication channel between web service and android app.

Step1: Web service send connection request to relay server.
Step2: Web service show QR code that contains GUID for connection.
Step3: Android app read QR code and send connection request to relay server.
Step4: Server emit event if two request match the GUID.
Step5: Connection request from web service is return with androd device token.
Step5: Web service can send any message to andriod usign device token.

Web service generate a QR code it contains GUID to connect with android device.

var QRious = require('qrious');
var qr = new QRious({
element: document.getElementById('qr'),
value: vm.deviceConnectionKey,
padding: 25,
size: 300,
});

deviceConnectionKey is generated by uuid4 function,

uuid4: function () {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
},

From web service send request to server which relay the connection.

connectToDevice: function () {
var connInfo = { action: "request", key: this.deviceConnectionKey };
var vm = this;
axios.post('https://smallet.co:3001/api/connectdevice', connInfo)
.then(function (response) {
var deviceInfo = response.data;
console.log(deviceInfo);
vm.account = deviceInfo.myAddress;
vm.connectedDeviceToken = deviceInfo.deviceToken;
vm.network = deviceInfo.network;
vm.connectedDevice = deviceInfo;
vm.buildZeroClient();
var connInfo = {
action: "connected",
key: vm.deviceConnectionKey,
serviceName: document.title
};
axios.post('https://smallet.co:3001/api/connectdevice', connInfo)
.then(function (response) {
console.log(deviceInfo);
$('#connectionDialog').modal('hide');
})
.catch(function (error) {
console.log(error);
});
})
.catch(function (error) {
console.log(error);
});
},

Relay server side,

var events = require('events');
var connectionEvent = new events.EventEmitter();
router.post('/connectdevice', async (request, response) => {
var connInfo = request.body;
var connKey = connInfo.key;
if (connInfo.action == "request") {
console.log("listen connKey=" + connKey);
connectionEvent.on(connKey, (deviceInfo) => {
connectionEvent.removeAllListeners(connKey);
response.json(deviceInfo);
});
} else if (connInfo.action == "connected") {
console.log("emit connected connKey=" + connKey);
connectionEvent.emit("connected:" + connKey, connInfo);
response.json(connInfo);
}
});

now, android app read QR code and send connection to relay server. connectionKey is in the QR code.

final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("key", connectionKey);
jsonObject.put("deviceToken", deviceToken);
jsonObject.put("myAddress", myAddress);
jsonObject.put("network", network);
} catch (JSONException e) {
e.printStackTrace();
}
String data = jsonObject.toString();
Log.d("wallet", "data=" + data);
RequestBody body = RequestBody.create(JSON, data);
final Request request = new Request.Builder()
.url("https://smallet.co:3001/api/connectweb")
.post(body)
.build();
final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60 * 5, TimeUnit.SECONDS)
.readTimeout(60 * 5,TimeUnit.SECONDS).build();
new Thread(new Runnable() {
@Override
public void run() {
try {
final Response response = client.newCall(request).execute();
final String result = response.body().string();
Log.d("wallet", "response=" + result);
tvStatus.post(new Runnable() {
@Override
public void run() {
Log.d("wallet", "datapayload=" + result);
try {
JSONObject res = new JSONObject(result);
String serviceName = (String)res.get("serviceName");
String result = String.format(getString(R.string.connection_success_to_service), serviceName);
tvStatus.setText(result , TextView.BufferType.EDITABLE);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

server side,

router.post('/connectweb', async (request, response) => {
var deviceInfo = request.body;
var connKey = deviceInfo.key;
console.log("emit connKey=" + JSON.stringify(request.body));
connectionEvent.emit(connKey, deviceInfo)
connectionEvent.on("connected:" + connKey, (deviceInfo) => {
connectionEvent.removeAllListeners("connected:" + connKey);
response.json(deviceInfo);
});
});

after all these connection process finished, web service has firebase device token. Now, you can send firebase message from web to android target.

var payload = {};
function requestSignTxByNotification(deviceToken, txObj) {
if (typeof txObj.to == 'undefined')
txObj.to = '';
if (typeof txObj.data == 'undefined')
txObj.data = '';
payload[deviceToken] = txObj;
var message = {
data: {
action: "signTx",
dataUrl: "https://smallet.co:3001/api/payload/?token=" + deviceToken
},
token: deviceToken
};
(async () => {
console.log(message);
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
})();
}

In my case, message payload is a little big(more than 10kb) and firebase messaging complain about that, I send a URL for receiver and android request the real data using that URL.

Android notification handler is as follows,

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "From: " + remoteMessage.getFrom());

// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
Bundle bundle = new Bundle();
for (Map.Entry<String, String> entry : remoteMessage.getData().entrySet()) {
bundle.putString(entry.getKey(), entry.getValue());
}
String action = bundle.getString("action");
if (action != null && action.equals("signTx")) {
OkHttpClient client = new OkHttpClient();
String url = bundle.getString("dataUrl");
Request request = new Request.Builder()
.url(url)
.build();
try {
okhttp3.Response response = client.newCall(request).execute();
String responseStr = response.body().string();
Log.d("wallet", "datapayload=" + responseStr);
JSONObject res = new JSONObject(responseStr);
bundle.putString("gas", (String)res.get("gas"));
bundle.putString("to", (String)res.get("to"));
bundle.putString("data", (String)res.get("data"));
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}

String gas = bundle.getString("gas");
if (gas == null)
return;
if (gas.startsWith("0x")) {
bundle.putString("gas", "" + Long.decode(bundle.getString("gas")));
}
if (MainActivity.isActivityVisible() && MainActivity.currentTabIndex == 0) {
Message msg = new Message();
String to = bundle.getString("to");
String data = bundle.getString("data");
if (!data.equals("")) {
if (to.equals(""))
to = Constants.CONTRACT_CREATION;
msg.what = Constants.SET_DATA_FOR_FUNCTION_CALL;
Bundle msgData = new Bundle();
msgData.putString("additional_data", data);
msgData.putString("contract_address", to);
msgData.putString("gas", bundle.getString("gas"));
msgData.putString("extra", Constants.BackToWeb);
msg.setData(msgData);
MainActivity.mHandle.sendMessageDelayed(msg, 1000);
}
} else {
Utils.sendNotification(this, bundle);
}
}
}

}

When everthing done in android part, return the result to relay server.

final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("result", txReady.toString());
jsonObject.put("deviceToken", deviceToken);
jsonObject.put("txRaw", txRaw);
} catch (JSONException e) {
e.printStackTrace();
}
String dataStr = jsonObject.toString();
Log.d("wallet", "data=" + dataStr);
RequestBody body = RequestBody.create(JSON, dataStr);
final Request request = new Request.Builder()
.url("https://smallet.co:3001/api/returnsignedtx")
.post(body)
.build();
final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60 * 5, TimeUnit.SECONDS)
.readTimeout(60 * 5,TimeUnit.SECONDS).build();
new Thread(new Runnable() {
@Override
public void run() {
try {
final okhttp3.Response response = client.newCall(request).execute();
final String result = response.body().string();
Log.d("wallet", "response=" + result);
ckReturnToWeb.post(new Runnable() {
@Override
public void run() { ...
                }
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();

Again relay server,

router.post('/returnsignedtx', async (request, response) => {
var deviceToken = request.body.deviceToken;
connectionEvent.emit(deviceToken, request.body);
response.json({result:"ok"});
});

You can run all these process this live working demo.

Like what you read? Give Chang a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.