├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── deps.versions.toml ├── detekt.yml ├── docs ├── _config.yml ├── binding_and_lifecycle.md ├── index.md ├── logging.md ├── media │ ├── lifecycle.jpg │ ├── logo │ │ ├── landscape │ │ │ ├── jpg │ │ │ │ ├── mvikotlin_black.jpg │ │ │ │ ├── mvikotlin_coloured.jpg │ │ │ │ └── mvikotlin_white.jpg │ │ │ ├── png │ │ │ │ ├── mvikotlin_black.png │ │ │ │ ├── mvikotlin_coloured.png │ │ │ │ └── mvikotlin_white.png │ │ │ └── svg │ │ │ │ ├── mvikotlin_black.svg │ │ │ │ ├── mvikotlin_coloured.svg │ │ │ │ └── mvikotlin_white.svg │ │ ├── logo_guide.jpg │ │ ├── logo_guide.pdf │ │ └── portrait │ │ │ ├── jpg │ │ │ ├── mvikotlin_black.jpg │ │ │ ├── mvikotlin_coloured.jpg │ │ │ └── mvikotlin_white.jpg │ │ │ ├── png │ │ │ ├── mvikotlin_black.png │ │ │ ├── mvikotlin_coloured.png │ │ │ └── mvikotlin_white.png │ │ │ └── svg │ │ │ ├── mvikotlin_black.svg │ │ │ ├── mvikotlin_coloured.svg │ │ │ └── mvikotlin_white.svg │ ├── mvi.jpg │ ├── mvikotlin.jpg │ ├── store.jpg │ └── time-travel-client-app.png ├── state_preservation.md ├── store.md ├── time_travel.md └── view.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keepers ├── .gitignore ├── api │ ├── android │ │ └── keepers.api │ └── jvm │ │ └── keepers.api ├── build.gradle.kts └── src │ ├── androidMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── keepers │ │ ├── instancekeeper │ │ ├── AndroidInstanceKeeper.kt │ │ └── InstanceKeeperViewModel.kt │ │ └── statekeeper │ │ ├── AndroidStateKeeper.kt │ │ ├── ParcelableStateKeeper.kt │ │ ├── ParcelableStateKeeperRegistry.kt │ │ ├── SerializableStateKeeper.kt │ │ └── SerializableStateKeeperRegistry.kt │ ├── androidTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── keepers │ │ └── instancekeeper │ │ └── AndroidInstanceKeeperTest.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── keepers │ │ ├── instancekeeper │ │ ├── DefaultInstanceKeeper.kt │ │ ├── ExperimentalInstanceKeeperApi.kt │ │ ├── InstanceKeeper.kt │ │ └── InstanceKeeperExt.kt │ │ └── statekeeper │ │ ├── ExperimentalStateKeeperApi.kt │ │ ├── StateKeeper.kt │ │ ├── StateKeeperRegistry.kt │ │ └── StateKeeperRegistryExt.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── keepers │ │ └── instancekeeper │ │ └── DefaultInstanceKeeperTest.kt │ └── main │ └── AndroidManifest.xml ├── mvikotlin-extensions-coroutines ├── .gitignore ├── api │ ├── android │ │ └── mvikotlin-extensions-coroutines.api │ └── jvm │ │ └── mvikotlin-extensions-coroutines.api ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── extensions │ │ └── coroutines │ │ ├── Binder.kt │ │ ├── CoroutineBootstrapper.kt │ │ ├── CoroutineExecutor.kt │ │ ├── StoreExt.kt │ │ ├── SuspendBootstrapper.kt │ │ ├── SuspendExecutor.kt │ │ ├── Utils.kt │ │ └── ViewExt.kt │ └── main │ └── AndroidManifest.xml ├── mvikotlin-extensions-reaktive ├── .gitignore ├── api │ ├── android │ │ └── mvikotlin-extensions-reaktive.api │ └── jvm │ │ └── mvikotlin-extensions-reaktive.api ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── extensions │ │ └── reaktive │ │ ├── Binder.kt │ │ ├── DisposableExt.kt │ │ ├── ReaktiveBootstrapper.kt │ │ ├── ReaktiveExecutor.kt │ │ ├── StoreExt.kt │ │ ├── Utils.kt │ │ └── ViewExt.kt │ └── main │ └── AndroidManifest.xml ├── mvikotlin-logging ├── .gitignore ├── api │ ├── android │ │ └── mvikotlin-logging.api │ └── jvm │ │ └── mvikotlin-logging.api ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── logging │ │ ├── logger │ │ ├── DefaultLogFormatter.kt │ │ ├── DefaultLogger.kt │ │ ├── LogFormatter.kt │ │ ├── Logger.kt │ │ ├── LoggerWrapper.kt │ │ └── LoggerWrapperExt.kt │ │ ├── store │ │ ├── LoggingExecutor.kt │ │ ├── LoggingReducer.kt │ │ ├── LoggingStore.kt │ │ └── LoggingStoreFactory.kt │ │ └── utils │ │ └── Utils.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── logging │ │ ├── logger │ │ └── DefaultLogFormatterTest.kt │ │ └── store │ │ ├── LoggingStoreFactoryTest.kt │ │ ├── TestLogFormatter.kt │ │ ├── TestLogger.kt │ │ ├── TestStore.kt │ │ └── TestStoreFactory.kt │ └── main │ └── AndroidManifest.xml ├── mvikotlin-main ├── .gitignore ├── api │ ├── android │ │ └── mvikotlin-main.api │ └── jvm │ │ └── mvikotlin-main.api ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── main │ │ └── store │ │ ├── DefaultStore.kt │ │ └── DefaultStoreFactory.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── main │ │ └── store │ │ └── DefaultStoreTest.kt │ └── main │ └── AndroidManifest.xml ├── mvikotlin-test-internal ├── .gitignore ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ └── test │ │ └── internal │ │ ├── ReducerBuilder.kt │ │ ├── StoreGenericTests.kt │ │ ├── TestBootstrapper.kt │ │ └── TestExecutor.kt │ └── main │ └── AndroidManifest.xml ├── mvikotlin-timetravel-client ├── app-desktop │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ └── jvmMain │ │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── client │ │ └── desktop │ │ ├── Main.kt │ │ ├── PreferencesKey.kt │ │ ├── Utils.kt │ │ └── ui │ │ ├── PopupDialog.kt │ │ ├── TimeTravelClientUi.kt │ │ ├── TimeTravelSettingsUi.kt │ │ ├── ToolbarButton.kt │ │ ├── Utils.kt │ │ ├── ValueTree.kt │ │ └── theme │ │ ├── Colors.kt │ │ └── Theme.kt ├── client-internal │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── timetravel │ │ │ └── client │ │ │ └── internal │ │ │ ├── client │ │ │ ├── TimeTravelClient.kt │ │ │ ├── adbcontroller │ │ │ │ └── AdbController.kt │ │ │ ├── integration │ │ │ │ ├── TimeTravelClientComponent.kt │ │ │ │ ├── TimeTravelClientStoreConnector.kt │ │ │ │ └── mappers │ │ │ │ │ └── StateToModel.kt │ │ │ └── store │ │ │ │ ├── TimeTravelClientStore.kt │ │ │ │ ├── TimeTravelClientStoreFactory.kt │ │ │ │ └── TimeTravelEvent.kt │ │ │ ├── settings │ │ │ ├── SettingsConfig.kt │ │ │ ├── TimeTravelSettings.kt │ │ │ ├── integration │ │ │ │ ├── TimeTravelSettingsComponent.kt │ │ │ │ ├── TimeTravelSettingsStoreSettings.kt │ │ │ │ └── mappers │ │ │ │ │ └── StateToModel.kt │ │ │ └── store │ │ │ │ ├── TimeTravelSettingsStore.kt │ │ │ │ └── TimeTravelSettingsStoreFactory.kt │ │ │ └── utils │ │ │ └── StoreExt.kt │ │ ├── jvmMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── timetravel │ │ │ └── client │ │ │ └── internal │ │ │ ├── SocketExt.kt │ │ │ ├── client │ │ │ ├── adbcontroller │ │ │ │ └── DefaultAdbController.kt │ │ │ └── integration │ │ │ │ └── TimeTravelClientStoreConnector.kt │ │ │ └── utils │ │ │ └── FileExtensions.kt │ │ └── main │ │ └── AndroidManifest.xml └── plugin-idea │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ └── main │ ├── java │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── plugin │ │ └── idea │ │ └── timetravel │ │ ├── Exporter.kt │ │ ├── Importer.kt │ │ ├── PreferencesKey.kt │ │ ├── SwingMainScheduler.kt │ │ ├── TimeTravelEventComponentProvider.kt │ │ ├── TimeTravelToolWindow.kt │ │ ├── TimeTravelToolWindowFactory.kt │ │ ├── TimeTravelToolWindowListener.kt │ │ ├── TimeTravelToolbar.kt │ │ ├── TimeTravelView.kt │ │ └── Utils.kt │ └── resources │ └── META-INF │ └── plugin.xml ├── mvikotlin-timetravel-proto-internal ├── .gitignore ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── proto │ │ └── internal │ │ ├── Constants.kt │ │ ├── data │ │ ├── ProtoObject.kt │ │ ├── storeeventtype │ │ │ ├── ReadWrite.kt │ │ │ └── StoreEventType.kt │ │ ├── timetravelcomand │ │ │ ├── ReadWrite.kt │ │ │ └── TimeTravelCommand.kt │ │ ├── timetravelevent │ │ │ ├── ReadWrite.kt │ │ │ └── TimeTravelEvent.kt │ │ ├── timetraveleventsupdate │ │ │ ├── ReadWrite.kt │ │ │ └── TimeTravelEventsUpdate.kt │ │ ├── timetraveleventvalue │ │ │ ├── ReadWrite.kt │ │ │ └── TimeTravelEventValue.kt │ │ ├── timetravelexport │ │ │ ├── ReadWrite.kt │ │ │ └── TimeTravelExport.kt │ │ ├── timetravelstateupdate │ │ │ ├── ReadWrite.kt │ │ │ └── TimeTravelStateUpdate.kt │ │ └── value │ │ │ ├── ReadWrite.kt │ │ │ ├── ValueNode.kt │ │ │ └── ValueParser.kt │ │ └── io │ │ ├── Constants.kt │ │ ├── DataReader.kt │ │ ├── DataReaderExt.kt │ │ ├── DataWriter.kt │ │ ├── DataWriterExt.kt │ │ ├── ProtoDecoder.kt │ │ ├── ProtoEncoder.kt │ │ ├── ProtoFrameDecoder.kt │ │ ├── ProtoFrameEncoder.kt │ │ └── ProtoObjectType.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── proto │ │ └── internal │ │ ├── data │ │ ├── AbstractReadWriteTest.kt │ │ ├── storeeventtype │ │ │ └── ReadWriteTest.kt │ │ ├── timetravelcomand │ │ │ └── ReadWriteTest.kt │ │ ├── timetravelevent │ │ │ └── ReadWriteTest.kt │ │ ├── timetraveleventsupdate │ │ │ └── ReadWriteTest.kt │ │ ├── timetraveleventvalue │ │ │ └── ReadWriteTest.kt │ │ ├── timetravelstateupdate │ │ │ └── ReadWriteTest.kt │ │ └── value │ │ │ └── ReadWriteTest.kt │ │ └── io │ │ ├── EncodeDecodeTest.kt │ │ └── ProtoFrameDecoderTest.kt │ ├── javaMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── proto │ │ └── internal │ │ ├── data │ │ └── value │ │ │ └── ValueParser.kt │ │ └── io │ │ ├── ReaderThread.kt │ │ ├── Utils.kt │ │ └── WriterThread.kt │ ├── jsMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── proto │ │ └── internal │ │ └── io │ │ ├── DataReaderExt.kt │ │ └── DataWriterExt.kt │ ├── jsNativeMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── proto │ │ └── internal │ │ └── data │ │ └── value │ │ └── ValueParser.kt │ ├── jvmNativeMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── proto │ │ └── internal │ │ └── io │ │ ├── DataReaderExtActual.kt │ │ └── DataWriterExtActual.kt │ ├── main │ └── AndroidManifest.xml │ └── nativeMain │ └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── timetravel │ └── proto │ └── internal │ ├── io │ ├── ReaderThread.kt │ └── WriterThread.kt │ └── thread │ ├── BaseThread.kt │ ├── LooperThread.kt │ ├── Thread.kt │ └── WorkerExt.kt ├── mvikotlin-timetravel ├── .gitignore ├── api │ ├── android │ │ └── mvikotlin-timetravel.api │ └── jvm │ │ └── mvikotlin-timetravel.api ├── build.gradle.kts └── src │ ├── androidMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ ├── server │ │ └── TimeTravelServer.kt │ │ └── widget │ │ ├── AsyncValueParser.kt │ │ ├── TimeTravelEventAdapter.kt │ │ ├── TimeTravelView.kt │ │ └── Utils.kt │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ ├── TimeTravelEvent.kt │ │ ├── TimeTravelState.kt │ │ ├── controller │ │ ├── TimeTravelController.kt │ │ ├── TimeTravelControllerImpl.kt │ │ └── TimeTravelControllerProvider.kt │ │ ├── export │ │ ├── TimeTravelExport.kt │ │ └── TimeTravelExportSerializer.kt │ │ ├── server │ │ ├── Mappings.kt │ │ └── StateDiff.kt │ │ └── store │ │ ├── TimeTravelStore.kt │ │ ├── TimeTravelStoreFactory.kt │ │ └── TimeTravelStoreImpl.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ ├── controller │ │ ├── TimeTravelControllerDebugTest.kt │ │ ├── TimeTravelControllerIdleTest.kt │ │ ├── TimeTravelControllerRecordingTest.kt │ │ ├── TimeTravelControllerStoppedTest.kt │ │ └── TimeTravelControllerTestingEnvironment.kt │ │ └── store │ │ ├── TestTimeTravelStore.kt │ │ ├── TimeTravelStoreDebugTest.kt │ │ └── TimeTravelStoreGenericTests.kt │ ├── darwinMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── server │ │ ├── ConnectionThread.kt │ │ └── TimeTravelServer.kt │ ├── javaMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ ├── export │ │ └── DefaultTimeTravelExportSerializer.kt │ │ └── server │ │ ├── ConnectionThread.kt │ │ └── TimeTravelServerImpl.kt │ ├── javaTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── export │ │ └── DefaultTimeTravelExportSerializerTest.kt │ ├── jvmMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── timetravel │ │ └── server │ │ └── TimeTravelServer.kt │ └── main │ ├── AndroidManifest.xml │ └── res │ ├── drawable-hdpi │ ├── ic_time_travel_cancel.png │ ├── ic_time_travel_debug.png │ ├── ic_time_travel_export.png │ ├── ic_time_travel_import.png │ ├── ic_time_travel_move_to_end.png │ ├── ic_time_travel_move_to_start.png │ ├── ic_time_travel_play.png │ ├── ic_time_travel_record.png │ ├── ic_time_travel_step_backward.png │ ├── ic_time_travel_step_forward.png │ └── ic_time_travel_stop.png │ ├── drawable-mdpi │ ├── ic_time_travel_cancel.png │ ├── ic_time_travel_debug.png │ ├── ic_time_travel_export.png │ ├── ic_time_travel_import.png │ ├── ic_time_travel_move_to_end.png │ ├── ic_time_travel_move_to_start.png │ ├── ic_time_travel_play.png │ ├── ic_time_travel_record.png │ ├── ic_time_travel_step_backward.png │ ├── ic_time_travel_step_forward.png │ └── ic_time_travel_stop.png │ ├── drawable-xhdpi │ ├── ic_time_travel_cancel.png │ ├── ic_time_travel_debug.png │ ├── ic_time_travel_export.png │ ├── ic_time_travel_import.png │ ├── ic_time_travel_move_to_end.png │ ├── ic_time_travel_move_to_start.png │ ├── ic_time_travel_play.png │ ├── ic_time_travel_record.png │ ├── ic_time_travel_step_backward.png │ ├── ic_time_travel_step_forward.png │ └── ic_time_travel_stop.png │ ├── drawable-xxhdpi │ ├── ic_time_travel_cancel.png │ ├── ic_time_travel_debug.png │ ├── ic_time_travel_export.png │ ├── ic_time_travel_import.png │ ├── ic_time_travel_move_to_end.png │ ├── ic_time_travel_move_to_start.png │ ├── ic_time_travel_play.png │ ├── ic_time_travel_record.png │ ├── ic_time_travel_step_backward.png │ ├── ic_time_travel_step_forward.png │ └── ic_time_travel_stop.png │ ├── drawable-xxxhdpi │ ├── ic_time_travel_cancel.png │ ├── ic_time_travel_debug.png │ ├── ic_time_travel_export.png │ ├── ic_time_travel_import.png │ ├── ic_time_travel_move_to_end.png │ ├── ic_time_travel_move_to_start.png │ ├── ic_time_travel_play.png │ ├── ic_time_travel_record.png │ ├── ic_time_travel_step_backward.png │ ├── ic_time_travel_step_forward.png │ └── ic_time_travel_stop.png │ ├── layout │ ├── item_time_travel_event.xml │ ├── item_time_travel_separator.xml │ └── view_time_travel.xml │ └── values │ ├── attrs.xml │ ├── strings.xml │ └── styles.xml ├── mvikotlin ├── .gitignore ├── api │ ├── android │ │ └── mvikotlin.api │ └── jvm │ │ └── mvikotlin.api ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ ├── annotations │ │ └── Annotations.kt │ │ ├── binder │ │ ├── Binder.kt │ │ ├── BinderExt.kt │ │ └── BinderLifecycleMode.kt │ │ ├── instancekeeper │ │ └── InstanceKeeperExt.kt │ │ ├── store │ │ ├── Bootstrapper.kt │ │ ├── Executor.kt │ │ ├── Reducer.kt │ │ ├── SimpleBootstrapper.kt │ │ ├── Store.kt │ │ ├── StoreEventType.kt │ │ ├── StoreFactory.kt │ │ └── StoreFactoryExt.kt │ │ ├── utils │ │ ├── Diff.kt │ │ ├── JvmSerializable.kt │ │ └── MainThreadAssert.kt │ │ └── view │ │ ├── BaseMviView.kt │ │ ├── MviView.kt │ │ ├── ViewEvents.kt │ │ └── ViewRenderer.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ └── binder │ │ └── BinderAttachToLifecycleTest.kt │ ├── javaMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ └── utils │ │ └── JvmSerializable.kt │ ├── jsNativeMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ └── utils │ │ └── JvmSerializable.kt │ ├── jvmMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ └── utils │ │ └── MainThreadAssert.kt │ ├── linuxX64Main │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── core │ │ └── utils │ │ └── MainThreadAssert.kt │ └── main │ └── AndroidManifest.xml ├── rx-internal ├── .gitignore ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ ├── BaseSubject.kt │ │ ├── BehaviorSubject.kt │ │ ├── DisposableBuilder.kt │ │ ├── Lock.kt │ │ ├── LockExt.kt │ │ ├── PublishSubject.kt │ │ ├── Serializer.kt │ │ └── Subject.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ ├── BaseSubjectTest.kt │ │ ├── BehaviorSubjectTest.kt │ │ └── SerializerTest.kt │ ├── darwinMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ └── Utils.kt │ ├── javaMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ └── Lock.kt │ ├── jsMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ └── Lock.kt │ ├── linuxX64Main │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ └── Utils.kt │ ├── main │ └── AndroidManifest.xml │ ├── nativeCommonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ └── internal │ │ ├── ThreadLocalSubjectTestNative.kt │ │ └── Utils.kt │ └── nativeMain │ └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── rx │ └── internal │ ├── Lock.kt │ └── Utils.kt ├── rx ├── .gitignore ├── api │ ├── android │ │ └── rx.api │ └── jvm │ │ └── rx.api ├── build.gradle.kts └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── rx │ │ ├── Disposable.kt │ │ ├── Observer.kt │ │ └── ObserverBuilder.kt │ └── main │ └── AndroidManifest.xml ├── sample ├── todo-app-android │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── android │ │ │ ├── App.kt │ │ │ ├── DebugDrawerFragment.kt │ │ │ ├── FrameworkType.kt │ │ │ ├── LifecycledConsumer.kt │ │ │ ├── MainActivity.kt │ │ │ ├── Relay.kt │ │ │ ├── Utils.kt │ │ │ ├── details │ │ │ ├── TodoDetailsFragment.kt │ │ │ └── TodoDetailsViewImpl.kt │ │ │ ├── list │ │ │ ├── TodoAddViewImpl.kt │ │ │ ├── TodoListAdapter.kt │ │ │ ├── TodoListFragment.kt │ │ │ └── TodoListViewImpl.kt │ │ │ └── root │ │ │ ├── DetailsOutputToListInput.kt │ │ │ └── RootFragment.kt │ │ └── res │ │ ├── anim │ │ ├── scale_fade_in.xml │ │ ├── scale_fade_out.xml │ │ ├── slide_fade_in_bottom.xml │ │ └── slide_fade_out_bottom.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_delete_dark.xml │ │ ├── ic_delete_light.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── content.xml │ │ ├── main_activity_debug.xml │ │ ├── todo_details.xml │ │ ├── todo_item.xml │ │ └── todo_list.xml │ │ ├── menu │ │ └── details.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml ├── todo-app-ios │ ├── .gitignore │ ├── todo-app-ios.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── todo-app-ios │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ │ ├── AutoCancellable.swift │ │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ │ ├── ContentView.swift │ │ ├── ControllerDeps.swift │ │ ├── DebugDrawer.swift │ │ ├── Info.plist │ │ ├── LazyView.swift │ │ ├── LifecycleRegistryExt.swift │ │ ├── LifecycleWrapper.swift │ │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ │ ├── RootView.swift │ │ ├── SceneDelegate.swift │ │ ├── TimeTravelView.swift │ │ ├── TimeTravelViewController.swift │ │ ├── TodoItemExt.swift │ │ ├── details │ │ ├── TodoDetails.swift │ │ ├── TodoDetailsParent.swift │ │ └── TodoDetailsViewProxy.swift │ │ └── list │ │ ├── TodoAdd.swift │ │ ├── TodoAddViewProxy.swift │ │ ├── TodoList.swift │ │ ├── TodoListParent.swift │ │ ├── TodoListViewProxy.swift │ │ └── TodoRow.swift ├── todo-app-js │ ├── .gitignore │ ├── README.md │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ ├── DisposableBuilder.kt │ │ ├── Util.kt │ │ ├── details │ │ │ ├── TodoDetailsComponent.kt │ │ │ └── TodoDetailsViewProxy.kt │ │ ├── list │ │ │ ├── TodoAdd.kt │ │ │ ├── TodoAddViewProxy.kt │ │ │ ├── TodoList.kt │ │ │ ├── TodoListComponent.kt │ │ │ └── TodoListViewProxy.kt │ │ ├── root │ │ │ ├── App.kt │ │ │ ├── AppbarWithButton.kt │ │ │ └── Main.kt │ │ └── timetravel │ │ │ ├── TimeTravelButtonsView.kt │ │ │ ├── TimeTravelComponent.kt │ │ │ ├── TimeTravelEventInfoDialogView.kt │ │ │ └── TimeTravelEventsView.kt │ │ └── resources │ │ └── index.html ├── todo-common-internal │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── common │ │ │ └── internal │ │ │ ├── mapper │ │ │ ├── TodoAddMappers.kt │ │ │ ├── TodoDetailsMappers.kt │ │ │ └── TodoListMappers.kt │ │ │ └── store │ │ │ ├── add │ │ │ ├── TodoAddStore.kt │ │ │ └── TodoAddStoreAbstractFactory.kt │ │ │ ├── details │ │ │ ├── TodoDetailsStore.kt │ │ │ └── TodoDetailsStoreAbstractFactory.kt │ │ │ └── list │ │ │ ├── TodoListStore.kt │ │ │ └── TodoListStoreAbstractFactory.kt │ │ └── main │ │ └── AndroidManifest.xml ├── todo-common │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── common │ │ │ └── database │ │ │ ├── TodoDatabaseImpl.kt │ │ │ ├── TodoDatabaseOpenHelper.kt │ │ │ ├── TodoItemContract.kt │ │ │ └── Utils.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── common │ │ │ ├── controller │ │ │ ├── TodoDetailsController.kt │ │ │ ├── TodoDetailsControllerDeps.kt │ │ │ ├── TodoListController.kt │ │ │ └── TodoListControllerDeps.kt │ │ │ ├── database │ │ │ ├── TodoDatabase.kt │ │ │ ├── TodoItem.kt │ │ │ └── TodoItemExt.kt │ │ │ └── view │ │ │ ├── TodoAddView.kt │ │ │ ├── TodoDetailsView.kt │ │ │ └── TodoListView.kt │ │ ├── darwinMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── common │ │ │ └── database │ │ │ └── TodoDatabaseImpl.kt │ │ ├── jsMain │ │ └── kotlin │ │ │ └── TodoDatabaseImpl.kt │ │ └── main │ │ └── AndroidManifest.xml ├── todo-coroutines │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── androidMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── coroutines │ │ │ └── Dispatchers.kt │ │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── coroutines │ │ │ ├── Dispatchers.kt │ │ │ ├── MapNotNull.kt │ │ │ ├── controller │ │ │ ├── TodoDetailsCoroutinesController.kt │ │ │ └── TodoListCoroutinesController.kt │ │ │ └── store │ │ │ ├── TodoAddStoreFactory.kt │ │ │ ├── TodoDetailsStoreFactory.kt │ │ │ └── TodoListStoreFactory.kt │ │ ├── commonTest │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── reaktive │ │ │ ├── TestDatabase.kt │ │ │ ├── controller │ │ │ ├── TestAddView.kt │ │ │ ├── TestDetailsView.kt │ │ │ ├── TestListView.kt │ │ │ ├── TestMviView.kt │ │ │ ├── TodoDetailsReaktiveControllerTest.kt │ │ │ └── TodoListReaktiveControllerTest.kt │ │ │ └── store │ │ │ ├── TodoAddStoreTest.kt │ │ │ ├── TodoDetailsStoreTest.kt │ │ │ └── TodoListStoreTest.kt │ │ ├── jsMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── coroutines │ │ │ └── Dispatchers.kt │ │ ├── jvmMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── coroutines │ │ │ └── Dispatchers.kt │ │ ├── main │ │ └── AndroidManifest.xml │ │ └── nativeMain │ │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── sample │ │ └── todo │ │ └── coroutines │ │ └── Dispatchers.kt ├── todo-darwin-umbrella │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── arkivanov │ │ │ └── mvikotlin │ │ │ └── sample │ │ │ └── todo │ │ │ └── darwin │ │ │ └── umbrella │ │ │ └── Dummy.kt │ │ └── main │ │ └── AndroidManifest.xml └── todo-reaktive │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── sample │ │ └── todo │ │ └── reaktive │ │ ├── controller │ │ ├── TodoDetailsReaktiveController.kt │ │ └── TodoListReaktiveController.kt │ │ └── store │ │ ├── TodoAddStoreFactory.kt │ │ ├── TodoDetailsStoreFactory.kt │ │ └── TodoListStoreFactory.kt │ ├── commonTest │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── sample │ │ └── todo │ │ └── reaktive │ │ ├── TestDatabase.kt │ │ ├── controller │ │ ├── TestAddView.kt │ │ ├── TestDetailsView.kt │ │ ├── TestListView.kt │ │ ├── TestMviView.kt │ │ ├── TodoDetailsReaktiveControllerTest.kt │ │ └── TodoListReaktiveControllerTest.kt │ │ └── store │ │ ├── TodoAddStoreTest.kt │ │ ├── TodoDetailsStoreTest.kt │ │ └── TodoListStoreTest.kt │ └── main │ └── AndroidManifest.xml ├── settings.gradle.kts ├── tools └── check-publication │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ ├── commonMain │ └── kotlin │ │ └── com │ │ └── arkivanov │ │ └── mvikotlin │ │ └── tools │ │ └── checkpublication │ │ └── Dummy.kt │ └── main │ └── AndroidManifest.xml └── utils-internal ├── .gitignore ├── build.gradle.kts └── src ├── androidMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ ├── Logs.kt │ └── MainThreadAssert.kt ├── commonMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ ├── Atomic.kt │ ├── AtomicExt.kt │ ├── Freeze.kt │ ├── IsolatedRef.kt │ ├── Logs.kt │ └── MainThreadAssert.kt ├── commonTest └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ ├── AtomicBooleanTest.kt │ ├── AtomicIntTest.kt │ ├── AtomicRefTest.kt │ └── LogsTest.kt ├── darwinMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ └── MainThreadAssert.kt ├── javaMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ └── Atomic.kt ├── jsMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ ├── Atomic.kt │ ├── Logs.kt │ └── MainThreadAssert.kt ├── jvmJsMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ ├── Freeze.kt │ └── IsolatedRef.kt ├── jvmMain └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ ├── Logs.kt │ └── MainThreadAssert.kt ├── linuxX64Main └── kotlin │ └── com │ └── arkivanov │ └── mvikotlin │ └── utils │ └── internal │ └── MainThreadAssert.kt ├── main └── AndroidManifest.xml └── nativeMain └── kotlin └── com └── arkivanov └── mvikotlin └── utils └── internal ├── Atomic.kt ├── Freeze.kt ├── IsolatedRef.kt └── Logs.kt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://www.buymeacoffee.com/arkivanov'] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | local.properties 4 | .idea 5 | /build 6 | .DS_Store -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /docs/media/lifecycle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/lifecycle.jpg -------------------------------------------------------------------------------- /docs/media/logo/landscape/jpg/mvikotlin_black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/landscape/jpg/mvikotlin_black.jpg -------------------------------------------------------------------------------- /docs/media/logo/landscape/jpg/mvikotlin_coloured.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/landscape/jpg/mvikotlin_coloured.jpg -------------------------------------------------------------------------------- /docs/media/logo/landscape/jpg/mvikotlin_white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/landscape/jpg/mvikotlin_white.jpg -------------------------------------------------------------------------------- /docs/media/logo/landscape/png/mvikotlin_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/landscape/png/mvikotlin_black.png -------------------------------------------------------------------------------- /docs/media/logo/landscape/png/mvikotlin_coloured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/landscape/png/mvikotlin_coloured.png -------------------------------------------------------------------------------- /docs/media/logo/landscape/png/mvikotlin_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/landscape/png/mvikotlin_white.png -------------------------------------------------------------------------------- /docs/media/logo/logo_guide.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/logo_guide.jpg -------------------------------------------------------------------------------- /docs/media/logo/logo_guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/logo_guide.pdf -------------------------------------------------------------------------------- /docs/media/logo/portrait/jpg/mvikotlin_black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/portrait/jpg/mvikotlin_black.jpg -------------------------------------------------------------------------------- /docs/media/logo/portrait/jpg/mvikotlin_coloured.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/portrait/jpg/mvikotlin_coloured.jpg -------------------------------------------------------------------------------- /docs/media/logo/portrait/jpg/mvikotlin_white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/portrait/jpg/mvikotlin_white.jpg -------------------------------------------------------------------------------- /docs/media/logo/portrait/png/mvikotlin_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/portrait/png/mvikotlin_black.png -------------------------------------------------------------------------------- /docs/media/logo/portrait/png/mvikotlin_coloured.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/portrait/png/mvikotlin_coloured.png -------------------------------------------------------------------------------- /docs/media/logo/portrait/png/mvikotlin_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/logo/portrait/png/mvikotlin_white.png -------------------------------------------------------------------------------- /docs/media/mvi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/mvi.jpg -------------------------------------------------------------------------------- /docs/media/mvikotlin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/mvikotlin.jpg -------------------------------------------------------------------------------- /docs/media/store.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/store.jpg -------------------------------------------------------------------------------- /docs/media/time-travel-client-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/docs/media/time-travel-client-app.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.jvmargs=-Xmx2g 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | 6 | systemProp.org.gradle.internal.publish.checksums.insecure=true 7 | 8 | # AndroidX package structure to make it clearer which packages are bundled with the 9 | # Android operating system, and which are packaged with the app's APK 10 | android.useAndroidX=true 11 | 12 | # Automatically convert third-party libraries to use AndroidX 13 | android.enableJetifier=true 14 | 15 | kotlin.mpp.enableGranularSourceSetsMetadata=true 16 | kotlin.native.enableDependencyPropagation=false 17 | kotlin.mpp.enableCInteropCommonization=true 18 | kotlin.mpp.commonizerLogLevel=info 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badoo/MVIKotlin/39de85b944a52cd1878478e2c5c4921f8896920d/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /keepers/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /keepers/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kotlin-multiplatform") 3 | id("com.android.library") 4 | id("com.arkivanov.gradle.setup") 5 | } 6 | 7 | setupMultiplatform { 8 | targets() 9 | publications() 10 | binaryCompatibilityValidator() 11 | } 12 | 13 | kotlin { 14 | sourceSets { 15 | named("commonMain") { 16 | dependencies { 17 | implementation(project(":utils-internal")) 18 | } 19 | } 20 | 21 | named("androidMain") { 22 | dependencies { 23 | implementation(deps.androidx.lifecycle.lifecycleViewmodel) 24 | implementation(deps.androidx.lifecycle.lifecycleViewmodelSavedstate) 25 | } 26 | } 27 | 28 | named("androidTest") { 29 | dependencies { 30 | implementation(deps.androidx.lifecycle.lifecycleRuntime) 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /keepers/src/androidMain/kotlin/com/arkivanov/mvikotlin/keepers/instancekeeper/AndroidInstanceKeeper.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.instancekeeper 2 | 3 | import androidx.lifecycle.ViewModelProvider 4 | import androidx.lifecycle.ViewModelStoreOwner 5 | 6 | @ExperimentalInstanceKeeperApi 7 | @Suppress("DeprecatedCallableAddReplaceWith") 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | fun ViewModelStoreOwner.getInstanceKeeper(): InstanceKeeper = 10 | ViewModelProvider(this) 11 | .get(InstanceKeeperViewModel::class.java) 12 | .instanceKeeper 13 | -------------------------------------------------------------------------------- /keepers/src/androidMain/kotlin/com/arkivanov/mvikotlin/keepers/instancekeeper/InstanceKeeperViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.instancekeeper 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | @ExperimentalInstanceKeeperApi 6 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 7 | internal class InstanceKeeperViewModel : ViewModel() { 8 | 9 | val instanceKeeper: DefaultInstanceKeeper = DefaultInstanceKeeper() 10 | 11 | override fun onCleared() { 12 | instanceKeeper.destroy() 13 | 14 | super.onCleared() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /keepers/src/androidMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/ParcelableStateKeeper.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | import android.os.Bundle 4 | import android.os.Parcelable 5 | import androidx.savedstate.SavedStateRegistry 6 | import kotlin.reflect.KClass 7 | 8 | @ExperimentalStateKeeperApi 9 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 10 | internal class ParcelableStateKeeper( 11 | registry: SavedStateRegistry, 12 | clazz: KClass, 13 | key: String 14 | ) : AndroidStateKeeper(registry, clazz, key) { 15 | 16 | override fun Bundle.getValue(key: String): S? = getParcelable(key) 17 | 18 | override fun Bundle.putValue(key: String, value: T) { 19 | putParcelable(key, value) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /keepers/src/androidMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/ParcelableStateKeeperRegistry.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | import android.os.Parcelable 4 | import androidx.savedstate.SavedStateRegistryOwner 5 | import kotlin.reflect.KClass 6 | 7 | @ExperimentalStateKeeperApi 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | fun SavedStateRegistryOwner.getParcelableStateKeeperRegistry(): StateKeeperRegistry = 10 | object : StateKeeperRegistry { 11 | override fun get(clazz: KClass, key: String): StateKeeper = 12 | ParcelableStateKeeper(registry = savedStateRegistry, clazz = clazz, key = key) 13 | } 14 | -------------------------------------------------------------------------------- /keepers/src/androidMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/SerializableStateKeeper.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | import android.os.Bundle 4 | import androidx.savedstate.SavedStateRegistry 5 | import java.io.Serializable 6 | import kotlin.reflect.KClass 7 | 8 | @ExperimentalStateKeeperApi 9 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 10 | internal class SerializableStateKeeper( 11 | registry: SavedStateRegistry, 12 | clazz: KClass, 13 | key: String 14 | ) : AndroidStateKeeper(registry, clazz, key) { 15 | 16 | @Suppress("UNCHECKED_CAST") 17 | override fun Bundle.getValue(key: String): S? = getSerializable(key) as S? 18 | 19 | override fun Bundle.putValue(key: String, value: T) { 20 | putSerializable(key, value) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /keepers/src/androidMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/SerializableStateKeeperRegistry.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | import androidx.savedstate.SavedStateRegistryOwner 4 | import java.io.Serializable 5 | import kotlin.reflect.KClass 6 | 7 | @ExperimentalStateKeeperApi 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | fun SavedStateRegistryOwner.getSerializableStateKeeperRegistry(): StateKeeperRegistry = 10 | object : StateKeeperRegistry { 11 | override fun get(clazz: KClass, key: String): StateKeeper = 12 | SerializableStateKeeper(registry = savedStateRegistry, clazz = clazz, key = key) 13 | } 14 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/instancekeeper/DefaultInstanceKeeper.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.instancekeeper 2 | 3 | import com.arkivanov.mvikotlin.utils.internal.ensureNeverFrozen 4 | 5 | /** 6 | * A simple [InstanceKeeper] implementation via `HashMap` 7 | */ 8 | @ExperimentalInstanceKeeperApi 9 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 10 | class DefaultInstanceKeeper : InstanceKeeper { 11 | 12 | init { 13 | ensureNeverFrozen() 14 | } 15 | 16 | private val map = HashMap() 17 | 18 | @Suppress("UNCHECKED_CAST") 19 | override fun get(key: Any, factory: () -> T): T = 20 | map.getOrPut(key, factory) as T 21 | 22 | /** 23 | * Destroys and removes all currently retained `Instances` 24 | */ 25 | fun destroy() { 26 | map.forEach { it.value.onDestroy() } 27 | map.clear() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/instancekeeper/ExperimentalInstanceKeeperApi.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.instancekeeper 2 | 3 | @RequiresOptIn(level = RequiresOptIn.Level.WARNING) 4 | @Retention(AnnotationRetention.BINARY) 5 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 6 | annotation class ExperimentalInstanceKeeperApi 7 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/instancekeeper/InstanceKeeper.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.instancekeeper 2 | 3 | /** 4 | * Provides a way to retain instances (e.g. a `Store`'s instance). 5 | * A typical use case is Android Activity recreation due to configuration changes. 6 | */ 7 | @ExperimentalInstanceKeeperApi 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | interface InstanceKeeper { 10 | 11 | /** 12 | * Either returns a currently retained [Instance] or creates (and retains) a new one. 13 | * 14 | * @param factory a factory function, called when there is no retained `Instance` yet 15 | * @return either a currently retained `Instance` or a new one 16 | */ 17 | fun get(key: Any, factory: () -> T): T 18 | 19 | /** 20 | * Represents a retained instance 21 | */ 22 | interface Instance { 23 | /** 24 | * Called when the `Instance` is destroyed. Clean-up any resources associated with the `Instance`. 25 | */ 26 | fun onDestroy() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/instancekeeper/InstanceKeeperExt.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.instancekeeper 2 | 3 | /** 4 | * Same as [InstanceKeeper.get] but the key is `T::class` 5 | */ 6 | @ExperimentalInstanceKeeperApi 7 | @Suppress("DeprecatedCallableAddReplaceWith") 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | inline fun InstanceKeeper.get(noinline factory: () -> T): T = get(T::class, factory) 10 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/ExperimentalStateKeeperApi.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | @RequiresOptIn(level = RequiresOptIn.Level.WARNING) 4 | @Retention(AnnotationRetention.BINARY) 5 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 6 | annotation class ExperimentalStateKeeperApi 7 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/StateKeeper.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | /** 4 | * Provides a way to save and restore state (e.g. a `Store`'s state). 5 | * A typical use case is Android Activity recreation due to system constraints. 6 | */ 7 | @ExperimentalStateKeeperApi 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | interface StateKeeper { 10 | 11 | /** 12 | * Returns a previously saved state, if any 13 | */ 14 | fun consume(): T? 15 | 16 | /** 17 | * Registers a state supplier 18 | * 19 | * @param supplier a state supplier that will be called when it's time to save the state 20 | */ 21 | fun register(supplier: () -> T) 22 | 23 | /** 24 | * Unregisters (removes) a previously registered state supplier 25 | */ 26 | fun unregister() 27 | } 28 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/StateKeeperRegistry.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | import kotlin.reflect.KClass 4 | 5 | /** 6 | * Provides and manages [StateKeeper]s 7 | */ 8 | @ExperimentalStateKeeperApi 9 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 10 | interface StateKeeperRegistry { 11 | 12 | /** 13 | * Provides instances of [StateKeeper] by key 14 | * 15 | * @param clazz a Kotlin class of values to be saved and restored by the [StateKeeper], *must not* be a local or an anonymous class. 16 | * @param key a string key, must be unique within the registry instance. The default value is [KClass.toString]. 17 | * @return a typed instance of the [StateKeeper] 18 | */ 19 | fun get(clazz: KClass, key: String = clazz.toString()): StateKeeper 20 | } 21 | -------------------------------------------------------------------------------- /keepers/src/commonMain/kotlin/com/arkivanov/mvikotlin/keepers/statekeeper/StateKeeperRegistryExt.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.keepers.statekeeper 2 | 3 | /** 4 | * A convenience method for [StateKeeperRegistry.get] 5 | */ 6 | @ExperimentalStateKeeperApi 7 | @Suppress("DeprecatedCallableAddReplaceWith") 8 | @Deprecated(message = "This API is now provided by Essenty library: github.com/arkivanov/Essenty") 9 | inline fun StateKeeperRegistry.get(key: String = S::class.toString()): StateKeeper = 10 | get(S::class, key) 11 | -------------------------------------------------------------------------------- /keepers/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /mvikotlin-extensions-coroutines/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mvikotlin-extensions-coroutines/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("kotlin-multiplatform") 3 | id("com.android.library") 4 | id("com.arkivanov.gradle.setup") 5 | } 6 | 7 | setupMultiplatform { 8 | targets() 9 | publications() 10 | binaryCompatibilityValidator() 11 | } 12 | 13 | kotlin { 14 | sourceSets { 15 | named("commonMain") { 16 | dependencies { 17 | implementation(project(":mvikotlin")) 18 | implementation(project(":rx")) 19 | implementation(project(":utils-internal")) 20 | implementation(deps.kotlinx.kotlinxCoroutinesCore) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mvikotlin-extensions-coroutines/src/commonMain/kotlin/com/arkivanov/mvikotlin/extensions/coroutines/StoreExt.kt: -------------------------------------------------------------------------------- 1 | package com.arkivanov.mvikotlin.extensions.coroutines 2 | 3 | import com.arkivanov.mvikotlin.core.store.Store 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlin.coroutines.CoroutineContext 7 | 8 | /** 9 | * Returns a [Flow] that emits [Store] `States`. 10 | * The first emission with the current `State` will be performed synchronously on collection. 11 | * Please not that the actual collection of the [Flow] may not be synchronous depending on [CoroutineContext] being used. 12 | */ 13 | @ExperimentalCoroutinesApi 14 | val Store<*, State, *>.states: Flow 15 | get() = toFlow(Store<*, State, *>::states) 16 | 17 | /** 18 | * Returns a [Flow] that emits [Store] `Labels` 19 | */ 20 | @ExperimentalCoroutinesApi 21 | val