diff --git a/app/build.gradle b/app/build.gradle
index 39a737d..e533526 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -43,6 +43,8 @@ dependencies {
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'com.google.code.gson:gson:2.8.9'
+ implementation project(path: ':navi-alfred')
+ implementation "com.github.anrwatchdog:anrwatchdog:1.4.0"
testImplementation "junit:junit:4.13.2"
androidTestImplementation "androidx.test.ext:junit:1.1.4"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 863fa86..ed64060 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
+ android:theme="@style/Theme.AppCompat.Light">
-
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/alfred/demo/MainApplication.kt b/app/src/main/java/com/alfred/demo/MainApplication.kt
new file mode 100644
index 0000000..e569533
--- /dev/null
+++ b/app/src/main/java/com/alfred/demo/MainApplication.kt
@@ -0,0 +1,213 @@
+package com.alfred.demo
+
+import android.app.Activity
+import android.app.Application
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import androidx.appcompat.widget.AppCompatTextView
+import com.alfred.demo.activity.DemoActivity
+import com.alfred.demo.activity.MainActivity
+import com.navi.alfred.AlfredConfig
+import com.navi.alfred.AlfredManager
+import com.navi.alfred.utils.AlfredConstants
+import com.navi.alfred.utils.log
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import com.github.anrwatchdog.ANRWatchDog
+import com.navi.alfred.callbacks.NetworkCallback
+import com.navi.alfred.model.ErrorMessage
+import com.navi.alfred.network.AlfredRetrofitProvider
+import okhttp3.Request
+import okhttp3.Response
+
+class MainApplication : Application(), Application.ActivityLifecycleCallbacks, NetworkCallback {
+ private var appForegroundCounter: Int = 0
+
+ override fun onCreate() {
+ super.onCreate()
+ registerActivityLifecycleCallbacks(this)
+
+ val alfredConfig = AlfredConfig(
+ "Alfred Demo App",
+ "1",
+ "1.0.0",
+ "qa",
+ "oMv77fgpBg9NFGom0Psizbf7lbrdBVJz"
+ )
+ AlfredManager.init(alfredConfig, this)
+ getAlfredCruiseInfo()
+
+
+ // Dumping anr data to click stream
+ ANRWatchDog().setIgnoreDebugger(true).setReportMainThreadOnly().setANRListener {
+ if (it.cause?.stackTrace.isNullOrEmpty()) {
+ return@setANRListener
+ }
+ val className = it.cause?.stackTrace?.get(0)?.className.orEmpty()
+ val anrEventProperties = mutableMapOf(
+ "SCREEN_NAME" to ("Alfred Demo App"),
+ "METHOD_NAME" to it.cause?.stackTrace?.get(0)?.methodName.orEmpty(),
+ "LINE_NUMBER" to it.cause?.stackTrace?.get(0)?.lineNumber.toString(),
+ "APP_IN_FOREGROUND" to isAppInForeground().toString()
+ )
+ if (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getAnrEnableStatus()) {
+ anrEventProperties["STACK_TRACE"] = it.cause?.stackTrace?.get(0).toString()
+ val anrView =
+ LayoutInflater.from(applicationContext).inflate(R.layout.anr_screen, null)
+ AlfredManager.measureInflatedView(anrView)
+ AlfredManager.handleAnrEvent(anrEventProperties, anrView)
+ }
+ }.start()
+
+ // Crash Reporting to backend
+ val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
+ Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
+ if (exception.stackTrace.isNullOrEmpty()) {
+ defaultHandler?.uncaughtException(thread, exception)
+ return@setDefaultUncaughtExceptionHandler
+ }
+ try {
+ val crashEventProperties = mutableMapOf(
+ "SCREEN_NAME" to ("Alfred Demo App"),
+ "METHOD_NAME" to exception.stackTrace[0]?.methodName.orEmpty(),
+ "LINE_NUMBER" to exception.stackTrace[0]?.lineNumber.toString(),
+ "APP_IN_FOREGROUND" to isAppInForeground().toString()
+ )
+ if (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getCrashEnableStatus()) {
+ exception.stackTrace[0]?.let { stackTraceElement ->
+ crashEventProperties["STACK_TRACE"] = stackTraceElement.toString()
+ }
+ val crashView =
+ LayoutInflater.from(applicationContext).inflate(R.layout.crash_screen, null)
+ AlfredManager.measureInflatedView(crashView)
+ AlfredManager.handleCrashEvent(crashEventProperties, crashView.rootView)
+ }
+ } finally {
+ defaultHandler?.uncaughtException(thread, exception)
+ }
+ }
+ }
+
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
+ }
+
+ override fun onActivityStarted(activity: Activity) {
+ appForegroundCounter++
+ }
+
+ override fun onActivityResumed(activity: Activity) {
+ if (appForegroundCounter > 0) {
+ startAlfredRecording(activity)
+ }
+ }
+
+ override fun onActivityPaused(activity: Activity) {
+ }
+
+ override fun onActivityStopped(activity: Activity) {
+ appForegroundCounter--
+ if (appForegroundCounter == 0) {
+ if (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus()) {
+ val appBackgroundView =
+ LayoutInflater.from(this)
+ .inflate(R.layout.app_background_screen, null)
+ AlfredManager.measureInflatedView(appBackgroundView)
+ AlfredManager.stopRecording(appBackgroundView)
+ }
+ }
+ }
+
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
+ }
+
+ override fun onActivityDestroyed(activity: Activity) {
+ }
+
+ private fun startAlfredRecording(activity: Activity) {
+ if (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus()) {
+ var screenName: kotlin.String? = null
+ var moduleName: kotlin.String? = null
+ var thirdPartyScreenView: View? = null
+ when (activity) {
+ is MainActivity -> {
+ screenName = "MainActivity"
+ moduleName = "Demo App"
+ }
+
+ is DemoActivity -> {
+ screenName = "DemoActivity"
+ moduleName = "Demo App"
+ }
+
+ else -> {
+ screenName = AlfredConstants.THIRD_PARTY_SCREEN
+ moduleName = AlfredConstants.THIRD_PARTY_MODULE
+ thirdPartyScreenView = LayoutInflater.from(applicationContext)
+ .inflate(R.layout.third_party_screen, null)
+ AlfredManager.measureInflatedView(thirdPartyScreenView)
+ thirdPartyScreenView.findViewById(R.id.tv_third_party_name).text =
+ "${activity.localClassName}"
+ }
+ }
+ try {
+ AlfredManager.startRecording(
+ activity.applicationContext,
+ activity.window.decorView.rootView,
+ screenName,
+ moduleName,
+ thirdPartyScreenView,
+ onSessionIdGenerated = { sessionId ->
+ Log.d("Alfred", "Alfred Session ID: $sessionId")
+ }
+ )
+ } catch (e: Exception) {
+ e.log()
+ }
+ }
+ }
+
+ private fun isAppInForeground(): Boolean {
+ return appForegroundCounter >= 1
+ }
+
+ private fun getAlfredCruiseInfo() {
+ Log.d("Alfred", "Alfred - getAlfredCruiseInfo started")
+ CoroutineScope(Dispatchers.IO).launch {
+ try {
+ Log.d(
+ "Alfred",
+ "Alfred - getAlfredCruiseInfo started inside try thread = ${Thread.currentThread().name}"
+ )
+ AlfredManager.getCruiseConfig(cruiseApiSuccessful = { response ->
+ AlfredRetrofitProvider.setNetworkCallback(this@MainApplication)
+ Log.d(
+ "Alfred",
+ "Alfred - getAlfredCruiseInfo cruiseApiSuccessful h response = $response"
+ )
+ })
+ } catch (e: Exception) {
+ Log.d("Alfred", "Alfred - getAlfredCruiseInfo catch e = $e")
+ e.log()
+ }
+ }
+ }
+
+ override fun onNetworkRequest(request: okhttp3.Request?) {
+ Log.d("Alfred", "Alfred - onNetworkRequest request = $request")
+ }
+
+ override fun onNetworkSuccess(response: Response?) {
+ Log.d("Alfred", "Alfred - onNetworkSuccess response = $response")
+ }
+
+ override fun onNetworkFailure(
+ exception: Exception,
+ errorMessage: ErrorMessage,
+ request: Request
+ ) {
+ Log.d("Alfred", "Alfred - onNetworkFailure exception = $exception request = $request")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alfred/demo/activity/DemoActivity.kt b/app/src/main/java/com/alfred/demo/activity/DemoActivity.kt
new file mode 100644
index 0000000..1114b06
--- /dev/null
+++ b/app/src/main/java/com/alfred/demo/activity/DemoActivity.kt
@@ -0,0 +1,23 @@
+package com.alfred.demo.activity
+
+import android.os.Bundle
+import android.view.MotionEvent
+import androidx.appcompat.app.AppCompatActivity
+import com.alfred.demo.R
+import com.navi.alfred.AlfredManager
+
+class DemoActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_demo)
+ }
+
+ override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
+ AlfredManager.handleTouchEvent(
+ ev,
+ screenName = "Demo Activity",
+ moduleName = "Alfred Demo App",
+ )
+ return super.dispatchTouchEvent(ev)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/alfred/demo/activity/MainActivity.kt b/app/src/main/java/com/alfred/demo/activity/MainActivity.kt
new file mode 100644
index 0000000..8c12858
--- /dev/null
+++ b/app/src/main/java/com/alfred/demo/activity/MainActivity.kt
@@ -0,0 +1,32 @@
+package com.alfred.demo.activity
+
+import android.content.Intent
+import android.os.Bundle
+import android.view.MotionEvent
+import android.widget.Button
+import androidx.appcompat.app.AppCompatActivity
+import com.alfred.demo.R
+import com.navi.alfred.AlfredManager
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ val button = findViewById