Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

❇️ Added video recording feature #24

Merged
merged 1 commit into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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