Skip to content

Commit 5ee8df4

Browse files
committed
Merge branch 'release/1.1.6' into main
2 parents ec15651 + 2867c3e commit 5ee8df4

File tree

11 files changed

+142
-32
lines changed

11 files changed

+142
-32
lines changed

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ android {
2323

2424
defaultConfig {
2525
applicationId = "com.twofasapp.pass"
26-
versionName = "1.1.2"
27-
versionCode = 25
26+
versionName = "1.1.6"
27+
versionCode = 26
2828
}
2929

3030
applicationVariants.all {

buildlogic/src/main/java/com/twofasapp/buildlogic/extension/KotlinAndroid.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.android.build.api.dsl.CommonExtension
1212
import com.twofasapp.buildlogic.version.SdkConfig
1313
import org.gradle.api.JavaVersion
1414
import org.gradle.api.Project
15+
import org.gradle.api.tasks.testing.Test
1516
import org.gradle.kotlin.dsl.assign
1617
import org.gradle.kotlin.dsl.dependencies
1718
import org.gradle.kotlin.dsl.withType
@@ -68,6 +69,10 @@ internal fun Project.applyKotlinAndroid(
6869
}
6970
}
7071

72+
tasks.withType<Test>().configureEach {
73+
failOnNoDiscoveredTests = false
74+
}
75+
7176
configureKotlin()
7277
}
7378

data/cloud/src/main/java/com/twofasapp/data/cloud/authenticate/AuthenticateCloudService.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import androidx.compose.runtime.Composable
1212
import com.twofasapp.data.cloud.domain.CloudConfig
1313

1414
enum class CloudServiceType {
15-
GoogleDrive,
15+
GoogleDrive, LegacyGoogleDrive
1616
}
1717

18+
val DefaultCloudServiceType = CloudServiceType.LegacyGoogleDrive
19+
1820
@Composable
1921
fun AuthenticateCloudService(
2022
type: CloudServiceType,
@@ -30,5 +32,13 @@ fun AuthenticateCloudService(
3032
onError = onError,
3133
)
3234
}
35+
36+
CloudServiceType.LegacyGoogleDrive -> {
37+
AuthenticateWithLegacyGoogle(
38+
onDismissRequest = onDismissRequest,
39+
onSuccess = onSuccess,
40+
onError = onError,
41+
)
42+
}
3343
}
3444
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* SPDX-License-Identifier: BUSL-1.1
3+
*
4+
* Copyright © 2025 Two Factor Authentication Service, Inc.
5+
* Licensed under the Business Source License 1.1
6+
* See LICENSE file for full terms
7+
*/
8+
9+
package com.twofasapp.data.cloud.authenticate
10+
11+
import android.app.Activity
12+
import androidx.activity.compose.rememberLauncherForActivityResult
13+
import androidx.activity.result.ActivityResult
14+
import androidx.activity.result.contract.ActivityResultContracts
15+
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.LaunchedEffect
17+
import androidx.compose.ui.platform.LocalContext
18+
import com.google.android.gms.auth.api.signin.GoogleSignIn
19+
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
20+
import com.google.android.gms.common.api.Scope
21+
import com.google.api.services.drive.DriveScopes
22+
import com.twofasapp.core.android.ktx.currentActivity
23+
import com.twofasapp.core.common.services.CrashlyticsInstance
24+
import com.twofasapp.data.cloud.domain.CloudConfig
25+
26+
@Composable
27+
internal fun AuthenticateWithLegacyGoogle(
28+
onDismissRequest: () -> Unit = {},
29+
onSuccess: (CloudConfig.GoogleDrive) -> Unit = {},
30+
onError: (Exception) -> Unit = {},
31+
) {
32+
val activity = LocalContext.currentActivity
33+
34+
val authorizationIntentLauncher =
35+
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
36+
if (result.resultCode == Activity.RESULT_OK) {
37+
if (result.data != null) {
38+
GoogleSignIn.getSignedInAccountFromIntent(result.data)
39+
.addOnSuccessListener {
40+
if (it.grantedScopes.contains(Scope(DriveScopes.DRIVE_APPDATA))) {
41+
onSuccess(
42+
CloudConfig.GoogleDrive(
43+
id = it.email.orEmpty(),
44+
credentialType = it.account?.type.orEmpty(),
45+
),
46+
)
47+
onDismissRequest()
48+
} else {
49+
onDismissRequest()
50+
}
51+
}
52+
.addOnCanceledListener { onDismissRequest() }
53+
.addOnFailureListener { onError(it) }
54+
} else {
55+
CrashlyticsInstance.logException(IllegalStateException("Legacy Authorization data is empty."))
56+
onDismissRequest()
57+
}
58+
} else {
59+
CrashlyticsInstance.logException(IllegalStateException("Legacy Authorization result failed."))
60+
onDismissRequest()
61+
}
62+
}
63+
64+
LaunchedEffect(Unit) {
65+
val signInOptions = GoogleSignInOptions.Builder()
66+
.requestEmail()
67+
.requestScopes(Scope(DriveScopes.DRIVE_APPDATA))
68+
.build()
69+
70+
val client = GoogleSignIn.getClient(activity, signInOptions)
71+
72+
client.signInIntent
73+
74+
authorizationIntentLauncher.launch(client.signInIntent)
75+
}
76+
}

