Skip to content

Commit

Permalink
❇️ Added video recording feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Archish27 committed Nov 30, 2023
1 parent b32115d commit ad452fa
Show file tree
Hide file tree
Showing 19 changed files with 2,086 additions and 154 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ implementation 'io.github.middleware-labs:android-sdk:1.0.1'

```java

class MyApplication extends Application {
class MiddlewareApplication extends Application {
private final String targetUrl = "<target-url>";
private final String rumAccessToken = "<your-access-token>";

Expand Down Expand Up @@ -243,4 +243,26 @@ logInstance.e("TAG", "I am error");
logInstance.i("TAG", "I am info");
logInstance.w("TAG", "I am warn");
```
### Enable Video Recording
Enable video recording in activity the following are the steps to enable.
Override the `onResume` & `onPause` methods to start a& stop video recording respectively.

NOTE: This feature is available above Android Nougat

```java
final MiddlewareRecorder recorder = Middleware.getInstance().getRecorder();

@RequiresApi(api = Build.VERSION_CODES.N)
@Override
protected void onResume() {
super.onResume();
recorder.startRecording(this);
}

@RequiresApi(api = Build.VERSION_CODES.N)
@Override
protected void onPause() {
super.onPause();
recorder.stopRecording();
}
```
17 changes: 17 additions & 0 deletions app/src/main/java/io/middleware/android/sample/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.MutableLiveData;

Expand All @@ -17,6 +19,7 @@
import java.util.concurrent.TimeUnit;

import io.middleware.android.sdk.Middleware;
import io.middleware.android.sdk.core.replay.MiddlewareRecorder;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.events.EventEmitter;
Expand All @@ -33,12 +36,26 @@
import okhttp3.Response;
import okhttp3.ResponseBody;

