Skip to content

Commit

Permalink
fix: fixed issues with user session on android platform
Browse files Browse the repository at this point in the history
  • Loading branch information
Desu Sai Venkat committed Nov 22, 2023
1 parent 1d81844 commit 937c931
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 51 deletions.
22 changes: 18 additions & 4 deletions packages/example/lib/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore_for_file: unused_element

import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:rudder_plugin_db_encryption/rudder_plugin_db_encryption.dart';
Expand Down Expand Up @@ -41,7 +43,11 @@ class HomeScreenState extends State<HomeScreen> {
RudderDBEncryption dbEncryption = RudderDBEncryption(true, "password");
MobileConfig mc = MobileConfig(
autoCollectAdvertId: false,
autoSessionTracking: true,
sessionTimeoutInMillis: 6000,
trackLifecycleEvents: false,
dbEncryption: dbEncryption,
recordScreenViews: true,
collectDeviceId: false);
RudderConfigBuilder builder = RudderConfigBuilder();
builder
Expand Down Expand Up @@ -169,12 +175,12 @@ class HomeScreenState extends State<HomeScreen> {
child: const Text('Track'),
),
ElevatedButton(
onPressed: __screen,
child: const Text('Screen'),
onPressed: __endSession,
child: const Text('End Session'),
),
ElevatedButton(
onPressed: __group,
child: const Text('Group'),
onPressed: __startSession,
child: const Text('Start Session'),
),
ElevatedButton(
onPressed: __reset,
Expand Down Expand Up @@ -203,4 +209,12 @@ class HomeScreenState extends State<HomeScreen> {
),
);
}

void __endSession() {
rudderClient.endSession();
}

