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

feat: support lazy initialization of the SDK #74

Merged
merged 8 commits into from
Mar 18, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner, @NonNull Life
if (isNewSession) {
autoRecordEventClient.handleSessionStart();
autoRecordEventClient.setIsEntrances();
sessionClient.startSession();
recordScreenViewAfterSessionStart();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public class AutoRecordEventClient {
*/
private boolean isFirstTime = true;

/**
* whether the app has entered the background at least once.
*/
private boolean isAppEndCalled = false;

/**
* current screen is entrances.
*/
Expand Down Expand Up @@ -252,10 +257,12 @@ public void handleAppStart() {
preferences.putBoolean("isFirstOpen", false);
isFirstOpen = false;
}
final AnalyticsEvent event =
this.clickstreamContext.getAnalyticsClient().createEvent(Event.PresetEvent.APP_START);
event.addAttribute(Event.ReservedAttribute.IS_FIRST_TIME, isFirstTime);
this.clickstreamContext.getAnalyticsClient().recordEvent(event);
if (isFirstTime || isAppEndCalled) {
final AnalyticsEvent event =
this.clickstreamContext.getAnalyticsClient().createEvent(Event.PresetEvent.APP_START);
event.addAttribute(Event.ReservedAttribute.IS_FIRST_TIME, isFirstTime);
this.clickstreamContext.getAnalyticsClient().recordEvent(event);
}
isFirstTime = false;
}

Expand All @@ -275,6 +282,7 @@ public void handleAppEnd() {
final AnalyticsEvent event =
this.clickstreamContext.getAnalyticsClient().createEvent(Event.PresetEvent.APP_END);
this.clickstreamContext.getAnalyticsClient().recordEvent(event);
isAppEndCalled = true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,27 @@ public ClickstreamManager(@NonNull final ClickstreamConfiguration config) {
}
LOG.debug(String.format(Locale.US,
"Clickstream SDK(%s) initialization successfully completed", BuildConfig.VERSION_NAME));
this.autoRecordEventClient.handleAppStart();
handleSessionStart();
} catch (final RuntimeException runtimeException) {
LOG.error(String.format(Locale.US,
"Cannot initialize Clickstream SDK %s", runtimeException.getMessage()));
throw new AmazonClientException(runtimeException.getLocalizedMessage());
}
}

/**
* handle session start after SDK initialize.
*/
private void handleSessionStart() {
boolean isNewSession = sessionClient.initialSession();
if (isNewSession) {
autoRecordEventClient.handleSessionStart();
autoRecordEventClient.setIsEntrances();
sessionClient.startSession();
}
}

/**
* Enable track app exception.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public final class Session {
private Long pauseTime;
private final int sessionIndex;

private boolean isStarted;

/**
* CONSTRUCTOR - ACTUAL Used by SessionClient.
*
Expand Down Expand Up @@ -118,6 +120,22 @@ public static Session getInstance(final ClickstreamContext context, Session prev
}
}

/**
* function for get session whether is started.
*
* @return session whether is started.
*/
public boolean isStarted() {
return this.isStarted;
}

/**
* function for flag the session is started.
*/
public void start() {
this.isStarted = true;
}

