Version Catalog & Spotless (#92)

This commit is contained in:
Shivam Goyal
2024-01-04 17:55:52 +05:30
committed by GitHub
parent 5ad25a2cc1
commit 6cd51b3011
63 changed files with 1077 additions and 531 deletions

View File

@@ -1,6 +1,6 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
alias libs.plugins.android.application
alias libs.plugins.kotlin.android
}
android {
@@ -41,15 +41,20 @@ android {
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'androidx.core:core-ktx:1.8.0'
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.10.1'
implementation project(path: ':navi-alfred')
implementation "com.github.anrwatchdog:anrwatchdog:1.4.0"
implementation project(':navi-alfred')
testImplementation "junit:junit:4.13.2"
androidTestImplementation "androidx.test.ext:junit:1.1.4"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
implementation libs.android.material
implementation libs.androidx.appcompat
implementation libs.androidx.core.ktx
implementation libs.androidx.lifecycle.viewmodel.ktx
implementation libs.anrwatchdog
implementation libs.gson
androidTestImplementation libs.androidx.test.espresso.core
androidTestImplementation libs.androidx.test.junit
testImplementation libs.junit
}

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.alfred.demo
import android.content.BroadcastReceiver
@@ -10,7 +17,6 @@ import com.navi.alfred.utils.AlfredConstants.BROADCAST_ACTION_TYPE
import com.navi.alfred.utils.AlfredConstants.BROADCAST_ERROR_MESSAGE
import com.navi.alfred.utils.AlfredConstants.BROADCAST_EXCEPTION
import com.navi.alfred.utils.AlfredConstants.BROADCAST_REQUEST
import android.widget.Toast
import com.navi.alfred.utils.log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.alfred.demo
import android.app.Activity
@@ -5,18 +12,18 @@ import android.app.Application
import android.content.IntentFilter
import android.os.Bundle
import android.util.Log
import com.alfred.demo.activity.SWWActivity
import com.alfred.demo.activity.MainActivity
import com.alfred.demo.activity.SWWActivity
import com.github.anrwatchdog.ANRWatchDog
import com.navi.alfred.AlfredConfig
import com.navi.alfred.AlfredManager
import com.navi.alfred.AlfredManager.isAlfredRecordingEnabled
import com.navi.alfred.utils.AlfredConstants
import com.navi.alfred.utils.AlfredConstants.BROADCAST_ACTION_TYPE
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.AlfredManager.isAlfredRecordingEnabled
import com.navi.alfred.utils.AlfredConstants.BROADCAST_ACTION_TYPE
class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
private var appForegroundCounter: Int = 0
@@ -25,32 +32,37 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
super.onCreate()
registerActivityLifecycleCallbacks(this)
val alfredConfig = AlfredConfig(
"Alfred Demo App",
"1000",
"1.0.0",
"qa",
"oMv77fgpBg9NFGom0Psizbf7lbrdBVJz"
)
val alfredConfig =
AlfredConfig(
"Alfred Demo App",
"1000",
"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
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()
)
anrEventProperties["STACK_TRACE"] = it.cause?.stackTrace?.get(0).toString()
AlfredManager.handleAnrEvent(anrEventProperties)
}
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()
)
anrEventProperties["STACK_TRACE"] = it.cause?.stackTrace?.get(0).toString()
AlfredManager.handleAnrEvent(anrEventProperties)
}.start()
.start()
// Crash Reporting to backend
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
@@ -60,12 +72,13 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
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()
)
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()
)
exception.stackTrace[0]?.let { stackTraceElement ->
crashEventProperties["STACK_TRACE"] = stackTraceElement.toString()
}
@@ -76,8 +89,7 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
}
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {
appForegroundCounter++
@@ -89,8 +101,7 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
}
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {
appForegroundCounter--
@@ -99,11 +110,9 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
}
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {
}
override fun onActivityDestroyed(activity: Activity) {}
private fun startAlfredRecording(activity: Activity) {
if (isAlfredRecordingEnabled()) {
@@ -114,12 +123,10 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
screenName = "MainActivity"
moduleName = "Demo App"
}
is SWWActivity -> {
screenName = "SWWActivity"
moduleName = "Demo App"
}
else -> {
screenName = AlfredConstants.THIRD_PARTY_SCREEN
moduleName = AlfredConstants.THIRD_PARTY_MODULE
@@ -156,16 +163,18 @@ class MainApplication : Application(), Application.ActivityLifecycleCallbacks {
"Alfred",
"Alfred - getAlfredCruiseInfo started inside try thread = ${Thread.currentThread().name}"
)
AlfredManager.getAlfredCruiseInfo(cruiseApiSuccessful = { response ->
Log.d(
"Alfred",
"Alfred - getAlfredCruiseInfo cruiseApiSuccessful h response = $response"
)
})
AlfredManager.getAlfredCruiseInfo(
cruiseApiSuccessful = { response ->
Log.d(
"Alfred",
"Alfred - getAlfredCruiseInfo cruiseApiSuccessful h response = $response"
)
}
)
} catch (e: Exception) {
Log.d("Alfred", "Alfred - getAlfredCruiseInfo catch e = $e")
e.log()
}
}
}
}
}

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.alfred.demo.activity
import android.content.Intent
@@ -29,4 +36,4 @@ class MainActivity : AppCompatActivity() {
)
return super.dispatchTouchEvent(ev)
}
}
}

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.alfred.demo.activity
import android.os.Bundle
@@ -14,15 +21,7 @@ class SWWActivity : AppCompatActivity() {
val button = findViewById<Button>(R.id.sww_button)
button.setOnClickListener {
onSSWError(
"SWW Button Clicked",
"400",
"SWW Activity",
"Alfred Demo App",
400,
"WIFI"
)
onSSWError("SWW Button Clicked", "400", "SWW Activity", "Alfred Demo App", 400, "WIFI")
}
}
@@ -54,4 +53,4 @@ class SWWActivity : AppCompatActivity() {
)
AlfredManager.handleSWWEvent(swwEventProperties)
}
}
}

View File

@@ -1,3 +1,11 @@
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>

View File

@@ -1,3 +1,11 @@
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<resources>
<string name="app_name">Alfred Android</string>
<string name="crash">Crash Occurred</string>

View File

@@ -1,4 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~
~ * Copyright © 2024 by Navi Technologies Limited
~ * All rights reserved. Strictly confidential
~
-->
<resources>
<style name="Theme.AlfredAndroid" parent="android:Theme.Material.Light.NoActionBar" />

View File

@@ -1,5 +1,12 @@
plugins {
id 'com.android.application' version '8.2.0' apply false
id 'com.android.library' version '8.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.9.22' apply false
}
alias libs.plugins.android.application apply false
alias libs.plugins.android.library apply false
alias libs.plugins.firebase.crashlytics apply false
alias libs.plugins.kotlin.android apply false
alias libs.plugins.kotlin.parcelize apply false
alias libs.plugins.ksp apply false
alias libs.plugins.spotless
}
apply from: 'spotless.gradle'
apply from: 'projectDependencyGraph.gradle'

70
gradle/libs.versions.toml Normal file
View File

@@ -0,0 +1,70 @@
[versions]
android-material = "1.9.0"
androidGradlePlugin = "8.2.0"
androidx-appcompat = "1.6.1"
androidx-core-ktx = "1.8.0"
androidx-lifecycle = "2.6.1"
androidx-test-espresso = "3.5.0"
androidx-test-junit = "1.1.5"
androidx-workRuntimeKtx = "2.8.1"
anrwatchdog = "1.4.0"
chucker = "4.0.0"
firebase-bom = "32.7.0"
firebase-crashlytics = "2.9.9"
gson = "2.10.1"
jakewharton-timber = "5.0.1"
junit = "4.13.2"
kotlin = "1.9.20"
ksp = "1.9.20-1.0.14"
loggingInterceptor = "4.12.0"
retrofit = "2.9.0"
room = "2.5.2"
spotless = "6.23.3"
[libraries]
android-material = { module = "com.google.android.material:material", version.ref = "android-material" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "room" }
androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" }
androidx-test-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-junit" }
androidx-workRuntime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-workRuntimeKtx" }
anrwatchdog = { module = "com.github.anrwatchdog:anrwatchdog", version.ref = "anrwatchdog" }
chucker-library = { module = "com.github.chuckerteam.chucker:library", version.ref = "chucker" }
chucker-libraryNoOp = { module = "com.github.chuckerteam.chucker:library-no-op", version.ref = "chucker" }
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" }
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
jakewharton-timber = { module = "com.jakewharton.timber:timber", version.ref = "jakewharton-timber" }
junit = { module = "junit:junit", version.ref = "junit" }
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" }
retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
retrofit-retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }

View File

