Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Android CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: detekt
run: ./gradlew detekt
6 changes: 4 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl

@Suppress("PropertyName")
val VERSION_NAME="3.3.0"
val VERSION_NAME="3.4.0"
@Suppress("PropertyName")
val VERSION_CODE=28
val VERSION_CODE=36
val useFpProDebugVersion =
false // switch to true when needed to debug the locally built library
val fingerprintProLib = if (useFpProDebugVersion) libs.fingerprint.pro.debug else libs.fingerprint.pro.asProvider()
Expand Down Expand Up @@ -152,4 +152,6 @@ dependencies {
androidTestImplementation(libs.androidx.compose.ui.test)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)

detektPlugins(libs.detekt.formatting)
}
7 changes: 3 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Splash"
>
android:theme="@style/Theme.Splash">
<activity
android:name=".MainActivity"
android:configChanges="orientation"
android:screenOrientation="portrait"
android:exported="true">
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ import com.fingerprintjs.android.fpjs_pro.Configuration
object Credentials {
val apiKey: String = Protected.apiKey
val endpointUrl: String = Configuration.Region.US.endpointUrl
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ object URLs {
val locationSpoofing = smartSignalOverviewUrl("geolocation-spoofing-detection")
val root = smartSignalOverviewUrl("rooted-device-detection")
val vpn = smartSignalOverviewUrl("vpn-detection-for-mobile-devices")
val tampering = smartSignalOverviewUrl("tampered-request-detection-for-mobile-apps")
val mitm = smartSignalOverviewUrl("mitm-attack-detection")

private fun smartSignalOverviewUrl(forAnchor: String): String {
return "https://dev.fingerprint.com/docs/smart-signals-overview#$forAnchor"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.fingerprintjs.android.fpjs_pro_demo.di.modules.AppModule
import dagger.BindsInstance
import dagger.Component


@AppScope
@Component(
modules = [
Expand All @@ -18,7 +17,7 @@ import dagger.Component
CommonComponent::class
]
)
interface AppComponent: ViewModelProvidingComponent {
interface AppComponent : ViewModelProvidingComponent {

@Component.Builder
interface Builder {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.fingerprintjs.android.fpjs_pro_demo.di


@Retention(AnnotationRetention.RUNTIME)
@javax.inject.Scope
annotation class AppScope
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ interface CommonComponent {
fun stateMocks(): StateMocks
fun homeScreenUiStateCreator(): HomeScreenUiStateCreator
fun smartSignalsBodyParser(): SmartSignalsBodyParser
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package com.fingerprintjs.android.fpjs_pro_demo.di.components.common

object CommonComponentStorage {
val commonComponent: CommonComponent by lazy { DaggerCommonComponent.create() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import kotlinx.serialization.json.ClassDiscriminatorMode
import kotlinx.serialization.json.Json
import javax.inject.Singleton


@Module
class CommonModule {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import android.app.Application
import android.content.Context
import com.fingerprintjs.android.fpjs_pro_demo.App
import com.fingerprintjs.android.fpjs_pro_demo.storage.BackingStorage
import com.fingerprintjs.android.fpjs_pro_demo.storage.impl.JsonSerializer
import com.fingerprintjs.android.fpjs_pro_demo.storage.Serializer
import com.fingerprintjs.android.fpjs_pro_demo.storage.impl.JsonSerializer
import com.fingerprintjs.android.fpjs_pro_demo.storage.impl.SharedPreferencesStorage
import dagger.Binds
import dagger.Module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class CustomApiKeysUseCase @Inject constructor(
_state.emit(loadedState)
}


private suspend fun saveState(state: CustomApiKeysState): Result<*, *> {
return appStorage.save(state.public, StorageKey.CustomPublicApiKey)
.andThen { appStorage.save(state.secret, StorageKey.CustomSecretApiKey) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@ import com.fingerprintjs.android.fpjs_pro.FingerprintJSProResponse
import com.github.michaelbull.result.Result

typealias FingerprintJSProResult = Result<FingerprintJSProResponse, Error>

Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,4 @@ class IdentificationProvider @Inject constructor(
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class AllowSignUpPromptUseCase @Inject constructor(

private suspend fun updateState() {
_showAllowed.emit(
(System.currentTimeMillis() - getSignupPromptHideTimeMillis() > MILLIS_IN_WEEK)
&& getFingerprintSuccessCount() >= 2
(System.currentTimeMillis() - getSignupPromptHideTimeMillis() > MILLIS_IN_WEEK) &&
getFingerprintSuccessCount() >= 2
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class SmartSignals(
val locationSpoofing: SmartSignalInfo<SmartSignal.LocationSpoofing>,
val root: SmartSignalInfo<SmartSignal.Root>,
val vpn: SmartSignalInfo<SmartSignal.Vpn>,
val tampering: SmartSignalInfo<SmartSignal.Tampering>,
val mitm: SmartSignalInfo<SmartSignal.Mitm>,
)

sealed class SmartSignalInfo<out T : SmartSignal>(val rawKey: String) {
Expand Down Expand Up @@ -83,4 +85,15 @@ sealed class SmartSignal {
val originTimezone: String? = null,
val originCountry: String? = null,
) : SmartSignal()

@Serializable
data class Tampering(
val result: Boolean,
val anomalyScore: Float,
) : SmartSignal()

@Serializable
data class Mitm(
val result: Boolean,
) : SmartSignal()
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class SmartSignalsBodyParser @Inject constructor(
)
}
),
tampering = products.getSmartSignal(key = "tampering"),
mitm = products.getSmartSignal(key = "mitmAttack"),
)
}.mapError { }
}
Expand Down Expand Up @@ -76,10 +78,10 @@ class SmartSignalsBodyParser @Inject constructor(
}

private fun String.isEssentiallyEmpty(): Boolean =
this.isEmpty()
|| this == "n\\a"
|| this.contentEquals("null", ignoreCase = true)
|| this.contentEquals("unknown", ignoreCase = true)
this.isEmpty() ||
this == "n\\a" ||
this.contentEquals("null", ignoreCase = true) ||
this.contentEquals("unknown", ignoreCase = true)

private fun String.takeIfNotEssentiallyEmpty(): String? = takeIf { !it.isEssentiallyEmpty() }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ sealed interface SmartSignalsError {
data object ParseError : SmartSignalsError
data object Unknown : SmartSignalsError


sealed interface APIError : SmartSignalsError
data object FeatureNotEnabled : SmartSignalsError, APIError
data object RequestNotFound : SmartSignalsError, APIError
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ class SmartSignalsProvider @Inject constructor(
if (!customKeysState.enabled) {
val baseUrl = Protected.smartSignalsBaseUrl
val origin = Protected.smartSignalsOrigin
if (baseUrl == null || origin == null)
if (baseUrl == null || origin == null) {
return Err(SmartSignalsError.EndpointInfoNotSetInApp)
}
url = Uri.parse(baseUrl)
.buildUpon()
.appendPath("event")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ package com.fingerprintjs.android.fpjs_pro_demo.domain.smart_signals

import com.github.michaelbull.result.Result

typealias SmartSignalsResponse = Result<SmartSignals, SmartSignalsError>
typealias SmartSignalsResponse = Result<SmartSignals, SmartSignalsError>
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,19 @@ import okhttp3.Request
import java.io.IOException
import javax.inject.Inject


@AppScope
class HttpClient @Inject constructor(private val client: OkHttpClient) {

sealed class Error <T: Throwable> (val cause: T) {
class IO(error: IOException): Error<IOException>(error)
class Unknown(error: Throwable): Error<Throwable>(error)
sealed class Error<T : Throwable> (val cause: T) {
class IO(error: IOException) : Error<IOException>(error)
class Unknown(error: Throwable) : Error<Throwable>(error)

companion object {
fun from(t: Throwable): Error<*> = when (t) {
is IOException -> IO(t)
else -> Unknown(t)
}
}
companion object {
fun from(t: Throwable): Error<*> = when (t) {
is IOException -> IO(t)
else -> Unknown(t)
}
}
}

data class Response(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import kotlin.reflect.KClass
class AppStorage @Inject constructor(
private val backingStorage: BackingStorage,
private val serializer: Serializer,
) {
) {

suspend fun save(data: Any, key: StorageKey): Result<*, *> {
return serializer.serialize(data)
.andThen { backingStorage.writeData(it, key) }
}

suspend fun <T: Any> load(key: StorageKey, classOfT: KClass<T>) : Result<T, *> {
suspend fun <T : Any> load(key: StorageKey, classOfT: KClass<T>): Result<T, *> {
return backingStorage.readData(key)
.andThen { serializer.deserialize(it, classOfT) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import kotlin.reflect.KClass

interface Serializer {
suspend fun serialize(data: Any): Result<ByteArray, *>
suspend fun <T: Any> deserialize(data: ByteArray, classOfT: KClass<T>): Result<T, *>
suspend fun <T : Any> deserialize(data: ByteArray, classOfT: KClass<T>): Result<T, *>
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ class SharedPreferencesStorage @Inject constructor(

private val sharedPrefsFileNameHistory = if (encryptedSharedPreferencesSupported) {
listOf(
// old shared prefs
"fpjs_prefs",

"fpjs_prefs", // old shared prefs
"fpjs_prefs_v1",
"fpjs_prefs_v2_legacy", // if device has been updated from api levels 21-22, let's delete unencrypted shared preferences
// current shared prefs
"fpjs_prefs_v2",
"fpjs_prefs_v2_legacy", // if device has been updated from api levels 21-22,
// let's delete unencrypted shared preferences
"fpjs_prefs_v2", // current shared prefs
)
} else {
listOf(
Expand Down Expand Up @@ -65,7 +65,6 @@ class SharedPreferencesStorage @Inject constructor(
}
}


// for now, let's not bother with migration, there is no important information to migrate
// from the previous released version of the app
@SuppressLint("ApplySharedPref")
Expand All @@ -87,7 +86,6 @@ class SharedPreferencesStorage @Inject constructor(
}
}


override suspend fun writeData(bytes: ByteArray, key: StorageKey): Result<*, *> {
return withContext(Dispatchers.IO) {
runCatching {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ fun Modifier.animatedAppBar(
}
}

private const val SHARED_APP_BAR_KEY = "appbar"
private const val SHARED_APP_BAR_KEY = "appbar"
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,17 @@ fun <T> Shimmable(

else -> {
// default transition
(fadeIn(
animationSpec = tween(
220,
delayMillis = 90
(
fadeIn(
animationSpec = tween(
220,
delayMillis = 90
)
) + scaleIn(
initialScale = 0.92f,
animationSpec = tween(220, delayMillis = 90)
)
) + scaleIn(
initialScale = 0.92f,
animationSpec = tween(220, delayMillis = 90)
)).togetherWith(fadeOut(animationSpec = tween(90)))
).togetherWith(fadeOut(animationSpec = tween(90)))
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ fun <T> ValuePickerTextField(
)
}
}

}
}

Expand Down
Loading