void __startSession() {
rudderClient.startSession();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.rudderstack.sdk.flutter;

import java.util.ArrayList;
import java.util.List;

public class LifeCycleRunnables {

public static final List<RunnableLifeCycleEventsInterface> runnableLifeCycleEvents = new ArrayList<>();

interface RunnableLifeCycleEventsInterface {
void run();
}

public static class ApplicationOpenedRunnable implements RunnableLifeCycleEventsInterface {
boolean fromBackground;

public ApplicationOpenedRunnable(boolean fromBackground) {
this.fromBackground = fromBackground;
}

@Override
public void run() {
RudderSdkFlutterPlugin.getInstance().trackApplicationOpened(fromBackground);
}
}

public static class ApplicationBackgroundedRunnable implements RunnableLifeCycleEventsInterface {

@Override
public void run() {
RudderSdkFlutterPlugin.getInstance().trackApplicationBackgrounded();
}
}

public static class ScreenViewRunnable implements RunnableLifeCycleEventsInterface {
String activityName;

public ScreenViewRunnable(String activityName) {
this.activityName = activityName;
}

@Override
public void run() {
RudderSdkFlutterPlugin.getInstance().trackScreen(activityName);
}
}

public static void executeRunnableLifeCycleEvent(RunnableLifeCycleEventsInterface lifeCycleEvent) {
if (RudderSdkFlutterPlugin.getInstance() == null || !RudderSdkFlutterPlugin.isInitialized.get()) {
runnableLifeCycleEvents.add(lifeCycleEvent);
} else {
lifeCycleEvent.run();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import static com.rudderstack.sdk.flutter.parsers.RudderOptionsParser.getRudderOptionsObject;
import static com.rudderstack.sdk.flutter.parsers.RudderTraitsParser.getRudderTraitsObject;

import android.app.Activity;
import static com.rudderstack.sdk.flutter.LifeCycleRunnables.RunnableLifeCycleEventsInterface;

import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
Expand All @@ -18,7 +19,6 @@
import com.rudderstack.android.sdk.core.RudderOption;
import com.rudderstack.android.sdk.core.RudderProperty;
import com.rudderstack.android.sdk.core.RudderTraits;
import com.rudderstack.android.sdk.core.ScreenPropertyBuilder;
import com.rudderstack.sdk.flutter.managers.ActivityLifeCycleManager;
import com.rudderstack.sdk.flutter.managers.ApplicationLifeCycleManager;
import com.rudderstack.sdk.flutter.managers.PreferenceManager;
Expand All @@ -31,6 +31,7 @@
import io.flutter.plugin.common.MethodChannel.Result;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* RudderSdkFlutterPlugin
Expand All @@ -42,6 +43,8 @@ public class RudderSdkFlutterPlugin implements FlutterPlugin, MethodCallHandler
public static final String CATEGORY = "category";
private Context context;
private static RudderSdkFlutterPlugin instance;

public static AtomicBoolean isInitialized = new AtomicBoolean(false);
private boolean autoTrackLifeCycleEvents = true;
private boolean autoRecordScreenViews = false;

Expand All @@ -50,6 +53,7 @@ public class RudderSdkFlutterPlugin implements FlutterPlugin, MethodCallHandler


private UserSessionManager userSessionManager;
private PreferenceManager preferenceManager;
private static List<RudderIntegration.Factory> integrationList;
/// The MethodChannel that will the communication between Flutter and native Android
///
Expand Down Expand Up @@ -82,6 +86,8 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin
channel.setMethodCallHandler(this);
context = flutterPluginBinding.getApplicationContext();
ActivityLifeCycleManager.registerActivityLifeCycleCallBacks(context);
preferenceManager = PreferenceManager.getInstance(context);
preferenceManager.migrateAppInfoPreferencesFromNative();
}

@Override
Expand Down Expand Up @@ -136,9 +142,10 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
}

private void initializeSDK(MethodCall call) {
instance = this;
initializeNativeSDK(call);
initializeBridgeSDK(call);
instance = this;
isInitialized.set(true);
}

private void initializeNativeSDK(MethodCall call) {
Expand All @@ -161,7 +168,6 @@ private void initializeBridgeSDK(MethodCall call) {
sessionTimeoutInMilliSeconds = getLong(configMap.get("sessionTimeoutInMillis"));
autoSessionTracking = (Boolean) configMap.get("autoSessionTracking");

PreferenceManager preferenceManager = PreferenceManager.getInstance(context);
userSessionManager = new UserSessionManager(autoSessionTracking, autoTrackLifeCycleEvents, preferenceManager, sessionTimeoutInMilliSeconds);
userSessionManager.handleAutoSessionTracking();
initiateLifeCycleManagers();
Expand All @@ -170,8 +176,8 @@ private void initializeBridgeSDK(MethodCall call) {
private void initiateLifeCycleManagers() {
ApplicationLifeCycleManager applicationLifeCycleManager = new ApplicationLifeCycleManager((Application) context, userSessionManager, autoTrackLifeCycleEvents);
applicationLifeCycleManager.trackApplicationLifeCycleEvents();
for (Runnable runnableTask : ActivityLifeCycleManager.runnableTasks) {
runnableTask.run();
for (RunnableLifeCycleEventsInterface runnableLifeCycleEvent : LifeCycleRunnables.runnableLifeCycleEvents) {
runnableLifeCycleEvent.run();
}
}

Expand Down Expand Up @@ -330,6 +336,9 @@ private static void getRudderContext(@NonNull Result result) {

public void trackApplicationOpened(boolean fromBackground) {
if (autoTrackLifeCycleEvents) {
if (fromBackground) {
this.userSessionManager.startAutoSessionIfCurrentIsExpired();
}
RudderProperty property = new RudderProperty();
property.put("from_background", fromBackground);
RudderClient.getInstance().track("Application Opened", property);
Expand All @@ -344,11 +353,11 @@ public void trackApplicationBackgrounded() {
}
}

public void trackScreen(Activity activity) {
public void trackScreen(String screenName) {
if (autoRecordScreenViews) {
RudderProperty property = new RudderProperty();
property.put("automatic", true);
RudderClient.getInstance().screen(activity.getLocalClassName(), property);
RudderClient.getInstance().screen(screenName, property);
this.userSessionManager.updateLastEventTimestamp();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.rudderstack.sdk.flutter.managers;

import static com.rudderstack.sdk.flutter.LifeCycleRunnables.executeRunnableLifeCycleEvent;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
Expand All @@ -8,15 +10,13 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.rudderstack.sdk.flutter.RudderSdkFlutterPlugin;

import static com.rudderstack.sdk.flutter.LifeCycleRunnables.ApplicationOpenedRunnable;
import static com.rudderstack.sdk.flutter.LifeCycleRunnables.ApplicationBackgroundedRunnable;
import static com.rudderstack.sdk.flutter.LifeCycleRunnables.ScreenViewRunnable;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ActivityLifeCycleManager implements Application.ActivityLifecycleCallbacks {
public static final List<Runnable> runnableTasks = new ArrayList<>();
private AtomicInteger noOfActivities;
private boolean fromBackground = false;

Expand All @@ -39,11 +39,9 @@ public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle saved
@Override
public void onActivityStarted(@NonNull Activity activity) {
if (noOfActivities.incrementAndGet() == 1) {
Runnable runnableTask = () -> RudderSdkFlutterPlugin.getInstance().trackApplicationOpened(fromBackground);
executeRunnable(runnableTask);
executeRunnableLifeCycleEvent(new ApplicationOpenedRunnable(fromBackground));
}
Runnable runnableTask = () -> RudderSdkFlutterPlugin.getInstance().trackScreen(activity);
executeRunnable(runnableTask);
executeRunnableLifeCycleEvent(new ScreenViewRunnable(activity.getLocalClassName()));
}

@Override
Expand All @@ -62,8 +60,7 @@ public void onActivityPaused(@NonNull Activity activity) {
public void onActivityStopped(@NonNull Activity activity) {
fromBackground = true;
if (noOfActivities.decrementAndGet() == 0) {
Runnable runnableTask = () -> RudderSdkFlutterPlugin.getInstance().trackApplicationBackgrounded();
executeRunnable(runnableTask);
executeRunnableLifeCycleEvent(new ApplicationBackgroundedRunnable());
}
}

Expand All @@ -78,12 +75,4 @@ public void onActivityDestroyed(@NonNull Activity activity) {
// No action needed in this method
// This method is intentionally left empty as there is no specific task to perform when the activity is destroyed.
}

private static void executeRunnable(Runnable runnableTask) {
if (RudderSdkFlutterPlugin.getInstance() == null) {
runnableTasks.add(runnableTask);
} else {
runnableTask.run();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package com.rudderstack.sdk.flutter.managers;

import android.content.Context;
import android.content.SharedPreferences;

import com.rudderstack.android.sdk.core.util.Utils;

public class PreferenceManager {
private static PreferenceManager instance;
private static final String PREFS_NAME = "rl_prefs";
private static final String FLUTTER_PREFS_NAME = "rl_prefs_flutter";
private static final String NATIVE_PREFS_NAME = "rl_prefs";
private static final String PREFS_KEY_BUILD_NUMBER = "rl_application_build_key";
private static final String PREFS_KEY_VERSION_NAME = "rl_application_version_key";
private final android.content.SharedPreferences preferences;
private final SharedPreferences preferences;

private final Context context;

private PreferenceManager(android.content.SharedPreferences preferences) {
this.preferences = preferences;
private PreferenceManager(Context context) {
this.context = context;
this.preferences = this.context.getSharedPreferences(FLUTTER_PREFS_NAME, Context.MODE_PRIVATE);
}

public static PreferenceManager getInstance(android.content.Context context) {
public static PreferenceManager getInstance(Context context) {
if (instance == null) {
instance = new PreferenceManager(context.getSharedPreferences(PREFS_NAME, android.content.Context.MODE_PRIVATE));
instance = new PreferenceManager(context);
}
return instance;
}
Expand Down Expand Up @@ -44,19 +51,21 @@ public boolean doesAutoSessionExists() {
return preferences.getBoolean("rl_auto_session_exists", false);
}

public void saveManualSessionExists(boolean manualSessionExists) {
preferences.edit().putBoolean("rl_manual_session_exists", manualSessionExists).apply();
}

public boolean doesManualSessionExists() {
return preferences.getBoolean("rl_manual_session_active", false);
}

public void updateLastEventTimestamp() {
preferences.edit().putLong("rl_last_event_timestamp", Utils.getCurrentTimeInMilliSeconds()).apply();
}

public long getLastEventTimestamp() {
return preferences.getLong("rl_last_event_timestamp", -1);
}

public void migrateAppInfoPreferencesFromNative() {
SharedPreferences nativePrefs = this.context.getSharedPreferences(NATIVE_PREFS_NAME, Context.MODE_PRIVATE);
if (nativePrefs.contains(PREFS_KEY_BUILD_NUMBER)) {
saveBuildNumber(nativePrefs.getInt(PREFS_KEY_BUILD_NUMBER, -1));
}
if (nativePrefs.contains(PREFS_KEY_VERSION_NAME)) {
saveVersionName(nativePrefs.getString(PREFS_KEY_VERSION_NAME, null));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,28 @@ public void handleAutoSessionTracking() {
* This method starts the auto session:
* If there was an existing auto session from the previous run of the app, check if it is expired or not. If it is expired, start a new auto session, else continue with the existing auto session.
*/
private void startAutoSessionIfNeeded() {
if (this.preferenceManager.doesAutoSessionExists() && !isExistingAutoSessionExpired()) {
RudderLogger.logVerbose("UserSessionManager: startAutoSessionIfNeeded: Continuing with the existing auto session");
public void startAutoSessionIfNeeded() {
if (!this.preferenceManager.doesAutoSessionExists()) {
startAutoSession();
} else {
RudderLogger.logVerbose("UserSessionManager: startAutoSessionIfNeeded: Starting a new auto session");
startSession();
startAutoSessionIfCurrentIsExpired();
}
}

public void startAutoSessionIfCurrentIsExpired() {
if (this.preferenceManager.doesAutoSessionExists()) {
if (isExistingAutoSessionExpired()) {
RudderLogger.logVerbose("UserSessionManager: startAutoSessionIfCurrentIsExpired: Starting a new auto session as the existing auto session is expired");
startAutoSession();
} else {
RudderLogger.logVerbose("UserSessionManager: startAutoSessionIfCurrentIsExpired: Continuing with the existing auto session");
}
}
}

private void startAutoSession() {
RudderLogger.logVerbose("UserSessionManager: startAutoSession: Starting a new auto session");
startSession();
persistAutoSessionStatus();
}

Expand Down Expand Up @@ -94,7 +109,7 @@ public void updateLastEventTimestamp() {
void startSession() {
if (RudderClient.getInstance() != null) {
RudderClient.getInstance().startSession();
RudderLogger.logVerbose("UserSessionManager: starting new session");
RudderLogger.logVerbose("UserSessionManager: startSession: starting new session");
}
}

Expand Down

0 comments on commit 937c931

Please sign in to comment.