Skip to content

Commit 81ed841

Browse files
author
Bitrise Buildbot
committed
Bitrise build 14411
1 parent 4611daf commit 81ed841

40 files changed

Lines changed: 1092 additions & 471 deletions

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ buildscript {
1111
classpath("com.android.tools.build:gradle:8.9.1")
1212
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
1313
classpath("org.jetbrains.kotlin:kotlin-serialization:${kotlinVersion}")
14+
classpath("io.sentry:sentry-android-gradle-plugin:5.12.2")
1415
// NOTE: Do not place your application dependencies here; they belong
1516
// in the individual module build.gradle.kts files
1617
}

geotabdrivesdk/build.gradle.kts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import java.util.Properties
22

3-
val versionName = "6.8.1_75323"
3+
val versionName = "6.8.1_75411"
44

55
plugins {
66
id("com.android.library")
@@ -16,24 +16,34 @@ apply {
1616
from("./../kotlin-lint.gradle.kts")
1717
}
1818

19+
ksp {
20+
arg("room.schemaLocation", "$projectDir/schemas")
21+
}
1922
android {
2023
compileSdk = 34
2124
namespace = "com.geotab.mobile.sdk"
2225

2326
defaultConfig {
2427
minSdk = 24
25-
2628
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2729

28-
ksp {
29-
arg("room.schemaLocation", "$projectDir/schemas")
30-
}
30+
3131

3232
buildConfigField("String", "KEYSTORE_ALIAS", "\"" + System.getenv("KEYSTORE_ALIAS") + "\"")
3333
buildConfigField("String", "VERSION_NAME", "\"${versionName}\"")
3434

3535
manifestPlaceholders["appLinkHost"] = ""
3636
manifestPlaceholders["appAuthRedirectPathPrefix"] = ""
37+
manifestPlaceholders["authScheme"] = ""
38+
manifestPlaceholders["authHost"] = ""
39+
}
40+
41+
testOptions {
42+
unitTests.all { test ->
43+
test.testLogging {
44+
events("passed", "skipped", "failed")
45+
}
46+
}
3747
}
3848

3949
buildTypes {
@@ -125,25 +135,32 @@ dependencies {
125135
dokkaPlugin("org.jetbrains.dokka:android-documentation-plugin:1.8.10")
126136
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
127137
implementation("androidx.core:core-ktx:1.12.0")
128-
implementation("net.openid:appauth:0.11.1")
138+
// This implementation is needed for consumers of the SDK have the option to not include the
139+
// RedirectUriReceiverActivity in their app if they don't use SSO login.
140+
api("net.openid:appauth:0.11.1")
141+
// Optional Sentry dependency for logging integration
142+
compileOnly("io.sentry:sentry-android:8.25.0")
129143
implementation("androidx.constraintlayout:constraintlayout:2.0.3")
130144
implementation("androidx.appcompat:appcompat:1.1.0")
131145
implementation("androidx.exifinterface:exifinterface:1.3.1")
132146
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2-native-mt")
133147
implementation("com.github.spullara.mustache.java:compiler:0.8.18")
134148
implementation("androidx.fragment:fragment-ktx:1.4.0-alpha07")
149+
implementation("androidx.work:work-runtime-ktx:2.9.0")
150+
testImplementation("androidx.work:work-testing:2.9.0")
135151
implementation("androidx.lifecycle:lifecycle-common:2.5.1")
136152
implementation("com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava")
137-
debugImplementation("androidx.fragment:fragment-testing:1.6.0-alpha04")
138153
implementation("com.google.code.gson:gson:2.11.0")
139-
implementation ("androidx.room:room-runtime:2.5.2")
140-
implementation("androidx.room:room-ktx:2.5.2")
141-
annotationProcessor("androidx.room:room-compiler:2.5.2")
154+
api ("androidx.room:room-runtime:2.6.1")
155+
api("androidx.room:room-ktx:2.6.1")
156+
annotationProcessor("androidx.room:room-compiler:2.6.1")
142157
ksp("androidx.room:room-compiler:2.6.1")
143158
testImplementation("junit:junit:4.13.2")
144159
testImplementation("org.jetbrains.kotlin:kotlin-test:2.0.20")
145160
testImplementation("io.mockk:mockk:1.12.3")
146161
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
162+
testImplementation("org.robolectric:robolectric:4.10.3")
163+
testImplementation ("org.json:json:20231013")
147164
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2-native-mt")
148165
implementation("org.jetbrains.kotlin:kotlin-reflect:1.5.31")
149166
androidTestImplementation("androidx.test:core:1.5.0")

geotabdrivesdk/native-sdk.d.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -503,23 +503,57 @@ declare namespace geotabModules {
503503
}
504504

505505
namespace login {
506-
enum GeotabAppId {
507-
GEOTAB_DRIVE = 0,
508-
MYGEOTAB
509-
}
510-
511506
/*******
512507
* Start the login function integrated with Chrome Custom Tabs.
513-
* @param argument: { clientId: string, discoveryUri: string, geotabAppId: GeotabAppId, loginHint: string? }
508+
* @param argument: { clientId: string, discoveryUri: string, loginHint: string }
509+
* @param callback:
510+
* - result: string. A GeotabAuthState object.
511+
* The loginHint parameter is mandatory as we use it to store it as username in the SecureStorage.
512+
* It is also used to pre-fill the username field in the login page.
513+
* On a successful call a GeotabAuthState object turned into JSON will be given as result
514+
* If there's an error while logging in, err will be given.
515+
* GeotabAuthState object contains the following properties:
516+
* { accessToken: string }
517+
*/
518+
function start(argument: { clientId: string, discoveryUri: string, loginHint: string }, callback: (err?: Error, result?: string) => void);
519+
}
520+
521+
namespace auth {
522+
/*******
523+
* Start the login function integrated with Chrome Custom Tabs.
524+
* @param argument: { clientId: string, discoveryUri: string, username: string }
514525
* @param callback:
515526
* - result: string. A GeotabAuthState object.
516-
* The loginHint parameter is optional. It is used to pre-fill the username field in the login page.
527+
* The username parameter is mandatory as we use it to store it as username in the SecureStorage.
528+
* It is also used to pre-fill the username field in the login page.
517529
* On a successful call a GeotabAuthState object turned into JSON will be given as result
518530
* If there's an error while logging in, err will be given.
519531
* GeotabAuthState object contains the following properties:
520-
* { accessToken: string, idToken: string, refreshToken: string }
532+
* { accessToken: string }
521533
*/
522-
function start(argument: { clientId: string, discoveryUri: string, geotabAppId: GeotabAppId, loginHint: string? }, callback: (err?: Error, result?: string) => void);
534+
function login(argument: { clientId: string, discoveryUri: string, username: string }, callback: (err?: Error, result?: string) => void);
535+
536+
/*******
537+
* Start the getAuthToken function.
538+
* @param argument: { username: string }
539+
* @param callback:
540+
* - result: string. A GeotabAuthState object.
541+
* On a successful call a GeotabAuthState object turned into JSON will be given as result
542+
* If there's an error while retrieving the access token, err will be given.
543+
* GeotabAuthState object contains the following properties:
544+
* { accessToken: string }
545+
*/
546+
function getToken(argument: { username: string }, callback: (err?: Error, result?: string) => void);
547+
548+
/*******
549+
* Start the logout function integrated with Chrome Custom Tabs.
550+
* @param argument: { username: string }
551+
* @param callback:
552+
* - result: string.
553+
* On a successful call a string will be given as result with a success message.
554+
* If there's an error while retrieving the access token, err will be given.
555+
*/
556+
function logout(argument: { username: string }, callback: (err?: Error, result?: string) => void);
523557
}
524558

525559
namespace appearance {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"formatVersion": 1,
3+
"database": {
4+
"version": 1,
5+
"identityHash": "9378721c0c6219146655b18dd3b7e54a",
6+
"entities": [
7+
{
8+
"tableName": "secureStorage",
9+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localKey` TEXT NOT NULL, `localValue` BLOB NOT NULL, PRIMARY KEY(`localKey`))",
10+
"fields": [
11+
{
12+
"fieldPath": "localKey",
13+
"columnName": "localKey",
14+
"affinity": "TEXT",
15+
"notNull": true
16+
},
17+
{
18+
"fieldPath": "localValue",
19+
"columnName": "localValue",
20+
"affinity": "BLOB",
21+
"notNull": true
22+
}
23+
],
24+
"primaryKey": {
25+
"autoGenerate": false,
26+
"columnNames": [
27+
"localKey"
28+
]
29+
},
30+
"indices": [],
31+
"foreignKeys": []
32+
}
33+
],
34+
"views": [],
35+
"setupQueries": [
36+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
37+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9378721c0c6219146655b18dd3b7e54a')"
38+
]
39+
}
40+
}

geotabdrivesdk/src/main/AndroidManifest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@
5050
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
5151
android:resource="@xml/accessory_filter" />
5252
</activity>
53-
53+
<!-- This implementation is needed for consumers of the SDK have the option to not include the
54+
RedirectUriReceiverActivity in their app if they don't use SSO login.-->
5455
<activity
5556
android:name="net.openid.appauth.RedirectUriReceiverActivity"
5657
android:exported="true"
57-
android:taskAffinity=""
5858
tools:node="replace">
5959
</activity>
6060
<service
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.geotab.mobile.sdk
2+
3+
import android.webkit.WebView
4+
import androidx.fragment.app.Fragment
5+
import androidx.lifecycle.Lifecycle
6+
import com.geotab.mobile.sdk.logging.Logger
7+
8+
/**
9+
* Base class for Geotab fragments that interact with WebView via JavaScript bridge.
10+
* Provides lifecycle-safe execution to prevent JNI reference leaks (MOB-3805).
11+
*/
12+
abstract class BaseGeotabFragment : Fragment() {
13+
companion object {
14+
const val TAG: String = "BaseGeotabFragment"
15+
}
16+
/**
17+
* Returns the WebView instance managed by this fragment.
18+
*/
19+
protected abstract fun getWebView(): WebView?
20+
21+
/**
22+
* Executes the block only if Fragment lifecycle is at least CREATED and WebView is attached.
23+
* Prevents JNI global reference leaks by skipping execution when Fragment is destroyed.
24+
*
25+
* **Important:** This function performs a non-local return when conditions are invalid,
26+
* meaning the calling function will exit immediately without executing subsequent code.
27+
*
28+
* Example:
29+
* ```kotlin
30+
* fun myFunction() {
31+
* executeIfValid {
32+
* // This code only runs if Fragment is valid
33+
* }
34+
* // ⚠️ This code will NOT run if executeIfValid's conditions fail!
35+
* }
36+
* ```
37+
* @param callback Code to execute if conditions are valid
38+
*/
39+
protected inline fun executeIfValid(callback: () -> Unit) {
40+
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED) ||
41+
getWebView()?.isAttachedToWindow != true
42+
) {
43+
Logger.shared.error(TAG, "Fragment is destroyed or WebView is detached; skipping execution to prevent JNI reference leak.")
44+
return // Non-local return - exits the calling function
45+
}
46+
callback()
47+
}
48+
}

0 commit comments

Comments
 (0)