@@ -1,9 +1,9 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
alias libs.plugins.android.library
alias libs.plugins.kotlin.android
alias libs.plugins.kotlin.parcelize
alias libs.plugins.ksp
id 'maven-publish'
id 'kotlin-parcelize'
}
def VERSION = "1.0.20"
@@ -83,35 +83,36 @@ publishing {
}
dependencies {
implementation 'com.google.android.material:material:1.8.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation 'androidx.core:core-ktx:1.6.0'
implementation "androidx.appcompat:appcompat:1.5.1"
implementation "com.google.android.material:material:1.7.0"
// Timber for logging
implementation 'com.jakewharton.timber:timber:5.0.1'
// Gson
implementation 'com.google.code.gson:gson:2.10.1'
// Firebase SDK for Push Notification
api 'com.google.firebase:firebase-analytics-ktx'
// Firebase SDK for Google Analytics (Kotlin)
api 'com.google.firebase:firebase-crashlytics:18.3.6'
// Import the BoM for the Firebase platform
api platform('com.google.firebase:firebase-bom:30.1.0')
implementation "androidx.room:room-runtime:2.4.3"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:2.7.1"
api platform(libs.firebase.bom)
// To use Kotlin annotation processing tool (kapt)
kapt "androidx.room:room-compiler:2.4.3"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:2.4.3"
api "com.squareup.retrofit2:retrofit:2.9.0"
api 'com.squareup.retrofit2:converter-gson:2.9.0'
api 'com.squareup.okhttp3:logging-interceptor:4.12.0'
debugImplementation 'com.github.chuckerteam.chucker:library:3.5.2'
releaseImplementation 'com.github.chuckerteam.chucker:library-no-op:3.5.2'
testImplementation "androidx.room:room-testing:2.4.3"
}
implementation libs.android.material
implementation libs.androidx.appcompat
implementation libs.androidx.core.ktx
implementation libs.androidx.room.ktx
implementation libs.androidx.room.runtime
implementation libs.androidx.workRuntime.ktx
implementation libs.gson
implementation libs.jakewharton.timber
debugImplementation libs.chucker.library
releaseImplementation libs.chucker.libraryNoOp
api libs.firebase.analytics
api libs.firebase.crashlytics
api libs.logging.interceptor
api libs.retrofit.converter.gson
api libs.retrofit.retrofit
ksp libs.androidx.room.compiler
androidTestImplementation libs.androidx.test.espresso.core
androidTestImplementation libs.androidx.test.junit
testImplementation libs.androidx.room.testing
testImplementation libs.junit
}

View File