/**
* Pauses the session object. Generates a stop time.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@ public SessionClient(@NonNull final ClickstreamContext clickstreamContext) {
public synchronized boolean initialSession() {
session = Session.getInstance(clickstreamContext, session);
this.clickstreamContext.getAnalyticsClient().setSession(session);
return session.isNewSession();
return session.isNewSession() && !session.isStarted();
}

/**
* method for start the session.
*/
public void startSession() {
session.start();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
import org.robolectric.RobolectricTestRunner;
import software.aws.solution.clickstream.client.AutoRecordEventClient;
import software.aws.solution.clickstream.client.ClickstreamManager;
import software.aws.solution.clickstream.client.ScreenRefererTool;
import software.aws.solution.clickstream.client.SessionClient;
import software.aws.solution.clickstream.util.ReflectUtil;

import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand All @@ -47,6 +49,7 @@ public final class ActivityLifecycleManagerUnitTest {
private Application.ActivityLifecycleCallbacks callbacks;
private Log log;
private LifecycleRegistry lifecycle;
private LifecycleOwner lifecycleOwner;
private ActivityLifecycleManager lifecycleManager;

/**
Expand All @@ -65,9 +68,10 @@ public void setup() throws Exception {
this.callbacks = lifecycleManager;
log = mock(Log.class);
ReflectUtil.modifyFiled(this.callbacks, "LOG", log);

lifecycle = new LifecycleRegistry(mock(LifecycleOwner.class));
lifecycleOwner = mock(LifecycleOwner.class);
lifecycle = new LifecycleRegistry(lifecycleOwner);
lifecycleManager.startLifecycleTracking(ApplicationProvider.getApplicationContext(), lifecycle);
ScreenRefererTool.clear();
}

/**
Expand Down Expand Up @@ -121,6 +125,7 @@ public void testContextIsInValid() {
*/
@Test
public void testOnAppForegrounded() {
assertNotNull(lifecycleOwner);
when(sessionClient.initialSession()).thenReturn(true);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
verify(autoRecordEventClient).updateStartEngageTimestamp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void setup() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
AWSClickstreamPluginConfiguration.Builder configurationBuilder = AWSClickstreamPluginConfiguration.builder();
configurationBuilder.withAppId("demo-app")
.withEndpoint("http://cs-se-serve-1qtj719j88vwn-1291141553.ap-southeast-1.elb.amazonaws.com/collect")
.withEndpoint("http://example.com/collect")
.withSendEventsInterval(10000);
AWSClickstreamPluginConfiguration clickstreamPluginConfiguration = configurationBuilder.build();
ClickstreamManager clickstreamManager =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public class AutoRecordEventClientTest {
private ClickstreamContext clickstreamContext;
private AutoRecordEventClient client;
private LifecycleRegistry lifecycle;
private LifecycleOwner lifecycleOwner;
private ClickstreamManager clickstreamManager;

/**
* prepare AutoRecordEventClient and context.
Expand All @@ -83,22 +85,91 @@ public void setup() {
dbUtil = new ClickstreamDBUtil(context);
AWSClickstreamPluginConfiguration.Builder configurationBuilder = AWSClickstreamPluginConfiguration.builder();
configurationBuilder.withAppId("demo-app")
.withEndpoint("http://cs-se-serve-1qtj719j88vwn-1291141553.ap-southeast-1.elb.amazonaws.com/collect")
.withEndpoint("http://example.com/collect")
.withSendEventsInterval(10000)
.withTrackScreenViewEvents(true)
.withTrackUserEngagementEvents(true);
AWSClickstreamPluginConfiguration clickstreamPluginConfiguration = configurationBuilder.build();
ClickstreamManager clickstreamManager =
ClickstreamManagerFactory.create(context, clickstreamPluginConfiguration);
clickstreamManager = ClickstreamManagerFactory.create(context, clickstreamPluginConfiguration);
client = clickstreamManager.getAutoRecordEventClient();
clickstreamContext = clickstreamManager.getClickstreamContext();
callbacks = new ActivityLifecycleManager(clickstreamManager);

ActivityLifecycleManager lifecycleManager = new ActivityLifecycleManager(clickstreamManager);
lifecycle = new LifecycleRegistry(mock(LifecycleOwner.class));
lifecycleOwner = mock(LifecycleOwner.class);
lifecycle = new LifecycleRegistry(lifecycleOwner);
lifecycleManager.startLifecycleTracking(ApplicationProvider.getApplicationContext(), lifecycle);
}

/**
* test events after SDK initialization.
*
* @throws Exception exception.
*/
@Test
public void testSDKInitializationEvents() throws Exception {
try (Cursor cursor = dbUtil.queryAllEvents()) {
List<String> eventList = new ArrayList<>();
while (cursor.moveToNext()) {
String eventString = cursor.getString(2);
JSONObject jsonObject = new JSONObject(eventString);
String eventName = jsonObject.getString("event_type");
eventList.add(eventName);
}
assertEquals(3, eventList.size());
assertEquals(Event.PresetEvent.FIRST_OPEN, eventList.get(0));
assertEquals(Event.PresetEvent.APP_START, eventList.get(1));
assertEquals(Event.PresetEvent.SESSION_START, eventList.get(2));
}
}

/**
* test SDK initialize with stored session.
*
* @throws Exception exception.
*/
@Test
public void testSDKInitializeWithStoredSession() throws Exception {
ReflectUtil.invokeMethod(clickstreamManager, "handleSessionStart");
try (Cursor cursor = dbUtil.queryAllEvents()) {
List<String> eventList = new ArrayList<>();
while (cursor.moveToNext()) {
String eventString = cursor.getString(2);
JSONObject jsonObject = new JSONObject(eventString);
String eventName = jsonObject.getString("event_type");
eventList.add(eventName);
}
assertEquals(3, eventList.size());
assertEquals(Event.PresetEvent.FIRST_OPEN, eventList.get(0));
assertEquals(Event.PresetEvent.APP_START, eventList.get(1));
assertEquals(Event.PresetEvent.SESSION_START, eventList.get(2));
}
}

/**
* test SDK lazy initialization.
*
* @throws Exception exception.
*/
@Test
public void testLazyInitialization() throws Exception {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
try (Cursor cursor = dbUtil.queryAllEvents()) {
List<String> eventList = new ArrayList<>();
while (cursor.moveToNext()) {
String eventString = cursor.getString(2);
JSONObject jsonObject = new JSONObject(eventString);
String eventName = jsonObject.getString("event_type");
eventList.add(eventName);
}
assertEquals(3, eventList.size());
assertEquals(Event.PresetEvent.FIRST_OPEN, eventList.get(0));
assertEquals(Event.PresetEvent.APP_START, eventList.get(1));
assertEquals(Event.PresetEvent.SESSION_START, eventList.get(2));
}
}


/**
* test record user engagement event after view screen more than 1 second.
*
Expand Down Expand Up @@ -147,6 +218,7 @@ public void testEventsHaveSameSessionId() throws Exception {
callbacks.onActivityCreated(activity, bundle);
callbacks.onActivityStarted(activity);
callbacks.onActivityResumed(activity);
assertNotNull(lifecycleOwner);
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
try (Cursor cursor = dbUtil.queryAllEvents()) {
List<String> eventList = new ArrayList<>();
Expand Down Expand Up @@ -864,7 +936,7 @@ public void testOSVersionForUpdate() throws Exception {
@Test
public void testHandleFirstOpen() throws Exception {
client.handleAppStart();
assertEquals(2, dbUtil.getTotalNumber());
assertEquals(3, dbUtil.getTotalNumber());
Cursor cursor = dbUtil.queryAllEvents();
cursor.moveToFirst();
String eventString = cursor.getString(2);
Expand All @@ -889,7 +961,7 @@ public void testHandleFirstOpenMultiTimes() throws Exception {
client.handleAppStart();
client.handleAppStart();
client.handleAppStart();
assertEquals(4, dbUtil.getTotalNumber());
assertEquals(3, dbUtil.getTotalNumber());
Cursor cursor = dbUtil.queryAllEvents();
cursor.moveToFirst();
String eventString = cursor.getString(2);
Expand All @@ -906,14 +978,14 @@ public void testHandleFirstOpenMultiTimes() throws Exception {
*/
@Test
public void testHandleAppStart() throws Exception {
client.handleAppStart();
Activity activity1 = mock(Activity.class);
Bundle bundle = mock(Bundle.class);
callbacks.onActivityCreated(activity1, bundle);
callbacks.onActivityStarted(activity1);
callbacks.onActivityResumed(activity1);
client.handleAppEnd();
client.handleAppStart();
assertEquals(4, dbUtil.getTotalNumber());
assertEquals(6, dbUtil.getTotalNumber());
try (Cursor cursor = dbUtil.queryAllEvents()) {
List<JSONObject> eventList = new ArrayList<>();
while (cursor.moveToNext()) {
Expand All @@ -928,10 +1000,11 @@ public void testHandleAppStart() throws Exception {
assertFalse(appStart1.has(ReservedAttribute.SCREEN_NAME));
assertFalse(appStart1.has(Event.ReservedAttribute.SCREEN_ID));

assertEquals(Event.PresetEvent.SCREEN_VIEW, eventList.get(2).getString("event_type"));

assertEquals(Event.PresetEvent.APP_START, eventList.get(3).getString("event_type"));
JSONObject appStart2 = eventList.get(3).getJSONObject("attributes");
assertEquals(Event.PresetEvent.SESSION_START, eventList.get(2).getString("event_type"));
assertEquals(Event.PresetEvent.SCREEN_VIEW, eventList.get(3).getString("event_type"));
assertEquals(Event.PresetEvent.APP_END, eventList.get(4).getString("event_type"));
assertEquals(Event.PresetEvent.APP_START, eventList.get(5).getString("event_type"));
JSONObject appStart2 = eventList.get(5).getJSONObject("attributes");
assertFalse(appStart2.getBoolean(Event.ReservedAttribute.IS_FIRST_TIME));
assertTrue(appStart2.has(ReservedAttribute.SCREEN_NAME));
assertTrue(appStart2.has(ReservedAttribute.SCREEN_UNIQUE_ID));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public void setup() throws Exception {

AWSClickstreamPluginConfiguration.Builder configurationBuilder = AWSClickstreamPluginConfiguration.builder();
configurationBuilder.withAppId("demo-app")
.withEndpoint("http://cs-se-serve-1qtj719j88vwn-1291141553.ap-southeast-1.elb.amazonaws.com/collect")
.withEndpoint("http://example.com/collect")
.withSendEventsInterval(10000);
AWSClickstreamPluginConfiguration clickstreamPluginConfiguration = configurationBuilder.build();
ClickstreamManager clickstreamManager =
Expand All @@ -137,6 +137,8 @@ public void setup() throws Exception {
(EventRecorder) ReflectUtil.newInstance(EventRecorder.class, clickstreamContext, dbUtil, executorService);
log = mock(Log.class);
ReflectUtil.modifyFiled(eventRecorder, "LOG", log);
assertEquals(3, dbUtil.getTotalNumber());
dbUtil.deleteBatchEvents(3);
}

/**
Expand Down Expand Up @@ -214,7 +216,7 @@ public void testGetBatchOfEventsForOneEvent() throws Exception {
String[] result = getBatchOfEvents(cursor);
assertEquals(result.length, 2);
assertTrue(result[0].contains(event.getEventId()));
assertEquals("1", result[1]);
assertEquals("4", result[1]);
cursor.close();
}

Expand All @@ -241,7 +243,7 @@ public void testGetBatchOfEventsForOneEventReachedLimitSize() throws Exception {
assertEquals(result.length, 2);
assertNotNull(result[0]);
assertTrue(result[0].length() > 512 * 1024);
assertEquals("1", result[1]);
assertEquals("4", result[1]);
cursor.close();
}

Expand All @@ -259,7 +261,7 @@ public void testGetBatchOfEventsNotReachedDefaultLimitSize() throws Exception {
String[] result = getBatchOfEvents(cursor);
assertEquals(2, result.length);
assertEquals(new JSONArray(result[0]).length(), 20);
assertEquals("20", result[1]);
assertEquals("23", result[1]);
cursor.close();
}

Expand All @@ -277,7 +279,7 @@ public void testGetBatchOfEventsReachedLimitNumber() throws Exception {
String[] result = getBatchOfEvents(cursor);
assertEquals(2, result.length);
assertEquals(new JSONArray(result[0]).length(), 100);
assertEquals("100", result[1]);
assertEquals("103", result[1]);
cursor.close();
}

Expand All @@ -299,7 +301,7 @@ public void testGetBatchOfEventsReachedDefaultLimitSize() throws Exception {
assertEquals(2, result.length);
int length = new JSONArray(result[0]).length();
assertTrue(length < 30);
assertEquals(String.valueOf(length), result[1]);
assertEquals(length, Integer.parseInt(result[1]) - 3);
cursor.close();
}

Expand Down
Loading
Loading