I've been trying to make a simple WebRTC app using Firebase Database and PeerJs that can switch cameras. I found one tutorial and it works properly, but I want to switch the camera between front and back which is not included in the tutorial.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
...
<uses-feature android:name="android.hardware.camera.any"/>
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
...
</manifest>
CallActivity.java
import static android.view.View.GONE;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.PermissionRequest;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import java.util.UUID;
public class CallActivity extends AppCompatActivity {
private static final String TAG = CallActivity.class.getSimpleName();
private final String CAMERA_FRONT = "user";
private final String CAMERA_BACK = "environment"; // Tried to use it on navigator.mediaDevices.getUserMedia({video: {facingMode: camera}}) but it didn't work.
private RelativeLayout layoutIncoming, layoutCall, layoutCallControl;
private Button buttonReject, buttonAccept, buttonCall, buttonAudio, buttonVideo, buttonCamera;
private EditText editTextCallName;
private TextView textViewIncoming;
private WebView webView;
private String name;
private String callerName;
private boolean isPeerConnected = false;
private DatabaseReference usersRef = FirebaseDatabase.getInstance("link_to_firebase_database").getReference("users");
private boolean videoEnabled = true;
private boolean audioEnabled = true;
private String camera = CAMERA_FRONT;
private String uniqueID;
//== Overridden ==//
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call);
layoutIncoming = findViewById(R.id.activity_call_layoutIncoming);
layoutCall = findViewById(R.id.activity_call_layoutCall);
layoutCallControl = findViewById(R.id.activity_call_layoutCallControl);
buttonAccept = findViewById(R.id.activity_call_buttonAccept);
buttonReject = findViewById(R.id.activity_call_buttonReject);
buttonCall = findViewById(R.id.activity_call_buttonCall);
buttonVideo = findViewById(R.id.activity_call_buttonVideo);
buttonAudio = findViewById(R.id.activity_call_buttonAudio);
buttonCamera = findViewById(R.id.activity_call_buttonCamera);
editTextCallName = findViewById(R.id.activity_call_editTextCallName);
textViewIncoming = findViewById(R.id.activity_call_textViewIncoming);
webView = findViewById(R.id.activity_call_webView);
if (getIntent().hasExtra("name")) {
name = getIntent().getStringExtra("name");
}
buttonCall.setOnClickListener(view -> {
callerName = editTextCallName.getText().toString().trim();
if (!callerName.isEmpty()) sendCallRequest();
});
buttonVideo.setOnClickListener(view -> {
videoEnabled = !videoEnabled;
callJsFunction("javascript:toggleVideo(\"" + videoEnabled + "\")");
if (videoEnabled)
buttonVideo.setText("Video Off");
else
buttonVideo.setText("Video On");
});
buttonAudio.setOnClickListener(view -> {
audioEnabled = !audioEnabled;
callJsFunction("javascript:toggleAudio(\"" + audioEnabled + "\")");
if (audioEnabled)
buttonAudio.setText("Mute");
else
buttonAudio.setText("Unmute");
});
buttonCamera.setOnClickListener(view -> {
if (camera.equals(CAMERA_FRONT)) camera = CAMERA_BACK;
else camera = CAMERA_FRONT;
switchCamera();
});
setupWebView();
}
//== Public ==//
public void onPeerConnected() {
isPeerConnected = true;
}
//== Private ==//
private void setupWebView() {
WebChromeClient client = new WebChromeClient() {
#Override
public void onPermissionRequest(PermissionRequest request) {
runOnUiThread(() -> request.grant(request.getResources()));
}
};
webView.setWebChromeClient(client);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
webView.addJavascriptInterface(new JsInterface(this), "Android");
loadVideoCall();
}
private void loadVideoCall() {
String filePath = "file:///android_asset/call.html";
webView.loadUrl(filePath);
WebViewClient client = new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
initializePeer();
}
};
webView.setWebViewClient(client);
}
private void initializePeer() {
uniqueID = getUniqueID();
callJsFunction("javascript:init(\"" + uniqueID + "\")");
usersRef.child(name).child("incoming").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Log.d(TAG, "Received incoming call!!!");
onCallRequest(snapshot.getValue(String.class));
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
private void sendCallRequest() {
if (!isPeerConnected) {
Toast.makeText(this, "You're not connected to internet. Please try again.", Toast.LENGTH_SHORT).show();
return;
}
usersRef.child(callerName).child("incoming").setValue(name);
usersRef.child(callerName).child("isAvailable").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
boolean isAvailable = snapshot.getValue() != null? snapshot.getValue(boolean.class): false;
if (isAvailable) {
listenForConnectionID();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
private void onCallRequest(String caller) {
if (caller == null) return;
String incomingMessage = caller + " is calling...";
textViewIncoming.setText(incomingMessage);
buttonAccept.setOnClickListener(view -> {
usersRef.child(name).child("connectionID").setValue(uniqueID);
usersRef.child(name).child("isAvailable").setValue(true);
layoutIncoming.setVisibility(GONE);
switchToCallControls();
});
buttonReject.setOnClickListener(view -> {
usersRef.child(name).child("incoming").setValue(null);
layoutIncoming.setVisibility(GONE);
});
layoutIncoming.setVisibility(View.VISIBLE);
}
private void listenForConnectionID() {
usersRef.child(callerName).child("connectionID").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.getValue() == null) return;
switchToCallControls();
callJsFunction("javascript:startCall(\"" + snapshot.getValue(String.class) + "\")");
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
private void switchToCallControls() {
layoutCall.setVisibility(GONE);
layoutCallControl.setVisibility(View.VISIBLE);
}
private void switchCamera() {
Log.d(TAG, "switchCamera: " + camera);
callJsFunction("javascript:switchCamera(\"" + camera + "\")");
}
private void callJsFunction(String functionString) {
webView.post(() -> webView.evaluateJavascript(functionString, value -> Log.d(TAG, value)));
}
private String getUniqueID() {
return UUID.randomUUID().toString();
}
}
call.html
<!DOCTYPE html>
<html>
<head>
<link href="./style.css" rel="stylesheet"/>
</head>
<body>
<script src="./peerjs.js"></script>
<video class="secondaryVideo" autoplay id="remoteVideo"></video>
<video class="primaryVideo" autoplay muted id="localVideo"></video>
<script src="./call.js"></script>
</body>
</html>
call.js
let localVideo = document.getElementById("localVideo")
let remoteVideo = document.getElementById("remoteVideo")
localVideo.style.opacity = 0
remoteVideo.style.opacity = 0
let peer
function init(userID) {
peer = new Peer(userID)
peer.on('open', () => {
Android.onPeerConnected();
})
listen()
}
let localStream
function listen() {
peer.on('call', (call) => {
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(function(mediaStream) {
localStream = mediaStream
localVideo.srcObject = localStream
localVideo.style.opacity = 1
call.answer(localStream)
call.on('stream', (remoteStream) => {
remoteVideo.srcObject = remoteStream
remoteVideo.style.opacity = 1
// Swap classes of localVideo and remoteVideo
localVideo.className = "secondaryVideo"
remoteVideo.className = "primaryVideo"
})
})
})
}
function startCall(otherUserID) {
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(function(mediaStream) {
localStream = mediaStream
localVideo.srcObject = localStream
localVideo.style.opacity = 1
const call = peer.call(otherUserID, localStream)
call.on('stream', (remoteStream) => {
remoteVideo.srcObject = remoteStream
remoteVideo.style.opacity = 1
// Swap classes of localVideo and remoteVideo
localVideo.className = "secondaryVideo"
remoteVideo.className = "primaryVideo"
})
})
}
function toggleVideo(b) {
if (b == "true") {
localStream.getVideoTracks()[0].enabled = true
} else {
localStream.getVideoTracks()[0].enabled = false
}
}
function toggleAudio(b) {
if (b == "true") {
localStream.getAudioTracks()[0].enabled = true
} else {
localStream.getAudioTracks()[0].enabled = false
}
}
let camIndex = 0
function switchCamera() {
navigator.mediaDevices.enumerateDevices().then(function(devices) {
var cameras = []
devices.forEach(function(device) {
'videoinput' === device.kind && cameras.push(device.deviceId)
})
console.log(cameras.length)
if (camIndex == cameras.length - 1) {
camIndex = 0
} else {
camIndex = camIndex + 1
}
var constraints = {
video: {deviceId: {exact: cameras[camIndex]}},
audio: true
}
navigator.mediaDevices.getUserMedia(constraints).then(function(mediaStream) {
localStream = mediaStream
localVideo.srcObject = localStream
console.log("camera switched to camIndex " + camIndex) // Only triggered when camIndex = 0
})
})
}
I assume that camIndex = 1 is a back camera but it gives this error message in the logcat
D/CallActivity: switchCamera: environment
E/chromium: [ERROR:web_contents_delegate.cc(218)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.
E/chromium: [ERROR:web_contents_delegate.cc(218)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.
D/CallActivity: null
I/chromium: [INFO:CONSOLE(97)] "2", source: file:///android_asset/call.js (97)
E/libc: Access denied finding property "persist.vendor.camera.privapp.list"
W/ThreadPoolSingl: type=1400 audit(0.0:35101): avc: denied { read } for name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=19669 scontext=u:r:untrusted_app:s0:c161,c256,c512,c768 tcontext=u:object_r:vendor_camera_prop:s0 tclass=file permissive=0
E/cr_VideoCapture: cameraDevice encountered an error
I/chromium: [INFO:CONSOLE(0)] "Uncaught (in promise) NotReadableError: Could not start video source", source: file:///android_asset/call.html (0)
D/CallActivity: switchCamera: user
E/chromium: [ERROR:web_contents_delegate.cc(218)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.
E/chromium: [ERROR:web_contents_delegate.cc(218)] WebContentsDelegate::CheckMediaAccessPermission: Not supported.
D/CallActivity: null
I/chromium: [INFO:CONSOLE(97)] "2", source: file:///android_asset/call.js (97)
D/: PlayerBase::stop() from IPlayer
D/AudioTrack: stop(398): called with 62088 frames delivered
I/chromium: [INFO:CONSOLE(115)] "camera switched to camIndex 0", source: file:///android_asset/call.js (115)
W/.testapp_webrt: Attempt to remove non-JNI local reference, dumping thread
W/AudioManager: Use of stream types is deprecated for operations other than volume control
W/AudioManager: See the documentation of requestAudioFocus() for what to use instead with android.media.AudioAttributes to qualify your playback use case
Related
Could you explain me how I could use concatMap on getPrices() and getDetails()?
export class HistoricalPricesComponent implements OnInit, OnDestroy {
private unsubscribe$ = new Subject < void > ();
infoTitle: string = "";
lines: HistoryPoint[] = [];
model: Currency = new Currency();
svm: string;
constructor(
private location: Location,
private service: HistoricalPricesService,
private activatedRoute: ActivatedRoute
) {}
ngOnInit(): void {
let svm: string | null;
svm = this.activatedRoute.snapshot.paramMap.get('svm');
if (!svm) {
this.goBack();
return;
}
this.svm = svm;
this.getPrices();
this.getDetails(svm)
}
ngOnDestroy(): void {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
getPrices(): void {
this.service.getInstrumentHistoryEquities(this.svm, this.model).pipe(
takeUntil(this.unsubscribe$)
).subscribe(res => {
if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
if (res.HISTO.POINT.length > 0) {
this.lines = res.HISTO.POINT.reverse();
}
}
});
}
getDetails(svm: string): void {
this.service.getInstrumentInfo(svm).pipe(
takeUntil(this.unsubscribe$)
).subscribe(res => {
if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
this.infoTitle += " " + res.ADVTITRE.BASIQUETITRE.LABEL + " (" + res.ADVTITRE.BASIQUETITRE.PLACELABEL + ")";
}
});
}
goBack(): void {
this.location.back();
}
}
I tried to look on this page
https://www.tektutorialshub.com/angular/using-concatmap-in-angular/
But I don't know where to start?
The example does not allow me to understand how I could create this?
here it is
concatMap is a operator stream so you just need to use it through a creator stream to that I initialize two Subject for each actions getPrices & getDetails.
Then I perform the AJAX call following the concatMap strategy and get that into Observable in order to be combined or used directly into the template of the component.
export class HistoricalPricesComponent implements OnInit, OnDestroy {
private unsubscribe$ = new Subject < void > ();
infoTitle: string = "";
lines: HistoryPoint[] = [];
model: Currency = new Currency();
svm: string;
// actions
getPrices$ = new Subject<void>();
getDetails$ = new Subject<string>();
// states
prices$: Observable<any>();
details$: Observable<any>();
constructor(
private location: Location,
private service: HistoricalPricesService,
private activatedRoute: ActivatedRoute
) {}
ngOnInit(): void {
let svm: string | null;
svm = this.activatedRoute.snapshot.paramMap.get('svm');
if (!svm) {
this.goBack();
return;
}
this.svm = svm;
this.prices$ = this.getPrices$.pipe(concatMap(this.getPrices)
this.details$ = this.getDetails$.pipe(concatMap((svm => this.getDetails(svm))
}
ngOnDestroy(): void {
this.getPrices$.complete()
this.getDetails$.complete()
this.unsubscribe$.complete();
}
getPrices(): void {
this.service.getInstrumentHistoryEquities(this.svm, this.model).pipe(
takeUntil(this.unsubscribe$)
).subscribe(res => {
if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
if (res.HISTO.POINT.length > 0) {
this.lines = res.HISTO.POINT.reverse();
}
}
});
}
getDetails(svm: string): void {
this.service.getInstrumentInfo(svm).pipe(
takeUntil(this.unsubscribe$)
).subscribe(res => {
if (res.RETURNCODE === ApiResponseCodeEnum.Ok) {
this.infoTitle += " " + res.ADVTITRE.BASIQUETITRE.LABEL + " (" + res.ADVTITRE.BASIQUETITRE.PLACELABEL + ")";
}
});
}
goBack(): void {
this.location.back();
}
}
I'm new into front end and I am trying to make a Zoom clone using Blazor. Right now I can open the camera and get the stream and send with signalR, but I can't find a way to play the video in the clients. I don't know much of JS, so I get the code from this questions in this very site:
Get a stream of bytes from navigator.mediaDevices.getUserMedia()?
Convert blob to base64
How to receive continuous chunk of video as a blob array and set to video tag dynamically in Websocket
The JS code
let stream = null;
let recorder = null;
let videoData = null;
let videoTeste = null;
let chunks = [];
let wholeVideo = [];
let mediaRecorder;
async function onStart(options) {
let video = document.getElementById(options.videoID);
if (navigator.mediaDevices.getUserMedia) {
try {
stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
video.play();
recorder = new MediaRecorder(stream);
recorder.ondataavailable = event => {
videoData = event.data;
chunks.push(videoData);
sendData();
};
recorder.start(100);
}
catch (err) {
console.log("An error occurred: " + err);
}
}
}
async function sendData() {
const superBuffer = new Blob(chunks, {
type: 'video/mp4'
});
//let base64data = window.URL.createObjectURL(superBuffer);
let base64data = await blobToBase64(superBuffer);
if (videoTeste) {
chunks = [];
videoTeste.invokeMethodAsync("SendVideoData", base64data);
window.URL.revokeObjectURL(base64data);
}
}
async function blobToBase64(blob) {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
return reader;
});
}
async function playVideo(source) {
try {
let video = document.getElementById("videoplayer");
video.srcObject = null;
let currentTime = video.currentTime;
let file = await fetch(source).then(r => r.blob());
video.src = file;
video.currentTime = currentTime;
video.play();
}
catch (err) {
console.log("An error occurred: " + err);
}
}
window.OnClassWebCam = {
start: async (options) => {
await onStart(options);
},
videoPlayer: async (source) => {
await playVideo(source);
},
dotNetHelper: async (dotNetHelper) => {
videoTeste = dotNetHelper;
}
};
The C# Front Code:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.JSInterop;
using System.Text.Json;
namespace OnClassBlazor.Pages
{
public class VideoTesteBase : ComponentBase
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }
private HubConnection? hubConnection;
protected string DataAtual = DateTime.Now.ToString();
protected string SourceVideo = string.Empty;
public async Task Start()
{
await JSRuntime.InvokeVoidAsync("OnClassWebCam.start", options);
}
protected override async Task OnInitializedAsync()
{
var dotNetReference = DotNetObjectReference.Create(this);
await JSRuntime.InvokeVoidAsync("OnClassWebCam.dotNetHelper", dotNetReference);
hubConnection = new HubConnectionBuilder()
.WithUrl(#"http://localhost:5000/videohub")
.ConfigureLogging(o => {
o.SetMinimumLevel(LogLevel.Trace);
})
.Build();
hubConnection.On<string>("ReceiveStream", (source) =>
{
JSRuntime.InvokeVoidAsync("OnClassWebCam.videoPlayer", source);
});
await hubConnection.StartAsync();
}
[JSInvokable]
public async Task SendVideoData(string stream)
{
Console.WriteLine($"stream size {stream.Length}");
if (IsConnected)
{
await hubConnection.SendAsync("UploadStreamBytes", stream);
}
}
public bool IsConnected =>
hubConnection?.State == HubConnectionState.Connected;
public async ValueTask DisposeAsync()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}
}
protected WebCamOptions options = new WebCamOptions()
{
CanvasID = "canvas",
VideoID = "video"
};
protected override void OnInitialized()
{
}
}
public class WebCamOptions
{
public int Width { get; set; } = 960;
public int Height { get; set; } = 540;
public string VideoID { get; set; }
public string CanvasID { get; set; }
public string Filter { get; set; } = null;
}
}
The C# Hub code:
using Microsoft.AspNetCore.SignalR;
using System.Text.Json;
using System.Threading.Channels;
namespace OnClass.API.Hubs
{
public class VideoHub : Hub
{
public async Task SendStream(object stream)
{
await Clients.All.SendAsync("ReceiveMessage", stream);
}
public async Task UploadStreamBytes(string stream)
{
Console.WriteLine($"UploadStreamBytes size: {stream.Length}");
await Clients.All.SendAsync("ReceiveStream", stream);
}
}
}
The component code:
#page "/videochat"
#inherits VideoTesteBase
<h3>VideoTeste</h3>
<div id="container">
<video id="#options.VideoID"
autoplay="true" muted="muted"
width="#options.Width"
height="#options.Height">
</video>
<button id="start" #onclick="Start" disabled="#(!IsConnected)">Start Video</button>
</div>
<div id="videodastream">
<video id="videoplayer"
autoplay="true" muted="muted"
width="100"
height="100">
</video>
<button id="aqui" >Video</button>
</div>
I try to communicate between a Java WebsocketServer (https://github.com/TooTallNate/Java-WebSocket) with an a Webpage via a JS-WebSocket.
My JS-Websocket:
window.websocket = new WebSocket("ws://localhost:8000");
window.websocket.onopen = () => {
window.websocket.send("Hello")
}
window.websocket.onmmessage = function(event) {
alert('Hi');
console.log(event.data);
}
and my Java-Websocket-Server:
package test;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
public class testsocket extends WebSocketServer {
private static int TCP_PORT = 9000;
private Set<WebSocket> conns;
public testsocket() throws UnknownHostException {
super(new InetSocketAddress(TCP_PORT));
conns = new HashSet<>();
}
#Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
conns.add(conn);
System.out.println("New connection from " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
}
#Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
}
#Override
public void onMessage(WebSocket conn, String message) {
System.out.println(message);
String s2send = "hello";
System.out.println(s2send);
conn.send(s2send);
}
#Override
public void onError(WebSocket conn, Exception ex) {
ex.printStackTrace();
if (conn != null) {
conns.remove(conn);
// do some thing if required
}
}
}
So my Java-Websocket prints "Hello", so I can communicate from JS to Java, but on the other hand my Websocket is neither showing the alert nor printing in the console, so I assume onmessage isn't firing
If you are sure, your java ws server is working... Then
There is a typo! the event listener should be window.websocket.onmessage. NOT window.websocket.onmmessage. Two "m"s :)
window.websocket = new WebSocket("ws://localhost:8000");
window.websocket.onopen = () => {
window.websocket.send("Hello");
};
window.websocket.onmessage = function(event) {
alert("Hi");
console.log(event.data);
};
I am working on an Android application where I need to connect to a web socket and receive messages to unlock the app from the web server. Receiving messages have been implemented through Stompclient on java script but i need to do the same on Android. I read about Okhttp3 and its support for web socket so I was able to implement it The websocket is opening but I don't know how to subscribe to receive the messages on Android.
Here is the Java Script Code:
<html>
<head>
<title>Chat WebSocket</title>
<script src="/gravity/resources/sockjs-0.3.4.js"></script>
<script src="/gravity/resources/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility
= connected ? 'visible' : 'hidden';
document.getElementById('response').innerHTML = '';
}
function connect() {
var socket = new SockJS('/gravity/api/gravitywebsocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/pos/asycnronous', function(messageOutput) {
var body = messageOutput.body; body.a=5;
showMessageOutput(body);
});
});
}
function disconnect() {
if(stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/api/poschannel", {},JSON.stringify({'from':from, 'text':text}));
}
function showMessageOutput(messageOutput) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(messageOutput.a + "***" +messageOutput.from + ": "
+ messageOutput.text + " (" + messageOutput.time + ")"));
response.appendChild(p);
}
</script>
</head>
<body onload="disconnect()">
<div>
<div>
<input type="text" id="from" placeholder="Choose a nickname"/>
</div>
<br />
<div>
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">
Disconnect
</button>
</div>
<br />
<div id="conversationDiv">
<input type="text" id="text" placeholder="Write a message..."/>
<button id="sendMessage" onclick="sendMessage();">Send</button>
<p id="response"></p>
</div>
</div>
</body>
</html>
My Android Implementation:
public class ServerConnection {
public enum ConnectionStatus {
DISCONNECTED,
CONNECTED
}
public interface ServerListener {
void onNewMessage(String message);
void onStatusChange(ConnectionStatus status);
}
private WebSocket mWebSocket;
private OkHttpClient mClient;
private String mServerUrl;
private Handler mMessageHandler;
private Handler mStatusHandler;
private ServerListener mListener;
public class SocketListener extends WebSocketListener {
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
try {
System.out.println("On Open: Observe Response: " + response.body().string());
} catch (Exception e) {
Utility.stringify(e);
}
Message message = mMessageHandler.obtainMessage(Contract.ACTIVATE_POS_MESSAGE, ConnectionStatus.CONNECTED);
mStatusHandler.sendMessage(message);
}
#Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
System.out.println("OnMessage: Observe result: " + text);
Message message = mMessageHandler.obtainMessage(Contract.ACTIVATE_POS_MESSAGE, text);
mMessageHandler.sendMessage(message);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes.toString());
System.out.println("OnMessage: Observe result: " + bytes.toString());
Message message = mMessageHandler.obtainMessage(Contract.ACTIVATE_POS_MESSAGE, bytes.toString());
mMessageHandler.sendMessage(message);
}
#Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
System.out.println("OnClosed: Observe Reason: " + reason);
Message message = mMessageHandler.obtainMessage(Contract.ACTIVATE_POS_MESSAGE, ConnectionStatus.DISCONNECTED);
mMessageHandler.sendMessage(message);
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
System.out.println("OnFailure: Observe Response: " + response);
disconnect();
}
}
public ServerConnection(String url) {
mClient = new OkHttpClient.Builder()
.readTimeout(5, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
mServerUrl = url;
}
public void connect(ServerListener listener) {
Request request = new Request.Builder()
.url(mServerUrl)
.build();
mWebSocket = mClient.newWebSocket(request, new SocketListener());
mWebSocket.send("Hello, Testing Connection!");
mListener = listener;
mMessageHandler = new Handler(msg -> {
mListener.onNewMessage((String) msg.obj);
return true;
});
mStatusHandler = new Handler(msg -> {
mListener.onStatusChange((ConnectionStatus) msg.obj);
return true;
});
}
public void disconnect() {
mWebSocket.close(1000, "Completed");
mListener = null;
mMessageHandler.removeCallbacksAndMessages(null);
mStatusHandler.removeCallbacksAndMessages(null);
}
public void sendMessage(String message) {
mWebSocket.send(message);
}
}
The LockScreenActivity that needs to receive unlock message from server:
public class LockScreenActivity extends AppCompatActivity implements ServerConnection.ServerListener{
private ServerConnection mServerConnection;
private JSONObject jsonObject;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lock_screen);
mServerConnection = new ServerConnection(TerminalDetails.WEB_SOCKET_URL);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
Timer timer = new Timer();
timer.schedule(timerTask, 10000);
}
#Override
protected void onResume() {
super.onResume();
mServerConnection.connect(this);
mServerConnection.sendMessage("Hello, Testing Connection!");
Utility.showToast(this, "Connected to the Web Socket");
}
#Override
public void onNewMessage(String message) {
try {
Utility.showToast(this, "A new message received!");
jsonObject = new JSONObject(message);
} catch (JSONException e) {
Utility.stringify(e);
}
validateResponse(jsonObject);
}
I will answer more questions about the code if needed. Thanks
I have a server, and I connect to it perfectly and receives messages if I work with node.js and javascript.
I started an app on Android Studio that has an EditText and TextView attributes.
My goal is to type in the EditText press send and send that msg to the server and print the response in the TextView.
For some reason I can't get it to work, and I can see the Socket does connect when I debug it. But I can't seem to get the responses from the server as I would in my js example.
This is my code:
public class MainActivity extends AppCompatActivity {
private Socket socket;
String response = "";
BufferedReader in = null;
PrintStream out = null;
TextView tv;
EditText et;
private static final int SERVERPORT = 80;
private static final String ONLY_HOST = "myServer.azurewebsites.net";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textView);
et = (EditText) findViewById(R.id.messages);
ClientThread client = new ClientThread();
client.execute();
}
class ClientThread extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
try {
socket = new Socket(ONLY_HOST, SERVERPORT);
byte[] buffer = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (inputStream.read(buffer) != -1) {
response += new String(buffer);
}
} catch (UnknownHostException e1) {
Log.e("UnknownHost","Socket not starting" );
e1.printStackTrace();
} catch (IOException e1) {
Log.e("IO Exception","Socket not starting" );
e1.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (tv != null) {
tv.setText(response);
}
super.onPostExecute(result);
}
}
public void sendIt(View v) {
try {
if (socket.isConnected()) {
out = new PrintStream(socket.getOutputStream(), true);
out.write(et.getText().toString().getBytes());
out.flush();
}
} catch (UnknownHostException e1) {
Log.e("sendIt","1st" );
e1.printStackTrace();
} catch (IOException e1) {
Log.e("sendIt","2nd" );
e1.printStackTrace();
}
}
}
Any help will do i've been on this too long already.
Thanks!