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