From d22d71eb5e5d15b1927edc8e8a3ab80fb4feffa5 Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Fri, 9 Oct 2026 20:33:31 +0200 Subject: [PATCH] Implement adaptive Android shell and safe navigation (#102) --- README.md | 2 + .../rfc1437/ironstorage/AdaptiveShellTest.kt | 177 +++ .../ironstorage/AuthenticationScreenTest.kt | 3 + .../de/rfc1437/ironstorage/EntryViewerTest.kt | 3 + .../rfc1437/ironstorage/PasswordScreenTest.kt | 3 + .../de/rfc1437/ironstorage/MainActivity.kt | 20 +- .../ironstorage/core/NativeEntryViewerTest.kt | 20 + .../core/AuthenticationRepository.kt | 6 +- .../core/NativeAuthenticationRepository.kt | 28 +- .../core/NativeFixtureShellRepository.kt | 12 +- .../core/NativeOnboardingRepository.kt | 4 + .../core/NativePasswordRepository.kt | 5 + .../ironstorage/core/PasswordRepository.kt | 1 + .../ironstorage/core/ProtectedPresentation.kt | 18 +- .../ironstorage/core/ShellRepository.kt | 2 +- .../core/NativeFixtureShellRepositoryTest.kt | 13 + .../core/ProtectedPresentationTest.kt | 27 + .../ironstorage/features/ApplicationTheme.kt | 25 + .../ironstorage/features/HomeScreen.kt | 8 +- .../ironstorage/features/OnboardingScreen.kt | 7 +- .../ironstorage/features/PasswordScreen.kt | 7 +- .../ironstorage/features/SearchScreen.kt | 5 +- .../ironstorage/features/ShellScreen.kt | 322 +++-- .../ironstorage/features/ShellViewModel.kt | 44 +- .../features/src/main/res/values/strings.xml | 5 + apple/Generated/ironstorage_mobile.swift | 1082 ++++++++++++++++- apple/Generated/ironstorage_mobileFFI.h | 361 ++++++ apple/Sources/App/IronStorageApp.swift | 1 + crates/mobile-bridge/src/lib.rs | 4 + crates/storage/src/mobile.rs | 6 + docs/android/adaptive-shell.md | 72 ++ tools/android-build/src/main.rs | 5 +- 32 files changed, 2194 insertions(+), 104 deletions(-) create mode 100644 android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AdaptiveShellTest.kt create mode 100644 android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ApplicationTheme.kt create mode 100644 docs/android/adaptive-shell.md diff --git a/README.md b/README.md index 19654d1..0dda686 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,8 @@ documented in [`docs/android/presentation-security.md`](docs/android/presentatio Name-only Android search and its state/security policy are documented in [`docs/android/name-search.md`](docs/android/name-search.md). +Adaptive navigation, appearance, safe restoration and shell validation are documented in +[`docs/android/adaptive-shell.md`](docs/android/adaptive-shell.md). ## Project layout diff --git a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AdaptiveShellTest.kt b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AdaptiveShellTest.kt new file mode 100644 index 0000000..112339e --- /dev/null +++ b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AdaptiveShellTest.kt @@ -0,0 +1,177 @@ +package de.rfc1437.ironstorage + +import android.content.pm.ActivityInfo +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.input.InputMode +import androidx.compose.ui.input.InputModeManager +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalInputModeManager +import androidx.compose.ui.semantics.SemanticsActions +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertHeightIsAtLeast +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsSelected +import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performKeyInput +import androidx.compose.ui.test.performScrollTo +import androidx.compose.ui.test.performSemanticsAction +import androidx.compose.ui.test.pressKey +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.dp +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModelProvider +import de.rfc1437.ironstorage.core.OnboardingRepository +import de.rfc1437.ironstorage.core.ShellFixtures +import de.rfc1437.ironstorage.core.ShellRepository +import de.rfc1437.ironstorage.core.ShellSnapshot +import de.rfc1437.ironstorage.core.ShellState +import de.rfc1437.ironstorage.core.ShellTab +import de.rfc1437.ironstorage.features.ApplicationTheme +import de.rfc1437.ironstorage.features.SetupFormViewModel +import de.rfc1437.ironstorage.features.ShellScreen +import de.rfc1437.ironstorage.features.ShellViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test + +class AdaptiveShellTest { + @get:Rule val compose = createAndroidComposeRule() + + @Test fun independentPreferencesStackAndSafeRecreationUseCompactAndExpandedNavigation() { + val original = ViewModelProvider(compose.activity)[ShellViewModel::class.java] + val setup = ViewModelProvider(compose.activity)[SetupFormViewModel::class.java] + val repository = Fixture() + val saved = SavedStateHandle() + var setupCancellations = 0 + val onboarding = object : OnboardingRepository by original.onboarding { + override fun cancel() { + setupCancellations += 1 + original.onboarding.cancel() + } + } + val model = + ShellViewModel( + repository, + onboarding, + original.passwords, + original.home, + original.search, + original.authentication, + saved + ) + compose.activity.runOnUiThread { + compose.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + compose.waitUntil(10000) { + compose.activity.resources.configuration.orientation == + android.content.res.Configuration.ORIENTATION_PORTRAIT + } + lateinit var inputMode: InputModeManager + fun show(viewModel: ShellViewModel, largeFont: Boolean = false) { + compose.activity.runOnUiThread { + compose.activity.setProtectedTestContent { + inputMode = LocalInputModeManager.current + val density = LocalDensity.current + CompositionLocalProvider( + LocalDensity provides Density(density.density, if (largeFont) 2f else 1f) + ) { + ApplicationTheme(original.authentication) { ShellScreen(viewModel, setup) } + } + } + } + } + show(model) + compose.onNodeWithTag("shell-bottom-navigation").assertIsDisplayed() + compose.runOnIdle { assertTrue(inputMode.requestInputMode(InputMode.Keyboard)) } + compose.onNodeWithTag("shell-tab-Preferences") + .assertHasClickAction().assertHeightIsAtLeast(48.dp) + .performSemanticsAction(SemanticsActions.RequestFocus) { assertTrue(it()) } + .performKeyInput { pressKey(Key.Enter) } + compose.runOnIdle { + setup.passphrase = "public fixture passphrase" + setup.armor = "public fixture armor" + setup.token = "public fixture token" + } + compose.onNodeWithTag("shell-open-appearance").performClick() + compose.runOnIdle { + assertEquals("", setup.passphrase) + assertEquals("", setup.armor) + assertEquals("", setup.token) + assertEquals(1, setupCancellations) + } + compose.onNodeWithText("System default").assertIsDisplayed() + compose.runOnIdle { setup.token = "public fixture token" } + compose.onNodeWithTag("shell-tab-Search").performClick() + compose.runOnIdle { + assertEquals("", setup.token) + assertEquals(2, setupCancellations) + } + compose.onNodeWithTag("shell-tab-Preferences").performClick() + compose.onNodeWithText("System default").assertIsDisplayed() + compose.onNodeWithText("Back").performScrollTo().performClick() + compose.onNodeWithTag("shell-open-appearance").assertIsDisplayed() + compose.onNodeWithTag("shell-open-appearance").performClick() + compose.runOnIdle { + assertEquals("Preferences", saved.get("shell.tab")) + assertEquals(true, saved.get("shell.appearance")) + assertTrue( + saved.keys().all { + saved.get(it) is String || + saved.get(it) is Boolean + } + ) + assertFalse(original.authentication.state.value.unlocked) + } + // A new ViewModel receives only primitive safe navigation values, as after process death. + val restored = + ShellViewModel( + repository, + onboarding, + original.passwords, + original.home, + original.search, + original.authentication, + SavedStateHandle( + saved.keys().associateWith { + saved.get(it) + } + ) + ) + show(restored) + compose.onNodeWithText("System default").assertIsDisplayed() + compose.onNodeWithTag("shell-tab-Preferences").assertIsSelected() + compose.activity.runOnUiThread { + compose.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + } + compose.waitUntil(10000) { + compose.activity.resources.configuration.orientation == + android.content.res.Configuration.ORIENTATION_LANDSCAPE + } + show(restored, largeFont = true) + compose.onNodeWithTag("shell-navigation-rail").assertIsDisplayed() + compose.onNodeWithTag("shell-tab-Preferences").assertIsSelected() + compose.onNodeWithText("Dark").performScrollTo().assertIsDisplayed() + compose.onNodeWithText("Back").performScrollTo().performClick() + compose.onNodeWithTag("shell-open-appearance").assertIsDisplayed() + } + + private class Fixture : ShellRepository { + override val snapshot = MutableStateFlow( + ShellSnapshot( + ShellState.Locked, + ShellFixtures.all.getValue(ShellState.Locked) + ) + ) + override fun selectTab(tab: ShellTab) { + snapshot.value = + snapshot.value.copy(selectedTab = tab) + } + } +} diff --git a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AuthenticationScreenTest.kt b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AuthenticationScreenTest.kt index 2b3cb99..4fe377c 100644 --- a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AuthenticationScreenTest.kt +++ b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/AuthenticationScreenTest.kt @@ -101,6 +101,9 @@ class AuthenticationScreenTest { var unlocks = 0 var disabled = 0 var cancelled = 0 + override fun setAppearance(appearance: de.rfc1437.ironstorage.core.Appearance) { + state.value = state.value.copy(appearance = appearance) + } override fun reload() = Unit override fun enroll(passphrase: String) { submitted = passphrase diff --git a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/EntryViewerTest.kt b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/EntryViewerTest.kt index e044a68..254b808 100644 --- a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/EntryViewerTest.kt +++ b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/EntryViewerTest.kt @@ -192,6 +192,9 @@ class EntryViewerTest { } } override val state = lease + override fun setAppearance(appearance: de.rfc1437.ironstorage.core.Appearance) { + state.value = state.value.copy(appearance = appearance) + } override fun reload() = Unit override fun unlock() = Unit override fun enroll(passphrase: String) = Unit diff --git a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/PasswordScreenTest.kt b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/PasswordScreenTest.kt index 499e39e..09bb92b 100644 --- a/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/PasswordScreenTest.kt +++ b/android/app/src/androidTest/kotlin/de/rfc1437/ironstorage/PasswordScreenTest.kt @@ -184,6 +184,9 @@ class PasswordScreenTest { } } override val state = lease + override fun setAppearance(appearance: de.rfc1437.ironstorage.core.Appearance) { + state.value = state.value.copy(appearance = appearance) + } override fun reload() = Unit override fun unlock() = error("Unexpected direct unlock") override fun enroll(passphrase: String) = error("Unexpected enrollment") diff --git a/android/app/src/main/kotlin/de/rfc1437/ironstorage/MainActivity.kt b/android/app/src/main/kotlin/de/rfc1437/ironstorage/MainActivity.kt index 328a7de..349098c 100644 --- a/android/app/src/main/kotlin/de/rfc1437/ironstorage/MainActivity.kt +++ b/android/app/src/main/kotlin/de/rfc1437/ironstorage/MainActivity.kt @@ -13,13 +13,20 @@ import android.view.WindowManager import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.luminance +import androidx.core.view.WindowCompat import androidx.fragment.app.FragmentActivity import dagger.hilt.android.AndroidEntryPoint import de.rfc1437.ironstorage.core.AuthenticationLifecycle import de.rfc1437.ironstorage.core.AuthenticationRepository import de.rfc1437.ironstorage.core.OnboardingRepository import de.rfc1437.ironstorage.core.ShellRepository +import de.rfc1437.ironstorage.features.ApplicationTheme import de.rfc1437.ironstorage.features.ProtectedContent import de.rfc1437.ironstorage.features.SetupFormViewModel import de.rfc1437.ironstorage.features.ShellViewModel @@ -132,8 +139,17 @@ class MainActivity : FragmentActivity() { enableEdgeToEdge() setContent { ProtectedContent(authentication.presentation) { - MaterialTheme { - VariantContent(viewModel, repository, setupForm) + ApplicationTheme(authentication) { + val lightBars = MaterialTheme.colorScheme.background.luminance() > 0.5f + SideEffect { + WindowCompat.getInsetsController(window, window.decorView).apply { + isAppearanceLightStatusBars = lightBars + isAppearanceLightNavigationBars = lightBars + } + } + Surface(Modifier.fillMaxSize()) { + VariantContent(viewModel, repository, setupForm) + } } } } diff --git a/android/core/src/androidTest/kotlin/de/rfc1437/ironstorage/core/NativeEntryViewerTest.kt b/android/core/src/androidTest/kotlin/de/rfc1437/ironstorage/core/NativeEntryViewerTest.kt index d8ac078..873ec92 100644 --- a/android/core/src/androidTest/kotlin/de/rfc1437/ironstorage/core/NativeEntryViewerTest.kt +++ b/android/core/src/androidTest/kotlin/de/rfc1437/ironstorage/core/NativeEntryViewerTest.kt @@ -59,6 +59,26 @@ class NativeEntryViewerTest { val key = setup.generate("Viewer fixture", password).selectedKey setup.setupLocal(requireNotNull(key), password) } + for (appearance in Appearance.entries) { + withContext(Dispatchers.Main.immediate) { authentication.setAppearance(appearance) } + withTimeout(30000) { + authentication.state.first { + !it.busy && + it.appearance == appearance + } + } + assertEquals(appearance, authentication.state.value.appearance) + assertFalse(authentication.state.value.unlocked) + } + val restoredAppearance = NativeAuthenticationRepository(session, scope, Dispatchers.IO) + withContext(Dispatchers.Main.immediate) { restoredAppearance.reload() } + withTimeout(30000) { + restoredAppearance.state.first { + !it.busy && + it.appearance == Appearance.Dark + } + } + assertFalse(restoredAppearance.state.value.unlocked) authentication.authenticated { auth -> val draft = auth.beginCreateEntry("", "viewer", password) val first = draft.page.fields.first() diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/AuthenticationRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/AuthenticationRepository.kt index 0c7208e..218cb6f 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/AuthenticationRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/AuthenticationRepository.kt @@ -2,6 +2,8 @@ package de.rfc1437.ironstorage.core import kotlinx.coroutines.flow.StateFlow +enum class Appearance { System, Light, Dark } + enum class AuthenticationFailureKind { PassphraseRequired, InvalidPassphrase, @@ -24,11 +26,13 @@ data class AuthenticationState( val biometricEnabled: Boolean = false, val remainingSeconds: ULong = 0u, val busy: Boolean = false, - val failure: AuthenticationFailure? = null + val failure: AuthenticationFailure? = null, + val appearance: Appearance = Appearance.System ) interface AuthenticationRepository { val presentation: ProtectedPresentation val state: StateFlow + fun setAppearance(appearance: Appearance) fun reload() fun unlock() fun enroll(passphrase: String) diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeAuthenticationRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeAuthenticationRepository.kt index 8323813..3740164 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeAuthenticationRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeAuthenticationRepository.kt @@ -2,6 +2,7 @@ package de.rfc1437.ironstorage.core import android.os.SystemClock import de.rfc1437.ironstorage.bridge.NativeBridgeException +import de.rfc1437.ironstorage.bridge.generated.MobileAppearance import de.rfc1437.ironstorage.bridge.generated.MobileAuthentication import de.rfc1437.ironstorage.bridge.generated.MobileAuthenticationErrorKind import de.rfc1437.ironstorage.bridge.generated.MobileAuthenticationFfiException @@ -48,7 +49,7 @@ class NativeAuthenticationRepository( try { val value = withContext(io) { current?.state() } if (ticket == sequence.get() && value != null) { - publish(value.toFacade()) + publish(value.toFacade().copy(appearance = mutable.value.appearance)) } } catch (_: MobileAuthenticationFfiException) { if (ticket == sequence.get()) lock() @@ -83,14 +84,19 @@ class NativeAuthenticationRepository( } result } - val value = withContext(io) { current!!.state() } - if (ticket == sequence.get()) publish(value.toFacade()) + val value = withContext(io) { + current!!.state().toFacade().copy( + appearance = current!!.mobileAppearance().toFacade() + ) + } + if (ticket == sequence.get()) publish(value) result } catch (error: MobileAuthenticationFfiException.Failed) { if (ticket == sequence.get()) { val value = withContext(io) { current?.state() } publish( (value?.toFacade() ?: AuthenticationState()).copy( + appearance = mutable.value.appearance, failure = error.toFacade() ) ) @@ -110,6 +116,15 @@ class NativeAuthenticationRepository( } } } + override fun setAppearance(appearance: Appearance) = run { + it.setMobileAppearance( + when (appearance) { + Appearance.System -> MobileAppearance.SYSTEM + Appearance.Light -> MobileAppearance.LIGHT + Appearance.Dark -> MobileAppearance.DARK + } + ) + } override fun reload() = run { it.state() } override fun unlock() = run { it.restoreBiometricLease() } override fun enroll(passphrase: String) = run { it.enrollBiometricUnlock(passphrase) } @@ -137,6 +152,7 @@ class NativeAuthenticationRepository( private fun unavailable() { publish( AuthenticationState( + appearance = mutable.value.appearance, failure = AuthenticationFailure( AuthenticationFailureKind.Configuration, "Authentication is unavailable", @@ -243,3 +259,9 @@ private fun MobileAuthenticationFfiException.Failed.toFacade() = AuthenticationF title, detail ) + +private fun MobileAppearance.toFacade(): Appearance = when (this) { + MobileAppearance.SYSTEM -> Appearance.System + MobileAppearance.LIGHT -> Appearance.Light + MobileAppearance.DARK -> Appearance.Dark +} diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepository.kt index b3c8259..eef85c5 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepository.kt @@ -33,12 +33,14 @@ class NativeFixtureShellRepository internal constructor( override val snapshot: StateFlow = mutableSnapshot private val revision = AtomicLong() private var job: Job? = null + private var selectedByUi = false init { refresh() } override fun selectTab(tab: ShellTab) { + selectedByUi = true mutableSnapshot.update { it.copy(selectedTab = tab) } } @@ -63,7 +65,13 @@ class NativeFixtureShellRepository internal constructor( mutableSnapshot.update { result.copy( fixture = fixture, - selectedTab = if (it.pages.isEmpty()) result.selectedTab else it.selectedTab + selectedTab = if (!selectedByUi && + it.pages.isEmpty() + ) { + result.selectedTab + } else { + it.selectedTab + } ) } } @@ -145,6 +153,7 @@ private fun MobileShellState.toFacade(): ShellState = when (this) { MobileShellState.READY -> ShellState.Ready MobileShellState.LOCKED -> ShellState.Locked MobileShellState.ERROR -> ShellState.Error + MobileShellState.STALE -> ShellState.Stale } private fun ShellState.toNative(): MobileShellState = when (this) { @@ -153,6 +162,7 @@ private fun ShellState.toNative(): MobileShellState = when (this) { ShellState.Ready -> MobileShellState.READY ShellState.Locked -> MobileShellState.LOCKED ShellState.Error -> MobileShellState.ERROR + ShellState.Stale -> MobileShellState.STALE } private fun MobileTab.toFacade(): ShellTab = when (this) { diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeOnboardingRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeOnboardingRepository.kt index 0367446..334a0e1 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeOnboardingRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativeOnboardingRepository.kt @@ -80,6 +80,7 @@ class NativeOnboardingRepository( } } val discovery = request.discover() + if (revoked.get()) throw interrupted() pollProgress = false mutableSetup.update { it.copy( @@ -116,6 +117,7 @@ class NativeOnboardingRepository( } override fun replaceToken(account: String, token: String) = runSetup { newOperation().replaceToken(account, token) + if (revoked.get()) throw interrupted() pollProgress = false mutableSetup.update { it.copy( @@ -143,6 +145,7 @@ class NativeOnboardingRepository( } } private fun completed(outcome: MobileOnboardingOutcome) { + if (revoked.get()) throw interrupted() updateStatus(requireNotNull(operation).status()) mutableSetup.update { it.copy(title = outcome.title, detail = outcome.detail, branches = emptyList()) @@ -152,6 +155,7 @@ class NativeOnboardingRepository( shell.refresh() } private fun updateStatus(status: MobileAndroidSetupStatus) { + if (revoked.get()) throw interrupted() pollProgress = false mutableSetup.update { it.copy( diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativePasswordRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativePasswordRepository.kt index 59ced40..1cdeef8 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativePasswordRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/NativePasswordRepository.kt @@ -134,6 +134,11 @@ class NativePasswordRepository( verified.present(page) } } + override fun leave() { + sequence.incrementAndGet() + mutable.value = mutable.value.cleared().copy(loading = false) + authentication.lifecycle(AuthenticationLifecycle.Background) + } override fun lock() { sequence.incrementAndGet() mutable.value = mutable.value.cleared().copy(loading = false) diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/PasswordRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/PasswordRepository.kt index 87b7fd1..2173bb2 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/PasswordRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/PasswordRepository.kt @@ -80,6 +80,7 @@ interface PasswordRepository { fun inspect(id: String, revision: String) fun back() fun unlock(passphrase: String) + fun leave() = lock() fun lock() fun reveal(id: ULong) fun hide(id: ULong) diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentation.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentation.kt index 6bcbe5b..2918225 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentation.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentation.kt @@ -12,23 +12,25 @@ class ProtectedPresentation { windows += 1 return AutoCloseable { windows -= 1 } } - private val metadata = linkedSetOf<() -> Unit>() + private val metadata = linkedMapOf Unit>() fun registerMetadata(clear: () -> Unit): AutoCloseable { - synchronized(metadata) { metadata.add(clear) } - return AutoCloseable { synchronized(metadata) { metadata.remove(clear) } } + val registration = Any() + synchronized(metadata) { metadata[registration] = clear } + return AutoCloseable { synchronized(metadata) { metadata.remove(registration) } } } fun clearMetadata() { - synchronized(metadata) { metadata.toList() }.forEach { it() } + synchronized(metadata) { metadata.values.toList() }.forEach { it() } } - private val listeners = linkedSetOf<() -> Unit>() + private val listeners = linkedMapOf Unit>() private val mutableGeneration = MutableStateFlow(0L) val generation: StateFlow = mutableGeneration fun register(clear: () -> Unit): AutoCloseable { - synchronized(listeners) { listeners.add(clear) } - return AutoCloseable { synchronized(listeners) { listeners.remove(clear) } } + val registration = Any() + synchronized(listeners) { listeners[registration] = clear } + return AutoCloseable { synchronized(listeners) { listeners.remove(registration) } } } fun clear() { - synchronized(listeners) { listeners.toList() }.forEach { it() } + synchronized(listeners) { listeners.values.toList() }.forEach { it() } mutableGeneration.value += 1 } } diff --git a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ShellRepository.kt b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ShellRepository.kt index 8943b48..a28705b 100644 --- a/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ShellRepository.kt +++ b/android/core/src/main/kotlin/de/rfc1437/ironstorage/core/ShellRepository.kt @@ -2,7 +2,7 @@ package de.rfc1437.ironstorage.core import kotlinx.coroutines.flow.StateFlow -enum class ShellState { Loading, Empty, Ready, Locked, Error } +enum class ShellState { Loading, Empty, Ready, Locked, Error, Stale } enum class ShellTab { Home, Passwords, Search, Totp, Preferences } enum class ShellIcon { diff --git a/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepositoryTest.kt b/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepositoryTest.kt index 68d5066..414ed81 100644 --- a/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepositoryTest.kt +++ b/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/NativeFixtureShellRepositoryTest.kt @@ -10,6 +10,19 @@ import org.junit.Test @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class NativeFixtureShellRepositoryTest { + @Test + fun restoredTabWinsOverInitialNativeCompletion() = runTest { + val pending = CompletableDeferred() + val repository = NativeFixtureShellRepository(backgroundScope) { state -> + pending.await() + ShellSnapshot(state, emptyList(), selectedTab = ShellTab.Home) + } + repository.selectTab(ShellTab.Preferences) + pending.complete(Unit) + runCurrent() + assertEquals(ShellTab.Preferences, repository.snapshot.value.selectedTab) + } + @Test fun cancelledNativeCompletionCannotReplaceNewerStateOrTabSelection() = runTest { val pending = CompletableDeferred() diff --git a/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentationTest.kt b/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentationTest.kt index 4d09ca2..7f4f1b9 100644 --- a/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentationTest.kt +++ b/android/core/src/test/kotlin/de/rfc1437/ironstorage/core/ProtectedPresentationTest.kt @@ -29,4 +29,31 @@ class ProtectedPresentationTest { assertEquals("disposed", secret) assertEquals(2L, presentation.generation.value) } + + @Test fun overlappingScreensCannotUnregisterAnotherOwnersIdenticalCallback() { + val presentation = ProtectedPresentation() + for (metadata in listOf(false, true)) { + var calls = 0 + val callback = { calls += 1 } + fun register() = if (metadata) { + presentation.registerMetadata( + callback + ) + } else { + presentation.register(callback) + } + fun clear() = if (metadata) presentation.clearMetadata() else presentation.clear() + val first = register() + val second = register() + clear() + assertEquals(2, calls) + first.close() + first.close() + clear() + assertEquals(3, calls) + second.close() + clear() + assertEquals(3, calls) + } + } } diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ApplicationTheme.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ApplicationTheme.kt new file mode 100644 index 0000000..5ccc40e --- /dev/null +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ApplicationTheme.kt @@ -0,0 +1,25 @@ +package de.rfc1437.ironstorage.features + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import de.rfc1437.ironstorage.core.Appearance +import de.rfc1437.ironstorage.core.AuthenticationRepository + +@Composable +fun ApplicationTheme(authentication: AuthenticationRepository, content: @Composable () -> Unit) { + val state by authentication.state.collectAsStateWithLifecycle() + val dark = when (state.appearance) { + Appearance.System -> isSystemInDarkTheme() + Appearance.Light -> false + Appearance.Dark -> true + } + MaterialTheme( + colorScheme = if (dark) darkColorScheme() else lightColorScheme(), + content = content + ) +} diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/HomeScreen.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/HomeScreen.kt index 13a059a..0a49e34 100644 --- a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/HomeScreen.kt +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/HomeScreen.kt @@ -46,13 +46,17 @@ import de.rfc1437.ironstorage.core.ShellIcon @OptIn(ExperimentalMaterial3Api::class) @Composable -fun HomeScreen(repository: HomeRepository, modifier: Modifier = Modifier) { +fun HomeScreen( + repository: HomeRepository, + modifier: Modifier = Modifier, + leaveOnDispose: Boolean = true +) { val state by repository.state.collectAsStateWithLifecycle() var committing by remember { mutableStateOf(false) } var message by remember { mutableStateOf("") } DisposableEffect(repository) { repository.enter() - onDispose { repository.leave() } + onDispose { if (leaveOnDispose) repository.leave() } } LifecycleEventEffect(Lifecycle.Event.ON_STOP) { committing = false diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/OnboardingScreen.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/OnboardingScreen.kt index 1d7acc2..93cb000 100644 --- a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/OnboardingScreen.kt +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/OnboardingScreen.kt @@ -54,7 +54,8 @@ class SetupFormViewModel @Inject constructor() : ViewModel() { fun OnboardingScreen( repository: OnboardingRepository, secrets: SetupFormViewModel, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + scroll: Boolean = true ) { ProtectedInputSurface() val state by repository.setup.collectAsStateWithLifecycle() @@ -77,7 +78,9 @@ fun OnboardingScreen( else -> step } Column( - modifier.widthIn(max = 640.dp).fillMaxWidth().verticalScroll(rememberScrollState()), + modifier.widthIn(max = 640.dp).fillMaxWidth().then( + if (scroll) Modifier.verticalScroll(rememberScrollState()) else Modifier + ), verticalArrangement = Arrangement.spacedBy(12.dp) ) { Text( diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/PasswordScreen.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/PasswordScreen.kt index 74bb2a0..48c67fd 100644 --- a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/PasswordScreen.kt +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/PasswordScreen.kt @@ -56,11 +56,12 @@ import de.rfc1437.ironstorage.core.ShellIcon fun PasswordScreen( repository: PasswordRepository, authentication: AuthenticationRepository, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + leaveOnDispose: Boolean = true ) { DisposableEffect(repository, authentication) { onDispose { - if (authentication.state.value.unlocked) repository.lock() + if (leaveOnDispose) repository.leave() } } val state by repository.state.collectAsStateWithLifecycle() @@ -87,7 +88,7 @@ fun PasswordScreen( LifecycleEventEffect(Lifecycle.Event.ON_STOP) { manual = false requested = false - repository.lock() + repository.leave() } LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { if (!state.loading && state.revision.isNotEmpty()) repository.refresh() diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/SearchScreen.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/SearchScreen.kt index 5877a05..13ae6de 100644 --- a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/SearchScreen.kt +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/SearchScreen.kt @@ -39,14 +39,15 @@ import de.rfc1437.ironstorage.core.SearchRepository fun SearchScreen( repository: SearchRepository, open: (PasswordRow) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + leaveOnDispose: Boolean = true ) { val state by repository.state.collectAsStateWithLifecycle() val keyboard = LocalSoftwareKeyboardController.current val focus = LocalFocusManager.current DisposableEffect(repository) { repository.refresh() - onDispose { repository.cancel() } + onDispose { if (leaveOnDispose) repository.cancel() } } LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { repository.refresh() } LifecycleEventEffect(Lifecycle.Event.ON_STOP) { repository.cancel() } diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellScreen.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellScreen.kt index 306233a..8f45ce3 100644 --- a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellScreen.kt +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellScreen.kt @@ -1,121 +1,303 @@ package de.rfc1437.ironstorage.features import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationRail +import androidx.compose.material3.NavigationRailItem +import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.liveRegion import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation3.runtime.NavEntry +import androidx.navigation3.ui.NavDisplay +import de.rfc1437.ironstorage.core.Appearance import de.rfc1437.ironstorage.core.NativeFailure import de.rfc1437.ironstorage.core.ShellIcon +import de.rfc1437.ironstorage.core.ShellSnapshot import de.rfc1437.ironstorage.core.ShellState import de.rfc1437.ironstorage.core.ShellTab +private enum class ShellRoute(val tab: ShellTab?) { + Home(ShellTab.Home), + Passwords(ShellTab.Passwords), + Search(ShellTab.Search), + Totp(ShellTab.Totp), + Preferences(ShellTab.Preferences), + Appearance(null) +} + @Composable fun ShellScreen(viewModel: ShellViewModel, setupForm: SetupFormViewModel) { val snapshot by viewModel.snapshot.collectAsStateWithLifecycle() - Scaffold( - bottomBar = { - NavigationBar { - snapshot.pages.forEach { page -> - val tab = page.tab - NavigationBarItem( - selected = snapshot.selectedTab == tab, - onClick = { viewModel.selectTab(tab) }, - icon = { - Icon( - painterResource(navigationIcon(page.icon)), - contentDescription = null + val appearancePage by viewModel.appearancePage.collectAsStateWithLifecycle() + fun selectTab(tab: ShellTab) { + if (tab != snapshot.selectedTab) setupForm.clearSecrets() + viewModel.selectTab(tab) + } + val route = when (snapshot.selectedTab) { + ShellTab.Home -> ShellRoute.Home + ShellTab.Passwords -> ShellRoute.Passwords + ShellTab.Search -> ShellRoute.Search + ShellTab.Totp -> ShellRoute.Totp + ShellTab.Preferences -> ShellRoute.Preferences + } + val backStack = if (route == ShellRoute.Preferences && appearancePage && !snapshot.fixture) { + listOf(route, ShellRoute.Appearance) + } else { + listOf(route) + } + // Actual window bounds, rather than device type, also handle desktop and split-screen resize. + BoxWithConstraints(Modifier.fillMaxSize()) { + val rail = maxWidth >= 600.dp + val largeFont = LocalDensity.current.fontScale >= 1.6f + Scaffold( + bottomBar = { + if (!rail) { + NavigationBar(Modifier.testTag("shell-bottom-navigation")) { + snapshot.pages.forEach { page -> + NavigationBarItem( + modifier = Modifier.testTag("shell-tab-${page.tab.name}"), + selected = snapshot.selectedTab == page.tab, + onClick = { selectTab(page.tab) }, + icon = { + Icon( + painterResource(navigationIcon(page.icon)), + if (largeFont) page.title else null + ) + }, + label = if (largeFont) null else ({ Text(page.title) }) ) - }, - label = { Text(page.title) } - ) + } + } } } + ) { padding -> + Row(Modifier.fillMaxSize().padding(padding)) { + if (rail) { + NavigationRail( + Modifier.testTag( + "shell-navigation-rail" + ).verticalScroll(rememberScrollState()) + ) { + snapshot.pages.forEach { page -> + NavigationRailItem( + modifier = Modifier.testTag("shell-tab-${page.tab.name}"), + selected = snapshot.selectedTab == page.tab, + onClick = { selectTab(page.tab) }, + icon = { Icon(painterResource(navigationIcon(page.icon)), null) }, + label = { Text(page.title) } + ) + } + } + } + NavDisplay( + backStack = backStack, + modifier = Modifier.weight( + 1f + ).fillMaxSize().padding(16.dp).testTag("shell-content"), + // Screen inputs are never saved by Navigation 3. + entryDecorators = emptyList(), + onBack = viewModel::backAppearance, + entryProvider = { key -> + NavEntry(key) { + if (key == ShellRoute.Appearance) { + AppearanceScreen(viewModel) + } else { + ShellDestination(key, snapshot, viewModel, setupForm) + } + } + } + ) + } } - ) { padding -> - Column( - modifier = Modifier.fillMaxSize().padding(padding).padding(24.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) - ) { - val ready = !snapshot.fixture && snapshot.nativeFailure == null && - snapshot.storageRecovery == null && - (snapshot.state == ShellState.Ready || snapshot.state == ShellState.Locked) - val browsing = ready && - snapshot.selectedTab == ShellTab.Passwords - val searching = ready && snapshot.selectedTab == ShellTab.Search - val home = ready && snapshot.selectedTab == ShellTab.Home - if (!browsing && !home && !searching) { + } +} + +@Composable +private fun ShellDestination( + route: ShellRoute, + snapshot: ShellSnapshot, + viewModel: ShellViewModel, + setupForm: SetupFormViewModel +) { + val ready = + !snapshot.fixture && snapshot.nativeFailure == null && snapshot.storageRecovery == null && + (snapshot.state == ShellState.Ready || snapshot.state == ShellState.Locked) + when { + !snapshot.fixture && snapshot.nativeFailure == null && snapshot.storageRecovery == null && + (snapshot.state == ShellState.Empty || route == ShellRoute.Preferences) -> { + Column( + Modifier.fillMaxSize().verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + if (ready && route == ShellRoute.Preferences) { + TextButton( + onClick = { + setupForm.clearSecrets() + viewModel.openAppearance() + }, + modifier = Modifier.testTag("shell-open-appearance") + ) { + Text(stringResource(R.string.shell_appearance)) + } + AuthenticationControls(viewModel.authentication) + } + OnboardingScreen(viewModel.onboarding, setupForm, scroll = false) + } + } + ready && route == ShellRoute.Passwords -> { + LaunchedEffect(viewModel) { viewModel.openPasswords() } + PasswordScreen( + viewModel.passwords, + viewModel.authentication, + Modifier.fillMaxSize(), + leaveOnDispose = false + ) + } + ready && route == ShellRoute.Search -> SearchScreen( + viewModel.search, + viewModel::openSearch, + Modifier.fillMaxSize(), + leaveOnDispose = false + ) + ready && route == ShellRoute.Home -> HomeScreen( + viewModel.home, + Modifier.fillMaxSize(), + leaveOnDispose = false + ) + else -> { + val page = snapshot.pages.firstOrNull { it.tab == route.tab } + Column( + Modifier.fillMaxSize().verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { Text( stringResource(R.string.app_name), style = MaterialTheme.typography.headlineLarge ) - } - val page = snapshot.pages.firstOrNull { it.tab == snapshot.selectedTab } - val nativeFailure = snapshot.nativeFailure - val title = if (nativeFailure != null) { - stringResource(R.string.native_failure_title) - } else { - page?.stateTitle ?: stringResource(R.string.scaffold_title) - } - val detail = if (nativeFailure == null) { - page?.detail ?: stringResource(R.string.scaffold_detail) - } else { - stringResource( - when (nativeFailure) { - NativeFailure.UnsupportedAbi -> R.string.native_unsupported_device - NativeFailure.MissingLibrary, - NativeFailure.ContractMismatch, - NativeFailure.ChecksumMismatch, - NativeFailure.LoadFailure -> R.string.native_update_required - NativeFailure.RustFailure, - NativeFailure.MainThreadCall -> R.string.native_operation_failed - } - ) - } - if (!snapshot.fixture && snapshot.nativeFailure == null && - snapshot.storageRecovery == null && - (snapshot.state == ShellState.Empty || snapshot.selectedTab == ShellTab.Preferences) - ) { - if (ready && snapshot.selectedTab == ShellTab.Preferences) { - AuthenticationControls(viewModel.authentication) - } - OnboardingScreen(viewModel.onboarding, setupForm) - } else if (browsing) { - LaunchedEffect(viewModel) { viewModel.openPasswords() } - PasswordScreen(viewModel.passwords, viewModel.authentication, Modifier.weight(1f)) - } else if (searching) { - SearchScreen(viewModel.search, viewModel::openSearch, Modifier.weight(1f)) - } else if (home) { - HomeScreen(viewModel.home, Modifier.weight(1f)) - } else { + val failure = snapshot.nativeFailure Text( - title, - modifier = Modifier.semantics { heading() }, + if (failure != + null + ) { + stringResource(R.string.native_failure_title) + } else { + page?.stateTitle + ?: stringResource(R.string.scaffold_title) + }, + modifier = Modifier.semantics { + heading() + liveRegion = LiveRegionMode.Polite + }, style = MaterialTheme.typography.headlineSmall ) - Text(detail) + Text( + if (failure == + null + ) { + page?.detail ?: stringResource(R.string.scaffold_detail) + } else { + stringResource( + when (failure) { + NativeFailure.UnsupportedAbi -> R.string.native_unsupported_device + NativeFailure.MissingLibrary, + NativeFailure.ContractMismatch, + NativeFailure.ChecksumMismatch, + NativeFailure.LoadFailure -> R.string.native_update_required + NativeFailure.RustFailure, + NativeFailure.MainThreadCall -> R.string.native_operation_failed + } + ) + } + ) if (snapshot.state == ShellState.Loading) CircularProgressIndicator() + if (!snapshot.fixture) { + TextButton(onClick = viewModel::refreshShell) { + Text(stringResource(R.string.shell_refresh)) + } + } } } } } +@Composable +private fun AppearanceScreen(viewModel: ShellViewModel) { + val state by viewModel.authentication.state.collectAsStateWithLifecycle() + Column( + Modifier.fillMaxSize().verticalScroll(rememberScrollState()).selectableGroup(), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + TextButton(onClick = viewModel::backAppearance) { + Text(stringResource(R.string.password_back)) + } + Text( + stringResource(R.string.shell_appearance), + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.semantics { + heading() + } + ) + Appearance.entries.forEach { appearance -> + val label = stringResource( + when (appearance) { + Appearance.System -> R.string.shell_appearance_system + Appearance.Light -> R.string.shell_appearance_light + Appearance.Dark -> R.string.shell_appearance_dark + } + ) + Row( + Modifier.fillMaxWidth().selectable( + selected = state.appearance == appearance, + enabled = !state.busy, + role = Role.RadioButton, + onClick = { viewModel.authentication.setAppearance(appearance) } + ).padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(label) + RadioButton( + selected = state.appearance == appearance, + enabled = !state.busy, + onClick = null + ) + } + } + state.failure?.let { + Text(it.title + "\n" + it.detail, color = MaterialTheme.colorScheme.error) + } + } +} + private fun navigationIcon(icon: ShellIcon): Int = when (icon) { ShellIcon.Home -> R.drawable.ic_home ShellIcon.Key -> R.drawable.ic_key diff --git a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellViewModel.kt b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellViewModel.kt index ae6cf7c..d088dfb 100644 --- a/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellViewModel.kt +++ b/android/features/src/main/kotlin/de/rfc1437/ironstorage/features/ShellViewModel.kt @@ -11,8 +11,11 @@ import de.rfc1437.ironstorage.core.PasswordRepository import de.rfc1437.ironstorage.core.PasswordRow import de.rfc1437.ironstorage.core.SearchRepository import de.rfc1437.ironstorage.core.ShellRepository +import de.rfc1437.ironstorage.core.ShellState import de.rfc1437.ironstorage.core.ShellTab import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @HiltViewModel @@ -26,8 +29,29 @@ class ShellViewModel @Inject constructor( private val saved: SavedStateHandle ) : ViewModel() { val snapshot = repository.snapshot + private val mutableAppearancePage = + MutableStateFlow(saved.get("shell.appearance") ?: false) + val appearancePage = mutableAppearancePage.asStateFlow() + + fun openAppearance() { + onboarding.cancel() + if (authentication.state.value.busy) authentication.cancel() + saved["shell.appearance"] = true + mutableAppearancePage.value = true + } + fun backAppearance() { + saved["shell.appearance"] = false + mutableAppearancePage.value = false + } init { + val restored = saved.get("shell.tab")?.let { name -> + ShellTab.entries.firstOrNull { it.name == name } + } + if (restored != null) repository.selectTab(restored) + viewModelScope.launch { + snapshot.collect { saved["shell.tab"] = it.selectedTab.name } + } viewModelScope.launch { passwords.state.collect { value -> if (!value.loading && value.error == null && value.revision.isNotEmpty()) { @@ -41,7 +65,7 @@ class ShellViewModel @Inject constructor( private var passwordsRestored = false fun openSearch(row: PasswordRow) { passwords.openSearch(row.id, row.revision) - repository.selectTab(ShellTab.Passwords) + selectTab(ShellTab.Passwords) } fun openPasswords() { if (!passwordsRestored) { @@ -58,10 +82,28 @@ class ShellViewModel @Inject constructor( passwords.refresh() } } + fun refreshShell() = repository.refresh() fun selectTab(tab: ShellTab) { + if (tab != snapshot.value.selectedTab) leaveDestination() + saved["shell.tab"] = tab.name repository.selectTab(tab) } + private fun leaveDestination() { + val current = snapshot.value + if (current.fixture) return + if (current.state == ShellState.Empty || current.selectedTab == ShellTab.Preferences) { + onboarding.cancel() + } + when (current.selectedTab) { + ShellTab.Home -> home.leave() + ShellTab.Passwords -> passwords.leave() + ShellTab.Search -> search.cancel() + ShellTab.Preferences -> if (authentication.state.value.busy) authentication.cancel() + ShellTab.Totp -> Unit + } + } override fun onCleared() { + leaveDestination() passwords.lock() } } diff --git a/android/features/src/main/res/values/strings.xml b/android/features/src/main/res/values/strings.xml index 4e2878a..cf6e7c7 100644 --- a/android/features/src/main/res/values/strings.xml +++ b/android/features/src/main/res/values/strings.xml @@ -113,4 +113,9 @@ Results: %1$d Open entry Search cancelled. Refresh to try again. + Appearance + System default + Light + Dark + Reload state diff --git a/apple/Generated/ironstorage_mobile.swift b/apple/Generated/ironstorage_mobile.swift index 056188e..849bc15 100644 --- a/apple/Generated/ironstorage_mobile.swift +++ b/apple/Generated/ironstorage_mobile.swift @@ -1035,6 +1035,363 @@ public func FfiConverterTypeMobileAndroidKeystore_lower(_ value: MobileAndroidKe +public protocol MobileAndroidOnboardingProtocol: AnyObject, Sendable { + + func cancel() + + func generate(userId: String, passphrase: String) throws -> MobileAndroidSetupStatus + + func importKey(armor: String, passphrase: String?) throws -> MobileAndroidSetupStatus + + func progress() -> MobileOnboardingProgress + + func remote(serverUrl: String, account: String, repositoryPath: String, applicationToken: String) throws -> MobileAndroidRemoteOnboarding + + func replaceToken(account: String, applicationToken: String) throws + + func setupLocal(fingerprint: String, passphrase: String?) throws -> MobileOnboardingOutcome + + func status() throws -> MobileAndroidSetupStatus + +} +open class MobileAndroidOnboarding: MobileAndroidOnboardingProtocol, @unchecked Sendable { + fileprivate let handle: UInt64 + + /// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public struct NoHandle { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + required public init(unsafeFromHandle handle: UInt64) { + self.handle = handle + } + + // This constructor can be used to instantiate a fake object. + // - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + // + // - Warning: + // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public init(noHandle: NoHandle) { + self.handle = 0 + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public func uniffiCloneHandle() -> UInt64 { + return try! rustCall { uniffi_ironstorage_mobile_fn_clone_mobileandroidonboarding(self.handle, $0) } + } + // No primary constructor declared for this class. + + deinit { + if handle == 0 { + // Mock objects have handle=0 don't try to free them + return + } + + try! rustCall { uniffi_ironstorage_mobile_fn_free_mobileandroidonboarding(handle, $0) } + } + + + + +open func cancel() {try! rustCall() { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_cancel( + self.uniffiCloneHandle(),uniffiCallStatus + ) +} +} + +open func generate(userId: String, passphrase: String)throws -> MobileAndroidSetupStatus { + return try FfiConverterTypeMobileAndroidSetupStatus_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_generate( + self.uniffiCloneHandle(), + FfiConverterString.lower(userId), + FfiConverterString.lower(passphrase),uniffiCallStatus + ) +}) +} + +open func importKey(armor: String, passphrase: String?)throws -> MobileAndroidSetupStatus { + return try FfiConverterTypeMobileAndroidSetupStatus_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_import_key( + self.uniffiCloneHandle(), + FfiConverterString.lower(armor), + FfiConverterOptionString.lower(passphrase),uniffiCallStatus + ) +}) +} + +open func progress() -> MobileOnboardingProgress { + return try! FfiConverterTypeMobileOnboardingProgress_lift(try! rustCall() { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_progress( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func remote(serverUrl: String, account: String, repositoryPath: String, applicationToken: String)throws -> MobileAndroidRemoteOnboarding { + return try FfiConverterTypeMobileAndroidRemoteOnboarding_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_remote( + self.uniffiCloneHandle(), + FfiConverterString.lower(serverUrl), + FfiConverterString.lower(account), + FfiConverterString.lower(repositoryPath), + FfiConverterString.lower(applicationToken),uniffiCallStatus + ) +}) +} + +open func replaceToken(account: String, applicationToken: String)throws {try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_replace_token( + self.uniffiCloneHandle(), + FfiConverterString.lower(account), + FfiConverterString.lower(applicationToken),uniffiCallStatus + ) +} +} + +open func setupLocal(fingerprint: String, passphrase: String?)throws -> MobileOnboardingOutcome { + return try FfiConverterTypeMobileOnboardingOutcome_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_setup_local( + self.uniffiCloneHandle(), + FfiConverterString.lower(fingerprint), + FfiConverterOptionString.lower(passphrase),uniffiCallStatus + ) +}) +} + +open func status()throws -> MobileAndroidSetupStatus { + return try FfiConverterTypeMobileAndroidSetupStatus_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_status( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMobileAndroidOnboarding: FfiConverter { + typealias FfiType = UInt64 + typealias SwiftType = MobileAndroidOnboarding + + public static func lift(_ handle: UInt64) throws -> MobileAndroidOnboarding { + return MobileAndroidOnboarding(unsafeFromHandle: handle) + } + + public static func lower(_ value: MobileAndroidOnboarding) -> UInt64 { + return value.uniffiCloneHandle() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileAndroidOnboarding { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + + public static func write(_ value: MobileAndroidOnboarding, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidOnboarding_lift(_ handle: UInt64) throws -> MobileAndroidOnboarding { + return try FfiConverterTypeMobileAndroidOnboarding.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidOnboarding_lower(_ value: MobileAndroidOnboarding) -> UInt64 { + return FfiConverterTypeMobileAndroidOnboarding.lower(value) +} + + + + + + +public protocol MobileAndroidRemoteOnboardingProtocol: AnyObject, Sendable { + + func cancel() + + func cloneStore(branch: String, fingerprint: String, passphrase: String?, useExisting: Bool) throws -> MobileOnboardingOutcome + + func connect() throws -> MobileOnboardingOutcome + + func discover() throws -> MobileOnboardingDiscovery + +} +open class MobileAndroidRemoteOnboarding: MobileAndroidRemoteOnboardingProtocol, @unchecked Sendable { + fileprivate let handle: UInt64 + + /// Used to instantiate a [FFIObject] without an actual handle, for fakes in tests, mostly. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public struct NoHandle { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + required public init(unsafeFromHandle handle: UInt64) { + self.handle = handle + } + + // This constructor can be used to instantiate a fake object. + // - Parameter noHandle: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + // + // - Warning: + // Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing handle the FFI lower functions will crash. +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public init(noHandle: NoHandle) { + self.handle = 0 + } + +#if swift(>=5.8) + @_documentation(visibility: private) +#endif + public func uniffiCloneHandle() -> UInt64 { + return try! rustCall { uniffi_ironstorage_mobile_fn_clone_mobileandroidremoteonboarding(self.handle, $0) } + } + // No primary constructor declared for this class. + + deinit { + if handle == 0 { + // Mock objects have handle=0 don't try to free them + return + } + + try! rustCall { uniffi_ironstorage_mobile_fn_free_mobileandroidremoteonboarding(handle, $0) } + } + + + + +open func cancel() {try! rustCall() { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_cancel( + self.uniffiCloneHandle(),uniffiCallStatus + ) +} +} + +open func cloneStore(branch: String, fingerprint: String, passphrase: String?, useExisting: Bool)throws -> MobileOnboardingOutcome { + return try FfiConverterTypeMobileOnboardingOutcome_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_clone_store( + self.uniffiCloneHandle(), + FfiConverterString.lower(branch), + FfiConverterString.lower(fingerprint), + FfiConverterOptionString.lower(passphrase), + FfiConverterBool.lower(useExisting),uniffiCallStatus + ) +}) +} + +open func connect()throws -> MobileOnboardingOutcome { + return try FfiConverterTypeMobileOnboardingOutcome_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_connect( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + +open func discover()throws -> MobileOnboardingDiscovery { + return try FfiConverterTypeMobileOnboardingDiscovery_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_discover( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + + + +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMobileAndroidRemoteOnboarding: FfiConverter { + typealias FfiType = UInt64 + typealias SwiftType = MobileAndroidRemoteOnboarding + + public static func lift(_ handle: UInt64) throws -> MobileAndroidRemoteOnboarding { + return MobileAndroidRemoteOnboarding(unsafeFromHandle: handle) + } + + public static func lower(_ value: MobileAndroidRemoteOnboarding) -> UInt64 { + return value.uniffiCloneHandle() + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileAndroidRemoteOnboarding { + let handle: UInt64 = try readInt(&buf) + return try lift(handle) + } + + public static func write(_ value: MobileAndroidRemoteOnboarding, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidRemoteOnboarding_lift(_ handle: UInt64) throws -> MobileAndroidRemoteOnboarding { + return try FfiConverterTypeMobileAndroidRemoteOnboarding.lift(handle) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidRemoteOnboarding_lower(_ value: MobileAndroidRemoteOnboarding) -> UInt64 { + return FfiConverterTypeMobileAndroidRemoteOnboarding.lower(value) +} + + + + + + public protocol MobileAuthenticationProtocol: AnyObject, Sendable { func acknowledgeWatchSnapshot(receipt: Data) throws -> MobileWatchSnapshotStatus @@ -1053,12 +1410,18 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable { func copyEntryField(path: String, field: UInt64) throws -> MobileEntryCopy + func copyPresentedEntryField(path: String, fieldId: UInt64, revision: String) throws -> MobileEntryCopy + + func copyPresentedTotpCode(path: String, revision: String, unixSeconds: UInt64) throws -> MobileEntryCopy + func copyTotpCode(path: String, unixSeconds: UInt64) throws -> MobileEntryCopy func createDirectory(parent: String, name: String) throws func discardEntryEditor(editor: UInt64) throws + func enrollBiometricUnlock(passphrase: String) throws -> MobileAuthenticationState + func entryEditor(editor: UInt64) throws -> MobileEntryEditorPage func entryPage(path: String, unixSeconds: UInt64) throws -> MobileEntryPresentation @@ -1073,14 +1436,20 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable { func mobileAppearance() throws -> MobileAppearance + func navigationEntry(directory: String, rowId: String, revision: String, passphrase: String?, unixSeconds: UInt64) throws -> MobileEntryPresentation + func performEntryMutation(request: MobileMutationRequest) throws -> MobileMutationOutcome func preferences(watchSupported: Bool, watchPaired: Bool, watchAppInstalled: Bool) throws -> MobilePreferences func prepareEntryMutation(path: String, action: MobileMutationAction) throws -> MobileMutationPlan + func preparePresentedEntryMutation(path: String, revision: String, action: MobileMutationAction) throws -> MobileMutationPlan + func prepareWatchSnapshot(platformPairingIdentity: String) throws -> MobileWatchSnapshotTransfer + func refreshEntryPresentation(path: String, revision: String, unixSeconds: UInt64) throws -> MobileEntryPresentation + func removeApplicationToken() throws func removeEntryEditorField(editor: UInt64, field: UInt64) throws -> MobileEntryEditorPage @@ -1089,8 +1458,14 @@ public protocol MobileAuthenticationProtocol: AnyObject, Sendable { func replaceEntryField(path: String, field: UInt64, value: String) throws -> MobileEntryPage + func replacePresentedEntryField(path: String, fieldId: UInt64, revision: String, value: String) throws -> MobileEntryPage + + func restoreBiometricLease() throws -> MobileAuthenticationState + func revealEntryField(path: String, field: UInt64) throws -> String + func revealPresentedEntryField(path: String, fieldId: UInt64, revision: String) throws -> String + func saveEntryEditor(editor: UInt64, fields: [MobileEntryEditorInput]) throws -> MobileEntryPage func searchCachedTotpPage(query: String) throws -> MobileTotpPage? @@ -1263,6 +1638,30 @@ open func copyEntryField(path: String, field: UInt64)throws -> MobileEntryCopy }) } +open func copyPresentedEntryField(path: String, fieldId: UInt64, revision: String)throws -> MobileEntryCopy { + return try FfiConverterTypeMobileEntryCopy_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_copy_presented_entry_field( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterUInt64.lower(fieldId), + FfiConverterString.lower(revision),uniffiCallStatus + ) +}) +} + +open func copyPresentedTotpCode(path: String, revision: String, unixSeconds: UInt64)throws -> MobileEntryCopy { + return try FfiConverterTypeMobileEntryCopy_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_copy_presented_totp_code( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterString.lower(revision), + FfiConverterUInt64.lower(unixSeconds),uniffiCallStatus + ) +}) +} + open func copyTotpCode(path: String, unixSeconds: UInt64)throws -> MobileEntryCopy { return try FfiConverterTypeMobileEntryCopy_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in @@ -1293,6 +1692,16 @@ open func discardEntryEditor(editor: UInt64)throws {try rustCallWithError(FfiC } } +open func enrollBiometricUnlock(passphrase: String)throws -> MobileAuthenticationState { + return try FfiConverterTypeMobileAuthenticationState_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_enroll_biometric_unlock( + self.uniffiCloneHandle(), + FfiConverterString.lower(passphrase),uniffiCallStatus + ) +}) +} + open func entryEditor(editor: UInt64)throws -> MobileEntryEditorPage { return try FfiConverterTypeMobileEntryEditorPage_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in @@ -1363,6 +1772,20 @@ open func mobileAppearance()throws -> MobileAppearance { }) } +open func navigationEntry(directory: String, rowId: String, revision: String, passphrase: String?, unixSeconds: UInt64)throws -> MobileEntryPresentation { + return try FfiConverterTypeMobileEntryPresentation_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_navigation_entry( + self.uniffiCloneHandle(), + FfiConverterString.lower(directory), + FfiConverterString.lower(rowId), + FfiConverterString.lower(revision), + FfiConverterOptionString.lower(passphrase), + FfiConverterUInt64.lower(unixSeconds),uniffiCallStatus + ) +}) +} + open func performEntryMutation(request: MobileMutationRequest)throws -> MobileMutationOutcome { return try FfiConverterTypeMobileMutationOutcome_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in @@ -1396,6 +1819,18 @@ open func prepareEntryMutation(path: String, action: MobileMutationAction)throws }) } +open func preparePresentedEntryMutation(path: String, revision: String, action: MobileMutationAction)throws -> MobileMutationPlan { + return try FfiConverterTypeMobileMutationPlan_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_prepare_presented_entry_mutation( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterString.lower(revision), + FfiConverterTypeMobileMutationAction_lower(action),uniffiCallStatus + ) +}) +} + open func prepareWatchSnapshot(platformPairingIdentity: String)throws -> MobileWatchSnapshotTransfer { return try FfiConverterTypeMobileWatchSnapshotTransfer_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in @@ -1406,6 +1841,18 @@ open func prepareWatchSnapshot(platformPairingIdentity: String)throws -> Mobile }) } +open func refreshEntryPresentation(path: String, revision: String, unixSeconds: UInt64)throws -> MobileEntryPresentation { + return try FfiConverterTypeMobileEntryPresentation_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_refresh_entry_presentation( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterString.lower(revision), + FfiConverterUInt64.lower(unixSeconds),uniffiCallStatus + ) +}) +} + open func removeApplicationToken()throws {try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in uniffi_ironstorage_mobile_fn_method_mobileauthentication_remove_application_token( @@ -1449,6 +1896,28 @@ open func replaceEntryField(path: String, field: UInt64, value: String)throws - }) } +open func replacePresentedEntryField(path: String, fieldId: UInt64, revision: String, value: String)throws -> MobileEntryPage { + return try FfiConverterTypeMobileEntryPage_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_replace_presented_entry_field( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterUInt64.lower(fieldId), + FfiConverterString.lower(revision), + FfiConverterString.lower(value),uniffiCallStatus + ) +}) +} + +open func restoreBiometricLease()throws -> MobileAuthenticationState { + return try FfiConverterTypeMobileAuthenticationState_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_restore_biometric_lease( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + open func revealEntryField(path: String, field: UInt64)throws -> String { return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in @@ -1460,6 +1929,18 @@ open func revealEntryField(path: String, field: UInt64)throws -> String { }) } +open func revealPresentedEntryField(path: String, fieldId: UInt64, revision: String)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileauthentication_reveal_presented_entry_field( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterUInt64.lower(fieldId), + FfiConverterString.lower(revision),uniffiCallStatus + ) +}) +} + open func saveEntryEditor(editor: UInt64, fields: [MobileEntryEditorInput])throws -> MobileEntryPage { return try FfiConverterTypeMobileEntryPage_lift(try rustCallWithError(FfiConverterTypeMobileAuthenticationFfiError_lift) { uniffiCallStatus in @@ -2182,6 +2663,8 @@ public func FfiConverterTypeMobileClock_lower(_ value: MobileClock) -> UInt64 { public protocol MobileHomeOperationProtocol: AnyObject, Sendable { + func activityKnown() -> Bool + func cached() throws -> MobileHomePage func cancel() @@ -2190,12 +2673,16 @@ public protocol MobileHomeOperationProtocol: AnyObject, Sendable { func fetch() throws -> MobileHomePage + func identity() throws -> MobileGitIdentity + func progress() -> MobileHomeProgress func pull() throws -> MobileHomePage func push() throws -> MobileHomePage + func refreshGesture() throws -> MobileHomePage + func refreshIfStale() throws -> MobileHomePage func sync() throws -> MobileHomePage @@ -2254,6 +2741,15 @@ open class MobileHomeOperation: MobileHomeOperationProtocol, @unchecked Sendable +open func activityKnown() -> Bool { + return try! FfiConverterBool.lift(try! rustCall() { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_activity_known( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + open func cached()throws -> MobileHomePage { return try FfiConverterTypeMobileHomePage_lift(try rustCallWithError(FfiConverterTypeMobileHomeFfiError_lift) { uniffiCallStatus in @@ -2290,6 +2786,15 @@ open func fetch()throws -> MobileHomePage { }) } +open func identity()throws -> MobileGitIdentity { + return try FfiConverterTypeMobileGitIdentity_lift(try rustCallWithError(FfiConverterTypeMobileHomeFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_identity( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + open func progress() -> MobileHomeProgress { return try! FfiConverterTypeMobileHomeProgress_lift(try! rustCall() { uniffiCallStatus in @@ -2317,6 +2822,15 @@ open func push()throws -> MobileHomePage { }) } +open func refreshGesture()throws -> MobileHomePage { + return try FfiConverterTypeMobileHomePage_lift(try rustCallWithError(FfiConverterTypeMobileHomeFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_refresh_gesture( + self.uniffiCloneHandle(),uniffiCallStatus + ) +}) +} + open func refreshIfStale()throws -> MobileHomePage { return try FfiConverterTypeMobileHomePage_lift(try rustCallWithError(FfiConverterTypeMobileHomeFfiError_lift) { uniffiCallStatus in @@ -2953,8 +3467,18 @@ public func FfiConverterTypeMobileOnboardingOperation_lower(_ value: MobileOnboa public protocol MobilePrivateRuntimeProtocol: AnyObject, Sendable { + func openPasswordSearchResult(id: String, revision: String) throws -> MobilePasswordNavigation + + func passwordNameSearch(query: String) throws -> [MobileNavigationRow] + + func passwordNavigation(path: String, selected: String?, revision: String?, activate: Bool) throws -> MobilePasswordNavigation + func activate() throws + func androidHomeOperation(backend: MobileSecretBackend, clock: MobileClock) throws -> MobileHomeOperation + + func androidOnboarding(backend: MobileSecretBackend) throws -> MobileAndroidOnboarding + func androidSecretBackend(keystore: MobileAndroidKeystore) throws -> MobileSecretBackend func authentication(backend: MobileSecretBackend, clock: MobileClock) throws -> MobileAuthentication @@ -3023,6 +3547,40 @@ open class MobilePrivateRuntime: MobilePrivateRuntimeProtocol, @unchecked Sendab +open func openPasswordSearchResult(id: String, revision: String)throws -> MobilePasswordNavigation { + return try FfiConverterTypeMobilePasswordNavigation_lift(try rustCallWithError(FfiConverterTypeMobilePasswordFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_open_password_search_result( + self.uniffiCloneHandle(), + FfiConverterString.lower(id), + FfiConverterString.lower(revision),uniffiCallStatus + ) +}) +} + +open func passwordNameSearch(query: String)throws -> [MobileNavigationRow] { + return try FfiConverterSequenceTypeMobileNavigationRow.lift(try rustCallWithError(FfiConverterTypeMobilePasswordFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_password_name_search( + self.uniffiCloneHandle(), + FfiConverterString.lower(query),uniffiCallStatus + ) +}) +} + +open func passwordNavigation(path: String, selected: String?, revision: String?, activate: Bool)throws -> MobilePasswordNavigation { + return try FfiConverterTypeMobilePasswordNavigation_lift(try rustCallWithError(FfiConverterTypeMobilePasswordFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_password_navigation( + self.uniffiCloneHandle(), + FfiConverterString.lower(path), + FfiConverterOptionString.lower(selected), + FfiConverterOptionString.lower(revision), + FfiConverterBool.lower(activate),uniffiCallStatus + ) +}) +} + open func activate()throws {try rustCallWithError(FfiConverterTypeMobilePrivateStorageFfiError_lift) { uniffiCallStatus in uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_activate( @@ -3031,6 +3589,27 @@ open func activate()throws {try rustCallWithError(FfiConverterTypeMobilePrivat } } +open func androidHomeOperation(backend: MobileSecretBackend, clock: MobileClock)throws -> MobileHomeOperation { + return try FfiConverterTypeMobileHomeOperation_lift(try rustCallWithError(FfiConverterTypeMobileHomeFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_android_home_operation( + self.uniffiCloneHandle(), + FfiConverterTypeMobileSecretBackend_lower(backend), + FfiConverterTypeMobileClock_lower(clock),uniffiCallStatus + ) +}) +} + +open func androidOnboarding(backend: MobileSecretBackend)throws -> MobileAndroidOnboarding { + return try FfiConverterTypeMobileAndroidOnboarding_lift(try rustCallWithError(FfiConverterTypeMobileOnboardingFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_android_onboarding( + self.uniffiCloneHandle(), + FfiConverterTypeMobileSecretBackend_lower(backend),uniffiCallStatus + ) +}) +} + open func androidSecretBackend(keystore: MobileAndroidKeystore)throws -> MobileSecretBackend { return try FfiConverterTypeMobileSecretBackend_lift(try rustCallWithError(FfiConverterTypeMobilePrivateStorageFfiError_lift) { uniffiCallStatus in @@ -3855,6 +4434,134 @@ public func FfiConverterTypeMobileAndroidKeySpecification_lower(_ value: MobileA } +public struct MobileAndroidSetupKey: Equatable, Hashable { + public var fingerprint: String + public var userIds: [String] + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(fingerprint: String, userIds: [String]) { + self.fingerprint = fingerprint + self.userIds = userIds + } + + + + +} + +#if compiler(>=6) +extension MobileAndroidSetupKey: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMobileAndroidSetupKey: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileAndroidSetupKey { + return + try MobileAndroidSetupKey( + fingerprint: FfiConverterString.read(from: &buf), + userIds: FfiConverterSequenceString.read(from: &buf) + ) + } + + public static func write(_ value: MobileAndroidSetupKey, into buf: inout [UInt8]) { + FfiConverterString.write(value.fingerprint, into: &buf) + FfiConverterSequenceString.write(value.userIds, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidSetupKey_lift(_ buf: RustBuffer) throws -> MobileAndroidSetupKey { + return try FfiConverterTypeMobileAndroidSetupKey.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidSetupKey_lower(_ value: MobileAndroidSetupKey) -> RustBuffer { + return FfiConverterTypeMobileAndroidSetupKey.lower(value) +} + + +public struct MobileAndroidSetupStatus: Equatable, Hashable { + public var configured: Bool + public var connected: Bool + public var existingClone: Bool + public var keys: [MobileAndroidSetupKey] + public var selectedKey: String? + public var title: String + public var detail: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(configured: Bool, connected: Bool, existingClone: Bool, keys: [MobileAndroidSetupKey], selectedKey: String?, title: String, detail: String) { + self.configured = configured + self.connected = connected + self.existingClone = existingClone + self.keys = keys + self.selectedKey = selectedKey + self.title = title + self.detail = detail + } + + + + +} + +#if compiler(>=6) +extension MobileAndroidSetupStatus: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMobileAndroidSetupStatus: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileAndroidSetupStatus { + return + try MobileAndroidSetupStatus( + configured: FfiConverterBool.read(from: &buf), + connected: FfiConverterBool.read(from: &buf), + existingClone: FfiConverterBool.read(from: &buf), + keys: FfiConverterSequenceTypeMobileAndroidSetupKey.read(from: &buf), + selectedKey: FfiConverterOptionString.read(from: &buf), + title: FfiConverterString.read(from: &buf), + detail: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: MobileAndroidSetupStatus, into buf: inout [UInt8]) { + FfiConverterBool.write(value.configured, into: &buf) + FfiConverterBool.write(value.connected, into: &buf) + FfiConverterBool.write(value.existingClone, into: &buf) + FfiConverterSequenceTypeMobileAndroidSetupKey.write(value.keys, into: &buf) + FfiConverterOptionString.write(value.selectedKey, into: &buf) + FfiConverterString.write(value.title, into: &buf) + FfiConverterString.write(value.detail, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidSetupStatus_lift(_ buf: RustBuffer) throws -> MobileAndroidSetupStatus { + return try FfiConverterTypeMobileAndroidSetupStatus.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileAndroidSetupStatus_lower(_ value: MobileAndroidSetupStatus) -> RustBuffer { + return FfiConverterTypeMobileAndroidSetupStatus.lower(value) +} + + public struct MobileAuthenticationState: Equatable, Hashable { public var unlocked: Bool public var biometricUnlockEnabled: Bool @@ -4471,13 +5178,15 @@ public func FfiConverterTypeMobileEntryField_lower(_ value: MobileEntryField) -> public struct MobileEntryPage: Equatable, Hashable { public var id: String + public var revision: String public var title: String public var sections: [MobileEntrySection] // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, title: String, sections: [MobileEntrySection]) { + public init(id: String, revision: String, title: String, sections: [MobileEntrySection]) { self.id = id + self.revision = revision self.title = title self.sections = sections } @@ -4499,6 +5208,7 @@ public struct FfiConverterTypeMobileEntryPage: FfiConverterRustBuffer { return try MobileEntryPage( id: FfiConverterString.read(from: &buf), + revision: FfiConverterString.read(from: &buf), title: FfiConverterString.read(from: &buf), sections: FfiConverterSequenceTypeMobileEntrySection.read(from: &buf) ) @@ -4506,6 +5216,7 @@ public struct FfiConverterTypeMobileEntryPage: FfiConverterRustBuffer { public static func write(_ value: MobileEntryPage, into buf: inout [UInt8]) { FfiConverterString.write(value.id, into: &buf) + FfiConverterString.write(value.revision, into: &buf) FfiConverterString.write(value.title, into: &buf) FfiConverterSequenceTypeMobileEntrySection.write(value.sections, into: &buf) } @@ -5741,6 +6452,60 @@ public func FfiConverterTypeMobileMutationRequest_lower(_ value: MobileMutationR } +public struct MobileNavigationRow: Equatable, Hashable { + public var row: MobilePasswordRow + public var revision: String + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(row: MobilePasswordRow, revision: String) { + self.row = row + self.revision = revision + } + + + + +} + +#if compiler(>=6) +extension MobileNavigationRow: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMobileNavigationRow: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobileNavigationRow { + return + try MobileNavigationRow( + row: FfiConverterTypeMobilePasswordRow.read(from: &buf), + revision: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: MobileNavigationRow, into buf: inout [UInt8]) { + FfiConverterTypeMobilePasswordRow.write(value.row, into: &buf) + FfiConverterString.write(value.revision, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileNavigationRow_lift(_ buf: RustBuffer) throws -> MobileNavigationRow { + return try FfiConverterTypeMobileNavigationRow.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobileNavigationRow_lower(_ value: MobileNavigationRow) -> RustBuffer { + return FfiConverterTypeMobileNavigationRow.lower(value) +} + + public struct MobileOnboardingDiscovery: Equatable, Hashable { public var branches: [String] public var selectedBranch: UInt32 @@ -5977,6 +6742,80 @@ public func FfiConverterTypeMobilePage_lower(_ value: MobilePage) -> RustBuffer } +public struct MobilePasswordNavigation: Equatable, Hashable { + public var path: String + public var title: String + public var parent: String? + public var revision: String + public var rows: [MobileNavigationRow] + public var selected: MobileNavigationRow? + public var stale: Bool + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(path: String, title: String, parent: String?, revision: String, rows: [MobileNavigationRow], selected: MobileNavigationRow?, stale: Bool) { + self.path = path + self.title = title + self.parent = parent + self.revision = revision + self.rows = rows + self.selected = selected + self.stale = stale + } + + + + +} + +#if compiler(>=6) +extension MobilePasswordNavigation: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeMobilePasswordNavigation: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MobilePasswordNavigation { + return + try MobilePasswordNavigation( + path: FfiConverterString.read(from: &buf), + title: FfiConverterString.read(from: &buf), + parent: FfiConverterOptionString.read(from: &buf), + revision: FfiConverterString.read(from: &buf), + rows: FfiConverterSequenceTypeMobileNavigationRow.read(from: &buf), + selected: FfiConverterOptionTypeMobileNavigationRow.read(from: &buf), + stale: FfiConverterBool.read(from: &buf) + ) + } + + public static func write(_ value: MobilePasswordNavigation, into buf: inout [UInt8]) { + FfiConverterString.write(value.path, into: &buf) + FfiConverterString.write(value.title, into: &buf) + FfiConverterOptionString.write(value.parent, into: &buf) + FfiConverterString.write(value.revision, into: &buf) + FfiConverterSequenceTypeMobileNavigationRow.write(value.rows, into: &buf) + FfiConverterOptionTypeMobileNavigationRow.write(value.selected, into: &buf) + FfiConverterBool.write(value.stale, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobilePasswordNavigation_lift(_ buf: RustBuffer) throws -> MobilePasswordNavigation { + return try FfiConverterTypeMobilePasswordNavigation.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeMobilePasswordNavigation_lower(_ value: MobilePasswordNavigation) -> RustBuffer { + return FfiConverterTypeMobilePasswordNavigation.lower(value) +} + + public struct MobilePasswordPage: Equatable, Hashable { public var id: String public var path: String @@ -6589,18 +7428,20 @@ public struct MobileTotpDetail: Equatable, Hashable { public var account: String public var code: String public var validUntil: UInt64 + public var remainingSeconds: UInt64 public var period: UInt64 public var sharedWithWatch: Bool public var watch: MobileWatchSnapshotStatus // Default memberwise initializers are never public by default, so we // declare one manually. - public init(path: String, issuer: String?, account: String, code: String, validUntil: UInt64, period: UInt64, sharedWithWatch: Bool, watch: MobileWatchSnapshotStatus) { + public init(path: String, issuer: String?, account: String, code: String, validUntil: UInt64, remainingSeconds: UInt64, period: UInt64, sharedWithWatch: Bool, watch: MobileWatchSnapshotStatus) { self.path = path self.issuer = issuer self.account = account self.code = code self.validUntil = validUntil + self.remainingSeconds = remainingSeconds self.period = period self.sharedWithWatch = sharedWithWatch self.watch = watch @@ -6627,6 +7468,7 @@ public struct FfiConverterTypeMobileTotpDetail: FfiConverterRustBuffer { account: FfiConverterString.read(from: &buf), code: FfiConverterString.read(from: &buf), validUntil: FfiConverterUInt64.read(from: &buf), + remainingSeconds: FfiConverterUInt64.read(from: &buf), period: FfiConverterUInt64.read(from: &buf), sharedWithWatch: FfiConverterBool.read(from: &buf), watch: FfiConverterTypeMobileWatchSnapshotStatus.read(from: &buf) @@ -6639,6 +7481,7 @@ public struct FfiConverterTypeMobileTotpDetail: FfiConverterRustBuffer { FfiConverterString.write(value.account, into: &buf) FfiConverterString.write(value.code, into: &buf) FfiConverterUInt64.write(value.validUntil, into: &buf) + FfiConverterUInt64.write(value.remainingSeconds, into: &buf) FfiConverterUInt64.write(value.period, into: &buf) FfiConverterBool.write(value.sharedWithWatch, into: &buf) FfiConverterTypeMobileWatchSnapshotStatus.write(value.watch, into: &buf) @@ -8879,6 +9722,8 @@ public enum MobileLifecycleEvent: Equatable, Hashable { case foreground case background case deviceLocked + case memoryPressure + case processRecreated case userActivity case cancel @@ -8908,9 +9753,13 @@ public struct FfiConverterTypeMobileLifecycleEvent: FfiConverterRustBuffer { case 3: return .deviceLocked - case 4: return .userActivity + case 4: return .memoryPressure - case 5: return .cancel + case 5: return .processRecreated + + case 6: return .userActivity + + case 7: return .cancel default: throw UniffiInternalError.unexpectedEnumCase } @@ -8932,13 +9781,21 @@ public struct FfiConverterTypeMobileLifecycleEvent: FfiConverterRustBuffer { writeInt(&buf, Int32(3)) - case .userActivity: + case .memoryPressure: writeInt(&buf, Int32(4)) - case .cancel: + case .processRecreated: writeInt(&buf, Int32(5)) + + case .userActivity: + writeInt(&buf, Int32(6)) + + + case .cancel: + writeInt(&buf, Int32(7)) + } } } @@ -10283,6 +11140,7 @@ public enum MobileShellState: Equatable, Hashable { case ready case locked case error + case stale @@ -10314,6 +11172,8 @@ public struct FfiConverterTypeMobileShellState: FfiConverterRustBuffer { case 5: return .error + case 6: return .stale + default: throw UniffiInternalError.unexpectedEnumCase } } @@ -10341,6 +11201,10 @@ public struct FfiConverterTypeMobileShellState: FfiConverterRustBuffer { case .error: writeInt(&buf, Int32(5)) + + case .stale: + writeInt(&buf, Int32(6)) + } } } @@ -10905,6 +11769,30 @@ fileprivate struct FfiConverterOptionTypeMobileKeyTransferKey: FfiConverterRustB } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterOptionTypeMobileNavigationRow: FfiConverterRustBuffer { + typealias SwiftType = MobileNavigationRow? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeMobileNavigationRow.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeMobileNavigationRow.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -11026,6 +11914,31 @@ fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeMobileAndroidSetupKey: FfiConverterRustBuffer { + typealias SwiftType = [MobileAndroidSetupKey] + + public static func write(_ value: [MobileAndroidSetupKey], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeMobileAndroidSetupKey.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [MobileAndroidSetupKey] { + let len: Int32 = try readInt(&buf) + var seq = [MobileAndroidSetupKey]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeMobileAndroidSetupKey.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -11301,6 +12214,31 @@ fileprivate struct FfiConverterSequenceTypeMobileMutationDestination: FfiConvert } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeMobileNavigationRow: FfiConverterRustBuffer { + typealias SwiftType = [MobileNavigationRow] + + public static func write(_ value: [MobileNavigationRow], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeMobileNavigationRow.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [MobileNavigationRow] { + let len: Int32 = try readInt(&buf) + var seq = [MobileNavigationRow]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeMobileNavigationRow.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -11482,6 +12420,17 @@ public func setSelectedMobileTab(tab: MobileTab)throws {try rustCallWithError( ) } } +/** + * Revoke a copy while the Android window still has foreground clipboard access. + */ +public func clearMobileEntryCopy(copy: MobileEntryCopy, clipboard: MobileClipboard)throws {try rustCallWithError(FfiConverterTypeMobilePlatformFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_func_clear_mobile_entry_copy( + FfiConverterTypeMobileEntryCopy_lower(copy), + FfiConverterTypeMobileClipboard_lower(clipboard),uniffiCallStatus + ) +} +} public func copyMobileEntry(copy: MobileEntryCopy, clipboard: MobileClipboard)throws -> MobileClipboardDisposition { return try FfiConverterTypeMobileClipboardDisposition_lift(try rustCallWithError(FfiConverterTypeMobilePlatformFfiError_lift) { uniffiCallStatus in @@ -11491,6 +12440,18 @@ public func copyMobileEntry(copy: MobileEntryCopy, clipboard: MobileClipboard)th ) }) } +/** + * Android never retains or resurrects previous clipboard contents. + */ +public func copyMobileEntryClearing(copy: MobileEntryCopy, clipboard: MobileClipboard)throws -> MobileClipboardDisposition { + return try FfiConverterTypeMobileClipboardDisposition_lift(try rustCallWithError(FfiConverterTypeMobilePlatformFfiError_lift) { + uniffiCallStatus in + uniffi_ironstorage_mobile_fn_func_copy_mobile_entry_clearing( + FfiConverterTypeMobileEntryCopy_lower(copy), + FfiConverterTypeMobileClipboard_lower(clipboard),uniffiCallStatus + ) +}) +} public func decodeMobileCameraFrame(frame: MobileCameraFrame)throws -> Data { return try FfiConverterData.lift(try rustCallWithError(FfiConverterTypeMobilePlatformFfiError_lift) { uniffiCallStatus in @@ -11599,9 +12560,15 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_func_set_selected_mobile_tab() != 23876) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_func_clear_mobile_entry_copy() != 51777) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_func_copy_mobile_entry() != 39081) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_func_copy_mobile_entry_clearing() != 43699) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_func_decode_mobile_camera_frame() != 45472) { return InitializationResult.apiChecksumMismatch } @@ -11644,6 +12611,12 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_entry_field() != 46283) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_presented_entry_field() != 16973) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_presented_totp_code() != 32197) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_totp_code() != 51013) { return InitializationResult.apiChecksumMismatch } @@ -11653,6 +12626,9 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_discard_entry_editor() != 59865) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_enroll_biometric_unlock() != 6533) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_entry_editor() != 40530) { return InitializationResult.apiChecksumMismatch } @@ -11674,6 +12650,9 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_mobile_appearance() != 38885) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_navigation_entry() != 45161) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_perform_entry_mutation() != 36745) { return InitializationResult.apiChecksumMismatch } @@ -11683,9 +12662,15 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_prepare_entry_mutation() != 24463) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_prepare_presented_entry_mutation() != 26205) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_prepare_watch_snapshot() != 63315) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_refresh_entry_presentation() != 17168) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_remove_application_token() != 34406) { return InitializationResult.apiChecksumMismatch } @@ -11698,9 +12683,18 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_replace_entry_field() != 5390) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_replace_presented_entry_field() != 2917) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_restore_biometric_lease() != 7408) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_reveal_entry_field() != 13272) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_reveal_presented_entry_field() != 3039) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_save_entry_editor() != 9869) { return InitializationResult.apiChecksumMismatch } @@ -11752,6 +12746,9 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileauthentication_lifecycle() != 7147) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_activity_known() != 61390) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_cached() != 29336) { return InitializationResult.apiChecksumMismatch } @@ -11764,6 +12761,9 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_fetch() != 25832) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_identity() != 10598) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_progress() != 21317) { return InitializationResult.apiChecksumMismatch } @@ -11773,6 +12773,9 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_push() != 23745) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_refresh_gesture() != 9425) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_refresh_if_stale() != 39139) { return InitializationResult.apiChecksumMismatch } @@ -11842,6 +12845,42 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobileandroidkeystore_cancel_pending() != 39774) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_cancel() != 32177) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_generate() != 35096) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_import_key() != 8889) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_progress() != 56154) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_remote() != 42493) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_replace_token() != 46064) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_setup_local() != 43343) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_status() != 41645) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_cancel() != 58015) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_clone_store() != 33517) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_connect() != 5133) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_discover() != 30506) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileclipboard_read() != 47092) { return InitializationResult.apiChecksumMismatch } @@ -11875,9 +12914,24 @@ private let initializationResult: InitializationResult = { if (uniffi_ironstorage_mobile_checksum_method_mobilesecretbackend_unlock() != 2737) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_open_password_search_result() != 26489) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_password_name_search() != 31132) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_password_navigation() != 52267) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_activate() != 64835) { return InitializationResult.apiChecksumMismatch } + if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_android_home_operation() != 20347) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_android_onboarding() != 59478) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_android_secret_backend() != 24373) { return InitializationResult.apiChecksumMismatch } @@ -11938,6 +12992,14 @@ extension MobileAndroidKeystoreError: CustomStringConvertible, CustomDebugString public var description: String { "MobileAndroidKeystoreError([REDACTED])" } public var debugDescription: String { description } } +extension MobileAndroidSetupKey: CustomStringConvertible, CustomDebugStringConvertible { + public var description: String { "MobileAndroidSetupKey([REDACTED])" } + public var debugDescription: String { description } +} +extension MobileAndroidSetupStatus: CustomStringConvertible, CustomDebugStringConvertible { + public var description: String { "MobileAndroidSetupStatus([REDACTED])" } + public var debugDescription: String { description } +} extension MobileAuthenticationFfiError: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { "MobileAuthenticationFfiError([REDACTED])" } public var debugDescription: String { description } @@ -12070,6 +13132,10 @@ extension MobileMutationRequest: CustomStringConvertible, CustomDebugStringConve public var description: String { "MobileMutationRequest([REDACTED])" } public var debugDescription: String { description } } +extension MobileNavigationRow: CustomStringConvertible, CustomDebugStringConvertible { + public var description: String { "MobileNavigationRow([REDACTED])" } + public var debugDescription: String { description } +} extension MobileOnboardingDiscovery: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { "MobileOnboardingDiscovery([REDACTED])" } public var debugDescription: String { description } @@ -12094,6 +13160,10 @@ extension MobilePasswordFfiError: CustomStringConvertible, CustomDebugStringConv public var description: String { "MobilePasswordFfiError([REDACTED])" } public var debugDescription: String { description } } +extension MobilePasswordNavigation: CustomStringConvertible, CustomDebugStringConvertible { + public var description: String { "MobilePasswordNavigation([REDACTED])" } + public var debugDescription: String { description } +} extension MobilePasswordPage: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { "MobilePasswordPage([REDACTED])" } public var debugDescription: String { description } diff --git a/apple/Generated/ironstorage_mobileFFI.h b/apple/Generated/ironstorage_mobileFFI.h index 943b23e..4e60e41 100644 --- a/apple/Generated/ironstorage_mobileFFI.h +++ b/apple/Generated/ironstorage_mobileFFI.h @@ -469,6 +469,16 @@ void uniffi_ironstorage_mobile_fn_method_mobileauthentication_cancel(uint64_t pt RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_copy_entry_field(uint64_t ptr, RustBuffer path, uint64_t field, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_ENTRY_FIELD +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_ENTRY_FIELD +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_copy_presented_entry_field(uint64_t ptr, RustBuffer path, uint64_t field_id, RustBuffer revision, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_TOTP_CODE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_TOTP_CODE +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_copy_presented_totp_code(uint64_t ptr, RustBuffer path, RustBuffer revision, uint64_t unix_seconds, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_COPY_TOTP_CODE #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_COPY_TOTP_CODE RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_copy_totp_code(uint64_t ptr, RustBuffer path, uint64_t unix_seconds, RustCallStatus *_Nonnull out_status @@ -484,6 +494,11 @@ void uniffi_ironstorage_mobile_fn_method_mobileauthentication_create_directory(u void uniffi_ironstorage_mobile_fn_method_mobileauthentication_discard_entry_editor(uint64_t ptr, uint64_t editor, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_ENROLL_BIOMETRIC_UNLOCK +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_ENROLL_BIOMETRIC_UNLOCK +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_enroll_biometric_unlock(uint64_t ptr, RustBuffer passphrase, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_ENTRY_EDITOR #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_ENTRY_EDITOR RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_entry_editor(uint64_t ptr, uint64_t editor, RustCallStatus *_Nonnull out_status @@ -519,6 +534,11 @@ void uniffi_ironstorage_mobile_fn_method_mobileauthentication_manual_lock(uint64 RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_mobile_appearance(uint64_t ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_NAVIGATION_ENTRY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_NAVIGATION_ENTRY +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_navigation_entry(uint64_t ptr, RustBuffer directory, RustBuffer row_id, RustBuffer revision, RustBuffer passphrase, uint64_t unix_seconds, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_PERFORM_ENTRY_MUTATION #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_PERFORM_ENTRY_MUTATION RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_perform_entry_mutation(uint64_t ptr, RustBuffer request, RustCallStatus *_Nonnull out_status @@ -534,11 +554,21 @@ RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_preferences( RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_prepare_entry_mutation(uint64_t ptr, RustBuffer path, RustBuffer action, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_PREPARE_PRESENTED_ENTRY_MUTATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_PREPARE_PRESENTED_ENTRY_MUTATION +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_prepare_presented_entry_mutation(uint64_t ptr, RustBuffer path, RustBuffer revision, RustBuffer action, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_prepare_watch_snapshot(uint64_t ptr, RustBuffer platform_pairing_identity, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REFRESH_ENTRY_PRESENTATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REFRESH_ENTRY_PRESENTATION +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_refresh_entry_presentation(uint64_t ptr, RustBuffer path, RustBuffer revision, uint64_t unix_seconds, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REMOVE_APPLICATION_TOKEN #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REMOVE_APPLICATION_TOKEN void uniffi_ironstorage_mobile_fn_method_mobileauthentication_remove_application_token(uint64_t ptr, RustCallStatus *_Nonnull out_status @@ -559,11 +589,26 @@ RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_reorder_entr RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_replace_entry_field(uint64_t ptr, RustBuffer path, uint64_t field, RustBuffer value, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REPLACE_PRESENTED_ENTRY_FIELD +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REPLACE_PRESENTED_ENTRY_FIELD +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_replace_presented_entry_field(uint64_t ptr, RustBuffer path, uint64_t field_id, RustBuffer revision, RustBuffer value, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_RESTORE_BIOMETRIC_LEASE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_RESTORE_BIOMETRIC_LEASE +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_restore_biometric_lease(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REVEAL_ENTRY_FIELD #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REVEAL_ENTRY_FIELD RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_reveal_entry_field(uint64_t ptr, RustBuffer path, uint64_t field, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REVEAL_PRESENTED_ENTRY_FIELD +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_REVEAL_PRESENTED_ENTRY_FIELD +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_reveal_presented_entry_field(uint64_t ptr, RustBuffer path, uint64_t field_id, RustBuffer revision, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_SAVE_ENTRY_EDITOR #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEAUTHENTICATION_SAVE_ENTRY_EDITOR RustBuffer uniffi_ironstorage_mobile_fn_method_mobileauthentication_save_entry_editor(uint64_t ptr, uint64_t editor, RustBuffer fields, RustCallStatus *_Nonnull out_status @@ -659,6 +704,11 @@ uint64_t uniffi_ironstorage_mobile_fn_clone_mobilehomeoperation(uint64_t handle, void uniffi_ironstorage_mobile_fn_free_mobilehomeoperation(uint64_t handle, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_ACTIVITY_KNOWN +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_ACTIVITY_KNOWN +int8_t uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_activity_known(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_CACHED #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_CACHED RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_cached(uint64_t ptr, RustCallStatus *_Nonnull out_status @@ -679,6 +729,11 @@ RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_commit(uint64 RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_fetch(uint64_t ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_IDENTITY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_IDENTITY +RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_identity(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_PROGRESS #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_PROGRESS RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_progress(uint64_t ptr, RustCallStatus *_Nonnull out_status @@ -694,6 +749,11 @@ RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_pull(uint64_t RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_push(uint64_t ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_REFRESH_GESTURE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_REFRESH_GESTURE +RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_refresh_gesture(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_REFRESH_IF_STALE #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEHOMEOPERATION_REFRESH_IF_STALE RustBuffer uniffi_ironstorage_mobile_fn_method_mobilehomeoperation_refresh_if_stale(uint64_t ptr, RustCallStatus *_Nonnull out_status @@ -874,6 +934,86 @@ RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidkeystore_aliases(uin void uniffi_ironstorage_mobile_fn_method_mobileandroidkeystore_cancel_pending(uint64_t ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_CLONE_MOBILEANDROIDONBOARDING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_CLONE_MOBILEANDROIDONBOARDING +uint64_t uniffi_ironstorage_mobile_fn_clone_mobileandroidonboarding(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FREE_MOBILEANDROIDONBOARDING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FREE_MOBILEANDROIDONBOARDING +void uniffi_ironstorage_mobile_fn_free_mobileandroidonboarding(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_CANCEL +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_CANCEL +void uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_cancel(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_GENERATE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_GENERATE +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_generate(uint64_t ptr, RustBuffer user_id, RustBuffer passphrase, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_IMPORT_KEY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_IMPORT_KEY +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_import_key(uint64_t ptr, RustBuffer armor, RustBuffer passphrase, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_PROGRESS +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_PROGRESS +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_progress(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_REMOTE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_REMOTE +uint64_t uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_remote(uint64_t ptr, RustBuffer server_url, RustBuffer account, RustBuffer repository_path, RustBuffer application_token, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_REPLACE_TOKEN +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_REPLACE_TOKEN +void uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_replace_token(uint64_t ptr, RustBuffer account, RustBuffer application_token, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_SETUP_LOCAL +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_SETUP_LOCAL +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_setup_local(uint64_t ptr, RustBuffer fingerprint, RustBuffer passphrase, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_STATUS +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDONBOARDING_STATUS +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidonboarding_status(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_CLONE_MOBILEANDROIDREMOTEONBOARDING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_CLONE_MOBILEANDROIDREMOTEONBOARDING +uint64_t uniffi_ironstorage_mobile_fn_clone_mobileandroidremoteonboarding(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FREE_MOBILEANDROIDREMOTEONBOARDING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FREE_MOBILEANDROIDREMOTEONBOARDING +void uniffi_ironstorage_mobile_fn_free_mobileandroidremoteonboarding(uint64_t handle, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_CANCEL +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_CANCEL +void uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_cancel(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_CLONE_STORE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_CLONE_STORE +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_clone_store(uint64_t ptr, RustBuffer branch, RustBuffer fingerprint, RustBuffer passphrase, int8_t use_existing, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_CONNECT +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_CONNECT +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_connect(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_DISCOVER +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEANDROIDREMOTEONBOARDING_DISCOVER +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileandroidremoteonboarding_discover(uint64_t ptr, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_CLONE_MOBILECLIPBOARD #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_CLONE_MOBILECLIPBOARD uint64_t uniffi_ironstorage_mobile_fn_clone_mobileclipboard(uint64_t handle, RustCallStatus *_Nonnull out_status @@ -984,11 +1124,36 @@ uint64_t uniffi_ironstorage_mobile_fn_clone_mobileprivateruntime(uint64_t handle void uniffi_ironstorage_mobile_fn_free_mobileprivateruntime(uint64_t handle, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_OPEN_PASSWORD_SEARCH_RESULT +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_OPEN_PASSWORD_SEARCH_RESULT +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_open_password_search_result(uint64_t ptr, RustBuffer id, RustBuffer revision, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAME_SEARCH +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAME_SEARCH +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_password_name_search(uint64_t ptr, RustBuffer query, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAVIGATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAVIGATION +RustBuffer uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_password_navigation(uint64_t ptr, RustBuffer path, RustBuffer selected, RustBuffer revision, int8_t activate, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ACTIVATE #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ACTIVATE void uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_activate(uint64_t ptr, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ANDROID_HOME_OPERATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ANDROID_HOME_OPERATION +uint64_t uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_android_home_operation(uint64_t ptr, uint64_t backend, uint64_t clock, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ANDROID_ONBOARDING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ANDROID_ONBOARDING +uint64_t uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_android_onboarding(uint64_t ptr, uint64_t backend, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ANDROID_SECRET_BACKEND #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_METHOD_MOBILEPRIVATERUNTIME_ANDROID_SECRET_BACKEND uint64_t uniffi_ironstorage_mobile_fn_method_mobileprivateruntime_android_secret_backend(uint64_t ptr, uint64_t keystore, RustCallStatus *_Nonnull out_status @@ -1101,11 +1266,21 @@ void uniffi_ironstorage_mobile_fn_func_replace_configured_mobile_application_tok void uniffi_ironstorage_mobile_fn_func_set_selected_mobile_tab(RustBuffer tab, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_CLEAR_MOBILE_ENTRY_COPY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_CLEAR_MOBILE_ENTRY_COPY +void uniffi_ironstorage_mobile_fn_func_clear_mobile_entry_copy(RustBuffer copy, uint64_t clipboard, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_COPY_MOBILE_ENTRY #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_COPY_MOBILE_ENTRY RustBuffer uniffi_ironstorage_mobile_fn_func_copy_mobile_entry(RustBuffer copy, uint64_t clipboard, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_COPY_MOBILE_ENTRY_CLEARING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_COPY_MOBILE_ENTRY_CLEARING +RustBuffer uniffi_ironstorage_mobile_fn_func_copy_mobile_entry_clearing(RustBuffer copy, uint64_t clipboard, RustCallStatus *_Nonnull out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_DECODE_MOBILE_CAMERA_FRAME #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_FN_FUNC_DECODE_MOBILE_CAMERA_FRAME RustBuffer uniffi_ironstorage_mobile_fn_func_decode_mobile_camera_frame(RustBuffer frame, RustCallStatus *_Nonnull out_status @@ -1478,12 +1653,24 @@ uint16_t uniffi_ironstorage_mobile_checksum_func_replace_configured_mobile_appli #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_SET_SELECTED_MOBILE_TAB uint16_t uniffi_ironstorage_mobile_checksum_func_set_selected_mobile_tab(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_CLEAR_MOBILE_ENTRY_COPY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_CLEAR_MOBILE_ENTRY_COPY +uint16_t uniffi_ironstorage_mobile_checksum_func_clear_mobile_entry_copy(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_COPY_MOBILE_ENTRY #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_COPY_MOBILE_ENTRY uint16_t uniffi_ironstorage_mobile_checksum_func_copy_mobile_entry(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_COPY_MOBILE_ENTRY_CLEARING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_COPY_MOBILE_ENTRY_CLEARING +uint16_t uniffi_ironstorage_mobile_checksum_func_copy_mobile_entry_clearing(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_FUNC_DECODE_MOBILE_CAMERA_FRAME @@ -1568,6 +1755,18 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_cancel(v #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_COPY_ENTRY_FIELD uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_entry_field(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_ENTRY_FIELD +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_ENTRY_FIELD +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_presented_entry_field(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_TOTP_CODE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_COPY_PRESENTED_TOTP_CODE +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_copy_presented_totp_code(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_COPY_TOTP_CODE @@ -1586,6 +1785,12 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_create_d #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_DISCARD_ENTRY_EDITOR uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_discard_entry_editor(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ENROLL_BIOMETRIC_UNLOCK +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ENROLL_BIOMETRIC_UNLOCK +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_enroll_biometric_unlock(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_ENTRY_EDITOR @@ -1628,6 +1833,12 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_manual_l #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_MOBILE_APPEARANCE uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_mobile_appearance(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_NAVIGATION_ENTRY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_NAVIGATION_ENTRY +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_navigation_entry(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PERFORM_ENTRY_MUTATION @@ -1646,12 +1857,24 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_preferen #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_ENTRY_MUTATION uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_prepare_entry_mutation(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_PRESENTED_ENTRY_MUTATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_PRESENTED_ENTRY_MUTATION +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_prepare_presented_entry_mutation(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_PREPARE_WATCH_SNAPSHOT uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_prepare_watch_snapshot(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REFRESH_ENTRY_PRESENTATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REFRESH_ENTRY_PRESENTATION +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_refresh_entry_presentation(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REMOVE_APPLICATION_TOKEN @@ -1676,12 +1899,30 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_reorder_ #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REPLACE_ENTRY_FIELD uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_replace_entry_field(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REPLACE_PRESENTED_ENTRY_FIELD +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REPLACE_PRESENTED_ENTRY_FIELD +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_replace_presented_entry_field(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_RESTORE_BIOMETRIC_LEASE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_RESTORE_BIOMETRIC_LEASE +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_restore_biometric_lease(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REVEAL_ENTRY_FIELD #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REVEAL_ENTRY_FIELD uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_reveal_entry_field(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REVEAL_PRESENTED_ENTRY_FIELD +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_REVEAL_PRESENTED_ENTRY_FIELD +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_reveal_presented_entry_field(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_SAVE_ENTRY_EDITOR @@ -1784,6 +2025,12 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_watch_sn #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEAUTHENTICATION_LIFECYCLE uint16_t uniffi_ironstorage_mobile_checksum_method_mobileauthentication_lifecycle(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_ACTIVITY_KNOWN +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_ACTIVITY_KNOWN +uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_activity_known(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_CACHED @@ -1808,6 +2055,12 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_commit(vo #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_FETCH uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_fetch(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_IDENTITY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_IDENTITY +uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_identity(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_PROGRESS @@ -1826,6 +2079,12 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_pull(void #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_PUSH uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_push(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_REFRESH_GESTURE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_REFRESH_GESTURE +uint16_t uniffi_ironstorage_mobile_checksum_method_mobilehomeoperation_refresh_gesture(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEHOMEOPERATION_REFRESH_IF_STALE @@ -1964,6 +2223,78 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidkeystore_aliases #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDKEYSTORE_CANCEL_PENDING uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidkeystore_cancel_pending(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_CANCEL +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_CANCEL +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_cancel(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_GENERATE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_GENERATE +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_generate(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_IMPORT_KEY +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_IMPORT_KEY +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_import_key(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_PROGRESS +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_PROGRESS +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_progress(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_REMOTE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_REMOTE +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_remote(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_REPLACE_TOKEN +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_REPLACE_TOKEN +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_replace_token(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_SETUP_LOCAL +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_SETUP_LOCAL +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_setup_local(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_STATUS +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDONBOARDING_STATUS +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidonboarding_status(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_CANCEL +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_CANCEL +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_cancel(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_CLONE_STORE +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_CLONE_STORE +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_clone_store(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_CONNECT +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_CONNECT +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_connect(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_DISCOVER +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEANDROIDREMOTEONBOARDING_DISCOVER +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileandroidremoteonboarding_discover(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILECLIPBOARD_READ @@ -2030,12 +2361,42 @@ uint16_t uniffi_ironstorage_mobile_checksum_method_mobilesecretbackend_lock(void #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILESECRETBACKEND_UNLOCK uint16_t uniffi_ironstorage_mobile_checksum_method_mobilesecretbackend_unlock(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_OPEN_PASSWORD_SEARCH_RESULT +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_OPEN_PASSWORD_SEARCH_RESULT +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_open_password_search_result(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAME_SEARCH +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAME_SEARCH +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_password_name_search(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAVIGATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_PASSWORD_NAVIGATION +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_password_navigation(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ACTIVATE #define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ACTIVATE uint16_t uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_activate(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ANDROID_HOME_OPERATION +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ANDROID_HOME_OPERATION +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_android_home_operation(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ANDROID_ONBOARDING +#define UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ANDROID_ONBOARDING +uint16_t uniffi_ironstorage_mobile_checksum_method_mobileprivateruntime_android_onboarding(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IRONSTORAGE_MOBILE_CHECKSUM_METHOD_MOBILEPRIVATERUNTIME_ANDROID_SECRET_BACKEND diff --git a/apple/Sources/App/IronStorageApp.swift b/apple/Sources/App/IronStorageApp.swift index bf72ff0..e8009b8 100644 --- a/apple/Sources/App/IronStorageApp.swift +++ b/apple/Sources/App/IronStorageApp.swift @@ -1024,6 +1024,7 @@ private final class ShellViewController: UITableViewController, MobileTabRoot { case .ready: page.systemImage case .locked: "lock.fill" case .error: "exclamationmark.triangle" + case .stale: "arrow.clockwise" } } } diff --git a/crates/mobile-bridge/src/lib.rs b/crates/mobile-bridge/src/lib.rs index 36705a8..f3b1f86 100644 --- a/crates/mobile-bridge/src/lib.rs +++ b/crates/mobile-bridge/src/lib.rs @@ -189,6 +189,7 @@ pub enum MobileShellState { Ready, Locked, Error, + Stale, } impl From for MobileShellState { @@ -199,6 +200,7 @@ impl From for MobileShellState { StorageShellState::Ready => Self::Ready, StorageShellState::Locked => Self::Locked, StorageShellState::Error => Self::Error, + StorageShellState::Stale => Self::Stale, } } } @@ -211,6 +213,7 @@ impl From for StorageShellState { MobileShellState::Ready => Self::Ready, MobileShellState::Locked => Self::Locked, MobileShellState::Error => Self::Error, + MobileShellState::Stale => Self::Stale, } } } @@ -2520,6 +2523,7 @@ mod tests { MobileShellState::Ready, MobileShellState::Locked, MobileShellState::Error, + MobileShellState::Stale, ] { let shell = super::mobile_shell_fixture(state); assert_eq!(shell.selected_tab, MobileTab::Home); diff --git a/crates/storage/src/mobile.rs b/crates/storage/src/mobile.rs index eeae6f6..a38094e 100644 --- a/crates/storage/src/mobile.rs +++ b/crates/storage/src/mobile.rs @@ -105,6 +105,7 @@ pub enum MobileShellState { Ready, Locked, Error, + Stale, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -250,6 +251,10 @@ fn state_copy(tab: MobileTab, state: MobileShellState, error: Option<&str>) -> ( "IronStorage is Locked".to_owned(), "Authenticate to reveal protected content.".to_owned(), ), + MobileShellState::Stale => ( + "Password Store Changed".to_owned(), + "Refresh to load the current repository state.".to_owned(), + ), MobileShellState::Error => ( "IronStorage Is Unavailable".to_owned(), error @@ -275,6 +280,7 @@ mod tests { MobileShellState::Ready, MobileShellState::Locked, MobileShellState::Error, + MobileShellState::Stale, ] { let shell = MobileShell::fixture(state); assert_eq!(shell.selected_tab(), MobileTab::Home); diff --git a/docs/android/adaptive-shell.md b/docs/android/adaptive-shell.md new file mode 100644 index 0000000..228877e --- /dev/null +++ b/docs/android/adaptive-shell.md @@ -0,0 +1,72 @@ +# Adaptive Android shell + +The single `MainActivity` is a `FragmentActivity` (a `ComponentActivity` subclass) +so AndroidX BiometricPrompt can retain its OS authentication fragment. Compose, +Hilt ViewModels, lifecycle-aware StateFlow collection, and Navigation 3 1.2.0 +present the Rust-owned Home, Passwords, Search, TOTP, and Preferences pages. + +The shell uses actual available window width: bottom navigation below 600 dp, +and a scrollable navigation rail at larger widths. Passwords retains Material 3 +Adaptive list/detail panes and the library's fold/hinge-aware directives. Live +window changes recompute both layouts without replacing native handles or +repository identities. Large text uses labelled navigation icons on compact +windows, leaving accessible names and 48 dp or larger selectable touch targets. +Page headings, selected tab semantics, radio groups, and keyboard activation use +Compose semantics rather than display-string interpretation. + +Navigation 3 renders each top-level root and Preferences' Appearance child. +Each destination retains its own navigation: Rust owns Passwords' directory, +entry, parent and revision; Preferences retains its child independently of tab +switches. Home and TOTP have root stacks, and Search hands a typed result to +Passwords. The TOTP root presents Rust shell availability; the TOTP list/detail +workflow is assigned to #107, discovery to #110, and full preferences to #104. + +SavedStateHandle stores only the selected tab, Appearance child flag, and +Password directory/entry/revision identifiers. Navigation 3 deliberately has no +saveable screen-content decorator. Search text, passphrases, editors, decrypted +fields, codes, credentials and native handles never enter navigation saved +state. Existing Rust process-recreated authentication starts locked and checks +restored object revisions before opening an entry. Backgrounding does not renew +a lease. Leaving Passwords invalidates pending operations, clears protected +presentation, and revokes the lease without treating a tab switch as manual +search-history clearing. Navigation cancels setup, Home and Search work before switching destinations and +clears setup secrets before opening Appearance or another tab. Outgoing animated +entries do not cancel work belonging to the incoming destination; sequence/revision checks reject late native results. Cleanup registrations +use distinct owner tokens: removing a screen with the same surface callback cannot +remove another screen's protection. The unchanged immediate memory-pressure +masking assertion also covers setup destinations during screen replacement. + +Appearance is read and written through Rust's existing mobile appearance API. +The shell's only decision is translating System/Light/Dark into Material color +schemes, using the OS preference for System. System-bar icon contrast follows the active +Material background while the activity remains edge-to-edge. Failed operations preserve the +last confirmed appearance. No Android preference store or domain policy is added. + +Rust fixtures generate Loading, Empty, Ready, Locked, Error and Stale pages for +all five destinations; no repository or secret is needed to exercise them. Tests +cover initial async loading versus restored selection, stale completion rejection, +all fixture pages, compact/expanded navigation, large text, keyboard activation, +independent Preferences navigation and reconstruction from primitive saved state. +The native encrypted-store fixture checks appearance persistence and locked +restoration along with entry navigation and viewer operations. + +Validation used ARM64 emulators on API 26 (4 KB), API 37 (4 KB), and API 37 +(strict 16 KB), including app/core/bridge instrumentation and five cold +starts per device. Both supported ARM ABIs passed release packaging, native +linker, backup exclusion, architecture, dependency/license and SBOM gates. +All four Rust workspace gates passed. + +Computer use in Android Studio's embedded emulator verified portrait bottom +navigation, landscape rail, Appearance navigation and light/dark/system changes. +TalkBack focus and activation were observed on Search and Preferences; text at +160% with 520 dpi display scaling retained accessible controls. Accessibility, +font and density settings were restored afterward. Instrumentation also exercises +keyboard activation and expanded navigation with 200% text. TalkBack audio was +not recorded; no fold-posture or desktop-window AVD was available, so those use +the adaptive library's directives without a claim of device-specific validation. +ARMv7 was built and packaged; the available live emulator images were ARM64. + +The additive shared shell-state contract regenerated the hardened Swift bindings. +The iPhone and dependent watch simulator targets built after XcodeGen generation; +the iPhone app was installed over its existing container and computer use verified +locked folder navigation. Existing biometric enrollment and app data were retained. diff --git a/tools/android-build/src/main.rs b/tools/android-build/src/main.rs index 5c2e3e8..23dfb8f 100644 --- a/tools/android-build/src/main.rs +++ b/tools/android-build/src/main.rs @@ -102,6 +102,7 @@ fn fixtures() -> String { MobileShellState::Ready, MobileShellState::Locked, MobileShellState::Error, + MobileShellState::Stale, ] { output.push_str(&format!(" ShellState.{state:?} to listOf(\n")); for page in MobileShell::fixture(state).pages() { @@ -252,8 +253,8 @@ mod tests { fn fixtures_are_complete_non_secret_and_deterministic() { let source = fixtures(); assert_eq!(source, fixtures()); - assert_eq!(source.matches(" to listOf(").count(), 5); - assert_eq!(source.matches("ShellPage(").count(), 25); + assert_eq!(source.matches(" to listOf(").count(), 6); + assert_eq!(source.matches("ShellPage(").count(), 30); assert!(!source.contains("Watch")); assert_eq!(kotlin_string("a\"$b\nc"), "\"a\\\"\\$b\\u000ac\""); assert_eq!(kotlin_string("\0\u{c}\\é"), "\"\\u0000\\u000c\\\\é\"");