@@ -65,7 +65,13 @@ data class AlfredConfig(
internal var batteryPercentageBeforeEventStart: Float? = null,
internal var cruiseAttributes: CruiseAttributes = CruiseAttributes()
) {
constructor(appName: String, appVersionCode: String, appVersionName: String, flavor: String, apiKey: String) : this(
constructor(
appName: String,
appVersionCode: String,
appVersionName: String,
flavor: String,
apiKey: String
) : this(
appVersionCode = appVersionCode,
appVersionName = appVersionName,
appName = appName,
@@ -103,7 +109,9 @@ data class AlfredConfig(
fun getAlfredEventId(): String = this.alfredEventId
fun setAlfredEventId() {
this.alfredEventId = getAlfredSessionId().plus(UNDERSCORE) + UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
this.alfredEventId =
getAlfredSessionId().plus(UNDERSCORE) +
UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
}
fun setAlfredSessionId() {
@@ -157,7 +165,8 @@ data class AlfredConfig(
fun getNetworkType(): String = getNetworkType(AlfredManager.applicationContext)
fun getNetworkStrength(): Float = com.navi.alfred.utils.getNetworkStrength(AlfredManager.applicationContext)
fun getNetworkStrength(): Float =
com.navi.alfred.utils.getNetworkStrength(AlfredManager.applicationContext)
fun getBatteryPercentage(): Float =
com.navi.alfred.utils.getBatteryPercentage(AlfredManager.applicationContext)
@@ -207,9 +216,7 @@ data class AlfredConfig(
fun getStorageUsage(): Float {
val (totalSize, freeSize) =
com.navi.alfred.utils.getStorageUsage(
context = AlfredManager.applicationContext
)
com.navi.alfred.utils.getStorageUsage(context = AlfredManager.applicationContext)
return (totalSize - freeSize).toFloat()
}
@@ -277,7 +284,7 @@ data class AlfredConfig(
fun getCurrentTimeZone(): String? {
timeZone = TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT)
return this.timeZone;
return this.timeZone
}
fun setZipFileName(zipFileName: String) {
@@ -319,15 +326,17 @@ data class AlfredConfig(
fun getCruiseAttributes(): CruiseAttributes = this.cruiseAttributes
fun setCruiseAttributes() {
this.cruiseAttributes = CruiseAttributes(
networkType = getNetworkType(applicationContext),
networkStrength = getNetworkStrength(),
deviceAttributes = DeviceAttributes(
battery = getBatteryPercentage(),
storage = getStorageUsage(),
memory = getMemoryUsagePercentage()
this.cruiseAttributes =
CruiseAttributes(
networkType = getNetworkType(applicationContext),
networkStrength = getNetworkStrength(),
deviceAttributes =
DeviceAttributes(
battery = getBatteryPercentage(),
storage = getStorageUsage(),
memory = getMemoryUsagePercentage()
)
)
)
}
fun getAppName(): String = this.appName

View File

@@ -62,14 +62,14 @@ import com.navi.alfred.utils.startSyncEvents
import com.navi.alfred.worker.AddEventTask
import com.navi.alfred.worker.AddMetricTask
import com.navi.alfred.worker.AddNegativeCase
import java.lang.ref.WeakReference
import java.util.Timer
import java.util.TimerTask
import java.util.concurrent.Executors
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import okhttp3.Request
import okhttp3.Response
import java.util.Timer
import java.util.TimerTask
import java.util.concurrent.Executors
import java.lang.ref.WeakReference
object AlfredManager {
@@ -142,11 +142,12 @@ object AlfredManager {
config.setSessionStartRecordingTime()
config.setEventStartRecordingTime()
handleDeviceAttributes()
val startRecordingEvent = buildEvent(
AlfredConstants.START_RECORDING_EVENT,
screenName = screenName,
moduleName = moduleName
)
val startRecordingEvent =
buildEvent(
AlfredConstants.START_RECORDING_EVENT,
screenName = screenName,
moduleName = moduleName
)
AlfredDispatcher.addTaskToQueue(AddEventTask(startRecordingEvent, context))
}
currentActivity = WeakReference(activity)
@@ -172,9 +173,13 @@ object AlfredManager {
LayoutInflater.from(applicationContext)
.inflate(R.layout.third_party_screen, null)
measureInflatedView(thirdPartyScreenView)
thirdPartyScreenView.findViewById<AppCompatTextView>(R.id.tv_third_party_name).text = currentScreen.name
thirdPartyScreenView
.findViewById<AppCompatTextView>(R.id.tv_third_party_name)
.text = currentScreen.name
bmpForThirdPartySdkScreen =
thirdPartyScreenView?.let { captureScreenshotOfCustomView(it) }
thirdPartyScreenView?.let {
captureScreenshotOfCustomView(it)
}
}
insertScreenShotPathInDb(
this,
@@ -206,8 +211,9 @@ object AlfredManager {
bmpForCanvas = createBitmapForView(view)
}
try {
val bottomSheetView = reactBottomSheetView?.get()
?: dialog?.window?.decorView?.rootView
val bottomSheetView =
reactBottomSheetView?.get()
?: dialog?.window?.decorView?.rootView
if (bottomSheetView != null) {
captureBottomSheet(
view,
@@ -250,11 +256,12 @@ object AlfredManager {
LayoutInflater.from(applicationContext)
.inflate(R.layout.app_background_screen, null)
measureInflatedView(appBackgroundView)
val stopRecordingEvent = buildEvent(
AlfredConstants.STOP_RECORDING_EVENT,
screenName = currentScreen.name,
moduleName = currentModuleName
)
val stopRecordingEvent =
buildEvent(
AlfredConstants.STOP_RECORDING_EVENT,
screenName = currentScreen.name,
moduleName = currentModuleName
)
AlfredDispatcher.addTaskToQueue(AddEventTask(stopRecordingEvent, applicationContext))
if (ScreenShotStorageHelper.images.size > 0) {
startAnrCrashZipUpload(appBackgroundView)
@@ -265,18 +272,21 @@ object AlfredManager {
fun getAlfredCruiseInfo(cruiseApiSuccessful: (response: CruiseResponse) -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
try {
getCruiseConfig(cruiseApiSuccessful = { response ->
cruiseApiSuccessful(response)
alfredDataBase = AlfredDatabaseHelper.getAnalyticsDatabase(applicationContext)
analyticsDao = alfredDataBase.analyticsDao()
screenShotDao = alfredDataBase.screenShotDao()
zipDetailsDao = alfredDataBase.zipDetailsDao()
apiMetricDao = alfredDataBase.apiMetricDao()
negativeCaseDao = alfredDataBase.negativeCaseDao()
failureEventDao = alfredDataBase.failureEventDao()
sensitiveComposeRepository = ComposeMaskingRepoImpl()
startSyncEvents()
})
getCruiseConfig(
cruiseApiSuccessful = { response ->
cruiseApiSuccessful(response)
alfredDataBase =
AlfredDatabaseHelper.getAnalyticsDatabase(applicationContext)
analyticsDao = alfredDataBase.analyticsDao()
screenShotDao = alfredDataBase.screenShotDao()
zipDetailsDao = alfredDataBase.zipDetailsDao()
apiMetricDao = alfredDataBase.apiMetricDao()
negativeCaseDao = alfredDataBase.negativeCaseDao()
failureEventDao = alfredDataBase.failureEventDao()
sensitiveComposeRepository = ComposeMaskingRepoImpl()
startSyncEvents()
}
)
} catch (e: Exception) {
e.log()
}
@@ -299,8 +309,10 @@ object AlfredManager {
startTime: Long,
endTime: Long
) {
if (config.getAlfredStatus() && config.getMetricsApiEnableStatus() && config.getAlfredSessionId()
.isNotEmpty()
if (
config.getAlfredStatus() &&
config.getMetricsApiEnableStatus() &&
config.getAlfredSessionId().isNotEmpty()
) {
coroutineDispatcher.executor.execute {
val duration: Long = endTime - startTime
@@ -349,8 +361,10 @@ object AlfredManager {
if (currentTouchEvent?.action == ACTION_DOWN) {
val actionPointer = currentTouchEvent.actionIndex
previousTouchEvent.action = currentTouchEvent.action
previousTouchEvent.pointerPositionX = currentTouchEvent.getX(actionPointer)
previousTouchEvent.pointerPositionY = currentTouchEvent.getY(actionPointer)
previousTouchEvent.pointerPositionX =
currentTouchEvent.getX(actionPointer)
previousTouchEvent.pointerPositionY =
currentTouchEvent.getY(actionPointer)
previousTouchEvent.rawX = currentTouchEvent.getRawX()
previousTouchEvent.rawY = currentTouchEvent.getRawY()
}
@@ -374,10 +388,7 @@ object AlfredManager {
}
}
fun handleScreenTransitionEvent(
screenName: String?,
moduleName: String? = null
) {
fun handleScreenTransitionEvent(screenName: String?, moduleName: String? = null) {
if (isAlfredRecordingEnabled()) {
if (config.getAlfredSessionId().isNotEmpty()) {
coroutineDispatcher.executor.execute {
@@ -399,8 +410,7 @@ object AlfredManager {
fun handleAnrEvent(anrEventProperties: Map<String, String>) {
if (config.getAlfredStatus() && config.getAnrEnableStatus()) {
val anrView =
LayoutInflater.from(applicationContext).inflate(R.layout.anr_screen, null)
val anrView = LayoutInflater.from(applicationContext).inflate(R.layout.anr_screen, null)
measureInflatedView(anrView)
startAnrCrashZipUpload(anrView)
if (config.getAlfredSessionId().isNotEmpty()) {
@@ -412,12 +422,7 @@ object AlfredManager {
screenName = currentScreen.name,
moduleName = currentModuleName
)
AlfredDispatcher.addTaskToQueue(
AddNegativeCase(
event,
this.applicationContext
)
)
AlfredDispatcher.addTaskToQueue(AddNegativeCase(event, this.applicationContext))
}
}
}
@@ -469,15 +474,15 @@ object AlfredManager {
}
fun setUserId(userId: String?) {
config.setUserId(userId)
config.setUserId(userId)
}
fun setUserLocation(latitude: Double, longitude: Double) {
config.setLocation(latitude, longitude)
config.setLocation(latitude, longitude)
}
fun setDisableDialogScreenShot(status: Boolean) {
config.setDisableDialogScreenShot(status)
config.setDisableDialogScreenShot(status)
}
fun setPhoneNumber(phoneNumber: String?) {

View File

@@ -6,6 +6,7 @@
*/
package com.navi.alfred.db
import androidx.room.Database
import androidx.room.RoomDatabase
import com.navi.alfred.db.dao.AnalyticsDAO
@@ -46,5 +47,4 @@ abstract class AlfredDatabase : RoomDatabase() {
abstract fun negativeCaseDao(): NegativeCaseDao
abstract fun failureEventDao(): FailureEventDao
}

View File

@@ -102,4 +102,4 @@ interface FailureEventDao {
fun deleteFailureEvents(idList: List<Int>)
@Query("SELECT count(*) FROM FailureEvent") fun getFailureEventCount(): Int
}
}

View File

@@ -11,8 +11,8 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.navi.alfred.model.FailureRequest
import timber.log.Timber
import java.lang.reflect.Type
import timber.log.Timber
class FailureDataDeserializer : JsonDeserializer<FailureRequest> {
override fun deserialize(

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2021-2023 by Navi Technologies Limited
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/

View File

@@ -1,12 +1,14 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.model
data class CurrentScreen(
var name: String? = "",
var isComposeScreen: Boolean? = false
) {
data class CurrentScreen(var name: String? = "", var isComposeScreen: Boolean? = false) {
override fun toString(): String {
return "CurrentScreen(name='$name', isComposeScreen=$isComposeScreen)"
}
}

View File

@@ -9,14 +9,11 @@ package com.navi.alfred.model
import android.os.Parcelable
import com.navi.alfred.db.model.ScreenShotPathHelper
import kotlinx.parcelize.Parcelize
import java.util.*
import kotlinx.parcelize.Parcelize
@Parcelize
data class ErrorMessage(
var statusCode: Int? = null,
var message: String? = null
) : Parcelable
data class ErrorMessage(var statusCode: Int? = null, var message: String? = null) : Parcelable
data class WorkManagerFailureInputData(
var alfredSessionId: String? = "",

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.model
import com.google.gson.annotations.SerializedName
@@ -18,17 +25,22 @@ data class Failure(
@SerializedName("error_name") val errorName: String? = "",
@SerializedName("error_status_code") val errorStatusCode: Long? = 0,
@SerializedName("error_message") val errorMessage: String? = "",
@SerializedName("session_id") val sessionId: String? = AlfredManager.config.getAlfredSessionId(),
@SerializedName("network_strength") val networkStrength: Float? = AlfredManager.config.getNetworkStrength(),
@SerializedName("session_id")
val sessionId: String? = AlfredManager.config.getAlfredSessionId(),
@SerializedName("network_strength")
val networkStrength: Float? = AlfredManager.config.getNetworkStrength(),
@SerializedName("event_id_list") val eventIdList: List<String>? = null
)
data class FailureAttributes(
@SerializedName("client_name") val clientName: String? = AlfredManager.config.getAppName(),
@SerializedName("device_id") val deviceId: String? = AlfredManager.config.getDeviceId(),
@SerializedName("phone_number") val phoneNumber: String? = AlfredManager.config.getPhoneNumber(),
@SerializedName("phone_number")
val phoneNumber: String? = AlfredManager.config.getPhoneNumber(),
@SerializedName("customer_id") val customerId: String? = AlfredManager.config.getUserId(),
@SerializedName("app_version_code") val appVersionCode: String? = AlfredManager.config.getAppVersionCode(),
@SerializedName("app_version_name") val appVersionName: String? = AlfredManager.config.getAppVersionName(),
@SerializedName("app_version_code")
val appVersionCode: String? = AlfredManager.config.getAppVersionCode(),
@SerializedName("app_version_name")
val appVersionName: String? = AlfredManager.config.getAppVersionName(),
@SerializedName("session_id") val sessionId: String? = AlfredManager.config.getAlfredSessionId()
)

View File

@@ -1,11 +1,13 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.model
data class MaskingBounds(
val left: Float,
val top: Float,
val right: Float,
val bottom: Float
) {
data class MaskingBounds(val left: Float, val top: Float, val right: Float, val bottom: Float) {
val width: Float
get() {
return right - left
@@ -19,4 +21,4 @@ data class MaskingBounds(
override fun toString(): String {
return "MaskingBounds(left=$left, top=$top, right=$right, bottom=$bottom)"
}
}
}

View File

@@ -29,7 +29,8 @@ data class EventAttribute(
@SerializedName("module_name") val moduleName: String? = null,
@SerializedName("fragment_list") val fragmentList: List<String>? = null,
@SerializedName("zip_name") val zipName: String? = null,
@SerializedName("session_time_stamp") val sessionTimeStamp: Long? = AlfredManager.config.getSessionStartRecordingTime(),
@SerializedName("session_time_stamp")
val sessionTimeStamp: Long? = AlfredManager.config.getSessionStartRecordingTime(),
@SerializedName("screenshot_timestamp") val screenShotTimeStamp: Long? = null
)
@@ -62,18 +63,15 @@ data class BaseAttribute(
@SerializedName("parent_session_id") val parentSessionId: String? = null,
@SerializedName("event_timestamp")
val eventTimeStamp: Long? = AlfredManager.config.getEventTimeStamp(),
@SerializedName("session_id")
val sessionId: String? = null,
@SerializedName("session_time_stamp")
val sessionTimeStamp: Long? = null,
@SerializedName("event_end_time_stamp")
val latestScreenshotTimestamp: Long? = null,
@SerializedName("timezone")
val timezone: String? = AlfredManager.config.getCurrentTimeZone(),
@SerializedName("session_id") val sessionId: String? = null,
@SerializedName("session_time_stamp") val sessionTimeStamp: Long? = null,
@SerializedName("event_end_time_stamp") val latestScreenshotTimestamp: Long? = null,
@SerializedName("timezone") val timezone: String? = AlfredManager.config.getCurrentTimeZone(),
@SerializedName("metadata") val metaData: MetaData = MetaData(),
@SerializedName("snapshot_per_second") val snapshotPerSecond: Int? = AlfredManager.config.getSnapshotPerSecond(),
@SerializedName("snapshot_per_second")
val snapshotPerSecond: Int? = AlfredManager.config.getSnapshotPerSecond(),
@SerializedName("phone_number") val phoneNumber: String? = AlfredManager.config.getPhoneNumber()
)
)
data class SessionEventAttribute(
@SerializedName("event_id") val eventId: String? = null,
@@ -90,9 +88,12 @@ data class DeviceAttributes(
)
data class MetaData(
@SerializedName("phone_number") val phoneNumber: String? = AlfredManager.config.getPhoneNumber(),
@SerializedName("agent_email_id") val agentEmailId: String? = AlfredManager.config.getAgentEmailId(),
@SerializedName("code_push_version") val codePushVersion: String? = AlfredManager.config.getCodePushVersion()
@SerializedName("phone_number")
val phoneNumber: String? = AlfredManager.config.getPhoneNumber(),
@SerializedName("agent_email_id")
val agentEmailId: String? = AlfredManager.config.getAgentEmailId(),
@SerializedName("code_push_version")
val codePushVersion: String? = AlfredManager.config.getCodePushVersion()
)
data class CruiseAttributes(

View File

@@ -6,6 +6,7 @@
*/
package com.navi.alfred.network
import android.content.Context
import android.content.Intent
import android.util.Log
@@ -21,12 +22,12 @@ import com.navi.alfred.network.model.RequestInfo
import com.navi.alfred.utils.AlfredConstants
import com.navi.alfred.utils.handleException
import com.navi.alfred.utils.orZero
import java.util.concurrent.TimeUnit
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object AlfredFailureRetrofitProvider {
private const val FAILURE_BASE_URL_QA = "https://qa-sa.navi.com/"
@@ -99,14 +100,19 @@ object AlfredFailureRetrofitProvider {
.build()
}
private fun setNetworkFailureBroadcast(e: Exception, errorMessage: ErrorMessage, request: Request) {
private fun setNetworkFailureBroadcast(
e: Exception,
errorMessage: ErrorMessage,
request: Request
) {
Log.d("Alfred", "AlfredRetrofitProvider setNetworkFailureBroadcast - sending Broadcast")
val intent = Intent(AlfredConstants.BROADCAST_ACTION_TYPE)
val requestInfo = RequestInfo(
request.url.toString(),
request.headers[AlfredConstants.HEADER_X_TARGET],
request.headers[AlfredConstants.HEADER_APP_REQUEST_ID]
)
val requestInfo =
RequestInfo(
request.url.toString(),
request.headers[AlfredConstants.HEADER_X_TARGET],
request.headers[AlfredConstants.HEADER_APP_REQUEST_ID]
)
intent.putExtra(AlfredConstants.BROADCAST_EXCEPTION, e)
intent.putExtra(AlfredConstants.BROADCAST_ERROR_MESSAGE, errorMessage)
intent.putExtra(AlfredConstants.BROADCAST_REQUEST, requestInfo)

View File

@@ -7,7 +7,6 @@
package com.navi.alfred.network
import com.navi.alfred.model.FailureRequest
import com.navi.alfred.utils.AlfredConstants.CONTENT_TYPE
import com.navi.alfred.utils.AlfredConstants.X_API_KEY

View File

@@ -42,7 +42,11 @@ class AlfredNetworkRepository {
return AlfredRetrofitProvider.getApiService().getPreSignedUrl(sessionId, ALFRED, apiKey)
}
suspend fun sendSession(url: String, apiKey: String, sessionRequest: SessionRequest): Response<Unit> {
suspend fun sendSession(
url: String,
apiKey: String,
sessionRequest: SessionRequest
): Response<Unit> {
return AlfredRetrofitProvider.getApiService()
.sendSession(url, "application/json", apiKey, ALFRED, sessionRequest)
}
@@ -60,7 +64,11 @@ class AlfredNetworkRepository {
return AlfredRetrofitProvider.getApiService().uploadToS3(preSignedUrl, request, ALFRED)
}
suspend fun eventMetric(url: String, apiKey: String, metricRequestBody: EventMetricRequest): Response<Unit> {
suspend fun eventMetric(
url: String,
apiKey: String,
metricRequestBody: EventMetricRequest
): Response<Unit> {
return AlfredRetrofitProvider.getApiService()
.sendMetric(url, ALFRED, apiKey, "application/json", metricRequestBody)
}

View File

@@ -6,6 +6,7 @@
*/
package com.navi.alfred.network
import android.content.Context
import android.content.Intent
import android.util.Log
@@ -29,12 +30,12 @@ import com.navi.alfred.utils.AlfredConstants.HEADER_APP_REQUEST_ID
import com.navi.alfred.utils.AlfredConstants.HEADER_X_TARGET
import com.navi.alfred.utils.handleException
import com.navi.alfred.utils.orZero
import java.util.concurrent.TimeUnit
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object AlfredRetrofitProvider {
private const val BASE_URL_DEBUG = "https://dev-sa.navi.com/"
@@ -117,14 +118,19 @@ object AlfredRetrofitProvider {
return apiService
}
private fun setNetworkFailureBroadcast(e: Exception, errorMessage: ErrorMessage, request: Request) {
private fun setNetworkFailureBroadcast(
e: Exception,
errorMessage: ErrorMessage,
request: Request
) {
Log.d("Alfred", "AlfredRetrofitProvider setNetworkFailureBroadcast - sending Broadcast")
val intent = Intent(BROADCAST_ACTION_TYPE)
val requestInfo = RequestInfo(
request.url.toString(),
request.headers[HEADER_X_TARGET],
request.headers[HEADER_APP_REQUEST_ID]
)
val requestInfo =
RequestInfo(
request.url.toString(),
request.headers[HEADER_X_TARGET],
request.headers[HEADER_APP_REQUEST_ID]
)
intent.putExtra(BROADCAST_EXCEPTION, e)
intent.putExtra(BROADCAST_ERROR_MESSAGE, errorMessage)
intent.putExtra(BROADCAST_REQUEST, requestInfo)

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.network.model
import android.os.Parcelable

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.repository
import android.view.View
@@ -6,9 +13,16 @@ import com.navi.alfred.model.MaskingBounds
interface ComposeMaskingRepo {
fun maskSensitiveComposable(id: String, coordinates: MaskingBounds?, rootView: View?)
fun maskSensitiveUiTronComposable(id: String, left: Float?, top: Float?, right: Float?, bottom: Float?, rootView: View?)
fun maskSensitiveUiTronComposable(
id: String,
left: Float?,
top: Float?,
right: Float?,
bottom: Float?,
rootView: View?
)
fun removeSensitiveComposable(id: String)
fun blurSensitiveScreen(isVisible: Boolean)
}
}

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.repository
import android.view.View
@@ -9,11 +16,8 @@ class ComposeMaskingRepoImpl : ComposeMaskingRepo {
private val sensitiveCoordinates = mutableMapOf<String, MaskingBounds>()
private var rootView = WeakReference<View>(null)
private var blurSensitiveScreen = false
override fun maskSensitiveComposable(
id: String,
coordinates: MaskingBounds?,
rootView: View?
) {
override fun maskSensitiveComposable(id: String, coordinates: MaskingBounds?, rootView: View?) {
if (AlfredManager.isAlfredRecordingEnabled()) {
if (rootView != this.rootView.get()) {
removeAllSensitiveComposables()
@@ -69,4 +73,4 @@ class ComposeMaskingRepoImpl : ComposeMaskingRepo {
internal fun getBlurSensitiveScreenStatus(): Boolean {
return blurSensitiveScreen
}
}
}

View File

@@ -110,4 +110,4 @@ object AlfredConstants {
const val SCREEN_TRANSITION_EVENT = "SCREEN_TRANSITION_EVENT"
const val LATEST_SCREENSHOT_TIMESTAMP = "LATEST_SCREENSHOT_TIMESTAMP"
const val SNAPSHOT_PER_SECOND = "SNAPSHOT_PER_SECOND"
}
}

View File

@@ -11,7 +11,6 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.navi.alfred.AlfredManager
import timber.log.Timber
private fun Any.tag(): String {
return try {
this::class.java.simpleName.ifEmpty { "Empty" }

View File

@@ -1,10 +1,16 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
import com.navi.alfred.AlfredManager
import com.navi.alfred.model.Failure
import com.navi.alfred.model.FailureAttributes
import com.navi.alfred.model.FailureRequest
import com.navi.alfred.network.AlfredNetworkRepository
import com.navi.alfred.network.model.CruiseResponse
import com.navi.alfred.utils.AlfredConstants.DEFAULT_CRUISE_CONFIG_URL
@@ -29,23 +35,25 @@ internal suspend fun getCruiseConfig(cruiseApiSuccessful: (response: CruiseRespo
e.log()
}
} else {
val cruiseFailure = Failure(
errorType = AlfredConstants.API_ERROR,
requestUrl = DEFAULT_CRUISE_CONFIG_URL,
requestMethod = AlfredConstants.GET_METHOD,
errorTimestamp = System.currentTimeMillis(),
errorName = AlfredConstants.GET_CRUISE_CONFIG_FAILURE,
errorStatusCode = response.code().toLong(),
errorMessage = response.message()
)
val cruiseFailure =
Failure(
errorType = AlfredConstants.API_ERROR,
requestUrl = DEFAULT_CRUISE_CONFIG_URL,
requestMethod = AlfredConstants.GET_METHOD,
errorTimestamp = System.currentTimeMillis(),
errorName = AlfredConstants.GET_CRUISE_CONFIG_FAILURE,
errorStatusCode = response.code().toLong(),
errorMessage = response.message()
)
try {
AlfredManager.networkRepository.sendFailure(
AlfredConstants.DEFAULT_SEND_FAILURE_POST_URL,
AlfredManager.config.getApiKey(),
failureRequest = FailureRequest(
failureAttributes = FailureAttributes(),
events = listOf(cruiseFailure)
)
failureRequest =
FailureRequest(
failureAttributes = FailureAttributes(),
events = listOf(cruiseFailure)
)
)
} catch (e: Exception) {
e.log()
@@ -60,9 +68,7 @@ private fun setCruiseConfig(cruiseResponse: CruiseResponse) {
cruiseConfig.source.let { source ->
source.enable?.let { sourceConfig ->
AlfredManager.config.setAlfredStatus(
sourceConfig
)
AlfredManager.config.setAlfredStatus(sourceConfig)
}
source.recordingsConfig?.let { recordingConfig ->
recordingConfig.enable?.let { enable ->

View File

@@ -15,10 +15,10 @@ import android.os.Build
import android.os.StatFs
import android.telephony.TelephonyManager
import com.navi.alfred.AlfredManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.BufferedReader
import java.io.InputStreamReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
internal fun handleDeviceAttributes() {
AlfredManager.coroutineScope.launch(Dispatchers.IO) {
@@ -53,7 +53,6 @@ internal fun getNetworkType(context: Context): String {
TelephonyManager.NETWORK_TYPE_CDMA,
TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_IDEN -> "2G"
TelephonyManager.NETWORK_TYPE_UMTS,
TelephonyManager.NETWORK_TYPE_EVDO_0,
TelephonyManager.NETWORK_TYPE_EVDO_A,
@@ -63,7 +62,6 @@ internal fun getNetworkType(context: Context): String {
TelephonyManager.NETWORK_TYPE_EVDO_B,
TelephonyManager.NETWORK_TYPE_EHRPD,
TelephonyManager.NETWORK_TYPE_HSPAP -> "3G"
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5G"
else -> "Unknown" + AlfredConstants.UNDERSCORE + mTelephonyManager.networkType
@@ -79,13 +77,13 @@ fun getNetworkStrength(context: Context): Float {
try {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val netInfo = cm.activeNetworkInfo ?: return 0f
val nc = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.getNetworkCapabilities(cm.activeNetwork)
} else {
return 0f
}
val nc =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.getNetworkCapabilities(cm.activeNetwork)
} else {
return 0f
}
return nc?.linkUpstreamBandwidthKbps?.toFloat() ?: 0f
} catch (e: Exception) {
e.log()
}

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
import androidx.annotation.WorkerThread
@@ -35,26 +42,28 @@ import com.navi.alfred.worker.AddEventTask
import com.navi.alfred.worker.AddFailureTask
import com.navi.alfred.worker.AddMetricTask
import com.navi.alfred.worker.AddNegativeCase
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.withLock
import java.lang.reflect.Type
import java.util.UUID
import kotlin.concurrent.fixedRateTimer
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.withLock
internal suspend fun sendEventsToServer(
clientTs: Long? = null,
snapshotPerSecond: Int? = null,
workManagerFlow: Boolean? = false
): Boolean {
if (workManagerFlow == true || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
if (
workManagerFlow == true ||
(AlfredManager.config.getAlfredStatus() &&
AlfredManager.config.getEnableRecordingStatus())
) {
try {
var baseAttributes = BaseAttribute()
if (workManagerFlow == true) {
baseAttributes = BaseAttribute(
clientTs = clientTs,
snapshotPerSecond = snapshotPerSecond
)
baseAttributes =
BaseAttribute(clientTs = clientTs, snapshotPerSecond = snapshotPerSecond)
AlfredManager.analyticsDao = AlfredManager.alfredDataBase.analyticsDao()
}
AlfredManager.mutex.withLock {
@@ -68,18 +77,21 @@ internal suspend fun sendEventsToServer(
val events: ArrayList<EventAttribute> =
Gson().fromJson(detailsList.toString(), listType)
if (events.size > 0) {
val request = AnalyticsRequest(
baseAttribute = baseAttributes,
events = events
)
val response = AlfredManager.networkRepository.sendEvents(
AlfredManager.config.getPostUrl(),
AlfredManager.config.getApiKey(),
request
)
val request =
AnalyticsRequest(baseAttribute = baseAttributes, events = events)
val response =
AlfredManager.networkRepository.sendEvents(
AlfredManager.config.getPostUrl(),
AlfredManager.config.getApiKey(),
request
)
return if (
response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) {
AlfredManager.analyticsDao.deleteEvents(analyticsEvents.map { it.eventId })
response.isSuccessful &&
response.code() == AlfredConstants.CODE_API_SUCCESS
) {
AlfredManager.analyticsDao.deleteEvents(
analyticsEvents.map { it.eventId }
)
true
} else {
val zipNamesList: MutableList<String> = mutableListOf()
@@ -90,17 +102,18 @@ internal suspend fun sendEventsToServer(
}
eventIdList.add(event.eventId ?: "")
}
val failureEvent = buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_SEND_EVENT_POST_URL,
requestMethod = POST_METHOD,
zipName = zipNamesList,
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_EVENT_FAILURE,
clientTs = request.baseAttribute?.clientTs,
eventIdList = eventIdList
)
val failureEvent =
buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_SEND_EVENT_POST_URL,
requestMethod = POST_METHOD,
zipName = zipNamesList,
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_EVENT_FAILURE,
clientTs = request.baseAttribute?.clientTs,
eventIdList = eventIdList
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(
failureEvent,
@@ -108,7 +121,9 @@ internal suspend fun sendEventsToServer(
)
)
if (response.code() == AlfredConstants.CODE_API_BAD_REQUEST) {
AlfredManager.analyticsDao.deleteEvents(analyticsEvents.map { it.eventId })
AlfredManager.analyticsDao.deleteEvents(
analyticsEvents.map { it.eventId }
)
true
} else {
false
@@ -138,19 +153,23 @@ internal suspend fun sendNegativeCaseToServer(
snapshotPerSecond: Int? = null,
workManagerFlow: Boolean? = false
): Boolean {
if (workManagerFlow == true || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
if (
workManagerFlow == true ||
(AlfredManager.config.getAlfredStatus() &&
AlfredManager.config.getEnableRecordingStatus())
) {
try {
var baseAttributes = BaseAttribute()
if (workManagerFlow == true) {
baseAttributes = BaseAttribute(
clientTs = clientTs,
snapshotPerSecond = snapshotPerSecond
)
baseAttributes =
BaseAttribute(clientTs = clientTs, snapshotPerSecond = snapshotPerSecond)
AlfredManager.negativeCaseDao = AlfredManager.alfredDataBase.negativeCaseDao()
}
AlfredManager.mutex.withLock {
val negativeEvents =
AlfredManager.negativeCaseDao.fetchNegativeCase(AlfredManager.config.getEventBatchSize())
AlfredManager.negativeCaseDao.fetchNegativeCase(
AlfredManager.config.getEventBatchSize()
)
if (negativeEvents.isNotEmpty()) {
try {
val detailsList = negativeEvents.map { it.details }
@@ -160,29 +179,31 @@ internal suspend fun sendNegativeCaseToServer(
Gson().fromJson(detailsList.toString(), listType)
if (events.size > 0) {
val request =
AnalyticsRequest(
baseAttribute = baseAttributes,
events = events
AnalyticsRequest(baseAttribute = baseAttributes, events = events)
val response =
AlfredManager.networkRepository.sendNegativeCase(
DEFAULT_SEND_CRASH_URL,
AlfredManager.config.getApiKey(),
request
)
val response = AlfredManager.networkRepository.sendNegativeCase(
DEFAULT_SEND_CRASH_URL,
AlfredManager.config.getApiKey(),
request
)
return if (
response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS
response.isSuccessful &&
response.code() == AlfredConstants.CODE_API_SUCCESS
) {
AlfredManager.negativeCaseDao.deleteNegativeCase(negativeEvents.map { it.negativeCaseId })
AlfredManager.negativeCaseDao.deleteNegativeCase(
negativeEvents.map { it.negativeCaseId }
)
true
} else {
val failureEvent = buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_SEND_CRASH_URL,
requestMethod = POST_METHOD,
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_CRASH_FAILURE
)
val failureEvent =
buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_SEND_CRASH_URL,
requestMethod = POST_METHOD,
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_CRASH_FAILURE
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(
failureEvent,
@@ -190,7 +211,9 @@ internal suspend fun sendNegativeCaseToServer(
)
)
if (response.code() == AlfredConstants.CODE_API_BAD_REQUEST) {
AlfredManager.negativeCaseDao.deleteNegativeCase(negativeEvents.map { it.negativeCaseId })
AlfredManager.negativeCaseDao.deleteNegativeCase(
negativeEvents.map { it.negativeCaseId }
)
true
} else {
false
@@ -215,42 +238,50 @@ internal suspend fun sendNegativeCaseToServer(
return false
}
internal suspend fun sendFailureEventsToServer(
workManagerFlow: Boolean? = false
): Boolean {
if (workManagerFlow == true || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
internal suspend fun sendFailureEventsToServer(workManagerFlow: Boolean? = false): Boolean {
if (
workManagerFlow == true ||
(AlfredManager.config.getAlfredStatus() &&
AlfredManager.config.getEnableRecordingStatus())
) {
try {
if (workManagerFlow == true) {
AlfredManager.failureEventDao = AlfredManager.alfredDataBase.failureEventDao()
}
AlfredManager.mutex.withLock {
val failureEvents =
AlfredManager.failureEventDao.fetchFailureEvents(AlfredManager.config.getFailureEventBatchSize())
AlfredManager.failureEventDao.fetchFailureEvents(
AlfredManager.config.getFailureEventBatchSize()
)
if (failureEvents.isNotEmpty()) {
try {
val detailsList = failureEvents.map { it.details }
val listType: Type =
object : TypeToken<ArrayList<Failure?>?>() {}.type
val listType: Type = object : TypeToken<ArrayList<Failure?>?>() {}.type
val events: ArrayList<Failure> =
Gson().fromJson(detailsList.toString(), listType)
if (events.size > 0) {
val failureAttributes = FailureAttributes(sessionId = events[0].sessionId)
val failureAttributes =
FailureAttributes(sessionId = events[0].sessionId)
val request =
FailureRequest(
failureAttributes = failureAttributes,
events = events
)
val response = AlfredManager.networkRepository.sendFailure(
DEFAULT_SEND_FAILURE_POST_URL,
AlfredManager.config.getApiKey(),
request
)
val response =
AlfredManager.networkRepository.sendFailure(
DEFAULT_SEND_FAILURE_POST_URL,
AlfredManager.config.getApiKey(),
request
)
return if (
(response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) or
(response.code() == AlfredConstants.CODE_API_BAD_REQUEST)
(response.isSuccessful &&
response.code() == AlfredConstants.CODE_API_SUCCESS) or
(response.code() == AlfredConstants.CODE_API_BAD_REQUEST)
) {
AlfredManager.failureEventDao.deleteFailureEvents(failureEvents.map { it.eventId })
AlfredManager.failureEventDao.deleteFailureEvents(
failureEvents.map { it.eventId }
)
true
} else {
false
@@ -299,83 +330,86 @@ internal fun sendAlfredSessionEvent(
request =
SessionRequest(
base_attribute =
BaseAttribute(
sessionId = sessionId,
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
clientTs = clientTs,
sessionTimeStamp = sessionTimeStamp,
latestScreenshotTimestamp = latestScreenshotTimestamp
),
BaseAttribute(
sessionId = sessionId,
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
clientTs = clientTs,
sessionTimeStamp = sessionTimeStamp,
latestScreenshotTimestamp = latestScreenshotTimestamp
),
session_upload_event_attributes =
SessionEventAttribute(
eventId = currentZipName,
beginningDeviceAttributes = DeviceAttributes(),
endDeviceAttributes = DeviceAttributes()
)
SessionEventAttribute(
eventId = currentZipName,
beginningDeviceAttributes = DeviceAttributes(),
endDeviceAttributes = DeviceAttributes()
)
)
} else {
request =
SessionRequest(
base_attribute =
BaseAttribute(
sessionId = AlfredManager.config.getAlfredSessionId(),
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
clientTs = AlfredManager.config.getEventStartRecordingTime(),
latestScreenshotTimestamp = latestScreenshotTimestamp,
sessionTimeStamp = AlfredManager.config.getSessionStartRecordingTime()
),
session_upload_event_attributes =
SessionEventAttribute(
eventId = currentZipName,
beginningDeviceAttributes =
DeviceAttributes(
battery = AlfredManager.config.batteryPercentageBeforeEventStart,
cpu = AlfredManager.config.cpuUsageBeforeEventStart,
memory = AlfredManager.config.memoryUsageBeforeEventStart,
storage = AlfredManager.config.storageUsageBeforeEventStart
BaseAttribute(
sessionId = AlfredManager.config.getAlfredSessionId(),
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
clientTs = AlfredManager.config.getEventStartRecordingTime(),
latestScreenshotTimestamp = latestScreenshotTimestamp,
sessionTimeStamp = AlfredManager.config.getSessionStartRecordingTime()
),
endDeviceAttributes =
DeviceAttributes(
battery = AlfredManager.config.getBatteryPercentage(),
cpu = AlfredManager.config.getCpuUsage(),
memory = AlfredManager.config.getMemoryUsagePercentage(),
storage = AlfredManager.config.getStorageUsage()
session_upload_event_attributes =
SessionEventAttribute(
eventId = currentZipName,
beginningDeviceAttributes =
DeviceAttributes(
battery = AlfredManager.config.batteryPercentageBeforeEventStart,
cpu = AlfredManager.config.cpuUsageBeforeEventStart,
memory = AlfredManager.config.memoryUsageBeforeEventStart,
storage = AlfredManager.config.storageUsageBeforeEventStart
),
endDeviceAttributes =
DeviceAttributes(
battery = AlfredManager.config.getBatteryPercentage(),
cpu = AlfredManager.config.getCpuUsage(),
memory = AlfredManager.config.getMemoryUsagePercentage(),
storage = AlfredManager.config.getStorageUsage()
)
)
)
)
}
AlfredManager.coroutineScope.launch {
val response = AlfredManager.networkRepository.sendSession(
DEFAULT_SEND_SESSION_POST_URL,
AlfredManager.config.getApiKey(),
request
)
val response =
AlfredManager.networkRepository.sendSession(
DEFAULT_SEND_SESSION_POST_URL,
AlfredManager.config.getApiKey(),
request
)
if (response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) {
if (!dumpFlow) {
AlfredManager.config.setEventStartRecordingTime(true)
handleDeviceAttributes()
} else {
index?.let { index -> AlfredManager.zipDetailsDao.deleteZipFileDetail(AlfredManager.zipFileDetails[index].id) }
index?.let { index ->
AlfredManager.zipDetailsDao.deleteZipFileDetail(
AlfredManager.zipFileDetails[index].id
)
}
if (AlfredManager.workFailureData.size > 0) {
AlfredManager.workFailureData.removeAt(0)
}
}
} else {
val failureEvent = buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_SEND_SESSION_POST_URL,
requestMethod = POST_METHOD,
zipName = request.session_upload_event_attributes.eventId?.let { listOf(it) },
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_SESSION_FAILURE,
clientTs = request.base_attribute.clientTs
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(
failureEvent,
context = AlfredManager.applicationContext
val failureEvent =
buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_SEND_SESSION_POST_URL,
requestMethod = POST_METHOD,
zipName = request.session_upload_event_attributes.eventId?.let { listOf(it) },
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_SESSION_FAILURE,
clientTs = request.base_attribute.clientTs
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
)
}
}
@@ -387,19 +421,23 @@ internal suspend fun sendIngestMetric(
snapshotPerSecond: Int? = null,
workManagerFlow: Boolean? = false
): Boolean {
if ((workManagerFlow == true) || (AlfredManager.config.getAlfredStatus() && AlfredManager.config.getEnableRecordingStatus())) {
if (
(workManagerFlow == true) ||
(AlfredManager.config.getAlfredStatus() &&
AlfredManager.config.getEnableRecordingStatus())
) {
try {
var baseAttributes = BaseAttribute()
if (workManagerFlow == true) {
baseAttributes = BaseAttribute(
clientTs = clientTs,
snapshotPerSecond = snapshotPerSecond
)
baseAttributes =
BaseAttribute(clientTs = clientTs, snapshotPerSecond = snapshotPerSecond)
AlfredManager.apiMetricDao = AlfredManager.alfredDataBase.apiMetricDao()
}
AlfredManager.mutex.withLock {
val metricEventList =
AlfredManager.apiMetricDao.fetchApiMetric(AlfredManager.config.getEventBatchSize())
AlfredManager.apiMetricDao.fetchApiMetric(
AlfredManager.config.getEventBatchSize()
)
if (metricEventList.isNotEmpty()) {
try {
val detailsList = metricEventList.map { it.metric }
@@ -420,19 +458,23 @@ internal suspend fun sendIngestMetric(
metricRequestBody = request
)
return if (
response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS
response.isSuccessful &&
response.code() == AlfredConstants.CODE_API_SUCCESS
) {
AlfredManager.apiMetricDao.deleteApiMetric(metricEventList.map { it.id })
AlfredManager.apiMetricDao.deleteApiMetric(
metricEventList.map { it.id }
)
true
} else {
val failureEvent = buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_INGEST_METRIC_URL,
requestMethod = POST_METHOD,
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_METRIC_FAILURE
)
val failureEvent =
buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_INGEST_METRIC_URL,
requestMethod = POST_METHOD,
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = INGEST_METRIC_FAILURE
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(
failureEvent,
@@ -440,7 +482,9 @@ internal suspend fun sendIngestMetric(
)
)
if (response.code() == AlfredConstants.CODE_API_BAD_REQUEST) {
AlfredManager.apiMetricDao.deleteApiMetric(metricEventList.map { it.id })
AlfredManager.apiMetricDao.deleteApiMetric(
metricEventList.map { it.id }
)
true
} else {
false

View File

@@ -1,13 +1,12 @@
/*
*
* * Copyright © 2021-2023 by Navi Technologies Limited
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
fun Boolean?.orFalse() = this ?: false
fun Int?.orZero() = this ?: 0

View File

@@ -10,7 +10,11 @@ package com.navi.alfred.utils
import android.view.View
import android.view.ViewGroup
internal fun findViewWithTagRecursive(view: View, tag: String, maskedViews: MutableList<View>): List<View> {
internal fun findViewWithTagRecursive(
view: View,
tag: String,
maskedViews: MutableList<View>
): List<View> {
if (view.tag == tag) {
maskedViews.add(view)
}
@@ -22,4 +26,4 @@ internal fun findViewWithTagRecursive(view: View, tag: String, maskedViews: Muta
}
}
return maskedViews
}
}

View File

@@ -42,4 +42,4 @@ internal fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
return connectivityManager?.activeNetworkInfo?.isConnected.orFalse()
}
}

View File

@@ -1,5 +1,11 @@
package com.navi.alfred.utils
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
@@ -8,13 +14,13 @@ import com.navi.alfred.AlfredManager
internal fun isScreenDisabled(screenName: String? = null, moduleName: String? = null): Boolean {
if (
moduleName != null &&
AlfredManager.config.getDisableModuleList()?.contains(moduleName) == true
AlfredManager.config.getDisableModuleList()?.contains(moduleName) == true
) {
return true
} else {
if (
screenName != null &&
AlfredManager.config.getDisableScreenList()?.contains(screenName) == true
AlfredManager.config.getDisableScreenList()?.contains(screenName) == true
) {
return true
}

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
import android.content.Context
@@ -12,18 +19,18 @@ import com.navi.alfred.AlfredManager
import com.navi.alfred.db.AlfredDatabaseHelper
import com.navi.alfred.db.model.ScreenShotPathHelper
import com.navi.alfred.model.MaskingBounds
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
internal suspend fun handleScreenShot() {
if (
ScreenShotStorageHelper.images.size == AlfredManager.imageThreshHoldValue ||
ScreenShotStorageHelper.images.size == AlfredManager.imageSecondThreshHoldValue
ScreenShotStorageHelper.images.size == AlfredManager.imageSecondThreshHoldValue
) {
toZip(AlfredManager.screenShotDao.fetchScreenShotsPath(AlfredManager.imageThreshHoldValue))
} else {
@@ -43,8 +50,8 @@ internal fun combineScreenshots(
): Bitmap? {
if (
backgroundScreenshot == null ||
bottomSheetScreenshot == null ||
isScreenDisabled(screenName, moduleName)
bottomSheetScreenshot == null ||
isScreenDisabled(screenName, moduleName)
) {
return null
}
@@ -79,8 +86,7 @@ internal fun insertScreenShotPathInDb(
val fileDir = context.filesDir
val currentTime = System.currentTimeMillis()
AlfredManager.config.setLatestScreenshotTimestamp(currentTime)
val fileName =
currentTime.toString() + AlfredConstants.IMAGE_FILE_EXTENSION
val fileName = currentTime.toString() + AlfredConstants.IMAGE_FILE_EXTENSION
val path = fileDir.path.plus("/").plus(fileName)
val imageUrl = File(path)
val fos = FileOutputStream(imageUrl)
@@ -89,15 +95,12 @@ internal fun insertScreenShotPathInDb(
VideoQuality.HIGH.name -> {
10
}
VideoQuality.LOW.name -> {
6
}
VideoQuality.MEDIUM.name -> {
8
}
else -> {
10
}
@@ -139,7 +142,10 @@ internal fun captureScreenshotOfCustomView(view: View): Bitmap? {
internal fun deleteScreenShot() {
try {
val screenShotPathList: List<ScreenShotPathHelper> =
if (AlfredManager.screenShotDao.getScreenShotCount() >= AlfredManager.imageThreshHoldValue) {
if (
AlfredManager.screenShotDao.getScreenShotCount() >=
AlfredManager.imageThreshHoldValue
) {
AlfredManager.screenShotDao.fetchScreenShotsPath(AlfredManager.imageThreshHoldValue)
} else {
AlfredManager.screenShotDao.fetchAllScreenShotsPath()
@@ -179,7 +185,6 @@ enum class VideoQuality {
MEDIUM
}
internal suspend fun captureScreen(
v: View,
context: Context,
@@ -195,31 +200,38 @@ internal suspend fun captureScreen(
}
if (canvas != null && bmp != null) {
val currentScreen = AlfredManager.currentScreen
val sensitiveCoordinates = AlfredManager.sensitiveComposeRepository.getAllSensitiveComposableCoordinates()
val sensitiveCoordinates =
AlfredManager.sensitiveComposeRepository.getAllSensitiveComposableCoordinates()
val rootView = AlfredManager.sensitiveComposeRepository.getRootViewOfComposeScreen()
if (isMaskingEnabled(screenName) || (v.tag == AlfredConstants.SENSITIVE_VIEW_TAG) || (v.tag == AlfredConstants.SENSITIVE_COMPOSE_VIEW_TAG)) {
if (
isMaskingEnabled(screenName) ||
(v.tag == AlfredConstants.SENSITIVE_VIEW_TAG) ||
(v.tag == AlfredConstants.SENSITIVE_COMPOSE_VIEW_TAG)
) {
try {
if (currentScreen.isComposeScreen == true) {
if (AlfredManager.sensitiveComposeRepository.getBlurSensitiveScreenStatus()) {
withContext(Dispatchers.Main) {
blurScreen(canvas, v)
}
withContext(Dispatchers.Main) { blurScreen(canvas, v) }
} else {
if (rootView != null) {
withContext(Dispatchers.Main) {
captureComposeViewWithMasking(canvas, sensitiveCoordinates, rootView)
captureComposeViewWithMasking(
canvas,
sensitiveCoordinates,
rootView
)
}
} else {
withContext(Dispatchers.Main) {
v.draw(canvas)
}
withContext(Dispatchers.Main) { v.draw(canvas) }
}
}
} else {
val maskedViewsList = findViewWithTagRecursive(
v,
AlfredConstants.SENSITIVE_VIEW_TAG, mutableListOf()
)
val maskedViewsList =
findViewWithTagRecursive(
v,
AlfredConstants.SENSITIVE_VIEW_TAG,
mutableListOf()
)
withContext(Dispatchers.Main) {
try {
if (maskedViewsList.isEmpty()) {
@@ -322,17 +334,19 @@ internal fun captureComposeViewWithMasking(
paint.color = Color.BLACK
sensitiveCoordinates.forEach { bounds ->
if (bounds.bottom > 0 && bounds.top < rootView.height && bounds.height > 0 && bounds.width > 0) {
if (
bounds.bottom > 0 &&
bounds.top < rootView.height &&
bounds.height > 0 &&
bounds.width > 0
) {
canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, paint)
}
}
}
}
internal fun blurScreen(
canvas: Canvas,
rootView: View
) {
internal fun blurScreen(canvas: Canvas, rootView: View) {
rootView.draw(canvas)
val paint = Paint()
paint.color = Color.GRAY

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
import androidx.work.Constraints
@@ -19,11 +26,10 @@ import com.navi.alfred.utils.AlfredConstants.ZIP_ERROR
import com.navi.alfred.worker.AddFailureTask
import com.navi.alfred.worker.UploadEventsWorker
import com.navi.alfred.worker.UploadFileWorker
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
import java.util.UUID
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
internal suspend fun getPreSignedUrl(
zipFileName: String,
@@ -37,7 +43,9 @@ internal suspend fun getPreSignedUrl(
val latestScreenshotName: Long
if (dumpFlow) {
currentZipName = alfredEventIdForDumpFlow ?: UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
currentZipName =
alfredEventIdForDumpFlow
?: UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
latestScreenshotName = latestScreenshotTimestamp ?: 0L
} else {
currentZipName = AlfredManager.config.getAlfredEventId()
@@ -63,20 +71,18 @@ internal suspend fun getPreSignedUrl(
}
}
} else {
val failureEvent = buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_GET_PRE_SIGNED_URL_URL.plus(bucketKey),
requestMethod = GET_METHOD,
zipName = listOf(currentZipName),
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = GET_PRE_SIGNED_URL_FAILURE
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(
failureEvent,
context = AlfredManager.applicationContext
val failureEvent =
buildFailureEvent(
errorType = API_ERROR,
requestUrl = DEFAULT_GET_PRE_SIGNED_URL_URL.plus(bucketKey),
requestMethod = GET_METHOD,
zipName = listOf(currentZipName),
errorStatusCode = response.code().toLong(),
errorMessage = response.message(),
errorName = GET_PRE_SIGNED_URL_FAILURE
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
)
if (!dumpFlow) {
@@ -209,20 +215,18 @@ internal suspend fun uploadFile(
uploadFile.delete()
sendAlfredSessionEvent(dumpFlow, index, currentZipName, latestScreenshotTimestamp)
} else {
val failureEvent = buildFailureEvent(
errorType = ZIP_ERROR,
requestUrl = url,
requestMethod = POST_METHOD,
zipName = listOf(currentZipName ?: ""),
errorStatusCode = uploadResponse.code().toLong(),
errorMessage = uploadResponse.message(),
errorName = UPLOAD_TO_S3_FAILURE
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(
failureEvent,
context = AlfredManager.applicationContext
val failureEvent =
buildFailureEvent(
errorType = ZIP_ERROR,
requestUrl = url,
requestMethod = POST_METHOD,
zipName = listOf(currentZipName ?: ""),
errorStatusCode = uploadResponse.code().toLong(),
errorMessage = uploadResponse.message(),
errorName = UPLOAD_TO_S3_FAILURE
)
AlfredDispatcher.addTaskToQueue(
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
)
if (!dumpFlow) {

View File

@@ -7,10 +7,8 @@
package com.navi.alfred.utils
import android.content.res.Resources
import android.view.MotionEvent
import com.navi.alfred.AlfredManager
import com.navi.alfred.model.NaviMotionEvent
import java.lang.Float.min
import java.lang.Math.subtractExact
@@ -27,19 +25,35 @@ internal fun getTouchEvent(
val screenHeight = Resources.getSystem().displayMetrics.heightPixels
val eventName =
if (
(difference(currentTouchEvent?.getX(pointerIndex), previousTouchEvent?.pointerPositionX) &&
difference(currentTouchEvent?.getY(pointerIndex), previousTouchEvent?.pointerPositionY)) ||
(difference(currentTouchEvent?.rawX, previousTouchEvent?.rawX) &&
difference(currentTouchEvent?.rawY, previousTouchEvent?.rawY))
(difference(
currentTouchEvent?.getX(pointerIndex),
previousTouchEvent?.pointerPositionX
) &&
difference(
currentTouchEvent?.getY(pointerIndex),
previousTouchEvent?.pointerPositionY
)) ||
(difference(currentTouchEvent?.rawX, previousTouchEvent?.rawX) &&
difference(currentTouchEvent?.rawY, previousTouchEvent?.rawY))
) {
properties[AlfredConstants.START_X] = min(previousTouchEvent?.rawX ?: 0f, screenWidth.toFloat()).times(0.5f).toString()
properties[AlfredConstants.START_Y] = min(previousTouchEvent?.rawY ?: 0f, screenHeight.toFloat()).times(0.5f).toString()
properties[AlfredConstants.START_X] =
min(previousTouchEvent?.rawX ?: 0f, screenWidth.toFloat()).times(0.5f).toString()
properties[AlfredConstants.START_Y] =
min(previousTouchEvent?.rawY ?: 0f, screenHeight.toFloat()).times(0.5f).toString()
AlfredConstants.TOUCH_EVENT
} else {
properties[AlfredConstants.START_X] = min(previousTouchEvent?.rawX ?: 0f, screenWidth.toFloat()).times(0.5f).toString()
properties[AlfredConstants.START_Y] = min(previousTouchEvent?.rawY ?: 0f, screenHeight.toFloat()).times(0.5f).toString()
properties[AlfredConstants.END_X] = min(currentTouchEvent?.getRawX() ?: 0f, screenWidth.toFloat()).times(0.5f).toString()
properties[AlfredConstants.END_Y] = min(currentTouchEvent?.getRawY() ?: 0f, screenHeight.toFloat()).times(0.5f).toString()
properties[AlfredConstants.START_X] =
min(previousTouchEvent?.rawX ?: 0f, screenWidth.toFloat()).times(0.5f).toString()
properties[AlfredConstants.START_Y] =
min(previousTouchEvent?.rawY ?: 0f, screenHeight.toFloat()).times(0.5f).toString()
properties[AlfredConstants.END_X] =
min(currentTouchEvent?.getRawX() ?: 0f, screenWidth.toFloat())
.times(0.5f)
.toString()
properties[AlfredConstants.END_Y] =
min(currentTouchEvent?.getRawY() ?: 0f, screenHeight.toFloat())
.times(0.5f)
.toString()
AlfredConstants.SCROLL_EVENT
}
return Pair(eventName, properties)

View File

@@ -1,3 +1,10 @@
/*
*
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.alfred.utils
import android.content.Context
@@ -9,9 +16,6 @@ import com.google.gson.Gson
import com.navi.alfred.AlfredManager
import com.navi.alfred.db.model.ScreenShotPathHelper
import com.navi.alfred.db.model.ZipDetailsHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
@@ -20,6 +24,9 @@ import java.io.FileOutputStream
import java.util.UUID
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
internal fun toZip(imagePathList: List<ScreenShotPathHelper>) {
val zipFileName = AlfredManager.config.getAlfredSessionId() + UUID.randomUUID().toString()
@@ -56,7 +63,7 @@ internal fun zip(_files: java.util.ArrayList<String>, zipFilePath: String?): Boo
try {
val imageFile = File(_files[i])
val imageFileName = imageFile.name
if(checkFileExists(imageFileName, AlfredManager.applicationContext) != null) {
if (checkFileExists(imageFileName, AlfredManager.applicationContext) != null) {
val fi = FileInputStream(_files[i])
origin = BufferedInputStream(fi, buffer)
val entry = ZipEntry(_files[i].substring(_files[i].lastIndexOf("/") + 1))
@@ -166,14 +173,14 @@ internal fun insertZipDetailsToDbForDumpingLater(
) {
AlfredManager.zipDetailsDao.insert(
data =
ZipDetailsHelper(
alfredSessionId = alfredSessionId,
sessionStartRecordingTime = sessionStartRecordingTime,
eventStartRecordingTime = eventStartRecordingTime,
zipFileName = zipFileName,
alfredEventId = alfredEventId,
latestScreenshotTimestamp = latestScreenshotTimestamp
)
ZipDetailsHelper(
alfredSessionId = alfredSessionId,
sessionStartRecordingTime = sessionStartRecordingTime,
eventStartRecordingTime = eventStartRecordingTime,
zipFileName = zipFileName,
alfredEventId = alfredEventId,
latestScreenshotTimestamp = latestScreenshotTimestamp
)
)
}
@@ -192,7 +199,10 @@ internal fun startAnrCrashZipUpload(view: View) {
}
}
internal fun uploadWorkManagerFailedZip(alfredEventId: String? = null, latestScreenshotTimestamp: Long? = null) {
internal fun uploadWorkManagerFailedZip(
alfredEventId: String? = null,
latestScreenshotTimestamp: Long? = null
) {
if (AlfredManager.workFailureData.size > 0) {
val inputData = AlfredManager.workFailureData[0]
if (AlfredManager.zipUploadRetryCount < 3) {
@@ -240,4 +250,3 @@ internal fun checkFileExists(fileName: String, context: Context): File? {
null
}
}

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2021-2023 by Navi Technologies Limited
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/

View File

@@ -1,6 +1,6 @@
/*
*
* * Copyright © 2021-2023 by Navi Technologies Limited
* * Copyright © 2023 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/

View File

@@ -15,9 +15,7 @@ import kotlinx.coroutines.runBlocking
class SyncFailureTask() : AnalyticsTask {
@WorkerThread
override fun execute(): Boolean {
return runBlocking {
sendFailureEventsToServer()
}
return runBlocking { sendFailureEventsToServer() }
}
override fun getTaskName(): String {

View File

@@ -15,9 +15,7 @@ import kotlinx.coroutines.runBlocking
class SyncNegativeCaseTask() : AnalyticsTask {
@WorkerThread
override fun execute(): Boolean {
return runBlocking {
sendNegativeCaseToServer()
}
return runBlocking { sendNegativeCaseToServer() }
}
override fun getTaskName(): String {

View File

@@ -32,7 +32,8 @@ class UploadEventsWorker(context: Context, workerParams: WorkerParameters) :
if (clientTs == 0L) {
Result.failure()
} else {
AlfredManager.alfredDataBase = AlfredDatabaseHelper.getAnalyticsDatabase(AlfredManager.applicationContext)
AlfredManager.alfredDataBase =
AlfredDatabaseHelper.getAnalyticsDatabase(AlfredManager.applicationContext)
sendEventsToServer(
clientTs = clientTs,
snapshotPerSecond = snapshotPerSecond,
@@ -48,9 +49,7 @@ class UploadEventsWorker(context: Context, workerParams: WorkerParameters) :
snapshotPerSecond = snapshotPerSecond,
workManagerFlow = true
)
sendFailureEventsToServer(
workManagerFlow = true
)
sendFailureEventsToServer(workManagerFlow = true)
Result.success()
}
} catch (e: Exception) {

View File

@@ -17,10 +17,10 @@ import com.navi.alfred.db.model.ScreenShotPathHelper
import com.navi.alfred.model.WorkManagerFailureInputData
import com.navi.alfred.utils.AlfredConstants
import com.navi.alfred.utils.toZipForWorkManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.lang.reflect.Type
import java.util.UUID
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class UploadFileWorker(context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
@@ -38,11 +38,11 @@ class UploadFileWorker(context: Context, workerParams: WorkerParameters) :
Gson().fromJson(screenShotList.toString(), listType)
val zipFileName =
alfredSessionId +
sessionStartRecordingTime +
AlfredConstants.WORKER_ZIP_FILENAME_ENDPOINT
sessionStartRecordingTime +
AlfredConstants.WORKER_ZIP_FILENAME_ENDPOINT
val alfredEventId =
inputData.getString(AlfredConstants.ALFRED_EVENT_ID) ?: UUID.randomUUID().toString()
.plus(AlfredConstants.ALFRED_EVENT_ID)
inputData.getString(AlfredConstants.ALFRED_EVENT_ID)
?: UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
val latestScreenshotTimestamp =
inputData.getLong(AlfredConstants.LATEST_SCREENSHOT_TIMESTAMP, 0L)
workFailureData.add(
@@ -60,8 +60,8 @@ class UploadFileWorker(context: Context, workerParams: WorkerParameters) :
try {
if (
alfredSessionId == null ||
sessionStartRecordingTime == 0L ||
eventStartRecordingTime == 0L
sessionStartRecordingTime == 0L ||
eventStartRecordingTime == 0L
) {
Result.failure()
} else {

View File

@@ -0,0 +1,124 @@
tasks.register('projectDependencyGraph') {
doLast {
def dot = new File(rootProject.buildDir, 'reports/dependency-graph/project.dot')
dot.parentFile.mkdirs()
dot.delete()
dot << 'digraph {\n'
dot << " graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4];\n"
dot << ' node [style=filled, fillcolor="#bbbbbb"];\n'
dot << ' rankdir=TB;\n'
def rootProjects = []
def queue = [rootProject]
while (!queue.isEmpty()) {
def project = queue.remove(0)
rootProjects.add(project)
queue.addAll(project.childProjects.values())
}
def projects = new LinkedHashSet<Project>()
def dependencies = new LinkedHashMap<Tuple2<Project, Project>, List<String>>()
def multiplatformProjects = []
def jsProjects = []
def androidProjects = []
def dynamicProjects = []
def javaProjects = []
queue = [rootProject]
while (!queue.isEmpty()) {
def project = queue.remove(0)
queue.addAll(project.childProjects.values())
if (project.plugins.hasPlugin('org.jetbrains.kotlin.multiplatform')) {
multiplatformProjects.add(project)
}
if (project.plugins.hasPlugin('org.jetbrains.kotlin.js')) {
jsProjects.add(project)
}
if (project.plugins.hasPlugin('com.android.library') || project.plugins.hasPlugin('com.android.application')) {
androidProjects.add(project)
}
if (project.plugins.hasPlugin('com.android.dynamic-feature')) {
dynamicProjects.add(project)
}
if (project.plugins.hasPlugin('java-library') || project.plugins.hasPlugin('java')) {
javaProjects.add(project)
}
project.configurations.configureEach { config ->
config.dependencies
.withType(ProjectDependency)
.collect { it.dependencyProject }
.each { dependency ->
projects.add(project)
projects.add(dependency)
rootProjects.remove(dependency)
def graphKey = new Tuple2<Project, Project>(project, dependency)
def traits = dependencies.computeIfAbsent(graphKey) { new ArrayList<String>() }
if (config.name.toLowerCase().endsWith('implementation') && !traits.contains('style=dotted')) {
traits.add('style=dotted')
}
}
}
}
projects = projects.sort { it.path }
dot << '\n # Projects\n\n'
for (project in projects) {
def traits = []
if (rootProjects.contains(project)) {
traits.add('shape=box')
}
if (multiplatformProjects.contains(project)) {
traits.add('fillcolor="#ffd2b3"')
} else if (jsProjects.contains(project)) {
traits.add('fillcolor="#ffffba"')
} else if (androidProjects.contains(project)) {
traits.add('fillcolor="#baffc9"')
} else if (dynamicProjects.contains(project)) {
traits.add('fillcolor="#bad4ff"')
} else if (javaProjects.contains(project)) {
traits.add('fillcolor="#ffb3ba"')
} else {
traits.add('fillcolor="#eeeeee"')
}
dot << " \"${project.path}\" [${traits.join(", ")}];\n"
}
dot << '\n {rank = same;'
for (project in projects) {
if (rootProjects.contains(project)) {
dot << " \"${project.path}\";"
}
}
dot << '}\n'
dot << '\n # Dependencies\n\n'
dependencies.forEach { key, traits ->
if (key.v1.path != key.v2.path) {
dot << " \"${key.v1.path}\" -> \"${key.v2.path}\""
if (!traits.isEmpty()) {
dot << " [${traits.join(", ")}]"
}
dot << '\n'
}
}
dot << '}\n'
def p = 'dot -Tpng -O project.dot'.execute([], dot.parentFile)
p.waitFor()
if (p.exitValue() != 0) {
throw new RuntimeException(p.errorStream.text)
}
println("Project module dependency graph created at ${dot.absolutePath}.png")
}
}

View File

@@ -5,6 +5,9 @@ pluginManagement {
gradlePluginPortal()
}
}
rootProject.name = "alfred"
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
@@ -12,6 +15,6 @@ dependencyResolutionManagement {
mavenCentral()
}
}
rootProject.name = "Alfred Android"
include ':app'
include ':navi-alfred'

32
spotless.gradle Normal file
View File

@@ -0,0 +1,32 @@
spotless {
ratchetFrom 'origin/master'
format 'misc', {
target '**/*.gradle', '**/*.md', '**/.gitignore'
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
}
kotlin {
target '**/*.kt'
licenseHeaderFile rootProject.file('spotless.license')
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
ktfmt().kotlinlangStyle()
}
java {
target '**/*.java'
licenseHeaderFile rootProject.file('spotless.license')
trimTrailingWhitespace()
indentWithSpaces()
endWithNewline()
removeUnusedImports()
googleJavaFormat().aosp()
}
}

7
spotless.license Normal file
View File

@@ -0,0 +1,7 @@
/*
*
* * Copyright © $YEAR by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/