data/cloud/src/main/java/com/twofasapp/data/cloud/services/googledrive/GoogleDriveCloudService.kt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import android.content.Context
1313
import androidx.credentials.ClearCredentialStateRequest
1414
import androidx.credentials.CredentialManager
1515
import com.google.android.gms.auth.UserRecoverableAuthException
16+
import com.google.android.gms.auth.api.signin.GoogleSignIn
17+
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
18+
import com.google.android.gms.common.api.Scope
1619
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
1720
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
1821
import com.google.api.client.http.ByteArrayContent
@@ -21,6 +24,8 @@ import com.google.api.client.json.gson.GsonFactory
2124
import com.google.api.services.drive.Drive
2225
import com.google.api.services.drive.DriveScopes
2326
import com.google.api.services.drive.model.File
27+
import com.twofasapp.data.cloud.authenticate.CloudServiceType
28+
import com.twofasapp.data.cloud.authenticate.DefaultCloudServiceType
2429
import com.twofasapp.data.cloud.domain.CloudConfig
2530
import com.twofasapp.data.cloud.domain.CloudFileInfo
2631
import com.twofasapp.data.cloud.domain.CloudResult
@@ -301,9 +306,23 @@ internal class GoogleDriveCloudService(
301306
}
302307

303308
override suspend fun disconnect() {
304-
CredentialManager.create(context).clearCredentialState(
305-
ClearCredentialStateRequest(),
306-
)
309+
when (DefaultCloudServiceType) {
310+
CloudServiceType.GoogleDrive -> {
311+
CredentialManager.create(context).clearCredentialState(
312+
ClearCredentialStateRequest(),
313+
)
314+
}
315+
316+
CloudServiceType.LegacyGoogleDrive -> {
317+
GoogleSignIn.getClient(
318+
context,
319+
GoogleSignInOptions.Builder()
320+
.requestEmail()
321+
.requestScopes(Scope(DriveScopes.DRIVE_APPDATA))
322+
.build(),
323+
).signOut()
324+
}
325+
}
307326
}
308327

