From d78f4d727fea5de2f80325fc24a74589ba6f3c5d Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 16:04:02 -0700 Subject: [PATCH 01/27] use okhttp instead of tyrus --- .gitignore | 1 + app/build.gradle | 20 ++- .../demoapp/activity/WebRtcActivity.java | 61 ++++---- .../signaling/SignalingListener.java | 22 +-- .../SignalingServiceWebSocketClient.java | 42 ++--- .../signaling/okhttp/WebSocketClient.java | 85 ++++++++++ .../signaling/tyrus/WebSocketClient.java | 145 ------------------ 7 files changed, 147 insertions(+), 229 deletions(-) rename app/src/main/java/com/amazonaws/kinesisvideo/signaling/{tyrus => okhttp}/SignalingServiceWebSocketClient.java (65%) create mode 100644 app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java delete mode 100644 app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/WebSocketClient.java diff --git a/.gitignore b/.gitignore index f58c803..78bae11 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ .externalNativeBuild .cxx local.properties +app/libs/libwebrtc-* diff --git a/app/build.gradle b/app/build.gradle index c70df72..fac548d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,12 +4,12 @@ plugins { } android { - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.amazonaws.kinesisvideo.webrtc_sdk_android" minSdk 29 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0.0" @@ -32,7 +32,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) - def aws_version = '2.73.0' + def aws_version = '2.75.0' implementation("com.amazonaws:aws-android-sdk-kinesisvideo:$aws_version@aar") { transitive = true } implementation("com.amazonaws:aws-android-sdk-kinesisvideo-signaling:$aws_version@aar") { transitive = true } implementation("com.amazonaws:aws-android-sdk-kinesisvideo-webrtcstorage:$aws_version@aar") { transitive = true } @@ -43,22 +43,20 @@ dependencies { implementation 'org.awaitility:awaitility:4.2.0' implementation 'org.json:json:20190722' implementation 'com.google.guava:guava:28.1-android' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.google.code.gson:gson:2.10.1' implementation 'org.apache.commons:commons-lang3:3.9' - implementation('org.glassfish.tyrus.bundles:tyrus-standalone-client:1.21') { - exclude module: 'javax.inject' - } + implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' } // Check that libwebrtc is present during build -task checkForLibWebRTC() { +tasks.register('checkForLibWebRTC') { doLast { boolean webrtcFound = false @@ -91,4 +89,4 @@ task checkForLibWebRTC() { } } -preBuild.dependsOn checkForLibWebRTC +preBuild.dependsOn checkForLibWebRTC \ No newline at end of file diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java index ed1eabd..366a41b 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java @@ -4,7 +4,6 @@ import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_CHANNEL_ARN; import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_CLIENT_ID; import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_ICE_SERVER_PASSWORD; -import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_ICE_SERVER_TTL; import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_ICE_SERVER_URI; import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_ICE_SERVER_USER_NAME; import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_IS_MASTER; @@ -47,7 +46,7 @@ import com.amazonaws.kinesisvideo.signaling.SignalingListener; import com.amazonaws.kinesisvideo.signaling.model.Event; import com.amazonaws.kinesisvideo.signaling.model.Message; -import com.amazonaws.kinesisvideo.signaling.tyrus.SignalingServiceWebSocketClient; +import com.amazonaws.kinesisvideo.signaling.okhttp.SignalingServiceWebSocketClient; import com.amazonaws.kinesisvideo.utils.AwsV4Signer; import com.amazonaws.kinesisvideo.utils.Constants; import com.amazonaws.kinesisvideo.webrtc.KinesisVideoPeerConnection; @@ -101,8 +100,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import javax.microedition.khronos.egl.EGLContext; - public class WebRtcActivity extends AppCompatActivity { private static final String TAG = "KVSWebRtcActivity"; private static final String AudioTrackID = "KvsAudioTrack"; @@ -172,14 +169,14 @@ public class WebRtcActivity extends AppCompatActivity { * offer/answer for a peer connection has been received and sent, the PeerConnection is added * to this map. */ - private final HashMap peerConnectionFoundMap = new HashMap(); + private final HashMap peerConnectionFoundMap = new HashMap<>(); /** * Only used when we are master. Mapping of the peer's sender id to its received ICE candidates. * Since we can receive ICE Candidates before we have sent the answer, we hold ICE candidates in * this queue until after we send the answer and the peer connection is established. */ - private final HashMap> pendingIceCandidatesMap = new HashMap>(); + private final HashMap> pendingIceCandidatesMap = new HashMap<>(); private void initWsConnection() { @@ -350,6 +347,7 @@ private void handlePendingIceCandidates(final String clientId) { while (pendingIceCandidatesQueueByClientId != null && !pendingIceCandidatesQueueByClientId.isEmpty()) { final IceCandidate iceCandidate = pendingIceCandidatesQueueByClientId.peek(); final PeerConnection peer = peerConnectionFoundMap.get(clientId); + assert peer != null; final boolean addIce = peer.addIceCandidate(iceCandidate); Log.d(TAG, "Added ice candidate after SDP exchange " + iceCandidate + " " + (addIce ? "Successfully" : "Failed")); pendingIceCandidatesQueueByClientId.remove(); @@ -366,18 +364,19 @@ private void checkAndAddIceCandidate(final Event message, final IceCandidate ice Log.d(TAG, "SDP exchange is not complete. Ice candidate " + iceCandidate + " + added to pending queue"); // If the entry for the client ID already exists (in case of subsequent ICE candidates), update the queue + final Queue pendingIceCandidatesQueueByClientId; if (pendingIceCandidatesMap.containsKey(message.getSenderClientId())) { - final Queue pendingIceCandidatesQueueByClientId = pendingIceCandidatesMap.get(message.getSenderClientId()); - pendingIceCandidatesQueueByClientId.add(iceCandidate); - pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId); + pendingIceCandidatesQueueByClientId = pendingIceCandidatesMap.get(message.getSenderClientId()); } // If the first ICE candidate before peer connection is received, add entry to map and ICE candidate to a queue else { - final Queue pendingIceCandidatesQueueByClientId = new LinkedList<>(); - pendingIceCandidatesQueueByClientId.add(iceCandidate); - pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId); + pendingIceCandidatesQueueByClientId = new LinkedList<>(); } + + assert pendingIceCandidatesQueueByClientId != null; + pendingIceCandidatesQueueByClientId.add(iceCandidate); + pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId); } // This is the case where peer connection is established and ICE candidates are received for the established @@ -386,6 +385,7 @@ private void checkAndAddIceCandidate(final Event message, final IceCandidate ice Log.d(TAG, "Peer connection found already"); // Remote sent us ICE candidates, add to local peer connection final PeerConnection peer = peerConnectionFoundMap.get(message.getSenderClientId()); + assert peer != null; final boolean addIce = peer.addIceCandidate(iceCandidate); Log.d(TAG, "Added ice candidate " + iceCandidate + " " + (addIce ? "Successfully" : "Failed")); @@ -486,7 +486,6 @@ protected void onCreate(final Bundle savedInstanceState) { isAudioSent = intent.getBooleanExtra(KEY_SEND_AUDIO, false); ArrayList mUserNames = intent.getStringArrayListExtra(KEY_ICE_SERVER_USER_NAME); ArrayList mPasswords = intent.getStringArrayListExtra(KEY_ICE_SERVER_PASSWORD); - ArrayList mTTLs = intent.getIntegerArrayListExtra(KEY_ICE_SERVER_TTL); ArrayList> mUrisList = (ArrayList>) intent.getSerializableExtra(KEY_ICE_SERVER_URI); mRegion = intent.getStringExtra(KEY_REGION); mCameraFacingFront = intent.getBooleanExtra(KEY_CAMERA_FRONT_FACING, true); @@ -506,6 +505,8 @@ protected void onCreate(final Bundle savedInstanceState) { for (int i = 0; i < mUrisList.size(); i++) { final String turnServer = mUrisList.get(i).toString(); if (turnServer != null) { + assert mUserNames != null; + assert mPasswords != null; final IceServer iceServer = IceServer.builder(turnServer.replace("[", "").replace("]", "")) .setUsername(mUserNames.get(i)) .setPassword(mPasswords.get(i)) @@ -677,6 +678,7 @@ public void onStateChange() { Log.d(TAG, "Remote Data Channel onStateChange: state: " + dataChannel.state().toString()); } + @SuppressLint("MissingPermission") @Override public void onMessage(DataChannel.Buffer buffer) { runOnUiThread(() -> { @@ -709,14 +711,12 @@ public void onMessage(DataChannel.Buffer buffer) { }); if (localPeer != null) { - printStatsExecutor.scheduleWithFixedDelay(() -> { - localPeer.getStats(rtcStatsReport -> { - final Map statsMap = rtcStatsReport.getStatsMap(); - for (final Map.Entry entry : statsMap.entrySet()) { - Log.d(TAG, "Stats: " + entry.getKey() + ", " + entry.getValue()); - } - }); - }, 0, 10, TimeUnit.SECONDS); + printStatsExecutor.scheduleWithFixedDelay(() -> localPeer.getStats(rtcStatsReport -> { + final Map statsMap = rtcStatsReport.getStatsMap(); + for (final Map.Entry entry : statsMap.entrySet()) { + Log.d(TAG, "Stats: " + entry.getKey() + ", " + entry.getValue()); + } + }), 0, 10, TimeUnit.SECONDS); } addDataChannelToLocalPeer(); @@ -760,7 +760,7 @@ private void addStreamToLocalPeer() { Log.e(TAG, "Add audio track failed"); } - if (stream.audioTracks.size() > 0) { + if (!stream.audioTracks.isEmpty()) { localPeer.addTrack(stream.audioTracks.get(0), Collections.singletonList(stream.getId())); Log.d(TAG, "Sending audio track"); } @@ -798,14 +798,11 @@ public void onMessage(final DataChannel.Buffer buffer) { } }); - sendDataChannelButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - localDataChannel.send(new DataChannel.Buffer( - ByteBuffer.wrap(dataChannelText.getText().toString() - .getBytes(Charset.defaultCharset())), false)); - dataChannelText.setText(""); - } + sendDataChannelButton.setOnClickListener(view -> { + localDataChannel.send(new DataChannel.Buffer( + ByteBuffer.wrap(dataChannelText.getText().toString() + .getBytes(Charset.defaultCharset())), false)); + dataChannelText.setText(""); }); } @@ -880,9 +877,9 @@ public void onCreateFailure(final String error) { private void addRemoteStreamToVideoView(MediaStream stream) { - final VideoTrack remoteVideoTrack = stream.videoTracks != null && stream.videoTracks.size() > 0 ? stream.videoTracks.get(0) : null; + final VideoTrack remoteVideoTrack = stream.videoTracks != null && !stream.videoTracks.isEmpty() ? stream.videoTracks.get(0) : null; - AudioTrack remoteAudioTrack = stream.audioTracks != null && stream.audioTracks.size() > 0 ? stream.audioTracks.get(0) : null; + AudioTrack remoteAudioTrack = stream.audioTracks != null && !stream.audioTracks.isEmpty() ? stream.audioTracks.get(0) : null; if (remoteAudioTrack != null) { remoteAudioTrack.setEnabled(true); diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java index 29be647..faa0fe3 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java @@ -1,13 +1,13 @@ package com.amazonaws.kinesisvideo.signaling; - import android.util.Base64; import android.util.Log; - -import com.amazonaws.kinesisvideo.signaling.model.Event; +import androidx.annotation.NonNull; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; import com.google.gson.Gson; -import javax.websocket.MessageHandler; +import com.amazonaws.kinesisvideo.signaling.model.Event; public abstract class SignalingListener implements Signaling { @@ -15,21 +15,21 @@ public abstract class SignalingListener implements Signaling { private final Gson gson = new Gson(); - private final MessageHandler messageHandler = new MessageHandler.Whole() { + private final WebSocketListener messageHandler = new WebSocketListener() { @Override - public void onMessage(final String message) { - if (message.isEmpty()) { + public void onMessage(@NonNull WebSocket webSocket, String text) { + if (text.isEmpty()) { return; } - Log.d(TAG, "Received message: " + message); + Log.d(TAG, "Received message: " + text); - if (!message.contains("messagePayload")) { + if (!text.contains("messagePayload")) { return; } - final Event evt = gson.fromJson(message, Event.class); + final Event evt = gson.fromJson(text, Event.class); if (evt == null || evt.getMessageType() == null || evt.getMessagePayload().isEmpty()) { return; @@ -59,7 +59,7 @@ public void onMessage(final String message) { } }; - public MessageHandler getMessageHandler() { + public WebSocketListener getMessageHandler() { return messageHandler; } } diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/SignalingServiceWebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/SignalingServiceWebSocketClient.java similarity index 65% rename from app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/SignalingServiceWebSocketClient.java rename to app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/SignalingServiceWebSocketClient.java index 908718d..ae67f05 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/SignalingServiceWebSocketClient.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/SignalingServiceWebSocketClient.java @@ -1,4 +1,4 @@ -package com.amazonaws.kinesisvideo.signaling.tyrus; +package com.amazonaws.kinesisvideo.signaling.okhttp; import android.util.Base64; import android.util.Log; @@ -7,8 +7,6 @@ import com.amazonaws.kinesisvideo.signaling.model.Message; import com.google.gson.Gson; -import org.glassfish.tyrus.client.ClientManager; - import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -29,8 +27,8 @@ public class SignalingServiceWebSocketClient { public SignalingServiceWebSocketClient(final String uri, final SignalingListener signalingListener, final ExecutorService executorService) { Log.d(TAG, "Connecting to URI " + uri + " as master"); - websocketClient = new WebSocketClient(uri, new ClientManager(), signalingListener, executorService); this.executorService = executorService; + websocketClient = new WebSocketClient(uri, signalingListener); } public boolean isOpen() { @@ -38,30 +36,20 @@ public boolean isOpen() { } public void sendSdpOffer(final Message offer) { - executorService.submit(new Runnable() { - @Override - public void run() { - if (offer.getAction().equalsIgnoreCase("SDP_OFFER")) { - - Log.d(TAG, "Sending Offer"); - - send(offer); - } + executorService.submit(() -> { + if (offer.getAction().equalsIgnoreCase("SDP_OFFER")) { + Log.d(TAG, "Sending Offer"); + send(offer); } }); } public void sendSdpAnswer(final Message answer) { - executorService.submit(new Runnable() { - @Override - public void run() { - if (answer.getAction().equalsIgnoreCase("SDP_ANSWER")) { - - Log.d(TAG, "Answer sent " + new String(Base64.decode(answer.getMessagePayload().getBytes(), - Base64.NO_WRAP | Base64.URL_SAFE))); - - send(answer); - } + executorService.submit(() -> { + if (answer.getAction().equalsIgnoreCase("SDP_ANSWER")) { + Log.d(TAG, "Answer sent " + new String(Base64.decode(answer.getMessagePayload().getBytes(), + Base64.NO_WRAP | Base64.URL_SAFE))); + send(answer); } }); } @@ -71,18 +59,12 @@ public void sendIceCandidate(final Message candidate) { if (candidate.getAction().equalsIgnoreCase("ICE_CANDIDATE")) { send(candidate); } - Log.d(TAG, "Sent Ice candidate message"); }); } public void disconnect() { - executorService.submit(new Runnable() { - @Override - public void run() { - websocketClient.disconnect(); - } - }); + executorService.submit(websocketClient::disconnect); try { executorService.shutdown(); if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) { diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java new file mode 100644 index 0000000..61c004e --- /dev/null +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java @@ -0,0 +1,85 @@ +package com.amazonaws.kinesisvideo.signaling.okhttp; + +import static org.awaitility.Awaitility.await; + +import android.util.Log; + +import com.amazonaws.kinesisvideo.signaling.SignalingListener; +import com.amazonaws.kinesisvideo.utils.Constants; + +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +/** + * An OkHttp based WebSocket client. + */ +class WebSocketClient { + + private static final String TAG = "WebSocketClient"; + + private final WebSocket webSocket; + private volatile boolean isOpen = false; + + WebSocketClient(final String uri, final SignalingListener signalingListener) { + + OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); + OkHttpClient client = clientBuilder.build(); + + Request request = new Request.Builder() + .url(uri) + .addHeader("User-Agent", Constants.APP_NAME + "/" + Constants.VERSION + " " + System.getProperty("http.agent")) + .build(); + + webSocket = client.newWebSocket(request, new WebSocketListener() { + @Override + public void onOpen(WebSocket webSocket, Response response) { + Log.d(TAG, "WebSocket connection opened"); + isOpen = true; + // Register message handler + signalingListener.getMessageHandler().onOpen(webSocket, response); + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + Log.d(TAG, "Received message: " + text); + signalingListener.getMessageHandler().onMessage(webSocket, text); + } + + @Override + public void onClosed(WebSocket webSocket, int code, String reason) { + Log.d(TAG, "WebSocket connection closed: " + reason); + isOpen = false; + signalingListener.getMessageHandler().onClosed(webSocket, code, reason); + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + Log.e(TAG, "WebSocket connection failed", t); + isOpen = false; + // Handle failure + signalingListener.onException((Exception) t); + } + }); + + // Await WebSocket connection + await().atMost(10, TimeUnit.SECONDS).until(WebSocketClient.this::isOpen); + } + + void send(String message) { + Log.d(TAG, "Sending message: " + message); + webSocket.send(message); + } + + void disconnect() { + webSocket.close(1000, "Disconnect requested"); + } + + boolean isOpen() { + return isOpen; + } +} diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/WebSocketClient.java deleted file mode 100644 index 0fa49f6..0000000 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/tyrus/WebSocketClient.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.amazonaws.kinesisvideo.signaling.tyrus; - -import static org.awaitility.Awaitility.await; - -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.amazonaws.kinesisvideo.signaling.SignalingListener; -import com.amazonaws.kinesisvideo.utils.Constants; - -import org.glassfish.tyrus.client.ClientManager; -import org.glassfish.tyrus.client.ClientProperties; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.websocket.ClientEndpointConfig; -import javax.websocket.CloseReason; -import javax.websocket.DeploymentException; -import javax.websocket.Endpoint; -import javax.websocket.EndpointConfig; -import javax.websocket.HandshakeResponse; -import javax.websocket.Session; - -/** - * A JSR356 based websocket client. - */ -class WebSocketClient { - - private static final String TAG = "WebSocketClient"; - - private Session session; - - private final ExecutorService executorService; - - WebSocketClient(final String uri, final ClientManager clientManager, - final SignalingListener signalingListener, - final ExecutorService executorService) { - - this.executorService = executorService; - final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create() - .configurator(new ClientEndpointConfig.Configurator() { - @Override - public void beforeRequest(final Map> headers) { - super.beforeRequest(headers); - - final String userAgent = Constants.APP_NAME + "/" + Constants.VERSION + " " + System.getProperty("http.agent"); - - headers.put("User-Agent", Collections.singletonList(userAgent.trim())); - } - - @Override - public void afterResponse(final HandshakeResponse hr) { - super.afterResponse(hr); - - hr.getHeaders().forEach((key, values) -> Log.d(TAG, "header - " + key + ": " + values)); - } - }) - .build(); - - clientManager.getProperties().put(ClientProperties.LOG_HTTP_UPGRADE, true); - - final Endpoint endpoint = new Endpoint() { - - @Override - public void onOpen(final Session session, final EndpointConfig endpointConfig) { - Log.d(TAG, "Registering message handler"); - session.addMessageHandler(signalingListener.getMessageHandler()); - } - - @Override - public void onClose(final Session session, final CloseReason closeReason) { - super.onClose(session, closeReason); - Log.d(TAG, "Session " + session.getRequestURI() + " closed with reason " + - closeReason.getReasonPhrase()); - } - - @Override - public void onError(final Session session, final Throwable thr) { - super.onError(session, thr); - Log.w(TAG, thr); - } - - }; - - executorService.submit(() -> { - try { - session = clientManager.connectToServer(endpoint, cec, new URI(uri)); - } catch (final DeploymentException | IOException | URISyntaxException e) { - signalingListener.onException(e); - } - }); - - await().atMost(10, TimeUnit.SECONDS).until(WebSocketClient.this::isOpen); - } - - boolean isOpen() { - if (session == null) { - Log.d(TAG, "isOpen: false"); - return false; - } - Log.d(TAG, "isOpen: " + session.isOpen()); - return session.isOpen(); - } - - void send(@NonNull final String message) { - if (!this.isOpen()) { - Log.e(TAG, "Connection isn't open!"); - return; - } - - try { - session.getBasicRemote().sendText(message); - } catch (final IOException e) { - Log.e(TAG, "Exception sending message: " + e.getMessage()); - } - } - - void disconnect() { - if (session == null) { - Log.e(TAG, "Connection hasn't opened yet!"); - return; - } - - if (!session.isOpen()) { - Log.w(TAG, "Connection already closed for " + session.getRequestURI()); - return; - } - - try { - session.close(); - executorService.shutdownNow(); - Log.i(TAG, "Disconnected from " + session.getRequestURI() + " successfully!"); - } catch (final IOException e) { - Log.e(TAG, "Exception closing: " + e.getMessage()); - } - } -} From 85e7cf471df4d8f05bf30657c26a34cbdaaa71a4 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 16:40:04 -0700 Subject: [PATCH 02/27] upgrade gradle version --- .github/workflows/ci.yml | 29 ++++++++++++++++++++++++ app/build.gradle | 2 ++ build.gradle | 6 ++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..db8c9ed --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: AWS KVS WebRTC Android SDK CI + +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: '17' + + - name: Build with Gradle + run: ./gradlew build + + - name: Run tests + run: ./gradlew test + diff --git a/app/build.gradle b/app/build.gradle index fac548d..ddef267 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,6 +7,8 @@ android { compileSdk 34 defaultConfig { + namespace("com.amazonaws.kinesisvideo.demoapp") + applicationId "com.amazonaws.kinesisvideo.webrtc_sdk_android" minSdk 29 targetSdk 34 diff --git a/build.gradle b/build.gradle index 5ae9a7b..421cf21 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,9 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.2.2' apply false - id 'com.android.library' version '7.2.2' apply false + id "com.android.application" version '8.1.1' apply false + id "com.android.library" version '8.1.1' apply false } -task clean(type: Delete) { +tasks.register('clean', Delete) { delete rootProject.buildDir } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6d9c90e..2b95caa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Sep 07 11:17:34 PDT 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From ba078cedbdf6a055a2faf8da5a7345cdbc5a5ddc Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 16:48:20 -0700 Subject: [PATCH 03/27] download libwebrtc to fix the ci --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db8c9ed..f726fd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,9 @@ jobs: distribution: 'adopt' java-version: '17' + - name: Download libwebrtc-123.0.0.aar + run: wget -O app/libs/libwebrtc-123.0.0.aar https://github.com/rno/WebRTC/releases/download/123.0.0/libwebrtc-123.0.0.aar + - name: Build with Gradle run: ./gradlew build From 5a5bc61c53771488e9c2f9f2ee24b55ca7dbafb8 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 16:54:25 -0700 Subject: [PATCH 04/27] add a matrix for java version and OS --- .github/workflows/ci.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f726fd0..391b6e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,12 @@ on: - master jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + java: [8, 11, 17] steps: - name: Checkout code @@ -19,7 +24,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'adopt' - java-version: '17' + java-version: ${{ matrix.java }} - name: Download libwebrtc-123.0.0.aar run: wget -O app/libs/libwebrtc-123.0.0.aar https://github.com/rno/WebRTC/releases/download/123.0.0/libwebrtc-123.0.0.aar @@ -29,4 +34,3 @@ jobs: - name: Run tests run: ./gradlew test - From 21aaa49a4d597180f15b4f51e89203176f16a501 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 16:58:15 -0700 Subject: [PATCH 05/27] java 11 and 17 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 391b6e3..088b572 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - java: [8, 11, 17] + java: [11, 17] steps: - name: Checkout code From ca0a535d2a7690c68ef287f14b0f53a4c1a75a75 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 17:00:19 -0700 Subject: [PATCH 06/27] java 17 and 21 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 088b572..4e7322b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - java: [11, 17] + java: [17, 21] steps: - name: Checkout code From 63f0e7c00bb7704bbcc21bfd8b539694f52c6215 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 17:01:58 -0700 Subject: [PATCH 07/27] java 17 --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e7322b..36bd976 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,6 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - java: [17, 21] steps: - name: Checkout code @@ -24,7 +23,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'adopt' - java-version: ${{ matrix.java }} + java-version: 17 - name: Download libwebrtc-123.0.0.aar run: wget -O app/libs/libwebrtc-123.0.0.aar https://github.com/rno/WebRTC/releases/download/123.0.0/libwebrtc-123.0.0.aar From 446a9a27865acb3b203fba434f3c840e22001db0 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 8 Apr 2024 17:17:00 -0700 Subject: [PATCH 08/27] more cleanup --- .../demoapp/activity/WebRtcActivity.java | 147 ++++++++---------- .../StreamWebRtcConfigurationFragment.java | 6 +- .../kinesisvideo/signaling/model/Event.java | 3 + 3 files changed, 71 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java index 366a41b..21a04d5 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java @@ -20,7 +20,6 @@ import android.content.Intent; import android.graphics.BitmapFactory; import android.media.AudioManager; -import android.os.Build; import android.os.Bundle; import android.util.Base64; import android.util.DisplayMetrics; @@ -277,54 +276,52 @@ public void onException(final Exception e) { // Step 11. Create SignalingServiceWebSocketClient. // This is the actual client that is used to send messages over the signaling channel. // SignalingServiceWebSocketClient will attempt to open the connection in its constructor. - if (wsHost != null) { - try { - client = new SignalingServiceWebSocketClient(wsHost, signalingListener, Executors.newFixedThreadPool(10)); - - Log.d(TAG, "Client connection " + (client.isOpen() ? "Successful" : "Failed")); - } catch (final Exception e) { - Log.e(TAG, "Exception with websocket client: " + e); - gotException = true; - return; - } + try { + client = new SignalingServiceWebSocketClient(wsHost, signalingListener, Executors.newFixedThreadPool(10)); - if (isValidClient()) { - - Log.d(TAG, "Client connected to Signaling service " + client.isOpen()); - - if (master) { - - // If webrtc endpoint is non-null ==> Ingest media was checked - if (webrtcEndpoint != null) { - new Thread(() -> { - try { - final AWSKinesisVideoWebRTCStorageClient storageClient = - new AWSKinesisVideoWebRTCStorageClient( - KinesisVideoWebRtcDemoApp.getCredentialsProvider().getCredentials()); - storageClient.setRegion(Region.getRegion(mRegion)); - storageClient.setSignerRegionOverride(mRegion); - storageClient.setServiceNameIntern("kinesisvideo"); - storageClient.setEndpoint(webrtcEndpoint); - - Log.i(TAG, "Channel ARN is: " + mChannelArn); - storageClient.joinStorageSession(new JoinStorageSessionRequest() - .withChannelArn(mChannelArn)); - Log.i(TAG, "Join storage session request sent!"); - } catch (Exception ex) { - Log.e(TAG, "Error sending join storage session request!", ex); - } - }).start(); - } - } else { - Log.d(TAG, "Signaling service is connected: " + - "Sending offer as viewer to remote peer"); // Viewer + Log.d(TAG, "Client connection " + (client.isOpen() ? "Successful" : "Failed")); + } catch (final Exception e) { + Log.e(TAG, "Exception with websocket client: " + e); + gotException = true; + return; + } - createSdpOffer(); + if (isValidClient()) { + + Log.d(TAG, "Client connected to Signaling service " + client.isOpen()); + + if (master) { + + // If webrtc endpoint is non-null ==> Ingest media was checked + if (webrtcEndpoint != null) { + new Thread(() -> { + try { + final AWSKinesisVideoWebRTCStorageClient storageClient = + new AWSKinesisVideoWebRTCStorageClient( + KinesisVideoWebRtcDemoApp.getCredentialsProvider().getCredentials()); + storageClient.setRegion(Region.getRegion(mRegion)); + storageClient.setSignerRegionOverride(mRegion); + storageClient.setServiceNameIntern("kinesisvideo"); + storageClient.setEndpoint(webrtcEndpoint); + + Log.i(TAG, "Channel ARN is: " + mChannelArn); + storageClient.joinStorageSession(new JoinStorageSessionRequest() + .withChannelArn(mChannelArn)); + Log.i(TAG, "Join storage session request sent!"); + } catch (Exception ex) { + Log.e(TAG, "Error sending join storage session request!", ex); + } + }).start(); } } else { - Log.e(TAG, "Error in connecting to signaling service"); - gotException = true; + Log.d(TAG, "Signaling service is connected: " + + "Sending offer as viewer to remote peer"); // Viewer + + createSdpOffer(); } + } else { + Log.e(TAG, "Error in connecting to signaling service"); + gotException = true; } } @@ -504,17 +501,15 @@ protected void onCreate(final Bundle savedInstanceState) { if (mUrisList != null) { for (int i = 0; i < mUrisList.size(); i++) { final String turnServer = mUrisList.get(i).toString(); - if (turnServer != null) { - assert mUserNames != null; - assert mPasswords != null; - final IceServer iceServer = IceServer.builder(turnServer.replace("[", "").replace("]", "")) - .setUsername(mUserNames.get(i)) - .setPassword(mPasswords.get(i)) - .createIceServer(); - - Log.d(TAG, "IceServer details (TURN) = " + iceServer.toString()); - peerIceServers.add(iceServer); - } + assert mUserNames != null; + assert mPasswords != null; + final IceServer iceServer = IceServer.builder(turnServer.replace("[", "").replace("]", "")) + .setUsername(mUserNames.get(i)) + .setPassword(mPasswords.get(i)) + .createIceServer(); + + Log.d(TAG, "IceServer details (TURN) = " + iceServer.toString()); + peerIceServers.add(iceServer); } } @@ -783,11 +778,7 @@ public void onStateChange() { if (sendDataChannelButton != null) { runOnUiThread(() -> { - if (localDataChannel.state() == DataChannel.State.OPEN) { - sendDataChannelButton.setEnabled(true); - } else { - sendDataChannelButton.setEnabled(false); - } + sendDataChannelButton.setEnabled(localDataChannel.state() == DataChannel.State.OPEN); }); } } @@ -991,30 +982,26 @@ public boolean onTouch(View view, MotionEvent motionEvent) { } private void resizeRemoteView() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - final DisplayMetrics displayMetrics = new DisplayMetrics(); - final ViewGroup.LayoutParams lp = remoteView.getLayoutParams(); - getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); - lp.height = (int) (displayMetrics.heightPixels * 0.75); - lp.width = (int) (displayMetrics.widthPixels * 0.75); - remoteView.setLayoutParams(lp); - localView.bringToFront(); - } + final DisplayMetrics displayMetrics = new DisplayMetrics(); + final ViewGroup.LayoutParams lp = remoteView.getLayoutParams(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + lp.height = (int) (displayMetrics.heightPixels * 0.75); + lp.width = (int) (displayMetrics.widthPixels * 0.75); + remoteView.setLayoutParams(lp); + localView.bringToFront(); } private void createNotificationChannel() { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - final CharSequence name = getString(R.string.data_channel_notification); - final String description = getString(R.string.data_channel_notification_description); - final int importance = NotificationManager.IMPORTANCE_HIGH; - final NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance); - channel.setDescription(description); - // Register the channel with the system; you can't change the importance - // or other notification behaviors after this - final NotificationManager notificationManager = getSystemService(NotificationManager.class); - notificationManager.createNotificationChannel(channel); - } + final CharSequence name = getString(R.string.data_channel_notification); + final String description = getString(R.string.data_channel_notification_description); + final int importance = NotificationManager.IMPORTANCE_HIGH; + final NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance); + channel.setDescription(description); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + final NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); } } diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/fragment/StreamWebRtcConfigurationFragment.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/fragment/StreamWebRtcConfigurationFragment.java index 3b4f8e7..824e553 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/fragment/StreamWebRtcConfigurationFragment.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/fragment/StreamWebRtcConfigurationFragment.java @@ -1,7 +1,6 @@ package com.amazonaws.kinesisvideo.demoapp.fragment; import android.Manifest; -import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.os.AsyncTask; @@ -11,7 +10,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; @@ -19,7 +17,6 @@ import android.widget.EditText; import android.widget.ListView; import android.widget.Spinner; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -55,7 +52,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; public class StreamWebRtcConfigurationFragment extends Fragment { private static final String TAG = StreamWebRtcConfigurationFragment.class.getSimpleName(); @@ -263,7 +259,7 @@ private Bundle setExtras(boolean isMaster) { extras.putString(KEY_STREAM_ARN, mStreamArn); extras.putBoolean(KEY_IS_MASTER, isMaster); - if (mIceServerList.size() > 0) { + if (!mIceServerList.isEmpty()) { ArrayList userNames = new ArrayList<>(mIceServerList.size()); ArrayList passwords = new ArrayList<>(mIceServerList.size()); ArrayList ttls = new ArrayList<>(mIceServerList.size()); diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/model/Event.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/model/Event.java index 65eceac..260bde3 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/model/Event.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/model/Event.java @@ -3,6 +3,8 @@ import android.util.Base64; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.common.base.Charsets; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -143,6 +145,7 @@ public static String parseOfferEvent(Event offerEvent) { .orElse(""); } + @NonNull @Override public String toString() { return "Event(" + From 5a297b1a656b67fa1cd7e4b5c7d16ce9e8423ecb Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 08:58:48 -0700 Subject: [PATCH 09/27] replace with lambda --- .../demoapp/activity/StartUpActivity.java | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/StartUpActivity.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/StartUpActivity.java index cea9c0e..2bb86af 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/StartUpActivity.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/StartUpActivity.java @@ -30,36 +30,30 @@ protected void onCreate(Bundle savedInstanceState) { final AppCompatActivity thisActivity = this; supportFinishAfterTransition(); - AsyncTask.execute(new Runnable() { - @Override - public void run() { - if (auth.isSignedIn()) { - ActivityUtils.startActivity(thisActivity, SimpleNavActivity.class); - } else { - auth.showSignIn(thisActivity, - SignInUIOptions.builder() - .logo(R.mipmap.kinesisvideo_logo) - .backgroundColor(Color.WHITE) - .nextActivity(SimpleNavActivity.class) - .build(), - new Callback() { - @Override - public void onResult(UserStateDetails result) { - Log.d(TAG, "onResult: User signed-in " + result.getUserState()); - } + AsyncTask.execute(() -> { + if (auth.isSignedIn()) { + ActivityUtils.startActivity(thisActivity, SimpleNavActivity.class); + } else { + auth.showSignIn(thisActivity, + SignInUIOptions.builder() + .logo(R.mipmap.kinesisvideo_logo) + .backgroundColor(Color.WHITE) + .nextActivity(SimpleNavActivity.class) + .build(), + new Callback() { + @Override + public void onResult(UserStateDetails result) { + Log.d(TAG, "onResult: User signed-in " + result.getUserState()); + } - @Override - public void onError(final Exception e) { - runOnUiThread(new Runnable() { - @Override - public void run() { - Log.e(TAG, "onError: User sign-in error", e); - Toast.makeText(StartUpActivity.this, "User sign-in error: " + e.getMessage(), Toast.LENGTH_LONG).show(); - } - }); - } - }); - } + @Override + public void onError(final Exception e) { + runOnUiThread(() -> { + Log.e(TAG, "onError: User sign-in error", e); + Toast.makeText(StartUpActivity.this, "User sign-in error: " + e.getMessage(), Toast.LENGTH_LONG).show(); + }); + } + }); } }); } From acfa67777662c428c648a56985e3222702a6501f Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 09:12:05 -0700 Subject: [PATCH 10/27] update signalingListener --- .../signaling/SignalingListener.java | 16 +++++------ .../signaling/okhttp/WebSocketClient.java | 27 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java index faa0fe3..2586878 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java @@ -15,21 +15,21 @@ public abstract class SignalingListener implements Signaling { private final Gson gson = new Gson(); - private final WebSocketListener messageHandler = new WebSocketListener() { + private final WebSocketListener websocketListener = new WebSocketListener() { @Override - public void onMessage(@NonNull WebSocket webSocket, String text) { - if (text.isEmpty()) { + public void onMessage(@NonNull WebSocket webSocket, String message) { + if (message.isEmpty()) { return; } - Log.d(TAG, "Received message: " + text); + Log.d(TAG, "Received message: " + message); - if (!text.contains("messagePayload")) { + if (!message.contains("messagePayload")) { return; } - final Event evt = gson.fromJson(text, Event.class); + final Event evt = gson.fromJson(message, Event.class); if (evt == null || evt.getMessageType() == null || evt.getMessagePayload().isEmpty()) { return; @@ -59,7 +59,7 @@ public void onMessage(@NonNull WebSocket webSocket, String text) { } }; - public WebSocketListener getMessageHandler() { - return messageHandler; + public WebSocketListener getWebsocketListener() { + return websocketListener; } } diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java index 61c004e..b57442d 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java @@ -1,20 +1,19 @@ package com.amazonaws.kinesisvideo.signaling.okhttp; import static org.awaitility.Awaitility.await; - +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; import android.util.Log; +import androidx.annotation.NonNull; import com.amazonaws.kinesisvideo.signaling.SignalingListener; import com.amazonaws.kinesisvideo.utils.Constants; import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; - /** * An OkHttp based WebSocket client. */ @@ -37,28 +36,28 @@ class WebSocketClient { webSocket = client.newWebSocket(request, new WebSocketListener() { @Override - public void onOpen(WebSocket webSocket, Response response) { + public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { Log.d(TAG, "WebSocket connection opened"); isOpen = true; // Register message handler - signalingListener.getMessageHandler().onOpen(webSocket, response); + signalingListener.getWebsocketListener().onOpen(webSocket, response); } @Override - public void onMessage(WebSocket webSocket, String text) { + public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) { Log.d(TAG, "Received message: " + text); - signalingListener.getMessageHandler().onMessage(webSocket, text); + signalingListener.getWebsocketListener().onMessage(webSocket, text); } @Override - public void onClosed(WebSocket webSocket, int code, String reason) { + public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { Log.d(TAG, "WebSocket connection closed: " + reason); isOpen = false; - signalingListener.getMessageHandler().onClosed(webSocket, code, reason); + signalingListener.getWebsocketListener().onClosed(webSocket, code, reason); } @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { + public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) { Log.e(TAG, "WebSocket connection failed", t); isOpen = false; // Handle failure From 0f751215707e1d24e0cfcad6e3ea1dd74fada16d Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 09:39:32 -0700 Subject: [PATCH 11/27] update signalingListener and WebSocketClient --- .../signaling/SignalingListener.java | 6 +++--- .../signaling/okhttp/WebSocketClient.java | 17 ++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java index 2586878..53b39c6 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java @@ -39,20 +39,20 @@ public void onMessage(@NonNull WebSocket webSocket, String message) { case "SDP_OFFER": Log.d(TAG, "Offer received: SenderClientId=" + evt.getSenderClientId()); Log.d(TAG, new String(Base64.decode(evt.getMessagePayload(), 0))); - onSdpOffer(evt); break; + case "SDP_ANSWER": Log.d(TAG, "Answer received: SenderClientId=" + evt.getSenderClientId()); - onSdpAnswer(evt); break; + case "ICE_CANDIDATE": Log.d(TAG, "Ice Candidate received: SenderClientId=" + evt.getSenderClientId()); Log.d(TAG, new String(Base64.decode(evt.getMessagePayload(), 0))); - onIceCandidate(evt); break; + default: break; } diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java index b57442d..6845b1e 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java @@ -20,18 +20,17 @@ class WebSocketClient { private static final String TAG = "WebSocketClient"; - private final WebSocket webSocket; private volatile boolean isOpen = false; WebSocketClient(final String uri, final SignalingListener signalingListener) { - OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder(); - OkHttpClient client = clientBuilder.build(); + OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request.Builder() .url(uri) - .addHeader("User-Agent", Constants.APP_NAME + "/" + Constants.VERSION + " " + System.getProperty("http.agent")) + .addHeader("User-Agent", Constants.APP_NAME + "/" + Constants.VERSION + + " " + System.getProperty("http.agent")) .build(); webSocket = client.newWebSocket(request, new WebSocketListener() { @@ -39,28 +38,24 @@ class WebSocketClient { public void onOpen(@NonNull WebSocket webSocket, @NonNull Response response) { Log.d(TAG, "WebSocket connection opened"); isOpen = true; - // Register message handler - signalingListener.getWebsocketListener().onOpen(webSocket, response); } @Override - public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) { - Log.d(TAG, "Received message: " + text); - signalingListener.getWebsocketListener().onMessage(webSocket, text); + public void onMessage(@NonNull WebSocket webSocket, @NonNull String message) { + Log.d(TAG, "Websocket received a message: " + message); + signalingListener.getWebsocketListener().onMessage(webSocket, message); } @Override public void onClosed(@NonNull WebSocket webSocket, int code, @NonNull String reason) { Log.d(TAG, "WebSocket connection closed: " + reason); isOpen = false; - signalingListener.getWebsocketListener().onClosed(webSocket, code, reason); } @Override public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Response response) { Log.e(TAG, "WebSocket connection failed", t); isOpen = false; - // Handle failure signalingListener.onException((Exception) t); } }); From 32523d3bec86a067f601c23fef4d1a6ae51fb359 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 09:44:30 -0700 Subject: [PATCH 12/27] cleanup KinesisVideoWebRtcDemoApp --- .../kinesisvideo/demoapp/KinesisVideoWebRtcDemoApp.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/KinesisVideoWebRtcDemoApp.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/KinesisVideoWebRtcDemoApp.java index aef6e27..8225659 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/KinesisVideoWebRtcDemoApp.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/KinesisVideoWebRtcDemoApp.java @@ -4,11 +4,8 @@ import android.util.Log; import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.kinesisvideo.common.logging.LogLevel; -import com.amazonaws.kinesisvideo.common.logging.OutputChannel; import com.amazonaws.mobile.client.AWSMobileClient; import com.amazonaws.mobile.config.AWSConfiguration; -import com.amazonaws.mobileconnectors.kinesisvideo.util.AndroidLogOutputChannel; import org.json.JSONException; import org.json.JSONObject; @@ -17,9 +14,6 @@ public class KinesisVideoWebRtcDemoApp extends Application { private static final String TAG = KinesisVideoWebRtcDemoApp.class.getSimpleName(); public static AWSCredentialsProvider getCredentialsProvider() { - final OutputChannel outputChannel = new AndroidLogOutputChannel(); - final com.amazonaws.kinesisvideo.common.logging.Log log = - new com.amazonaws.kinesisvideo.common.logging.Log(outputChannel, LogLevel.VERBOSE, TAG); return AWSMobileClient.getInstance(); } From 820cbc87a6c482430dfc1bb5bf3624cf43e7f74d Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 16:27:55 -0700 Subject: [PATCH 13/27] try java 8,11,17 --- .github/workflows/ci.yml | 7 ++++--- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36bd976..148a330 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] + java: [8, 11, 17] steps: - name: Checkout code @@ -23,10 +24,10 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'adopt' - java-version: 17 + java-version: ${{ matrix.java }} - - name: Download libwebrtc-123.0.0.aar - run: wget -O app/libs/libwebrtc-123.0.0.aar https://github.com/rno/WebRTC/releases/download/123.0.0/libwebrtc-123.0.0.aar + - name: Download libwebrtc + run: wget -O app/libs/libwebrtc-115.0.0.aar https://github.com/rno/WebRTC/releases/download/115.0.0/libwebrtc-115.0.0.aar - name: Build with Gradle run: ./gradlew build diff --git a/build.gradle b/build.gradle index 421cf21..e145bbd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id "com.android.application" version '8.1.1' apply false - id "com.android.library" version '8.1.1' apply false + id "com.android.application" version '7.2.2' apply false + id "com.android.library" version '7.2.2' apply false } tasks.register('clean', Delete) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2b95caa..6d9c90e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Sep 07 11:17:34 PDT 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 50d316a2ac31116309e039d26734b5b59c8f87f3 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 16:29:47 -0700 Subject: [PATCH 14/27] try java 11,17 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 148a330..e1685d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - java: [8, 11, 17] + java: [11, 17] steps: - name: Checkout code From 2a88281fc12750143ef9f0974417a704eec01d5b Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 16:32:32 -0700 Subject: [PATCH 15/27] missing newline at the end --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ddef267..8146e27 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -91,4 +91,4 @@ tasks.register('checkForLibWebRTC') { } } -preBuild.dependsOn checkForLibWebRTC \ No newline at end of file +preBuild.dependsOn checkForLibWebRTC From f070e820d3eab173b5dd3773423e46bf600ecb2b Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 16:34:44 -0700 Subject: [PATCH 16/27] compileSdk 33 targetSdk 33 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8146e27..0da87e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,14 +4,14 @@ plugins { } android { - compileSdk 34 + compileSdk 33 defaultConfig { namespace("com.amazonaws.kinesisvideo.demoapp") applicationId "com.amazonaws.kinesisvideo.webrtc_sdk_android" minSdk 29 - targetSdk 34 + targetSdk 33 versionCode 1 versionName "1.0.0" From 5387c771f2cfd9b3b9cf11859829ed1c6b4790ac Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Tue, 9 Apr 2024 16:40:42 -0700 Subject: [PATCH 17/27] compileSdk 34 targetSdk 34 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0da87e1..8146e27 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,14 +4,14 @@ plugins { } android { - compileSdk 33 + compileSdk 34 defaultConfig { namespace("com.amazonaws.kinesisvideo.demoapp") applicationId "com.amazonaws.kinesisvideo.webrtc_sdk_android" minSdk 29 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0.0" From 9e6419a56b32b3e0e0e89f447e1840a38a434295 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Wed, 10 Apr 2024 08:19:12 -0700 Subject: [PATCH 18/27] add logs --- .../kinesisvideo/signaling/okhttp/WebSocketClient.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java index 6845b1e..c4b8dcd 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java @@ -66,11 +66,17 @@ public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Respon void send(String message) { Log.d(TAG, "Sending message: " + message); - webSocket.send(message); + if (!webSocket.send(message)) { + Log.d(TAG, "Could not send " + message + " as the connection may have " + + "closing, closed, or canceled."); + } } void disconnect() { - webSocket.close(1000, "Disconnect requested"); + if (!webSocket.close(1000, "Disconnect requested")) { + Log.d(TAG, "Websocket could not disconnect as a graceful shutdown was already " + + "underway or the web socket is already closed or canceled"); + } } boolean isOpen() { From d1e7e71ab714ef3f61ef4e72e6329ecb2a98b578 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Wed, 10 Apr 2024 08:20:43 -0700 Subject: [PATCH 19/27] single quotes in build.gradle --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index e145bbd..72ec5bb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id "com.android.application" version '7.2.2' apply false - id "com.android.library" version '7.2.2' apply false + id 'com.android.application' version '7.2.2' apply false + id 'com.android.library' version '7.2.2' apply false } tasks.register('clean', Delete) { From 91c9206d28d7b539afffdf0e167d72ec72d9d887 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Wed, 10 Apr 2024 08:34:36 -0700 Subject: [PATCH 20/27] notification permission --- .../kinesisvideo/demoapp/activity/WebRtcActivity.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java index 21a04d5..46fde57 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java @@ -13,11 +13,13 @@ import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_WEBRTC_ENDPOINT; import static com.amazonaws.kinesisvideo.demoapp.fragment.StreamWebRtcConfigurationFragment.KEY_WSS_ENDPOINT; +import android.Manifest; import android.annotation.SuppressLint; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.BitmapFactory; import android.media.AudioManager; import android.os.Bundle; @@ -35,6 +37,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; @@ -673,7 +676,6 @@ public void onStateChange() { Log.d(TAG, "Remote Data Channel onStateChange: state: " + dataChannel.state().toString()); } - @SuppressLint("MissingPermission") @Override public void onMessage(DataChannel.Buffer buffer) { runOnUiThread(() -> { @@ -696,7 +698,9 @@ public void onMessage(DataChannel.Buffer buffer) { final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext()); // notificationId is a unique int for each notification that you must define - notificationManager.notify(mNotificationId++, builder.build()); + if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { + notificationManager.notify(mNotificationId++, builder.build()); + } Toast.makeText(getApplicationContext(), "New message from peer, check notification.", Toast.LENGTH_SHORT).show(); }); From 2520e982cfe059843a005a258ecfc391e735465c Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Wed, 10 Apr 2024 09:11:10 -0700 Subject: [PATCH 21/27] fix gitignore --- .gitignore | 4 +++- app/.gitignore | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 app/.gitignore diff --git a/.gitignore b/.gitignore index 78bae11..ea23c2d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,11 @@ /.idea/navEditor.xml /.idea/assetWizardSettings.xml .DS_Store -/build /captures .externalNativeBuild .cxx local.properties app/libs/libwebrtc-* +app/src/main/res/raw/awsconfiguration.json +app/build + diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 79bd139..0000000 --- a/app/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -awsconfiguration.json \ No newline at end of file From 5ba4db00772263e0614dcbeae22a3e4e3f3a2b6a Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Wed, 10 Apr 2024 11:07:32 -0700 Subject: [PATCH 22/27] add null checks --- .../demoapp/activity/WebRtcActivity.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java index 46fde57..cffd0c3 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/demoapp/activity/WebRtcActivity.java @@ -347,9 +347,10 @@ private void handlePendingIceCandidates(final String clientId) { while (pendingIceCandidatesQueueByClientId != null && !pendingIceCandidatesQueueByClientId.isEmpty()) { final IceCandidate iceCandidate = pendingIceCandidatesQueueByClientId.peek(); final PeerConnection peer = peerConnectionFoundMap.get(clientId); - assert peer != null; - final boolean addIce = peer.addIceCandidate(iceCandidate); - Log.d(TAG, "Added ice candidate after SDP exchange " + iceCandidate + " " + (addIce ? "Successfully" : "Failed")); + if (peer != null) { + final boolean addIce = peer.addIceCandidate(iceCandidate); + Log.d(TAG, "Added ice candidate after SDP exchange " + iceCandidate + " " + (addIce ? "Successfully" : "Failed")); + } pendingIceCandidatesQueueByClientId.remove(); } // After sending pending ICE candidates, the client ID's peer connection need not be tracked @@ -374,9 +375,10 @@ private void checkAndAddIceCandidate(final Event message, final IceCandidate ice pendingIceCandidatesQueueByClientId = new LinkedList<>(); } - assert pendingIceCandidatesQueueByClientId != null; - pendingIceCandidatesQueueByClientId.add(iceCandidate); - pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId); + if (pendingIceCandidatesQueueByClientId != null) { + pendingIceCandidatesQueueByClientId.add(iceCandidate); + pendingIceCandidatesMap.put(message.getSenderClientId(), pendingIceCandidatesQueueByClientId); + } } // This is the case where peer connection is established and ICE candidates are received for the established @@ -385,10 +387,10 @@ private void checkAndAddIceCandidate(final Event message, final IceCandidate ice Log.d(TAG, "Peer connection found already"); // Remote sent us ICE candidates, add to local peer connection final PeerConnection peer = peerConnectionFoundMap.get(message.getSenderClientId()); - assert peer != null; - final boolean addIce = peer.addIceCandidate(iceCandidate); - - Log.d(TAG, "Added ice candidate " + iceCandidate + " " + (addIce ? "Successfully" : "Failed")); + if (peer != null){ + final boolean addIce = peer.addIceCandidate(iceCandidate); + Log.d(TAG, "Added ice candidate " + iceCandidate + " " + (addIce ? "Successfully" : "Failed")); + } } } @@ -504,15 +506,15 @@ protected void onCreate(final Bundle savedInstanceState) { if (mUrisList != null) { for (int i = 0; i < mUrisList.size(); i++) { final String turnServer = mUrisList.get(i).toString(); - assert mUserNames != null; - assert mPasswords != null; - final IceServer iceServer = IceServer.builder(turnServer.replace("[", "").replace("]", "")) - .setUsername(mUserNames.get(i)) - .setPassword(mPasswords.get(i)) - .createIceServer(); - - Log.d(TAG, "IceServer details (TURN) = " + iceServer.toString()); - peerIceServers.add(iceServer); + if (mUserNames != null && mPasswords != null) { + final IceServer iceServer = IceServer.builder(turnServer.replace("[", "").replace("]", "")) + .setUsername(mUserNames.get(i)) + .setPassword(mPasswords.get(i)) + .createIceServer(); + + Log.d(TAG, "IceServer details (TURN) = " + iceServer.toString()); + peerIceServers.add(iceServer); + } } } From dd992d5ea56b7a3349a571d53bb7b7f2942723a3 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Thu, 11 Apr 2024 13:33:14 -0700 Subject: [PATCH 23/27] readme testing note --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 95be9a4..a5bc286 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,16 @@ However, in the case that it needs to be disabled, locate the RTCConfiguration p PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(); rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY; ``` +## 7. Testing +This SDK has been tested with Java 11, 17 to build the Gradle dependencies and Java 8, 11, and, 17 in the compile options in build.gradle + +```agsl +compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +``` +For more details about the different Java versions, please check [Java versions in Android builds](https://developer.android.com/build/jdks#jdk-config-in-studio) ## License From beebfc2c4fa1cfef7ac88d7001102e4f93d70b9a Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Thu, 11 Apr 2024 13:56:03 -0700 Subject: [PATCH 24/27] add isOpen check --- .../signaling/okhttp/WebSocketClient.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java index c4b8dcd..89a586c 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java @@ -65,17 +65,27 @@ public void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, Respon } void send(String message) { - Log.d(TAG, "Sending message: " + message); - if (!webSocket.send(message)) { - Log.d(TAG, "Could not send " + message + " as the connection may have " + - "closing, closed, or canceled."); + if (isOpen) { + if (webSocket.send(message)) { + Log.d(TAG, "Successfully sent " + message); + } else { + Log.d(TAG, "Could not send " + message + " as the connection may have closing, closed, or canceled."); + } + } else { + Log.d(TAG, "Cannot send the websocket message as it is not open."); } } void disconnect() { - if (!webSocket.close(1000, "Disconnect requested")) { - Log.d(TAG, "Websocket could not disconnect as a graceful shutdown was already " + - "underway or the web socket is already closed or canceled"); + if (isOpen) { + if (!webSocket.close(1000, "Disconnect requested")) { + Log.d(TAG, "Websocket could not disconnect in a graceful shutdown. Going to cancel it to release resources."); + webSocket.cancel(); + } else { + Log.d(TAG, "Websocket successfully disconnected."); + } + } else { + Log.d(TAG, "Cannot close the websocket as it is not open."); } } From e0ed1b22483a94a95e387e8ae9939307762726d9 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Fri, 12 Apr 2024 09:43:33 -0700 Subject: [PATCH 25/27] sdk 33 --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8146e27..1c3c58f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,14 +4,14 @@ plugins { } android { - compileSdk 34 + compileSdk 33 defaultConfig { namespace("com.amazonaws.kinesisvideo.demoapp") applicationId "com.amazonaws.kinesisvideo.webrtc_sdk_android" minSdk 29 - targetSdk 34 + targetSdk 33 versionCode 1 versionName "1.0.0" @@ -50,7 +50,7 @@ dependencies { implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.11.0' + implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' From a44d095672aaba9a047fa66cb48b9461a93f218d Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 15 Apr 2024 13:46:56 -0700 Subject: [PATCH 26/27] address comments --- .github/workflows/ci.yml | 6 ++---- .../kinesisvideo/signaling/SignalingListener.java | 2 +- .../signaling/okhttp/WebSocketClient.java | 15 +++++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1685d7..b6e9100 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,13 +9,11 @@ on: - master jobs: build: - runs-on: ${{ matrix.os }} - strategy: matrix: os: [ubuntu-latest, macos-latest] java: [11, 17] - + runs-on: ${{ matrix.os }} steps: - name: Checkout code uses: actions/checkout@v4 @@ -23,7 +21,7 @@ jobs: - name: Set up JDK uses: actions/setup-java@v4 with: - distribution: 'adopt' + distribution: 'corretto' java-version: ${{ matrix.java }} - name: Download libwebrtc diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java index 53b39c6..3ed35f2 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/SignalingListener.java @@ -18,7 +18,7 @@ public abstract class SignalingListener implements Signaling { private final WebSocketListener websocketListener = new WebSocketListener() { @Override - public void onMessage(@NonNull WebSocket webSocket, String message) { + public void onMessage(@NonNull final WebSocket webSocket, @NonNull final String message) { if (message.isEmpty()) { return; } diff --git a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java index 89a586c..9bacc34 100644 --- a/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java +++ b/app/src/main/java/com/amazonaws/kinesisvideo/signaling/okhttp/WebSocketClient.java @@ -23,14 +23,17 @@ class WebSocketClient { private final WebSocket webSocket; private volatile boolean isOpen = false; - WebSocketClient(final String uri, final SignalingListener signalingListener) { + WebSocketClient(@NonNull final String uri, @NonNull final SignalingListener signalingListener) { OkHttpClient client = new OkHttpClient.Builder().build(); + String userAgent = (Constants.APP_NAME + "/" + Constants.VERSION + " " + System.getProperty("http.agent")).trim(); + + Log.d(TAG, "User agent: " + userAgent); + Request request = new Request.Builder() .url(uri) - .addHeader("User-Agent", Constants.APP_NAME + "/" + Constants.VERSION - + " " + System.getProperty("http.agent")) + .addHeader("User-Agent", userAgent) .build(); webSocket = client.newWebSocket(request, new WebSocketListener() { @@ -78,11 +81,11 @@ void send(String message) { void disconnect() { if (isOpen) { - if (!webSocket.close(1000, "Disconnect requested")) { + if (webSocket.close(1000, "Disconnect requested")) { + Log.d(TAG, "Websocket successfully disconnected."); + } else { Log.d(TAG, "Websocket could not disconnect in a graceful shutdown. Going to cancel it to release resources."); webSocket.cancel(); - } else { - Log.d(TAG, "Websocket successfully disconnected."); } } else { Log.d(TAG, "Cannot close the websocket as it is not open."); From 49efe7f048d1dadf57f60f04b370426bb2248ef3 Mon Sep 17 00:00:00 2001 From: niyatim23 Date: Mon, 15 Apr 2024 14:43:59 -0700 Subject: [PATCH 27/27] fix indent in readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a5bc286..eaa2a1e 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ This SDK has been tested with Java 11, 17 to build the Gradle dependencies and J ```agsl compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 +} ``` For more details about the different Java versions, please check [Java versions in Android builds](https://developer.android.com/build/jdks#jdk-config-in-studio)