@RequiresApi(api = Build.VERSION_CODES.N)
public class MainActivity extends AppCompatActivity {

private Call.Factory okHttpClient;
private final MutableLiveData<String> httpResponse = new MutableLiveData<>();
private Middleware middleware;
int count = 0;
private MiddlewareRecorder recorder = Middleware.getInstance().getRecorder();

@Override
protected void onResume() {
super.onResume();
recorder.startRecording(this);
}

@Override
protected void onPause() {
super.onPause();
recorder.stopRecording();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.1.2' apply false
id 'com.android.application' version '8.1.2' apply false
id 'com.android.library' version '8.1.2' apply false
}
5 changes: 3 additions & 2 deletions sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

mavenPublishing {
coordinates("io.github.middleware-labs", "android-sdk", "1.0.1")
coordinates("io.github.middleware-labs", "android-sdk", "1.0.2")

pom {
name = "Middleware Android RUM SDK"
Expand Down Expand Up @@ -78,7 +78,8 @@ dependencies {
implementation 'io.opentelemetry:opentelemetry-extension-trace-propagators:1.32.0'
api "com.squareup.okhttp3:okhttp:4.12.0"
implementation "io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0"
implementation 'androidx.work:work-runtime:2.8.1'
implementation 'androidx.work:work-runtime:2.9.0'
implementation 'com.github.ChickenHook:RestrictionBypass:2.2'
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"

testImplementation('org.mockito:mockito-core:5.7.0')
Expand Down
26 changes: 20 additions & 6 deletions sdk/src/main/java/io/middleware/android/sdk/Middleware.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@

import android.app.Application;
import android.location.Location;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.webkit.WebView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
Expand All @@ -27,6 +29,8 @@
import io.middleware.android.sdk.core.RumInitializer;
import io.middleware.android.sdk.core.RumSetup;
import io.middleware.android.sdk.core.models.NativeRumSessionId;
import io.middleware.android.sdk.core.replay.MiddlewareRecorder;
import io.middleware.android.sdk.core.replay.ReplayRecording;
import io.middleware.android.sdk.extractors.RumResponseAttributesExtractor;
import io.middleware.android.sdk.interfaces.IMiddleware;
import io.middleware.android.sdk.utils.ServerTimingHeaderParser;
Expand All @@ -42,8 +46,6 @@
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import okhttp3.Call;
Expand Down Expand Up @@ -118,6 +120,18 @@ public static Middleware getInstance() {
return INSTANCE;
}

/**
* Returns the MiddlewareRecording enables recording functionality on activity.
* NOTE: This api is available above Android Nougat version.
*
* @return MiddlewareRecorder
*/
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public MiddlewareRecorder getRecorder() {
return new MiddlewareRecorder(this);
}

/**
* Wrap the provided {@link OkHttpClient} with OpenTelemetry and RUM instrumentation. Since
* {@link Call.Factory} is the primary useful interface implemented by the OkHttpClient, this
Expand Down Expand Up @@ -162,17 +176,17 @@ public String getRumSessionId() {
}

//NOTE: This method is not used as of now will be used in future purposes.
private void addRumEvent(String name, Attributes attributes) {
public void addRumEvent(ReplayRecording replayRecording, Attributes attributes) {
if (isInitialized()) {
INSTANCE.sendRumEvent(name, attributes);
INSTANCE.sendRumEvent(replayRecording, attributes);
} else {
Log.d(RUM_TRACER_NAME, "Unable to send rum event setup is not done properly.");
}
}

private void sendRumEvent(String name, Attributes attributes) {
private void sendRumEvent(ReplayRecording replayRecording, Attributes attributes) {
attributes.toBuilder().put("session.id", getRumSessionId());
rumInitializer.sendRumEvent(name, attributes);
rumInitializer.sendRumEvent(replayRecording, attributes);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.google.gson.Gson;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
Expand All @@ -27,7 +30,7 @@
import io.middleware.android.sdk.builders.MiddlewareBuilder;
import io.middleware.android.sdk.core.models.InitializationEvents;
import io.middleware.android.sdk.core.models.RumData;
import io.middleware.android.sdk.core.services.RumServiceManager;
import io.middleware.android.sdk.core.replay.ReplayRecording;
import io.middleware.android.sdk.interfaces.IRum;
import io.middleware.android.sdk.messages.Rum;
import io.middleware.android.sdk.messages.RumMessage;
Expand All @@ -41,6 +44,13 @@
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.resources.ResourceBuilder;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class RumInitializer implements IRum {
private final MiddlewareBuilder builder;
Expand Down Expand Up @@ -116,41 +126,55 @@ public Middleware initialize(Function<Application, CurrentNetworkProvider> curre
return new Middleware(openTelemetryRum, rumSetup, globalAttributesSpanAppender);
}

@SuppressLint("ObsoleteSdkInt")
public void sendRumEvent(String name, Attributes attributes) {
String timestamp;

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
LocalDateTime utcDateTime = LocalDateTime.now(ZoneId.of("UTC"));
timestamp = utcDateTime.toString();
} else {
@SuppressLint("SimpleDateFormat") SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar c = Calendar.getInstance();
timestamp = df.format(c.getTime());
}
RumMessage rumMessage = new RumMessage.Builder(name)
.setAttributes(attributes)
.timestamp(timestamp)
public void sendRumEvent(ReplayRecording replayRecording, Attributes attributes) {
RumMessage rumMessage = new RumMessage.Builder()
.events(replayRecording.getPayload())
.sessionId(attributes.get(AttributeKey.stringKey("session.id")))
.version(RumUtil.getVersion(Objects.requireNonNull(application)))
.os("Android")
.osVersion(Build.VERSION.RELEASE)
.platform(String.format("%s %s", Build.MANUFACTURER, Build.MODEL))
.build();

final Rum rum = new Rum();
rum.setEventData(new RumMessage[]{rumMessage});
final RumData rumData = new RumData();
rumData.setAccessToken(builder.rumAccessToken);
rumData.setEndpoint(builder.target + "/v1/metrics/rum");
rumData.setPayload(new Gson().toJson(rum));
rumData.setPayload(new Gson().toJson(rumMessage));
startRumService(rumData);
}

private void startRumService(RumData rumData) {
Log.d("Middleware", "Starting RUM Service to send rum data");
RumServiceManager.startWorker(application.getApplicationContext(), rumData);
postRum(rumData);
}

private void postRum(RumData rumData) {
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.Companion.create(rumData.getPayload(), MEDIA_TYPE_JSON);
Request request = new Request.Builder()
.url(rumData.getEndpoint())
.header("Origin", builder.projectName)
.header("MW_API_KEY", rumData.getAccessToken())
.header("Content-Type", "application/json")
.post(requestBody)
.build();
try {
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
e.printStackTrace();
}

@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
Log.d("RumService", "Video Recorded Successfully" + response.code());
}
assert response.body() != null;
response.body().close();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

private Resource createMiddlewareResource() {
Expand Down
Loading

0 comments on commit ad452fa

Please sign in to comment.