309328
private fun CloudConfig.GoogleDrive.drive(): Drive {

data/purchases/src/main/java/com/twofasapp/data/purchases/domain/Entitlements.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ data class Entitlements(
1515
) {
1616
companion object {
1717
val Free = Entitlements(
18-
itemsLimit = 50,
18+
itemsLimit = 200,
1919
unlimitedConnectedBrowsers = false,
2020
multiDeviceSync = false,
2121
)

feature/cloudsync/src/main/java/com/twofasapp/feature/cloudsync/ui/googledrive/GoogleDriveSyncScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import com.twofasapp.core.design.foundation.preview.PreviewTheme
3838
import com.twofasapp.core.design.foundation.topbar.TopAppBar
3939
import com.twofasapp.core.locale.MdtLocale
4040
import com.twofasapp.data.cloud.authenticate.AuthenticateCloudService
41-
import com.twofasapp.data.cloud.authenticate.CloudServiceType
41+
import com.twofasapp.data.cloud.authenticate.DefaultCloudServiceType
4242
import com.twofasapp.data.cloud.domain.CloudConfig
4343
import com.twofasapp.feature.cloudsync.ui.common.SyncStatus
4444
import org.koin.androidx.compose.koinViewModel
@@ -144,7 +144,7 @@ private fun Content(
144144

145145
if (showCloudAuthentication) {
146146
AuthenticateCloudService(
147-
type = CloudServiceType.GoogleDrive,
147+
type = DefaultCloudServiceType,
148148
onDismissRequest = { showCloudAuthentication = false },
149149
onSuccess = onCloudAuthenticated,
150150
onError = onCloudAuthenticationError,

feature/startup/src/main/java/com/twofasapp/feature/startup/ui/restorevault/RestoreVaultScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import com.twofasapp.core.design.theme.RoundedShape16
4646
import com.twofasapp.core.design.theme.ScreenPadding
4747
import com.twofasapp.core.locale.MdtLocale
4848
import com.twofasapp.data.cloud.authenticate.AuthenticateCloudService
49-
import com.twofasapp.data.cloud.authenticate.CloudServiceType
49+
import com.twofasapp.data.cloud.authenticate.DefaultCloudServiceType
5050
import org.koin.androidx.compose.koinViewModel
5151

5252
@Composable
@@ -83,7 +83,7 @@ internal fun RestoreVaultScreen(
8383

8484
if (showCloudAuthentication) {
8585
AuthenticateCloudService(
86-
type = CloudServiceType.GoogleDrive,
86+
type = DefaultCloudServiceType,
8787
onDismissRequest = { showCloudAuthentication = false },
8888
onSuccess = {
8989
viewModel.updateRestoreCloudConfig(it)

gradle.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
# http://www.gradle.org/docs/current/userguide/build_environment.html
77
# Specifies the JVM arguments used for the daemon process.
88
# The setting is particularly useful for tweaking memory settings.
9-
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
9+
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
1010
# When configured, Gradle will run in incubating parallel mode.
1111
# This option should only be used with decoupled projects. For more details, visit
1212
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13-
# org.gradle.parallel=true
13+
org.gradle.parallel=true
14+
org.gradle.caching=true
1415
# AndroidX package structure to make it clearer which packages are bundled with the
1516
# Android operating system, and which are packaged with your app's APK
1617
# https://developer.android.com/topic/libraries/support-library/androidx-rn

gradle/libs.versions.toml

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
[versions]
2-
agp = "8.11.1"
2+
agp = "8.12.1"
33
cameraX = "1.4.2"
44
coil = "3.3.0"
55
composeActivity = "1.10.1"
6-
composeNavigation = "2.9.2"
7-
composeBom = "2025.07.00"
6+
composeNavigation = "2.9.3"
7+
composeBom = "2025.08.00"
88
credentials = "1.5.0"
9-
firebase = "34.0.0"
9+
firebase = "34.1.0"
1010
elmyr = "1.3.4"
1111
koin = "4.1.0"
12-
kotlin = "2.2.0"
12+
kotlin = "2.2.10"
1313
kotlinCoroutines = "1.10.2"
14-
kotlinKsp = "2.2.0-2.0.2"
15-
ktor = "3.2.2"
14+
kotlinKsp = "2.2.10-2.0.2"
15+
ktor = "3.2.3"
1616
ktlint = "5.1.1"
1717
lifecycle = "2.9.2"
1818
room = "2.7.2"
1919
pluto = "3.0.1"
20-
revenuecat = "9.1.2"
20+
revenuecat = "9.3.0"
2121
junit = "4.13.2"
22-
kotest = "5.9.1"
23-
espresso = "3.6.1"
24-
uiAndroid = "1.8.3"
22+
kotest = "6.0.0"
23+
espresso = "3.7.0"
24+
uiAndroid = "1.9.0"
2525
aboutLibraries = "12.2.4"
2626

2727
[libraries]
@@ -50,7 +50,6 @@ composeUi = { module = "androidx.compose.ui:ui" }
5050
composeUiTooling = { module = "androidx.compose.ui:ui-tooling" }
5151
credentialsPlayServices = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentials" }
5252
credentials = { module = "androidx.credentials:credentials", version.ref = "credentials" }
53-
core = "androidx.core:core-ktx:1.16.0"
5453
coreSplash = "androidx.core:core-splashscreen:1.0.1"
5554
colorPicker = "com.github.skydoves:colorpicker-compose:1.1.2"
5655
dataStore = "androidx.datastore:datastore-preferences:1.1.7"
@@ -60,7 +59,7 @@ firebaseCrashlytics = { module = "com.google.firebase:firebase-crashlytics" }
6059
firebaseMessaging = { module = "com.google.firebase:firebase-messaging" }
6160
elmyrCore = { module = "com.github.xgouchet.Elmyr:core", version.ref = "elmyr" }
6261
elmyrJunit4 = { module = "com.github.xgouchet.Elmyr:junit4", version.ref = "elmyr" }
63-
googleApiClientAndroid = "com.google.api-client:google-api-client-android:2.8.0"
62+
googleApiClientAndroid = "com.google.api-client:google-api-client-android:2.8.1"
6463
googleAuth = "com.google.android.gms:play-services-auth:21.4.0"
6564
googleDrive = "com.google.apis:google-api-services-drive:v3-rev20250717-2.0.0"
6665
googleId = "com.google.android.libraries.identity.googleid:googleid:1.1.1"
@@ -84,7 +83,7 @@ lifecycleProcess = { module = "androidx.lifecycle:lifecycle-process", version.re
8483
lifecycleSavedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "lifecycle" }
8584
lottie = "com.airbnb.android:lottie-compose:6.6.7"
8685
material = "com.google.android.material:material:1.12.0"
87-
opencsv = "com.opencsv:opencsv:5.11.2"
86+
opencsv = "com.opencsv:opencsv:5.12.0"
8887
playServicesCorutines = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.2"
8988
revenuecat = { module = "com.revenuecat.purchases:purchases", version.ref = "revenuecat" }
9089
revenuecatUi = { module = "com.revenuecat.purchases:purchases-ui", version.ref = "revenuecat" }
@@ -93,7 +92,7 @@ roomKtx = { module = "androidx.room:room-ktx", version.ref = "room" }
9392
roomRuntime = { module = "androidx.room:room-runtime", version.ref = "room" }
9493
timber = "com.jakewharton.timber:timber:5.0.1"
9594
truetime = "com.github.instacart:truetime-android:4.0.0.alpha"
96-
workManager = "androidx.work:work-runtime-ktx:2.10.2"
95+
workManager = "androidx.work:work-runtime-ktx:2.10.3"
9796
webkit = "androidx.webkit:webkit:1.14.0"
9897
pdfbox = "com.tom-roush:pdfbox-android:2.0.27.0"
9998
pluto = { module = "com.androidpluto:pluto", version.ref = "pluto" }
@@ -111,9 +110,9 @@ turbine = "app.cash.turbine:turbine:1.2.1"
111110
uiTestJunit = { module = "androidx.compose.ui:ui-test-junit4" }
112111
uiTestManifest = { module = "androidx.compose.ui:ui-test-manifest" }
113112
uiTestNavigation = { module = "androidx.navigation:navigation-testing", version.ref = "composeNavigation" }
114-
uiTestRunner = "androidx.test:runner:1.6.2"
115-
uiTestOrchestrator = "androidx.test:orchestrator:1.5.1"
116-
testCore = "androidx.test:core:1.6.1"
113+
uiTestRunner = "androidx.test:runner:1.7.0"
114+
uiTestOrchestrator = "androidx.test:orchestrator:1.6.1"
115+
testCore = "androidx.test:core:1.7.0"
117116
robolectric = "org.robolectric:robolectric:4.15.1"
118117
slf4jNoOp = "org.slf4j:slf4j-nop:2.0.17"
119118
kotlinCoroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinCoroutines" }

0 commit comments

Comments
 (0)