Analytics
VLPlay SDK ships pre-wired with AppsFlyer as the canonical event pipeline plus Firebase Analytics for crash + session data. There is no first-party VLPlay event API — the SDK doesn't post to a /api/v1/analytics/* endpoint of its own. Instead, it fires ~125 named events into AppsFlyer's funnel (lifecycle, login, register, third-party login, payment, anti-addiction, retention) which you view in the AppsFlyer dashboard, and a smaller set into Firebase for crash + session-time reporting.
This means your "analytics integration" is mostly: ensure your appsflyer_dev_key is correct, ensure the CMS appsFlyerTracking master kill-switch is on, and let the SDK do the rest. For game-specific events (level-up, IAP funnel, custom screens), call hitActivityFirebase on Android (which logs to Firebase Analytics) or any AppsFlyer SDK method directly from your code.
The 125 funnel events fire automatically — you don't need to opt in to each one. Disabling all event tracking is a single CMS toggle (appsFlyerTracking: false), which short-circuits every AppsFlyerHelper.trackXxx call so no events leave the device. This is the legal kill-switch for compliance with regional privacy regs.
What's Tracked Automatically
The SDK fires events into AppsFlyer at these lifecycle points:
| Event group | Events | Trigger |
|---|---|---|
| Lifecycle | af_app_launch, sdk_start_initialization, sdk_initialized, sdk_first_initialized, show_login_scene_success | SDK init, first launch |
| Login funnel | af_open_login, login_start, af_click_login, login_submit, login_success, login_completed, login_success_final | Sign-in flow |
| Register funnel | register_start, register_input_account, register_submit, register_success, register_completed, af_complete_registration | New-account flow |
| Third-party login | third_login_clicked, third_login_invoked, third_login_callback_success/fail, third_login_completed | Facebook / Google / Apple sign-in |
| Auto-login | auto_login_start, auto_login_success, auto_login_fail | Refresh-token-based auto-login on app launch |
| Payment funnel | purchase_initiated, product_selected, sdk_payment_init, open_payment_screen, sdk_payment_callback_success/fail/cancel, sdk_payment_validated, sdk_payment_validated_success/fail, sdk_payment_completed, af_purchase | V3 IAP 2-step flow |
| Logout | logout_click, logout_success, logout_fail | Sign-out |
| Delete account | delete_account_entry_click, delete_account_confirm, delete_account_success/fail/cancel | Account deletion flow |
| Bind phone | bind_check_required, input_phone, request_sms_code, request_sms_code_success/fail, bind_check_submit, bind_success, bind_completed, bind_fail, skip_bind | Phone-OTP bind |
| Game events | enter_gameplay, character_creation_started/finished, play_time, level_up, vip_level_up, level_achieved, ~67 total | Long-tail game-state events |
| Retention | af_first_launch, check_retention, payment_success, content_view | Day-N return tracking |
The full list lives in AppsFlyerHelper.java (Android) and AppsFlyerHelper.m (iOS). Each method follows the convention trackXxx(params...) and is gated on the appsFlyerTracking CMS flag.
Triggering Custom Game Events
For game-specific events (level-up, custom screen views, achievements), use the AppsFlyer SDK or Firebase Analytics directly. The VLPlay SDK exposes a small wrapper for Firebase:
- iOS
- Android (Kotlin)
import VLPlaySDK
// Built-in helper for AppsFlyer game events
SDKManager.default()?.hitActivity(
.levelUp, // APICategoryType enum
extendData: "{\"newLevel\":42}",
forUser: "username",
userId: "65a1f3b...",
completion: { _, _, _ in }
)
// Or use AppsFlyer SDK directly for full flexibility
import AppsFlyerLib
AppsFlyerLib.shared().logEvent(name: "tutorial_complete", values: [
"stage": "intro",
"duration_seconds": 87,
])
Note: As of SDK 1.0.0, the iOS
hitActivity:*family is dead code —NetworkModal+Tracking.mhas the BE call commented out andVLPlayEventLogging.mGA tracking is disabled. The methods stay for ABI compatibility but don't emit events. Use AppsFlyerLib directly for custom events on iOS until the next SDK release.
import sdk.vlplay.vn.tracking.VLPlaySDKManager
// Log to Firebase Analytics via VLPlay's wrapper
val params = Bundle().apply {
putInt("level", 42)
putString("class", "warrior")
}
VLPlaySDKManager.hitActivityFirebase("level_up_42", params)
// Or AppsFlyer directly
import com.appsflyer.AppsFlyerLib
AppsFlyerLib.getInstance().logEvent(
context,
"tutorial_complete",
mapOf("stage" to "intro", "duration_seconds" to 87)
)
The AppsFlyer SDK is already initialized by VLPlay's bootstrap, so you can call AppsFlyerLib.shared() / getInstance() from anywhere after [SDKManager handleApplication:...] / VLPlaySDKManager.initApplication.
User Properties
Set persistent user properties to segment your analytics dashboard by player attributes. AppsFlyer automatically attaches the customer_user_id (= accountId) to every event after sign-in via the SDK's call to AppsFlyerLib.shared().customerUserID = id. Additional properties go through Firebase Analytics:
- iOS
- Android
import FirebaseAnalytics
Analytics.setUserProperty("gold", forName: "vip_tier")
Analytics.setUserProperty("42", forName: "player_level")
Analytics.setUserProperty("vi", forName: "preferred_language")
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
Firebase.analytics.setUserProperty("vip_tier", "gold")
Firebase.analytics.setUserProperty("player_level", "42")
Firebase.analytics.setUserProperty("preferred_language", "vi")
User properties have a 25-property cap (Firebase limit). Don't set high-cardinality values (e.g. last_login_timestamp) — use events for those instead.
Disabling Tracking
If a player opts out of analytics (GDPR, CCPA, or VN PDPA), or your CMS sets appsFlyerTracking: false, the SDK short-circuits every trackXxx call and the AppsFlyer SDK enters anonymized mode. To explicitly verify the kill-switch is active:
- iOS
- Android
let trackingOn = SDKManager.isFeatureEnabled(VLPlaySDKFeatureAppsFlyerTracking)
print("AppsFlyer tracking enabled:", trackingOn)
val trackingOn = VLPlaySDKManager.isFeatureEnabled(SdkConfig.FEATURE_APPSFLYER_TRACKING)
Log.d("VLPlay", "AppsFlyer tracking enabled: $trackingOn")
When the master kill-switch is OFF, the SDK still calls AppsFlyer's init (so the dev key is set), but no logEvent calls fire. AppsFlyer's automatic install attribution still works — it just doesn't see in-app events.
App Tracking Transparency (iOS)
iOS 14+ requires the App Tracking Transparency prompt to track players across apps + websites (IDFA access). The SDK exposes a helper:
import VLPlaySDK
func applicationDidBecomeActive(_ application: UIApplication) {
SDKManager.default()?.initATT()
}
initATT shows the system tracking prompt the first time it's called, then no-ops on subsequent calls. The Info.plist must declare NSUserTrackingUsageDescription (see iOS Installation).
If the player denies tracking, AppsFlyer continues to work but with reduced attribution accuracy (no IDFA). VLPlay funnel events still fire — only cross-app attribution is impacted.
Viewing the Data
Three dashboards expose VLPlay events:
- AppsFlyer Dashboard (app.appsflyer.com) — funnel + retention + LTV. Filter events with
event_nameto slice by funnel stage. Custom events fromlogEventshow up under the same dashboard. - Firebase Console → Analytics —
hitActivityFirebaseevents plus auto-collected events (screen views, session duration, crashes). Best for ad-hoc cohort analysis. - VLPlay CMS Reports — server-side aggregation of
Player,Purchase, andLoginHistorycollections. DAU / MAU / NRU / RRPU / LTV. Ask your VLPlay rep for access.
What's Not Tracked
VLPlay SDK does not track:
- Background location (no Always permission requested).
- Microphone / camera content (only avatar selection, with player consent).
- Contacts / calendar.
- Other apps installed on the device.
- Inputs other than the game's own taps (no key-logging).
The SDK collects the player's accountId, session start/end timestamps, IP address (one per session), device model + OS version, and IAP records. Full data inventory is in the VLPlay Privacy Policy linked in FAQ → Privacy & Compliance.
Performance
Events are logged synchronously into AppsFlyer's in-memory buffer and flushed in batches every 5 seconds (or sooner if 100+ events queue up). The SDK doesn't add measurable latency to the calling site — hitActivityFirebase and track* methods return in <1ms.
If your game is exceptionally chatty (1000+ events per minute), you can throttle by debouncing your own event firing. The SDK doesn't drop events, so every track call costs network on flush.