Full Code of arkivanov/Essenty for AI

master 703543ddf558 cached
137 files
391.7 KB
100.4k tokens
1 requests
Download .txt
Showing preview only (441K chars total). Download the full file or copy to clipboard to get everything.
Repository: arkivanov/Essenty
Branch: master
Commit: 703543ddf558
Files: 137
Total size: 391.7 KB

Directory structure:
gitextract_dhrtofoi/

├── .editorconfig
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── build.yml
│       └── publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── back-handler/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── back-handler.api
│   │   ├── back-handler.klib.api
│   │   └── jvm/
│   │       └── back-handler.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── backhandler/
│       │                       └── AndroidBackHandler.kt
│       ├── androidUnitTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── backhandler/
│       │                       ├── AndroidBackHandlerTest.kt
│       │                       ├── AndroidBackHandlerWithLifecycleTest.kt
│       │                       └── OnBackPressedCallbackAdapterTest.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── backhandler/
│       │                       ├── BackCallback.kt
│       │                       ├── BackDispatcher.kt
│       │                       ├── BackEvent.kt
│       │                       ├── BackHandler.kt
│       │                       ├── BackHandlerOwner.kt
│       │                       ├── DefaultBackDispatcher.kt
│       │                       └── Utils.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── backhandler/
│                               └── DefaultBackDispatcherTest.kt
├── build.gradle.kts
├── deps.versions.toml
├── detekt.yml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── instance-keeper/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── instance-keeper.api
│   │   ├── instance-keeper.klib.api
│   │   └── jvm/
│   │       └── instance-keeper.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── instancekeeper/
│       │                       └── AndroidExt.kt
│       ├── androidUnitTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── instancekeeper/
│       │                       └── AndroidInstanceKeeperTest.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── instancekeeper/
│       │                       ├── DefaultInstanceKeeperDispatcher.kt
│       │                       ├── ExperimentalInstanceKeeperApi.kt
│       │                       ├── InstanceKeeper.kt
│       │                       ├── InstanceKeeperDispatcher.kt
│       │                       ├── InstanceKeeperExt.kt
│       │                       └── InstanceKeeperOwner.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── instancekeeper/
│                               ├── DefaultInstanceKeeperDispatcherTest.kt
│                               └── InstanceKeeperExtTest.kt
├── lifecycle/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── lifecycle.api
│   │   ├── jvm/
│   │   │   └── lifecycle.api
│   │   └── lifecycle.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── AndroidExt.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       ├── Lifecycle.kt
│       │                       ├── LifecycleExt.kt
│       │                       ├── LifecycleOwner.kt
│       │                       ├── LifecycleRegistry.kt
│       │                       ├── LifecycleRegistryExt.kt
│       │                       └── LifecycleRegistryImpl.kt
│       ├── commonTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       ├── LifecycleExtTest.kt
│       │                       └── LifecycleRegistryTest.kt
│       ├── itvosMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── ApplicationLifecycle.kt
│       └── itvosTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── lifecycle/
│                               ├── ApplicationLifecyclePlatformTest.kt
│                               └── ApplicationLifecycleTest.kt
├── lifecycle-coroutines/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── lifecycle-coroutines.api
│   │   ├── jvm/
│   │   │   └── lifecycle-coroutines.api
│   │   └── lifecycle-coroutines.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── coroutines/
│       │                           ├── CoroutineScopeWithLifecycle.kt
│       │                           ├── DispatchersExt.kt
│       │                           ├── FlowWithLifecycle.kt
│       │                           └── RepeatOnLifecycle.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── lifecycle/
│                               └── coroutines/
│                                   ├── CoroutineScopeWithLifecycleTest.kt
│                                   ├── DispatchersExtTest.kt
│                                   └── LifecycleCoroutinesExtTest.kt
├── lifecycle-reaktive/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── lifecycle-reaktive.api
│   │   ├── jvm/
│   │   │   └── lifecycle-reaktive.api
│   │   └── lifecycle-reaktive.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── reaktive/
│       │                           └── DisposableWithLifecycle.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── lifecycle/
│                               └── reaktive/
│                                   └── DisposableWithLifecycleTest.kt
├── settings.gradle.kts
├── state-keeper/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── state-keeper.api
│   │   ├── jvm/
│   │   │   └── state-keeper.api
│   │   └── state-keeper.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── AndroidExt.kt
│       │                       ├── BundleExt.kt
│       │                       └── PersistableBundleExt.kt
│       ├── androidUnitTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── AndroidStateKeeperTest.kt
│       │                       ├── BundleExtTest.kt
│       │                       └── TestUtils.android.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── DefaultStateKeeperDispatcher.kt
│       │                       ├── ExperimentalStateKeeperApi.kt
│       │                       ├── PolymorphicSerializer.kt
│       │                       ├── SerializableContainer.kt
│       │                       ├── StateKeeper.kt
│       │                       ├── StateKeeperDispatcher.kt
│       │                       ├── StateKeeperExt.kt
│       │                       ├── StateKeeperOwner.kt
│       │                       ├── Utils.kt
│       │                       └── base64/
│       │                           ├── Decoder.kt
│       │                           ├── Dictionaries.kt
│       │                           ├── Encoder.kt
│       │                           └── README.md
│       ├── commonTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── CodingTest.kt
│       │                       ├── DefaultStateKeeperDispatcherTest.kt
│       │                       ├── PolymorphicSerializerTest.kt
│       │                       ├── SerializableContainerTest.kt
│       │                       ├── SerializableData.kt
│       │                       ├── StateKeeperExtTest.kt
│       │                       ├── TestUtils.kt
│       │                       └── base64/
│       │                           ├── Base64ImplTest.kt
│       │                           └── README.md
│       ├── javaMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       └── Utils.java.kt
│       ├── jsTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       └── DefaultStateKeeperDispatcherJsTest.kt
│       └── nonJavaMain/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── statekeeper/
│                               └── Utils.kt
├── state-keeper-benchmarks/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       ├── main/
│       │   └── res/
│       │       └── AndroidManifest.xml
│       └── test/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── statekeeper/
│                               └── benchmarks/
│                                   └── Benchmarks.kt
├── tools/
│   └── check-publication/
│       ├── .gitignore
│       ├── build.gradle.kts
│       └── src/
│           ├── androidMain/
│           │   └── AndroidManifest.xml
│           └── commonMain/
│               └── kotlin/
│                   └── com/
│                       └── arkivanov/
│                           └── essenty/
│                               └── tools/
│                                   └── checkpublication/
│                                       └── Dummy.kt
└── utils-internal/
    ├── .gitignore
    ├── build.gradle.kts
    └── src/
        ├── androidMain/
        │   └── AndroidManifest.xml
        └── commonMain/
            └── kotlin/
                └── com/
                    └── arkivanov/
                        └── essenty/
                            └── utils/
                                └── internal/
                                    ├── ExperimentalEssentyApi.kt
                                    └── InternalEssentyApi.kt

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
insert_final_newline = true

[{*.kt, *.kts}]
max_line_length = 140
ij_kotlin_packages_to_use_import_on_demand = ^
ij_continuation_indent_size = 4
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = true
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = false
ij_kotlin_continuation_indent_for_expression_bodies = false
ij_kotlin_continuation_indent_in_argument_lists = false
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = true
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: arkivanov
custom: ["https://www.buymeacoffee.com/arkivanov"]


================================================
FILE: .github/workflows/build.yml
================================================
name: Build

on:
  pull_request:
    paths-ignore:
      - 'docs/**'

jobs:
  linux-build:
    name: Build on Linux
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Install Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: 17
      - name: Update dependencies
        run: sudo apt-get update
      - name: Install dependencies
        run: sudo apt-get install nodejs chromium-browser
      - name: Build
        uses: gradle/gradle-build-action@v2
        with:
          arguments: build -Dsplit_targets
  macos-build:
    name: Build on macOS
    runs-on: macos-14
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Install Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: 17
      - name: Build project
        uses: gradle/gradle-build-action@v2
        with:
          arguments: build -Dsplit_targets


================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish

on:
  workflow_dispatch:

jobs:
  create-staging-repository:
    runs-on: ubuntu-latest
    name: Create staging repository
    outputs:
      repository_id: ${{ steps.create.outputs.repository_id }}
    steps:
      - id: create
        uses: nexus-actions/create-nexus-staging-repo@v1.3.0
        with:
          username: arkivanov
          password: ${{ secrets.SONATYPE_PASSWORD }}
          staging_profile_id: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
          description: Created by GitHub Actions
          base_url: https://s01.oss.sonatype.org/service/local/
  publish:
    name: Publish
    runs-on: macos-14
    needs: create-staging-repository
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Install Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: 17
      - name: Publish
        env:
          SONATYPE_REPOSITORY_ID: ${{ needs.create-staging-repository.outputs.repository_id }}
          SONATYPE_USER_NAME: ${{ secrets.SONATYPE_USER_NAME }}
          SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
          SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
          SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
        run: ./gradlew publish
  close-staging-repository:
    name: Close staging repository
    runs-on: ubuntu-latest
    needs: [ create-staging-repository, publish ]
    steps:
      - name: Close staging repository
        uses: nexus-actions/release-nexus-staging-repo@v1.1
        with:
          username: arkivanov
          password: ${{ secrets.SONATYPE_PASSWORD }}
          staging_repository_id: ${{ needs.create-staging-repository.outputs.repository_id }}
          base_url: https://s01.oss.sonatype.org/service/local/
          close_only: 'true'
  check-publication:
    name: Check publication
    runs-on: macos-14
    needs: close-staging-repository
    steps:
      - name: Checkout
        uses: actions/checkout@v1
      - name: Install Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: 17
      - name: Check publication
        run: ./gradlew kotlinUpgradeYarnLock :tools:check-publication:build -Pcheck_publication


================================================
FILE: .gitignore
================================================
*.iml
.gradle
local.properties
.idea
/build
.DS_Store
.kotlin


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
[![Maven Central](https://img.shields.io/maven-central/v/com.arkivanov.essenty/lifecycle?color=blue)](https://search.maven.org/search?q=g:com.arkivanov.essenty)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![Twitter URL](https://img.shields.io/badge/Twitter-@arkann1985-blue.svg?style=social&logo=twitter)](https://twitter.com/arkann1985)

# Essenty

The most essential libraries for Kotlin Multiplatform development.

Supported targets:

- `android`
- `jvm`
- `js`
- `wasmJs`
- `ios`
- `watchos`
- `tvos`
- `macos`
- `linuxX64`

## Lifecycle

When writing Kotlin Multiplatform (common) code we often need to handle lifecycle events of a screen. For example, to stop background operations when the screen is destroyed, or to reload some data when the screen is activated. Essenty provides the `Lifecycle` API to help with lifecycle handling in the common code. It is very similar to [Android Activity lifecycle](https://developer.android.com/guide/components/activities/activity-lifecycle).

### Setup

Groovy:
```groovy
// Add the dependency, typically under the commonMain source set
implementation "com.arkivanov.essenty:lifecycle:<essenty_version>"
```

Kotlin:
```kotlin
// Add the dependency, typically under the commonMain source set
implementation("com.arkivanov.essenty:lifecycle:<essenty_version>")
```

### Lifecycle state transitions

<img src="docs/media/LifecycleStates.png" width="512">

### Content

The main [Lifecycle](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/Lifecycle.kt) interface provides ability to observe the lifecycle state changes. There are also handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleExt.kt) for convenience.

The [LifecycleRegistry](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistry.kt) interface extends both the `Lifecycle` and the `Lifecycle.Callbacks` at the same time. It can be used to manually control the lifecycle, for example in tests. You can also find some useful [extension functions](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistryExt.kt).

The [LifecycleOwner](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleOwner.kt) just holds the `Lifecyle`. It may be implemented by an arbitrary class, to provide convenient API.

#### Android extensions

From Android, the `Lifecycle` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/androidMain/kotlin/com/arkivanov/essenty/lifecycle/AndroidExt.kt).

#### iOS and tvOS extensions

There is [ApplicationLifecycle](https://github.com/arkivanov/Essenty/blob/master/lifecycle/src/itvosMain/kotlin/com/arkivanov/essenty/lifecycle/ApplicationLifecycle.kt) available for `ios` and `tvos` targets. It follows the `UIApplication` lifecycle notifications.

> ⚠️  Since this implementation subscribes to `UIApplication` global lifecycle events, the instance and all its registered callbacks (and whatever they capture) will stay in memory until the application is destroyed or until `ApplicationLifecycle#destroy` method is called. It's ok to use it in a global scope like `UIApplicationDelegate`, but it may cause memory leaks when used in a narrower scope like `UIViewController` if it gets destroyed earlier. Use the `destroy` method to destroy the lifecycle manually and prevent memory leaks.

#### Reaktive extensions

There are some useful `Lifecycle` extensions for Reaktive.

- Automatic management of `Disposable` and `DisposableScope` by `Lifecycle`, can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-reaktive/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/reaktive/DisposableWithLifecycle.kt).

#### Coroutines extensions

There are some useful `Lifecycle` extensions for Coroutines.

- Automatic management of `CoroutineScope` by `Lifecycle`, can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/CoroutineScopeWithLifecycle.kt)
- `Flow.withLifecycle(Lifecycle): Flow` - can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/FlowWithLifecycle.kt).
- `Lifecycle.repeatOnLifecycle(block)` - can be found [here](https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/RepeatOnLifecycle.kt).

### Usage example

#### Observing the Lifecyle

The lifecycle can be observed using its `subscribe`/`unsubscribe` methods:

```kotlin
import com.arkivanov.essenty.lifecycle.Lifecycle

class SomeLogic(lifecycle: Lifecycle) {
    init {
        lifecycle.subscribe(
            object : Lifecycle.Callbacks {
                override fun onCreate() {
                    // Handle lifecycle created
                }

                // onStart, onResume, onPause, onStop are also available

                override fun onDestroy() {
                    // Handle lifecycle destroyed
                }
            }
        )
    }
}
```

Or using the extension functions:

```kotlin
import com.arkivanov.essenty.lifecycle.Lifecycle
import com.arkivanov.essenty.lifecycle.doOnCreate
import com.arkivanov.essenty.lifecycle.doOnDestroy
import com.arkivanov.essenty.lifecycle.subscribe

class SomeLogic(lifecycle: Lifecycle) {
    init {
        lifecycle.subscribe(
            onCreate = { /* Handle lifecycle created */ },
            // onStart, onResume, onPause, onStop are also available
            onDestroy = { /* Handle lifecycle destroyed */ }
        )

        lifecycle.doOnCreate {
            // Handle lifecycle created
        }

        // doOnStart, doOnResume, doOnPause, doOnStop are also available

        lifecycle.doOnDestroy {
            // Handle lifecycle destroyed
        }
    }
}
```

#### Using the LifecycleRegistry manually

A default implementation of the `LifecycleRegisty` interface can be instantiated using the corresponding builder function:

```kotlin
import com.arkivanov.essenty.lifecycle.LifecycleRegistry
import com.arkivanov.essenty.lifecycle.resume
import com.arkivanov.essenty.lifecycle.destroy

val lifecycleRegistry = LifecycleRegistry()
val someLogic = SomeLogic(lifecycleRegistry)

lifecycleRegistry.resume()

// At some point later
lifecycleRegistry.destroy()
```

## StateKeeper

When writing common code targeting Android, it might be required to preserve some data over process death or Android configuration changes. For this purpose, Essenty provides the `StateKeeper` API, which is inspired by the AndroidX [SavedStateHandle](https://developer.android.com/reference/androidx/lifecycle/SavedStateHandle).

### Setup

Groovy:
```groovy
// Add the dependency, typically under the commonMain source set
implementation "com.arkivanov.essenty:state-keeper:<essenty_version>"
```

Kotlin:
```kotlin
// Add the dependency, typically under the commonMain source set
implementation("com.arkivanov.essenty:state-keeper:<essenty_version>")
```

### Content

The main [StateKeeper](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeper.kt) interface provides ability to register/unregister state suppliers, and also to consume any previously saved state. You can also find some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt). You can also find some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt).

The [StateKeeperDispatcher](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperDispatcher.kt) interface extends `StateKeeper` and  allows state saving, by calling all registered state providers.

The [StateKeeperOwner](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperOwner.kt) interface is just a holder of `StateKeeper`. It may be implemented by an arbitrary class, to provide convenient API.

#### Android extensions

From Android side, `StateKeeper` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/AndroidExt.kt).

There are also some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/BundleExt.kt) for serializing/deserializing `KSerializable` objects to/from [Bundle](https://developer.android.com/reference/android/os/Bundle):

- `fun <T : Any> Bundle.putSerializable(key: String?, value: T?, strategy: SerializationStrategy<T>)`
- `fun <T : Any> Bundle.getSerializable(key: String?, strategy: DeserializationStrategy<T>): T?`
- `fun Bundle.putSerializableContainer(key: String?, value: SerializableContainer?)`
- `fun Bundle.getSerializableContainer(key: String?): SerializableContainer?`

Similar extensions are also available for [PersistableBundle](https://developer.android.com/reference/android/os/PersistableBundle).

### Usage example

#### Using StateKeeper

> ⚠️  Make sure you [setup](https://github.com/Kotlin/kotlinx.serialization#setup) `kotlinx-serialization` properly. 

```kotlin
import com.arkivanov.essenty.statekeeper.StateKeeper
import kotlinx.serialization.Serializable

class SomeLogic(stateKeeper: StateKeeper) {
    // Use the saved State if any, otherwise create a new State
    private var state: State = stateKeeper.consume(key = "SAVED_STATE", strategy = State.serializer()) ?: State()

    init {
        // Register the State supplier
        stateKeeper.register(key = "SAVED_STATE", strategy = State.serializer()) { state }
    }

    @Serializable
    private class State(
        val someValue: Int = 0
    )
}
```

#### Saveable properties (experimental since version `2.2.0-alpha01`)

```kotlin
import com.arkivanov.essenty.statekeeper.StateKeeper
import com.arkivanov.essenty.statekeeper.saveable
import kotlinx.serialization.Serializable

class SomeLogic(stateKeeper: StateKeeper) {
    private var state: State by stateKeeper.saveable(serializer = State.serializer(), init = ::State)

    @Serializable
    private class State(val someValue: Int = 0)
}
```

#### Saveable state holders (experimental since version `2.2.0-alpha01`)

```kotlin
import com.arkivanov.essenty.statekeeper.StateKeeper
import com.arkivanov.essenty.statekeeper.saveable
import kotlinx.serialization.Serializable

class SomeLogic(stateKeeper: StateKeeper) {
    private val viewModel by stateKeeper.saveable(serializer = State.serializer(), state = ViewModel::state) { savedState ->
        ViewModel(state = savedState ?: State())
    }

    private class ViewModel(var state: State)

    @Serializable
    private class State(val someValue: Int = 0)
}
```

##### Polymorphic serialization (experimental)

Sometimes it might be necessary to serialize an interface or an abstract class that you don't own but have implemented. For this purpose Essenty provides `polymorphicSerializer` function that can be used to create custom polymorphic serializers for unowned base types.

For example a third-party library may have the following interface.

```kotlin
interface Filter {
    // Omitted code
}
```

Then we can have multiple implementations of `Filter`.

```kotlin
@Serializable
class TextFilter(val text: String) : Filter { /* Omitted code */ }

@Serializable
class RatingFilter(val stars: Int) : Filter { /* Omitted code */ }
```

Now we can create a polymorphic serializer for `Filter` as follows. It can be used to save and restore `Filter` directly via StateKeeper, or to have `Filter` as part of another `Serializable` class.

```kotlin
import com.arkivanov.essenty.statekeeper.polymorphicSerializer
import com.slack.circuit.runtime.screen.Screen
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic

object FilterSerializer : KSerializer<Filter> by polymorphicSerializer(
    SerializersModule {
        polymorphic(Filter::class) {
            subclass(TextFilter::class, TextFilter.serializer())
            subclass(RatingFilter::class, RatingFilter.serializer())
        }
    }
)
```

#### Using the StateKeeperDispatcher manually

On Android, the `StateKeeper` obtained via one of the extensions described above automatically saves and restores the state. On other platforms (if needed) the state can be saved and restored manually. A default implementation of `StateKeeperDisptacher` interface can be instantiated using the corresponding builder function. The state can be encoded as a JSON string and saved using the corresponding platform-specific API.

```kotlin
import com.arkivanov.essenty.statekeeper.SerializableContainer
import com.arkivanov.essenty.statekeeper.StateKeeper
import com.arkivanov.essenty.statekeeper.StateKeeperDispatcher

val stateKeeperDispatcher = StateKeeperDispatcher(/*Previously saved state, or null*/)
val someLogic = SomeLogic(stateKeeperDispatcher)

// At some point later when it's time to save the state
val savedState: SerializableContainer = stateKeeperDispatcher.save()

// The returned SerializableContainer can now be saved using the corresponding platform-specific API
```

## InstanceKeeper

When writing common code targetting Android, it might be required to retain objects over Android configuration changes. This use case is covered by the `InstanceKeeper` API, which is similar to the AndroidX [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel).

### Setup

Groovy:
```groovy
// Add the dependency, typically under the commonMain source set
implementation "com.arkivanov.essenty:instance-keeper:<essenty_version>"
```

Kotlin:
```kotlin
// Add the dependency, typically under the commonMain source set
implementation("com.arkivanov.essenty:instance-keeper:<essenty_version>")
```

### Content

The main [InstanceKeeper](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeper.kt) interface is responsible for storing object instances, represented by the [InstanceKeeper.Instance] interface. Instances of the `InstanceKeeper.Instance` interface survive Android Configuration changes, the `InstanceKeeper.Instance.onDestroy()` method is called when `InstanceKeeper` goes out of scope (e.g. the screen is finished). You can also find some handy [extension functions](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperExt.kt).

The [InstanceKeeperDispatcher](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher.kt) interface extends `InstanceKeeper` and adds ability to destroy all registered instances.

The [InstanceKeeperOwner](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner.kt) interface is just a holder of `InstanceKeeper`. It may be implemented by an arbitrary class, to provide convenient API.

#### Android extensions

From Android side, `InstanceKeeper` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/instance-keeper/src/androidMain/kotlin/com/arkivanov/essenty/instancekeeper/AndroidExt.kt).

### Usage example

#### Using the InstanceKeeper

```kotlin
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate

class SomeLogic(instanceKeeper: InstanceKeeper) {
    // Get the existing instance or create a new one
    private val viewModel = instanceKeeper.getOrCreate { ViewModel() }
}

/*
 * Survives Android configuration changes.
 * ⚠️ Pay attention to not leak any dependencies.
 */
class ViewModel : InstanceKeeper.Instance {
    override fun onDestroy() {
        // Called when the screen is finished
    }
}
```

##### Alternative way (experimental since version 2.2.0-alpha01, stable since 2.2.0)

```kotlin
class SomeLogic(instanceKeeperOwner: InstanceKeeperOwner) : InstanceKeeperOwner by instanceKeeperOwner {
    // Get the existing instance or create a new one
    private val viewModel = retainedInstance { ViewModel() }
}
```

#### Using the InstanceKeeperDispatcher manually

A default implementation of the `InstanceKeeperDispatcher` interface can be instantiated using the corresponding builder function:

```kotlin
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.InstanceKeeperDispatcher

// Create a new instance of InstanceKeeperDispatcher, or reuse an existing one
val instanceKeeperDispatcher = InstanceKeeperDispatcher()
val someLogic = SomeLogic(instanceKeeperDispatcher)

// At some point later
instanceKeeperDispatcher.destroy()
```

## BackHandler

The `BackHandler` API provides ability to handle back button clicks (e.g. the Android device's back button), in common code. This API is similar to AndroidX [OnBackPressedDispatcher](https://developer.android.com/reference/androidx/activity/OnBackPressedDispatcher).

### Setup

Groovy:
```groovy
// Add the dependency, typically under the commonMain source set
implementation "com.arkivanov.essenty:back-handler:<essenty_version>"
```

Kotlin:
```kotlin
// Add the dependency, typically under the commonMain source set
implementation("com.arkivanov.essenty:back-handler:<essenty_version>")
```

### Content

The [BackHandler](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackHandler.kt) interface provides ability to register and unregister back button callbacks. When the device's back button is pressed, all registered callbacks are called in reverse order, the first enabled callback is called and the iteration finishes.

> Starting from `v1.2.x`, when the device's back button is pressed, all registered callbacks are sorted in ascending order first by priority and then by index, the last enabled callback is called.

[BackCallback](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackCallback.kt) allows handling back events, including predictive back gestures.

The [BackDispatcher](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackDispatcher.kt) interface extends `BackHandler` and is responsible for triggering the registered callbacks. The `BackDispatcher.back()` method triggers all registered callbacks in reverse order, and returns `true` if an enabled callback was called, and `false` if no enabled callback was found.

#### Android extensions

From Android side, `BackHandler` can be obtained by using special functions, can be found [here](https://github.com/arkivanov/Essenty/blob/master/back-handler/src/androidMain/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandler.kt).

### Predictive Back Gesture

Both `BackHandler` and `BackDispatcher` bring the new [Android Predictive Back Gesture](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture) to Kotlin Multiplatform. 

#### Predictive Back Gesture on Android

On Android, the predictive back gesture only works starting with Android T. On Android T, it works only between Activities, if enabled in the system settings. Starting with Android U, the predictive back gesture also works between application's screens inside an Activity. In the latter case, back gesture events can be handled using `BackCallback`.

#### Predictive Back Gesture on other platforms

On all other platforms, predictive back gestures can be dispatched manually via `BackDispatcher`. This can be done e.g. by adding an overlay on top of the UI and handling touch events manually.

### Usage example

#### Using the BackHandler

```kotlin
import com.arkivanov.essenty.backhandler.BackHandler

class SomeLogic(backHandler: BackHandler) {
    private val callback = BackCallback {
        // Called when the back button is pressed
    }

    init {
        backHandler.register(callback)

        // Disable the callback when needed
        callback.isEnabled = false
    }
}
```

#### Using the BackDispatcher manually

A default implementation of the `BackDispatcher` interface can be instantiated using the corresponding builder function:

```kotlin
import com.arkivanov.essenty.backhandler.BackDispatcher

val backDispatcher = BackDispatcher()
val someLogic = SomeLogic(backDispatcher)

if (!backDispatcher.back()) {
    // The back pressed event was not handled
}
```

## Author

Twitter: [@arkann1985](https://twitter.com/arkann1985)

If you like this project you can always <a href="https://www.buymeacoffee.com/arkivanov" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" height=32></a> ;-)


================================================
FILE: back-handler/.gitignore
================================================
/build


================================================
FILE: back-handler/api/android/back-handler.api
================================================
public final class com/arkivanov/essenty/backhandler/AndroidBackHandlerKt {
	public static final fun BackHandler (Landroidx/activity/OnBackPressedDispatcher;)Lcom/arkivanov/essenty/backhandler/BackHandler;
	public static final fun BackHandler (Landroidx/activity/OnBackPressedDispatcher;Landroidx/lifecycle/LifecycleOwner;)Lcom/arkivanov/essenty/backhandler/BackHandler;
	public static final fun backHandler (Landroidx/activity/OnBackPressedDispatcherOwner;)Lcom/arkivanov/essenty/backhandler/BackHandler;
	public static final fun connectOnBackPressedCallback (Lcom/arkivanov/essenty/backhandler/BackDispatcher;)Landroidx/activity/OnBackPressedCallback;
}

public abstract class com/arkivanov/essenty/backhandler/BackCallback {
	public static final field Companion Lcom/arkivanov/essenty/backhandler/BackCallback$Companion;
	public static final field PRIORITY_DEFAULT I
	public static final field PRIORITY_MAX I
	public static final field PRIORITY_MIN I
	public fun <init> ()V
	public fun <init> (ZI)V
	public synthetic fun <init> (ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V
	public final fun addEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public final fun getPriority ()I
	public final fun isEnabled ()Z
	public abstract fun onBack ()V
	public fun onBackCancelled ()V
	public fun onBackProgressed (Lcom/arkivanov/essenty/backhandler/BackEvent;)V
	public fun onBackStarted (Lcom/arkivanov/essenty/backhandler/BackEvent;)V
	public final fun removeEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public final fun setEnabled (Z)V
	public final fun setPriority (I)V
}

public final class com/arkivanov/essenty/backhandler/BackCallback$Companion {
}

public final class com/arkivanov/essenty/backhandler/BackCallbackKt {
	public static final fun BackCallback (ZILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/essenty/backhandler/BackCallback;
	public static synthetic fun BackCallback$default (ZILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/essenty/backhandler/BackCallback;
}

public abstract interface class com/arkivanov/essenty/backhandler/BackDispatcher : com/arkivanov/essenty/backhandler/BackHandler {
	public abstract fun addEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public abstract fun back ()Z
	public abstract fun cancelPredictiveBack ()V
	public abstract fun isEnabled ()Z
	public abstract fun progressPredictiveBack (Lcom/arkivanov/essenty/backhandler/BackEvent;)V
	public abstract fun removeEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public abstract fun startPredictiveBack (Lcom/arkivanov/essenty/backhandler/BackEvent;)Z
}

public final class com/arkivanov/essenty/backhandler/BackDispatcherKt {
	public static final fun BackDispatcher ()Lcom/arkivanov/essenty/backhandler/BackDispatcher;
}

public final class com/arkivanov/essenty/backhandler/BackEvent {
	public fun <init> ()V
	public fun <init> (FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FF)V
	public synthetic fun <init> (FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FFILkotlin/jvm/internal/DefaultConstructorMarker;)V
	public final fun component1 ()F
	public final fun component2 ()Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public final fun component3 ()F
	public final fun component4 ()F
	public final fun copy (FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FF)Lcom/arkivanov/essenty/backhandler/BackEvent;
	public static synthetic fun copy$default (Lcom/arkivanov/essenty/backhandler/BackEvent;FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FFILjava/lang/Object;)Lcom/arkivanov/essenty/backhandler/BackEvent;
	public fun equals (Ljava/lang/Object;)Z
	public final fun getProgress ()F
	public final fun getSwipeEdge ()Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public final fun getTouchX ()F
	public final fun getTouchY ()F
	public fun hashCode ()I
	public fun toString ()Ljava/lang/String;
}

public final class com/arkivanov/essenty/backhandler/BackEvent$SwipeEdge : java/lang/Enum {
	public static final field LEFT Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static final field RIGHT Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static final field UNKNOWN Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static fun getEntries ()Lkotlin/enums/EnumEntries;
	public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static fun values ()[Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
}

public abstract interface class com/arkivanov/essenty/backhandler/BackHandler {
	public abstract fun isRegistered (Lcom/arkivanov/essenty/backhandler/BackCallback;)Z
	public abstract fun register (Lcom/arkivanov/essenty/backhandler/BackCallback;)V
	public abstract fun unregister (Lcom/arkivanov/essenty/backhandler/BackCallback;)V
}

public abstract interface class com/arkivanov/essenty/backhandler/BackHandlerOwner {
	public abstract fun getBackHandler ()Lcom/arkivanov/essenty/backhandler/BackHandler;
}



================================================
FILE: back-handler/api/back-handler.klib.api
================================================
// Klib ABI Dump
// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true

// Library unique name: <com.arkivanov.essenty:back-handler>
abstract interface com.arkivanov.essenty.backhandler/BackDispatcher : com.arkivanov.essenty.backhandler/BackHandler { // com.arkivanov.essenty.backhandler/BackDispatcher|null[0]
    abstract val isEnabled // com.arkivanov.essenty.backhandler/BackDispatcher.isEnabled|{}isEnabled[0]
        abstract fun <get-isEnabled>(): kotlin/Boolean // com.arkivanov.essenty.backhandler/BackDispatcher.isEnabled.<get-isEnabled>|<get-isEnabled>(){}[0]

    abstract fun addEnabledChangedListener(kotlin/Function1<kotlin/Boolean, kotlin/Unit>) // com.arkivanov.essenty.backhandler/BackDispatcher.addEnabledChangedListener|addEnabledChangedListener(kotlin.Function1<kotlin.Boolean,kotlin.Unit>){}[0]
    abstract fun back(): kotlin/Boolean // com.arkivanov.essenty.backhandler/BackDispatcher.back|back(){}[0]
    abstract fun cancelPredictiveBack() // com.arkivanov.essenty.backhandler/BackDispatcher.cancelPredictiveBack|cancelPredictiveBack(){}[0]
    abstract fun progressPredictiveBack(com.arkivanov.essenty.backhandler/BackEvent) // com.arkivanov.essenty.backhandler/BackDispatcher.progressPredictiveBack|progressPredictiveBack(com.arkivanov.essenty.backhandler.BackEvent){}[0]
    abstract fun removeEnabledChangedListener(kotlin/Function1<kotlin/Boolean, kotlin/Unit>) // com.arkivanov.essenty.backhandler/BackDispatcher.removeEnabledChangedListener|removeEnabledChangedListener(kotlin.Function1<kotlin.Boolean,kotlin.Unit>){}[0]
    abstract fun startPredictiveBack(com.arkivanov.essenty.backhandler/BackEvent): kotlin/Boolean // com.arkivanov.essenty.backhandler/BackDispatcher.startPredictiveBack|startPredictiveBack(com.arkivanov.essenty.backhandler.BackEvent){}[0]
}

abstract interface com.arkivanov.essenty.backhandler/BackHandler { // com.arkivanov.essenty.backhandler/BackHandler|null[0]
    abstract fun isRegistered(com.arkivanov.essenty.backhandler/BackCallback): kotlin/Boolean // com.arkivanov.essenty.backhandler/BackHandler.isRegistered|isRegistered(com.arkivanov.essenty.backhandler.BackCallback){}[0]
    abstract fun register(com.arkivanov.essenty.backhandler/BackCallback) // com.arkivanov.essenty.backhandler/BackHandler.register|register(com.arkivanov.essenty.backhandler.BackCallback){}[0]
    abstract fun unregister(com.arkivanov.essenty.backhandler/BackCallback) // com.arkivanov.essenty.backhandler/BackHandler.unregister|unregister(com.arkivanov.essenty.backhandler.BackCallback){}[0]
}

abstract interface com.arkivanov.essenty.backhandler/BackHandlerOwner { // com.arkivanov.essenty.backhandler/BackHandlerOwner|null[0]
    abstract val backHandler // com.arkivanov.essenty.backhandler/BackHandlerOwner.backHandler|{}backHandler[0]
        abstract fun <get-backHandler>(): com.arkivanov.essenty.backhandler/BackHandler // com.arkivanov.essenty.backhandler/BackHandlerOwner.backHandler.<get-backHandler>|<get-backHandler>(){}[0]
}

abstract class com.arkivanov.essenty.backhandler/BackCallback { // com.arkivanov.essenty.backhandler/BackCallback|null[0]
    constructor <init>(kotlin/Boolean = ..., kotlin/Int = ...) // com.arkivanov.essenty.backhandler/BackCallback.<init>|<init>(kotlin.Boolean;kotlin.Int){}[0]

    final var isEnabled // com.arkivanov.essenty.backhandler/BackCallback.isEnabled|{}isEnabled[0]
        final fun <get-isEnabled>(): kotlin/Boolean // com.arkivanov.essenty.backhandler/BackCallback.isEnabled.<get-isEnabled>|<get-isEnabled>(){}[0]
        final fun <set-isEnabled>(kotlin/Boolean) // com.arkivanov.essenty.backhandler/BackCallback.isEnabled.<set-isEnabled>|<set-isEnabled>(kotlin.Boolean){}[0]
    final var priority // com.arkivanov.essenty.backhandler/BackCallback.priority|{}priority[0]
        final fun <get-priority>(): kotlin/Int // com.arkivanov.essenty.backhandler/BackCallback.priority.<get-priority>|<get-priority>(){}[0]
        final fun <set-priority>(kotlin/Int) // com.arkivanov.essenty.backhandler/BackCallback.priority.<set-priority>|<set-priority>(kotlin.Int){}[0]

    abstract fun onBack() // com.arkivanov.essenty.backhandler/BackCallback.onBack|onBack(){}[0]
    final fun addEnabledChangedListener(kotlin/Function1<kotlin/Boolean, kotlin/Unit>) // com.arkivanov.essenty.backhandler/BackCallback.addEnabledChangedListener|addEnabledChangedListener(kotlin.Function1<kotlin.Boolean,kotlin.Unit>){}[0]
    final fun removeEnabledChangedListener(kotlin/Function1<kotlin/Boolean, kotlin/Unit>) // com.arkivanov.essenty.backhandler/BackCallback.removeEnabledChangedListener|removeEnabledChangedListener(kotlin.Function1<kotlin.Boolean,kotlin.Unit>){}[0]
    open fun onBackCancelled() // com.arkivanov.essenty.backhandler/BackCallback.onBackCancelled|onBackCancelled(){}[0]
    open fun onBackProgressed(com.arkivanov.essenty.backhandler/BackEvent) // com.arkivanov.essenty.backhandler/BackCallback.onBackProgressed|onBackProgressed(com.arkivanov.essenty.backhandler.BackEvent){}[0]
    open fun onBackStarted(com.arkivanov.essenty.backhandler/BackEvent) // com.arkivanov.essenty.backhandler/BackCallback.onBackStarted|onBackStarted(com.arkivanov.essenty.backhandler.BackEvent){}[0]

    final object Companion { // com.arkivanov.essenty.backhandler/BackCallback.Companion|null[0]
        final const val PRIORITY_DEFAULT // com.arkivanov.essenty.backhandler/BackCallback.Companion.PRIORITY_DEFAULT|{}PRIORITY_DEFAULT[0]
            final fun <get-PRIORITY_DEFAULT>(): kotlin/Int // com.arkivanov.essenty.backhandler/BackCallback.Companion.PRIORITY_DEFAULT.<get-PRIORITY_DEFAULT>|<get-PRIORITY_DEFAULT>(){}[0]
        final const val PRIORITY_MAX // com.arkivanov.essenty.backhandler/BackCallback.Companion.PRIORITY_MAX|{}PRIORITY_MAX[0]
            final fun <get-PRIORITY_MAX>(): kotlin/Int // com.arkivanov.essenty.backhandler/BackCallback.Companion.PRIORITY_MAX.<get-PRIORITY_MAX>|<get-PRIORITY_MAX>(){}[0]
        final const val PRIORITY_MIN // com.arkivanov.essenty.backhandler/BackCallback.Companion.PRIORITY_MIN|{}PRIORITY_MIN[0]
            final fun <get-PRIORITY_MIN>(): kotlin/Int // com.arkivanov.essenty.backhandler/BackCallback.Companion.PRIORITY_MIN.<get-PRIORITY_MIN>|<get-PRIORITY_MIN>(){}[0]
    }
}

final class com.arkivanov.essenty.backhandler/BackEvent { // com.arkivanov.essenty.backhandler/BackEvent|null[0]
    constructor <init>(kotlin/Float = ..., com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge = ..., kotlin/Float = ..., kotlin/Float = ...) // com.arkivanov.essenty.backhandler/BackEvent.<init>|<init>(kotlin.Float;com.arkivanov.essenty.backhandler.BackEvent.SwipeEdge;kotlin.Float;kotlin.Float){}[0]

    final val progress // com.arkivanov.essenty.backhandler/BackEvent.progress|{}progress[0]
        final fun <get-progress>(): kotlin/Float // com.arkivanov.essenty.backhandler/BackEvent.progress.<get-progress>|<get-progress>(){}[0]
    final val swipeEdge // com.arkivanov.essenty.backhandler/BackEvent.swipeEdge|{}swipeEdge[0]
        final fun <get-swipeEdge>(): com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge // com.arkivanov.essenty.backhandler/BackEvent.swipeEdge.<get-swipeEdge>|<get-swipeEdge>(){}[0]
    final val touchX // com.arkivanov.essenty.backhandler/BackEvent.touchX|{}touchX[0]
        final fun <get-touchX>(): kotlin/Float // com.arkivanov.essenty.backhandler/BackEvent.touchX.<get-touchX>|<get-touchX>(){}[0]
    final val touchY // com.arkivanov.essenty.backhandler/BackEvent.touchY|{}touchY[0]
        final fun <get-touchY>(): kotlin/Float // com.arkivanov.essenty.backhandler/BackEvent.touchY.<get-touchY>|<get-touchY>(){}[0]

    final fun component1(): kotlin/Float // com.arkivanov.essenty.backhandler/BackEvent.component1|component1(){}[0]
    final fun component2(): com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge // com.arkivanov.essenty.backhandler/BackEvent.component2|component2(){}[0]
    final fun component3(): kotlin/Float // com.arkivanov.essenty.backhandler/BackEvent.component3|component3(){}[0]
    final fun component4(): kotlin/Float // com.arkivanov.essenty.backhandler/BackEvent.component4|component4(){}[0]
    final fun copy(kotlin/Float = ..., com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge = ..., kotlin/Float = ..., kotlin/Float = ...): com.arkivanov.essenty.backhandler/BackEvent // com.arkivanov.essenty.backhandler/BackEvent.copy|copy(kotlin.Float;com.arkivanov.essenty.backhandler.BackEvent.SwipeEdge;kotlin.Float;kotlin.Float){}[0]
    final fun equals(kotlin/Any?): kotlin/Boolean // com.arkivanov.essenty.backhandler/BackEvent.equals|equals(kotlin.Any?){}[0]
    final fun hashCode(): kotlin/Int // com.arkivanov.essenty.backhandler/BackEvent.hashCode|hashCode(){}[0]
    final fun toString(): kotlin/String // com.arkivanov.essenty.backhandler/BackEvent.toString|toString(){}[0]

    final enum class SwipeEdge : kotlin/Enum<com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge> { // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge|null[0]
        enum entry LEFT // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.LEFT|null[0]
        enum entry RIGHT // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.RIGHT|null[0]
        enum entry UNKNOWN // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.UNKNOWN|null[0]

        final val entries // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.entries|#static{}entries[0]
            final fun <get-entries>(): kotlin.enums/EnumEntries<com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge> // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.entries.<get-entries>|<get-entries>#static(){}[0]

        final fun valueOf(kotlin/String): com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.valueOf|valueOf#static(kotlin.String){}[0]
        final fun values(): kotlin/Array<com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge> // com.arkivanov.essenty.backhandler/BackEvent.SwipeEdge.values|values#static(){}[0]
    }
}

final fun com.arkivanov.essenty.backhandler/BackCallback(kotlin/Boolean = ..., kotlin/Int = ..., kotlin/Function1<com.arkivanov.essenty.backhandler/BackEvent, kotlin/Unit>? = ..., kotlin/Function1<com.arkivanov.essenty.backhandler/BackEvent, kotlin/Unit>? = ..., kotlin/Function0<kotlin/Unit>? = ..., kotlin/Function0<kotlin/Unit>): com.arkivanov.essenty.backhandler/BackCallback // com.arkivanov.essenty.backhandler/BackCallback|BackCallback(kotlin.Boolean;kotlin.Int;kotlin.Function1<com.arkivanov.essenty.backhandler.BackEvent,kotlin.Unit>?;kotlin.Function1<com.arkivanov.essenty.backhandler.BackEvent,kotlin.Unit>?;kotlin.Function0<kotlin.Unit>?;kotlin.Function0<kotlin.Unit>){}[0]
final fun com.arkivanov.essenty.backhandler/BackDispatcher(): com.arkivanov.essenty.backhandler/BackDispatcher // com.arkivanov.essenty.backhandler/BackDispatcher|BackDispatcher(){}[0]


================================================
FILE: back-handler/api/jvm/back-handler.api
================================================
public abstract class com/arkivanov/essenty/backhandler/BackCallback {
	public static final field Companion Lcom/arkivanov/essenty/backhandler/BackCallback$Companion;
	public static final field PRIORITY_DEFAULT I
	public static final field PRIORITY_MAX I
	public static final field PRIORITY_MIN I
	public fun <init> ()V
	public fun <init> (ZI)V
	public synthetic fun <init> (ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V
	public final fun addEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public final fun getPriority ()I
	public final fun isEnabled ()Z
	public abstract fun onBack ()V
	public fun onBackCancelled ()V
	public fun onBackProgressed (Lcom/arkivanov/essenty/backhandler/BackEvent;)V
	public fun onBackStarted (Lcom/arkivanov/essenty/backhandler/BackEvent;)V
	public final fun removeEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public final fun setEnabled (Z)V
	public final fun setPriority (I)V
}

public final class com/arkivanov/essenty/backhandler/BackCallback$Companion {
}

public final class com/arkivanov/essenty/backhandler/BackCallbackKt {
	public static final fun BackCallback (ZILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/essenty/backhandler/BackCallback;
	public static synthetic fun BackCallback$default (ZILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lcom/arkivanov/essenty/backhandler/BackCallback;
}

public abstract interface class com/arkivanov/essenty/backhandler/BackDispatcher : com/arkivanov/essenty/backhandler/BackHandler {
	public abstract fun addEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public abstract fun back ()Z
	public abstract fun cancelPredictiveBack ()V
	public abstract fun isEnabled ()Z
	public abstract fun progressPredictiveBack (Lcom/arkivanov/essenty/backhandler/BackEvent;)V
	public abstract fun removeEnabledChangedListener (Lkotlin/jvm/functions/Function1;)V
	public abstract fun startPredictiveBack (Lcom/arkivanov/essenty/backhandler/BackEvent;)Z
}

public final class com/arkivanov/essenty/backhandler/BackDispatcherKt {
	public static final fun BackDispatcher ()Lcom/arkivanov/essenty/backhandler/BackDispatcher;
}

public final class com/arkivanov/essenty/backhandler/BackEvent {
	public fun <init> ()V
	public fun <init> (FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FF)V
	public synthetic fun <init> (FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FFILkotlin/jvm/internal/DefaultConstructorMarker;)V
	public final fun component1 ()F
	public final fun component2 ()Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public final fun component3 ()F
	public final fun component4 ()F
	public final fun copy (FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FF)Lcom/arkivanov/essenty/backhandler/BackEvent;
	public static synthetic fun copy$default (Lcom/arkivanov/essenty/backhandler/BackEvent;FLcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;FFILjava/lang/Object;)Lcom/arkivanov/essenty/backhandler/BackEvent;
	public fun equals (Ljava/lang/Object;)Z
	public final fun getProgress ()F
	public final fun getSwipeEdge ()Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public final fun getTouchX ()F
	public final fun getTouchY ()F
	public fun hashCode ()I
	public fun toString ()Ljava/lang/String;
}

public final class com/arkivanov/essenty/backhandler/BackEvent$SwipeEdge : java/lang/Enum {
	public static final field LEFT Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static final field RIGHT Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static final field UNKNOWN Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static fun getEntries ()Lkotlin/enums/EnumEntries;
	public static fun valueOf (Ljava/lang/String;)Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
	public static fun values ()[Lcom/arkivanov/essenty/backhandler/BackEvent$SwipeEdge;
}

public abstract interface class com/arkivanov/essenty/backhandler/BackHandler {
	public abstract fun isRegistered (Lcom/arkivanov/essenty/backhandler/BackCallback;)Z
	public abstract fun register (Lcom/arkivanov/essenty/backhandler/BackCallback;)V
	public abstract fun unregister (Lcom/arkivanov/essenty/backhandler/BackCallback;)V
}

public abstract interface class com/arkivanov/essenty/backhandler/BackHandlerOwner {
	public abstract fun getBackHandler ()Lcom/arkivanov/essenty/backhandler/BackHandler;
}



================================================
FILE: back-handler/build.gradle.kts
================================================
import com.arkivanov.gradle.bundle
import com.arkivanov.gradle.setupBinaryCompatibilityValidator
import com.arkivanov.gradle.setupMultiplatform
import com.arkivanov.gradle.setupPublication
import com.arkivanov.gradle.setupSourceSets

plugins {
    id("kotlin-multiplatform")
    id("com.android.library")
    id("com.arkivanov.gradle.setup")
}

setupMultiplatform()
setupPublication()
setupBinaryCompatibilityValidator()

android {
    namespace = "com.arkivanov.essenty.backhandler"
}

kotlin {
    setupSourceSets {
        val android by bundle()

        common.main.dependencies {
            implementation(project(":utils-internal"))
        }

        android.main.dependencies {
            implementation(deps.androidx.activity.activityKtx)
        }
    }
}


================================================
FILE: back-handler/src/androidMain/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest/>


================================================
FILE: back-handler/src/androidMain/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandler.kt
================================================
package com.arkivanov.essenty.backhandler

import androidx.activity.BackEventCompat
import androidx.activity.OnBackPressedCallback
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.lifecycle.LifecycleOwner

/**
 * Creates a new instance of [BackHandler] and attaches it to the provided AndroidX [OnBackPressedDispatcher].
 */
fun BackHandler(onBackPressedDispatcher: OnBackPressedDispatcher): BackHandler =
    BackDispatcher().also { dispatcher ->
        onBackPressedDispatcher.addCallback(dispatcher.connectOnBackPressedCallback())
    }

/**
 * Creates a new instance of [BackHandler] and attaches it to the provided AndroidX [OnBackPressedDispatcher]
 * only when the [LifecycleOwner]'s Lifecycle is [STARTED][androidx.lifecycle.Lifecycle.State.STARTED].
 */
fun BackHandler(
    onBackPressedDispatcher: OnBackPressedDispatcher,
    lifecycleOwner: LifecycleOwner,
): BackHandler =
    BackDispatcher().also { dispatcher ->
        onBackPressedDispatcher.addCallback(lifecycleOwner, dispatcher.connectOnBackPressedCallback())
    }

/**
 * Creates a new instance of [BackHandler] and attaches it to the AndroidX [OnBackPressedDispatcher].
 */
fun OnBackPressedDispatcherOwner.backHandler(): BackHandler =
    BackHandler(onBackPressedDispatcher = onBackPressedDispatcher)

/**
 * Creates a new instance of [OnBackPressedCallback] and connects it with this [BackDispatcher].
 * All events from the returned [OnBackPressedCallback] are forwarded to this [BackDispatcher].
 * The enabled state from this [BackDispatcher] is forwarded to the returned [OnBackPressedCallback].
 */
fun BackDispatcher.connectOnBackPressedCallback(): OnBackPressedCallback =
    OnBackPressedCallbackAdapter(dispatcher = this)

private class OnBackPressedCallbackAdapter(
    private val dispatcher: BackDispatcher,
) : OnBackPressedCallback(enabled = dispatcher.isEnabled) {

    init {
        dispatcher.addEnabledChangedListener { isEnabled = it }
    }

    override fun handleOnBackPressed() {
        dispatcher.back()
    }

    override fun handleOnBackStarted(backEvent: BackEventCompat) {
        dispatcher.startPredictiveBack(backEvent.toEssentyBackEvent())
    }

    override fun handleOnBackProgressed(backEvent: BackEventCompat) {
        dispatcher.progressPredictiveBack(backEvent.toEssentyBackEvent())
    }

    override fun handleOnBackCancelled() {
        dispatcher.cancelPredictiveBack()
    }

    private fun BackEventCompat.toEssentyBackEvent(): BackEvent =
        BackEvent(
            progress = progress,
            swipeEdge = when (swipeEdge) {
                BackEventCompat.EDGE_LEFT -> BackEvent.SwipeEdge.LEFT
                BackEventCompat.EDGE_RIGHT -> BackEvent.SwipeEdge.RIGHT
                else -> BackEvent.SwipeEdge.UNKNOWN
            },
            touchX = touchX,
            touchY = touchY,
        )
}


================================================
FILE: back-handler/src/androidUnitTest/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandlerTest.kt
================================================
package com.arkivanov.essenty.backhandler

import androidx.activity.BackEventCompat
import androidx.activity.BackEventCompat.Companion.EDGE_LEFT
import androidx.activity.BackEventCompat.Companion.EDGE_RIGHT
import androidx.activity.OnBackPressedDispatcher
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@Suppress("TestFunctionName")
class AndroidBackHandlerTest {

    private val dispatcher = OnBackPressedDispatcher()
    private val handler = BackHandler(onBackPressedDispatcher = dispatcher)

    @Test
    fun WHEN_created_THEN_hasEnabledCallbacks_false() {
        assertFalse(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun WHEN_enabled_callback_registered_THEN_hasEnabledCallbacks_true() {
        handler.register(callback(isEnabled = true))

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun WHEN_disabled_callback_registered_THEN_hasEnabledCallbacks_false() {
        handler.register(callback(isEnabled = false))

        assertFalse(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun WHEN_multiple_callbacks_registered_and_one_enabled_THEN_hasEnabledCallbacks_true() {
        handler.register(callback(isEnabled = false))
        handler.register(callback(isEnabled = true))
        handler.register(callback(isEnabled = false))

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_multiple_disabled_callbacks_WHEN_one_callback_enabled_THEN_hasEnabledCallbacks_true() {
        val callback2 = callback(isEnabled = false)
        handler.register(callback(isEnabled = false))
        handler.register(callback2)
        handler.register(callback(isEnabled = false))

        callback2.isEnabled = true

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_multiple_enabled_callbacks_WHEN_all_callbacks_disabled_except_one_THEN_hasEnabledCallbacks_true() {
        val callbacks = listOf(callback(isEnabled = true), callback(isEnabled = true), callback(isEnabled = true))
        callbacks.forEach(handler::register)

        callbacks.drop(1).forEach { it.isEnabled = false }

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_multiple_enabled_callbacks_WHEN_all_callbacks_disabled_THEN_hasEnabledCallbacks_false() {
        val callbacks = listOf(callback(isEnabled = true), callback(isEnabled = true), callback(isEnabled = true))
        callbacks.forEach(handler::register)

        callbacks.forEach { it.isEnabled = false }

        assertFalse(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_multiple_enabled_callbacks_WHEN_all_callbacks_removed_THEN_hasEnabledCallbacks_false() {
        val callbacks = listOf(callback(isEnabled = true), callback(isEnabled = true), callback(isEnabled = true))
        callbacks.forEach(handler::register)

        callbacks.forEach(handler::unregister)

        assertFalse(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_multiple_enabled_callbacks_WHEN_all_callbacks_removed_except_one_THEN_hasEnabledCallbacks_true() {
        val callbacks = listOf(callback(isEnabled = true), callback(isEnabled = true), callback(isEnabled = true))
        callbacks.forEach(handler::register)

        callbacks.drop(1).forEach(handler::unregister)

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_all_callbacks_disabled_WHEN_onBackPressed_THEN_callbacks_not_called() {
        var isCalled = false
        repeat(3) {
            handler.register(callback(isEnabled = false) { isCalled = true })
        }

        dispatcher.onBackPressed()

        assertFalse(isCalled)
    }

    @Test
    fun GIVEN_all_callbacks_enabled_WHEN_onBackPressed_THEN_only_last_callback_called() {
        val called = MutableList(3) { false }

        repeat(called.size) { index ->
            handler.register(callback(isEnabled = true) { called[index] = true })
        }

        dispatcher.onBackPressed()

        assertContentEquals(listOf(false, false, true), called)
    }

    @Test
    fun GIVEN_only_one_callback_enabled_WHEN_onBackPressed_THEN_only_enabled_callback_called() {
        val called = MutableList(3) { false }

        repeat(called.size) { index ->
            handler.register(callback(isEnabled = index == 0) { called[index] = true })
        }

        dispatcher.onBackPressed()

        assertContentEquals(listOf(true, false, false), called)
    }

    @Test
    fun GIVEN_multiple_enabled_callbacks_registered_and_all_callbacks_removed_except_one_WHEN_onBackPressed_THEN_callback_called() {
        val called = MutableList(3) { false }
        val callbacks = List(called.size) { index -> callback(isEnabled = true) { called[index] = true } }
        callbacks.forEach(handler::register)
        callbacks.drop(1).forEach(handler::unregister)

        dispatcher.onBackPressed()

        assertContentEquals(listOf(true, false, false), called)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_with_priorities_WHEN_onBackPressed_THEN_last_callback_with_higher_priority_called() {
        val list = ArrayList<Int>()
        handler.register(callback(isEnabled = true, priority = 0) { list += 1 })
        handler.register(callback(isEnabled = true, priority = 1) { list += 2 })
        handler.register(callback(isEnabled = true, priority = 2) { list += 3 })
        handler.register(callback(isEnabled = true, priority = 1) { list += 4 })
        handler.register(callback(isEnabled = true, priority = 2) { list += 5 })
        handler.register(callback(isEnabled = true, priority = 0) { list += 6 })
        handler.register(callback(isEnabled = true, priority = 1) { list += 7 })
        handler.register(callback(isEnabled = true, priority = 0) { list += 8 })

        dispatcher.onBackPressed()

        assertContentEquals(listOf(5), list)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_with_priorities_WHEN_priority_changed_and_onBackPressed_THEN_last_callback_with_higher_priority_called() {
        val list = ArrayList<Int>()
        handler.register(callback(isEnabled = true, priority = 0) { list += 1 })
        val callback = callback(isEnabled = true, priority = 1) { list += 2 }
        handler.register(callback)
        handler.register(callback(isEnabled = true, priority = 2) { list += 3 })

        callback.priority = 3
        dispatcher.onBackPressed()

        assertContentEquals(listOf(2), list)
    }

    @Test
    fun WHEN_progress_with_back_THEN_callbacks_called() {
        val receivedEvents = ArrayList<Any?>()

        handler.register(
            BackCallback(
                onBackStarted = { receivedEvents += it },
                onBackProgressed = { receivedEvents += it },
                onBackCancelled = { receivedEvents += "Cancel" },
                onBack = { receivedEvents += "Back" },
            )
        )

        dispatcher.dispatchOnBackStarted(BackEventCompat(progress = 0.1F, swipeEdge = EDGE_LEFT, touchX = 1F, touchY = 2F))
        dispatcher.dispatchOnBackProgressed(BackEventCompat(progress = 0.2F, swipeEdge = EDGE_RIGHT, touchX = 2F, touchY = 3F))
        dispatcher.dispatchOnBackProgressed(BackEventCompat(progress = 0.3F, swipeEdge = EDGE_LEFT, touchX = 3F, touchY = 4F))
        dispatcher.onBackPressed()

        assertContentEquals(
            listOf(
                BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F),
                BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F),
                BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F),
                "Back",
            ),
            receivedEvents,
        )
    }

    @Test
    fun WHEN_progress_with_cancel_THEN_callbacks_called() {
        val receivedEvents = ArrayList<Any?>()

        handler.register(
            BackCallback(
                onBackStarted = { receivedEvents += it },
                onBackProgressed = { receivedEvents += it },
                onBackCancelled = { receivedEvents += "Cancel" },
                onBack = { receivedEvents += "Back" },
            )
        )

        dispatcher.dispatchOnBackStarted(BackEventCompat(progress = 0.1F, swipeEdge = EDGE_LEFT, touchX = 1F, touchY = 2F))
        dispatcher.dispatchOnBackProgressed(BackEventCompat(progress = 0.2F, swipeEdge = EDGE_RIGHT, touchX = 2F, touchY = 3F))
        dispatcher.dispatchOnBackProgressed(BackEventCompat(progress = 0.3F, swipeEdge = EDGE_LEFT, touchX = 3F, touchY = 4F))
        dispatcher.dispatchOnBackCancelled()

        assertContentEquals(
            listOf(
                BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F),
                BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F),
                BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F),
                "Cancel",
            ),
            receivedEvents,
        )
    }

    private fun callback(
        isEnabled: Boolean = true,
        priority: Int = BackCallback.PRIORITY_DEFAULT,
        onBack: () -> Unit = {},
    ): BackCallback =
        BackCallback(
            isEnabled = isEnabled,
            priority = priority,
            onBack = onBack,
        )
}


================================================
FILE: back-handler/src/androidUnitTest/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandlerWithLifecycleTest.kt
================================================
package com.arkivanov.essenty.backhandler

import androidx.activity.OnBackPressedDispatcher
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@Suppress("TestFunctionName")
class AndroidBackHandlerWithLifecycleTest {

    private val dispatcher = OnBackPressedDispatcher()
    private val lifecycleOwner = LifecycleOwnerImpl()

    @Test
    fun GIVEN_lifecycle_created_WHEN_handler_created_THEN_hasEnabledCallbacks_returns_false() {
        lifecycleOwner.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)

        val handler = handler()
        handler.register(callback())

        assertFalse(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_lifecycle_started_WHEN_handler_created_THEN_hasEnabledCallbacks_returns_true() {
        lifecycleOwner.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)

        val handler = handler()
        handler.register(callback())

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_handler_created_WHEN_lifecycle_started_THEN_hasEnabledCallbacks_returns_true() {
        val handler = handler()
        handler.register(callback())

        lifecycleOwner.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)

        assertTrue(dispatcher.hasEnabledCallbacks())
    }

    @Test
    fun GIVEN_lifecycle_started_WHEN_lifecycle_stopped_THEN_hasEnabledCallbacks_returns_false() {
        lifecycleOwner.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
        val handler = handler()
        handler.register(callback())

        lifecycleOwner.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_STOP)

        assertFalse(dispatcher.hasEnabledCallbacks())
    }

    private fun handler(): BackHandler =
        BackHandler(
            onBackPressedDispatcher = dispatcher,
            lifecycleOwner = lifecycleOwner,
        )

    private fun callback(): BackCallback =
        BackCallback(isEnabled = true, onBack = {})

    private class LifecycleOwnerImpl : LifecycleOwner {
        override val lifecycle: LifecycleRegistry = LifecycleRegistry.createUnsafe(this)
    }
}


================================================
FILE: back-handler/src/androidUnitTest/kotlin/com/arkivanov/essenty/backhandler/OnBackPressedCallbackAdapterTest.kt
================================================
package com.arkivanov.essenty.backhandler

import androidx.activity.BackEventCompat
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@Suppress("TestFunctionName")
class OnBackPressedCallbackAdapterTest {

    private val dispatcher = BackDispatcher()

    @Test
    fun WHEN_connected_with_empty_dispatcher_THEN_disabled() {
        val callback = dispatcher.connectOnBackPressedCallback()

        assertFalse(callback.isEnabled)
    }

    @Test
    fun WHEN_connected_with_not_empty_disabled_dispatcher_THEN_disabled() {
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))

        val callback = dispatcher.connectOnBackPressedCallback()

        assertFalse(callback.isEnabled)
    }

    @Test
    fun WHEN_connected_with_not_empty_enabled_dispatcher_THEN_enabled() {
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))

        val callback = dispatcher.connectOnBackPressedCallback()

        assertTrue(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_empty_dispatcher_WHEN_disabled_callback_registered_in_dispatcher_THEN_disabled() {
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))

        assertFalse(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_empty_dispatcher_WHEN_enabled_callback_registered_in_dispatcher_THEN_enabled() {
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))

        assertTrue(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_disabled_dispatcher_WHEN_disabled_callback_registered_in_dispatcher_THEN_disabled() {
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))

        assertFalse(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_disabled_dispatcher_WHEN_enabled_callback_registered_in_dispatcher_THEN_enabled() {
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))

        assertTrue(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_enabled_dispatcher_WHEN_disabled_callback_registered_in_dispatcher_THEN_enabled() {
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))

        assertTrue(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_enabled_dispatcher_WHEN_enabled_callback_registered_in_dispatcher_THEN_enabled() {
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))

        assertTrue(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_disabled_dispatcher_WHEN_all_callbacks_unregistered_from_dispatcher_THEN_disabled() {
        val essentyCallback = BackCallback(isEnabled = false, onBack = {})
        dispatcher.register(essentyCallback)
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.unregister(essentyCallback)

        assertFalse(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_enabled_dispatcher_WHEN_not_all_callbacks_unregistered_from_dispatcher_THEN_enabled() {
        val essentyCallback = BackCallback(isEnabled = true, onBack = {})
        dispatcher.register(essentyCallback)
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.unregister(essentyCallback)

        assertTrue(callback.isEnabled)
    }

    @Test
    fun GIVEN_connected_with_not_empty_enabled_dispatcher_WHEN_all_callbacks_unregistered_from_dispatcher_THEN_disabled() {
        val essentyCallback = BackCallback(isEnabled = true, onBack = {})
        dispatcher.register(essentyCallback)
        val callback = dispatcher.connectOnBackPressedCallback()

        dispatcher.unregister(essentyCallback)

        assertFalse(callback.isEnabled)
    }

    @Test
    fun WHEN_progress_with_back_THEN_forwards() {
        val callback = dispatcher.connectOnBackPressedCallback()
        val receivedEvents = ArrayList<Any?>()
        dispatcher.register(
            BackCallback(
                onBackStarted = { receivedEvents += it },
                onBackProgressed = { receivedEvents += it },
                onBackCancelled = { receivedEvents += "Cancel" },
                onBack = { receivedEvents += "Back" },
            )
        )

        callback.handleOnBackStarted(BackEventCompat(progress = 0.1F, swipeEdge = BackEventCompat.EDGE_LEFT, touchX = 1F, touchY = 2F))
        callback.handleOnBackProgressed(BackEventCompat(progress = 0.2F, swipeEdge = BackEventCompat.EDGE_RIGHT, touchX = 2F, touchY = 3F))
        callback.handleOnBackProgressed(BackEventCompat(progress = 0.3F, swipeEdge = BackEventCompat.EDGE_LEFT, touchX = 3F, touchY = 4F))
        callback.handleOnBackPressed()

        assertContentEquals(
            listOf(
                BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F),
                BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F),
                BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F),
                "Back",
            ),
            receivedEvents,
        )
    }

    @Test
    fun WHEN_progress_with_cancel_THEN_forwards() {
        val callback = dispatcher.connectOnBackPressedCallback()
        val receivedEvents = ArrayList<Any?>()
        dispatcher.register(
            BackCallback(
                onBackStarted = { receivedEvents += it },
                onBackProgressed = { receivedEvents += it },
                onBackCancelled = { receivedEvents += "Cancel" },
                onBack = { receivedEvents += "Back" },
            )
        )

        callback.handleOnBackStarted(BackEventCompat(progress = 0.1F, swipeEdge = BackEventCompat.EDGE_LEFT, touchX = 1F, touchY = 2F))
        callback.handleOnBackProgressed(BackEventCompat(progress = 0.2F, swipeEdge = BackEventCompat.EDGE_RIGHT, touchX = 2F, touchY = 3F))
        callback.handleOnBackProgressed(BackEventCompat(progress = 0.3F, swipeEdge = BackEventCompat.EDGE_LEFT, touchX = 3F, touchY = 4F))
        callback.handleOnBackCancelled()

        assertContentEquals(
            listOf(
                BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F),
                BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F),
                BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F),
                "Cancel",
            ),
            receivedEvents,
        )
    }
}


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackCallback.kt
================================================
package com.arkivanov.essenty.backhandler

import kotlin.properties.Delegates

/**
 * A callback for back button handling.
 *
 * @param isEnabled the initial enabled state of the callback.
 * @param priority determines the order of callback execution.
 * When calling, callbacks are sorted in ascending order first by priority and then by index,
 * the last enabled callback gets called.
 */
abstract class BackCallback(
    isEnabled: Boolean = true,
    var priority: Int = PRIORITY_DEFAULT,
) {
    private var enabledListeners = emptySet<(Boolean) -> Unit>()

    /**
     * Controls the enabled state of the callback.
     */
    var isEnabled: Boolean by Delegates.observable(isEnabled) { _, _, newValue ->
        enabledListeners.forEach { it(newValue) }
    }

    /**
     * Registers the specified [listener] to be called when the enabled state of the callback changes.
     */
    fun addEnabledChangedListener(listener: (isEnabled: Boolean) -> Unit) {
        this.enabledListeners += listener
    }

    /**
     * Unregisters the specified [listener].
     */
    fun removeEnabledChangedListener(listener: (isEnabled: Boolean) -> Unit) {
        this.enabledListeners -= listener
    }

    /**
     * Called when the back button is pressed, or the predictive back gesture is finished.
     */
    abstract fun onBack()

    /**
     * Called when the predictive back gesture starts.
     */
    open fun onBackStarted(backEvent: BackEvent) {
    }

    /**
     * Called on every progress of the predictive back gesture.
     */
    open fun onBackProgressed(backEvent: BackEvent) {
    }

    /**
     * Called when the predictive back gesture is cancelled.
     */
    open fun onBackCancelled() {
    }

    companion object {
        const val PRIORITY_DEFAULT: Int = 0
        const val PRIORITY_MIN: Int = Int.MIN_VALUE
        const val PRIORITY_MAX: Int = Int.MAX_VALUE
    }
}

fun BackCallback(
    isEnabled: Boolean = true,
    priority: Int = 0,
    onBackStarted: ((BackEvent) -> Unit)? = null,
    onBackProgressed: ((BackEvent) -> Unit)? = null,
    onBackCancelled: (() -> Unit)? = null,
    onBack: () -> Unit,
): BackCallback =
    object : BackCallback(isEnabled = isEnabled, priority = priority) {
        override fun onBackStarted(backEvent: BackEvent) {
            onBackStarted?.invoke(backEvent)
        }

        override fun onBackProgressed(backEvent: BackEvent) {
            onBackProgressed?.invoke(backEvent)
        }

        override fun onBackCancelled() {
            onBackCancelled?.invoke()
        }

        override fun onBack() {
            onBack.invoke()
        }
    }


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackDispatcher.kt
================================================
package com.arkivanov.essenty.backhandler

import kotlin.js.JsName

/**
 * Provides a way to manually trigger back button handlers.
 */
interface BackDispatcher : BackHandler {

    /**
     * Returns `true` if there is at least one enabled handler, `false` otherwise.
     */
    val isEnabled: Boolean

    /**
     * Adds the provided [listener], which will be called every time the enabled state of
     * this [BackDispatcher] changes.
     */
    fun addEnabledChangedListener(listener: (isEnabled: Boolean) -> Unit)

    /**
     * Removes the provided enabled state changed [listener].
     */
    fun removeEnabledChangedListener(listener: (isEnabled: Boolean) -> Unit)

    /**
     * If no predictive back gesture is currently in progress, finds the last enabled
     * callback with the highest priority and calls [BackCallback.onBack].
     *
     * If the predictive back gesture is currently in progress, calls [BackCallback.onBack] on
     * the previously selected callback.
     *
     * @return `true` if any callback was triggered, `false` otherwise.
     */
    fun back(): Boolean

    /**
     * Starts handling the predictive back gesture. Picks one of the enabled callback (if any)
     * that will be handling the gesture and calls [BackCallback.onBackStarted].
     *
     * @return `true` if any callback was triggered, `false` otherwise.
     */
    fun startPredictiveBack(backEvent: BackEvent): Boolean

    /**
     * Calls [BackCallback.onBackProgressed] on the previously selected callback.
     */
    fun progressPredictiveBack(backEvent: BackEvent)

    /**
     * Calls [BackCallback.onBackCancelled] on the previously selected callback.
     */
    fun cancelPredictiveBack()
}

/**
 * Creates and returns a default implementation of [BackDispatcher].
 */
@JsName("backDispatcher")
fun BackDispatcher(): BackDispatcher =
    DefaultBackDispatcher()


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackEvent.kt
================================================
package com.arkivanov.essenty.backhandler

/**
 * Represents an event of the predictive back gesture.
 *
 * @param progress progress factor of the back gesture, must be between 0 and 1.
 * @param swipeEdge Indicates which edge the gesture is being performed from.
 * @param touchX absolute X location of the touch point of this event.
 * @param touchY absolute Y location of the touch point of this event.
 */
data class BackEvent(
    val progress: Float = 0F,
    val swipeEdge: SwipeEdge = SwipeEdge.UNKNOWN,
    val touchX: Float = 0F,
    val touchY: Float = 0F,
) {

    init {
        require(progress in 0F..1F) { "The 'progress' argument must be between 0 and 1 (both inclusive)" }
    }

    enum class SwipeEdge {
        UNKNOWN,
        LEFT,
        RIGHT,
    }
}


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackHandler.kt
================================================
package com.arkivanov.essenty.backhandler

/**
 * A handler for back button presses.
 */
interface BackHandler {

    /**
     * Checks whether the provided [BackCallback] is registered or not.
     */
    fun isRegistered(callback: BackCallback): Boolean

    /**
     * Registers the specified [callback] to be called when the back button is invoked.
     */
    fun register(callback: BackCallback)

    /**
     * Unregisters the specified [callback].
     */
    fun unregister(callback: BackCallback)
}


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackHandlerOwner.kt
================================================
package com.arkivanov.essenty.backhandler

/**
 * Represents a holder of [BackHandler]. It may be implemented by an arbitrary class, to provide convenient API.
 */
interface BackHandlerOwner {

    val backHandler: BackHandler
}


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/DefaultBackDispatcher.kt
================================================
package com.arkivanov.essenty.backhandler

internal class DefaultBackDispatcher : BackDispatcher {

    private var set = emptySet<BackCallback>()
    private var progressData: ProgressData? = null
    override val isEnabled: Boolean get() = set.any(BackCallback::isEnabled)
    private var enabledChangedListeners = emptySet<(Boolean) -> Unit>()
    private var hasEnabledCallback: Boolean = false
    private val onCallbackEnabledChanged: (Boolean) -> Unit = { onCallbackEnabledChanged() }

    private fun onCallbackEnabledChanged() {
        val hasEnabledCallback = isEnabled
        if (this.hasEnabledCallback != hasEnabledCallback) {
            this.hasEnabledCallback = hasEnabledCallback
            enabledChangedListeners.forEach { it.invoke(hasEnabledCallback) }
        }
    }

    override fun isRegistered(callback: BackCallback): Boolean =
        callback in set

    override fun register(callback: BackCallback) {
        check(callback !in set) { "Callback is already registered" }

        this.set += callback
        callback.addEnabledChangedListener(onCallbackEnabledChanged)
        onCallbackEnabledChanged()
    }

    override fun unregister(callback: BackCallback) {
        check(callback in set) { "Callback is not registered" }

        this.set -= callback
        callback.removeEnabledChangedListener(onCallbackEnabledChanged)

        if (callback == progressData?.callback) {
            progressData?.callback = null
            callback.onBackCancelled()
        }

        onCallbackEnabledChanged()
    }

    override fun addEnabledChangedListener(listener: (isEnabled: Boolean) -> Unit) {
        enabledChangedListeners += listener
    }

    override fun removeEnabledChangedListener(listener: (isEnabled: Boolean) -> Unit) {
        enabledChangedListeners -= listener
    }

    override fun back(): Boolean {
        val callback = progressData?.callback ?: set.findMostImportant()
        progressData = null
        callback?.onBack()

        return callback != null
    }

    override fun startPredictiveBack(backEvent: BackEvent): Boolean {
        val callback = set.findMostImportant() ?: return false
        progressData = ProgressData(startEvent = backEvent, callback = callback)
        callback.onBackStarted(backEvent)

        return true
    }

    override fun progressPredictiveBack(backEvent: BackEvent) {
        val progressData = progressData ?: return

        if (progressData.callback == null) {
            progressData.callback = set.findMostImportant()
            progressData.callback?.onBackStarted(progressData.startEvent)
        }

        progressData.callback?.onBackProgressed(backEvent)
    }

    override fun cancelPredictiveBack() {
        progressData?.callback?.onBackCancelled()
        progressData = null
    }

    private class ProgressData(
        val startEvent: BackEvent,
        var callback: BackCallback?,
    )
}


================================================
FILE: back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/Utils.kt
================================================
package com.arkivanov.essenty.backhandler

internal fun Iterable<BackCallback>.findMostImportant(): BackCallback? =
    sortedBy(BackCallback::priority).lastOrNull(BackCallback::isEnabled)


================================================
FILE: back-handler/src/commonTest/kotlin/com/arkivanov/essenty/backhandler/DefaultBackDispatcherTest.kt
================================================
package com.arkivanov.essenty.backhandler

import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@Suppress("TestFunctionName")
class DefaultBackDispatcherTest {

    private val dispatcher = DefaultBackDispatcher()

    @Test
    fun WHEN_created_THEN_disabled() {
        assertFalse(dispatcher.isEnabled)
    }

    @Test
    fun WHEN_enabled_callback_registered_THEN_enabled() {
        dispatcher.register(callback(isEnabled = true))

        assertTrue(dispatcher.isEnabled)
    }

    @Test
    fun WHEN_two_enabled_callbacks_registered_THEN_enabled() {
        dispatcher.register(callback(isEnabled = true))
        dispatcher.register(callback(isEnabled = true))

        assertTrue(dispatcher.isEnabled)
    }

    @Test
    fun WHEN_two_callbacks_registered_one_disabled_THEN_enabled() {
        dispatcher.register(callback(isEnabled = false))
        dispatcher.register(callback(isEnabled = true))

        assertTrue(dispatcher.isEnabled)
    }

    @Test
    fun GIVEN_two_enabled_callbacks_registered_WHEN_one_callback_disabled_THEN_enabled() {
        val callback1 = callback(isEnabled = true)
        dispatcher.register(callback1)
        dispatcher.register(callback(isEnabled = true))

        callback1.isEnabled = false

        assertTrue(dispatcher.isEnabled)
    }

    @Test
    fun GIVEN_two_enabled_callbacks_registered_WHEN_all_callbacks_disabled_THEN_disabled() {
        val callback1 = callback(isEnabled = true)
        val callback2 = callback(isEnabled = true)
        dispatcher.register(callback1)
        dispatcher.register(callback2)

        callback1.isEnabled = false
        callback2.isEnabled = false

        assertFalse(dispatcher.isEnabled)
    }

    @Test
    fun WHEN_two_disabled_callbacks_registered_THEN_disabled() {
        dispatcher.register(callback(isEnabled = false))
        dispatcher.register(callback(isEnabled = false))

        assertFalse(dispatcher.isEnabled)
    }

    @Test
    fun GIVEN_enabled_callback_registered_WHEN_callback_unregistered_THEN_disabled() {
        val callback = callback(isEnabled = true)
        dispatcher.register(callback)

        dispatcher.unregister(callback)

        assertFalse(dispatcher.isEnabled)
    }

    @Test
    fun GIVEN_two_enabled_callbacks_registered_WHEN_one_callback_unregistered_THEN_enabled() {
        val callback1 = callback(isEnabled = true)
        dispatcher.register(callback1)
        dispatcher.register(callback(isEnabled = true))

        dispatcher.unregister(callback1)

        assertTrue(dispatcher.isEnabled)
    }

    @Test
    fun GIVEN_two_enabled_callbacks_registered_WHEN_all_callback_unregistered_THEN_disabled() {
        val callback1 = callback(isEnabled = true)
        val callback2 = callback(isEnabled = true)
        dispatcher.register(callback1)
        dispatcher.register(callback2)

        dispatcher.unregister(callback1)
        dispatcher.unregister(callback2)

        assertFalse(dispatcher.isEnabled)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_WHEN_back_THEN_last_callback_called() {
        val list = ArrayList<Int>()
        dispatcher.register(callback(isEnabled = true) { list += 1 })
        dispatcher.register(callback(isEnabled = true) { list += 2 })

        dispatcher.back()

        assertContentEquals(listOf(2), list)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_with_priorities_WHEN_back_THEN_last_callback_with_higher_priority_called() {
        val list = ArrayList<Int>()
        dispatcher.register(callback(isEnabled = true, priority = 0) { list += 1 })
        dispatcher.register(callback(isEnabled = true, priority = 1) { list += 2 })
        dispatcher.register(callback(isEnabled = true, priority = 2) { list += 3 })
        dispatcher.register(callback(isEnabled = true, priority = 1) { list += 4 })
        dispatcher.register(callback(isEnabled = true, priority = 2) { list += 5 })
        dispatcher.register(callback(isEnabled = true, priority = 0) { list += 6 })
        dispatcher.register(callback(isEnabled = true, priority = 1) { list += 7 })
        dispatcher.register(callback(isEnabled = true, priority = 0) { list += 8 })

        dispatcher.back()

        assertContentEquals(listOf(5), list)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_with_priorities_WHEN_priority_changed_and_back_THEN_last_callback_with_higher_priority_called() {
        val list = ArrayList<Int>()
        dispatcher.register(callback(isEnabled = true, priority = 0) { list += 1 })
        val callback = callback(isEnabled = true, priority = 1) { list += 2 }
        dispatcher.register(callback)
        dispatcher.register(callback(isEnabled = true, priority = 2) { list += 3 })

        callback.priority = 3
        dispatcher.back()

        assertContentEquals(listOf(2), list)
    }

    @Test
    fun GIVEN_callbacks_not_registered_WHEN_back_THEN_returned_false() {
        val result = dispatcher.back()

        assertFalse(result)
    }

    @Test
    fun GIVEN_enabled_callback_registered_WHEN_back_THEN_returned_true() {
        dispatcher.register(callback(isEnabled = true))

        val result = dispatcher.back()

        assertTrue(result)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_and_then_some_disabled_WHEN_back_THEN_last_enabled_callback_called() {
        val list = ArrayList<Int>()
        val callback2 = callback(isEnabled = true) { list += 2 }
        val callback4 = callback(isEnabled = true) { list += 4 }
        dispatcher.register(callback(isEnabled = true) { list += 1 })
        dispatcher.register(callback2)
        dispatcher.register(callback(isEnabled = true) { list += 3 })
        dispatcher.register(callback4)
        callback2.isEnabled = false
        callback4.isEnabled = false

        dispatcher.back()

        assertContentEquals(listOf(3), list)
    }

    @Test
    fun GIVEN_some_disabled_callbacks_registered_WHEN_back_THEN_returned_true() {
        val callback2 = callback(isEnabled = true)
        val callback4 = callback(isEnabled = true)
        dispatcher.register(callback(isEnabled = true))
        dispatcher.register(callback2)
        dispatcher.register(callback(isEnabled = true))
        dispatcher.register(callback4)
        callback2.isEnabled = false
        callback4.isEnabled = false

        val result = dispatcher.back()

        assertTrue(result)
    }

    @Test
    fun GIVEN_some_disabled_callbacks_registered_WHEN_back_THEN_last_enabled_callback_called() {
        val list = ArrayList<Int>()
        dispatcher.register(callback(isEnabled = true) { list += 1 })
        dispatcher.register(callback(isEnabled = false) { list += 2 })
        dispatcher.register(callback(isEnabled = true) { list += 3 })
        dispatcher.register(callback(isEnabled = false) { list += 4 })

        dispatcher.back()

        assertContentEquals(listOf(3), list)
    }

    @Test
    fun GIVEN_callbacks_registered_and_some_disabled_WHEN_back_THEN_returned_true() {
        dispatcher.register(callback(isEnabled = true))
        dispatcher.register(callback(isEnabled = false))
        dispatcher.register(callback(isEnabled = true))
        dispatcher.register(callback(isEnabled = false))

        val result = dispatcher.back()

        assertTrue(result)
    }

    @Test
    fun GIVEN_enabled_callbacks_registered_and_then_all_disabled_WHEN_back_THEN_returned_false() {
        val callback1 = callback(isEnabled = true)
        val callback2 = callback(isEnabled = true)
        dispatcher.register(callback1)
        dispatcher.register(callback2)
        callback1.isEnabled = false
        callback2.isEnabled = false

        val result = dispatcher.back()

        assertFalse(result)
    }

    @Test
    fun GIVEN_disabled_callbacks_registered_WHEN_back_THEN_returned_false() {
        dispatcher.register(callback = callback(isEnabled = false))
        dispatcher.register(callback = callback(isEnabled = false))

        val result = dispatcher.back()

        assertFalse(result)
    }

    @Test
    fun GIVEN_callback_not_registered_WHEN_startPredictiveBack_THEN_returns_false() {
        val result = dispatcher.startPredictiveBack(BackEvent())

        assertFalse(result)
    }

    @Test
    fun GIVEN_disabled_callback_registered_WHEN_startPredictiveBack_THEN_returns_false() {
        dispatcher.register(callback = callback(isEnabled = false))

        val result = dispatcher.startPredictiveBack(BackEvent())

        assertFalse(result)
    }

    @Test
    fun GIVEN_enabled_callback_registered_WHEN_startPredictiveBack_THEN_returns_true() {
        dispatcher.register(callback = callback(isEnabled = true))

        val result = dispatcher.startPredictiveBack(BackEvent())

        assertTrue(result)
    }

    @Test
    fun WHEN_progress_with_back_THEN_callbacks_called() {
        val startEvent = BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F)
        val progressEvent1 = BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F)
        val progressEvent2 = BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F)
        val callback = LoggingCallback()
        dispatcher.register(callback)

        dispatcher.startPredictiveBack(startEvent)
        dispatcher.progressPredictiveBack(progressEvent1)
        dispatcher.progressPredictiveBack(progressEvent2)
        dispatcher.back()

        callback.assertEvents(
            "start" to startEvent,
            "progress" to progressEvent1,
            "progress" to progressEvent2,
            "back" to null,
        )
    }

    @Test
    fun WHEN_progress_with_cancel_THEN_callbacks_called() {
        val startEvent = BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F)
        val progressEvent1 = BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F)
        val progressEvent2 = BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F)
        val callback = LoggingCallback()
        dispatcher.register(callback)

        dispatcher.startPredictiveBack(startEvent)
        dispatcher.progressPredictiveBack(progressEvent1)
        dispatcher.progressPredictiveBack(progressEvent2)
        dispatcher.cancelPredictiveBack()

        callback.assertEvents(
            "start" to startEvent,
            "progress" to progressEvent1,
            "progress" to progressEvent2,
            "cancel" to null,
        )
    }

    @Test
    fun GIVEN_callback_registered_and_gesture_started_WHEN_unregister_THEN_callback_cancelled() {
        val callback = LoggingCallback()
        dispatcher.register(callback)
        dispatcher.startPredictiveBack(BackEvent())
        callback.clear()

        dispatcher.unregister(callback)

        callback.assertEvents("cancel" to null)
    }

    @Test
    fun GIVEN_two_callbacks_registered_and_gesture_started_and_progress_callback_unregistered_WHEN_progress_THEN_another_callback_started_and_progressed() {
        val callback1 = LoggingCallback()
        val callback2 = LoggingCallback()
        dispatcher.register(callback1)
        dispatcher.register(callback2)
        dispatcher.startPredictiveBack(BackEvent(progress = 0F))
        dispatcher.progressPredictiveBack(BackEvent(progress = 0.1F))
        dispatcher.unregister(callback2)

        dispatcher.progressPredictiveBack(BackEvent(progress = 0.2F))

        callback1.assertEvents("start" to BackEvent(progress = 0F), "progress" to BackEvent(progress = 0.2F))
    }

    @Test
    fun GIVEN_two_callbacks_registered_and_gesture_started_and_progress_callback_unregistered_WHEN_back_THEN_another_callback_back() {
        val callback1 = LoggingCallback()
        val callback2 = LoggingCallback()
        dispatcher.register(callback1)
        dispatcher.register(callback2)
        dispatcher.startPredictiveBack(BackEvent(progress = 0F))
        dispatcher.progressPredictiveBack(BackEvent(progress = 0.1F))
        dispatcher.unregister(callback2)

        dispatcher.back()

        callback1.assertEvents("back" to null)
    }

    @Test
    fun GIVEN_two_callbacks_registered_and_gesture_started_and_progress_callback_unregistered_WHEN_cancel_THEN_another_callback_not_called() {
        val callback1 = LoggingCallback()
        val callback2 = LoggingCallback()
        dispatcher.register(callback1)
        dispatcher.register(callback2)
        dispatcher.startPredictiveBack(BackEvent(progress = 0F))
        dispatcher.progressPredictiveBack(BackEvent(progress = 0.1F))
        dispatcher.unregister(callback2)

        dispatcher.cancelPredictiveBack()

        callback1.assertEvents()
    }

    @Test
    fun WHEN_another_callback_registered_while_in_progress_THEN_old_callback_called() {
        val startEvent = BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F)
        val progressEvent1 = BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F)
        val progressEvent2 = BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F)
        val callback = LoggingCallback()
        dispatcher.register(callback)

        dispatcher.startPredictiveBack(startEvent)
        dispatcher.progressPredictiveBack(progressEvent1)
        dispatcher.register(LoggingCallback())
        dispatcher.progressPredictiveBack(progressEvent2)
        dispatcher.cancelPredictiveBack()

        callback.assertEvents(
            "start" to startEvent,
            "progress" to progressEvent1,
            "progress" to progressEvent2,
            "cancel" to null,
        )
    }

    @Test
    fun WHEN_another_callback_registered_while_in_progress_THEN_new_callback_not_called() {
        val startEvent = BackEvent(progress = 0.1F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 1F, touchY = 2F)
        val progressEvent1 = BackEvent(progress = 0.2F, swipeEdge = BackEvent.SwipeEdge.RIGHT, touchX = 2F, touchY = 3F)
        val progressEvent2 = BackEvent(progress = 0.3F, swipeEdge = BackEvent.SwipeEdge.LEFT, touchX = 3F, touchY = 4F)
        val callback = LoggingCallback()
        dispatcher.register(LoggingCallback())

        dispatcher.startPredictiveBack(startEvent)
        dispatcher.progressPredictiveBack(progressEvent1)
        dispatcher.register(callback)
        dispatcher.progressPredictiveBack(progressEvent2)
        dispatcher.cancelPredictiveBack()

        callback.assertEvents()
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_WHEN_enabled_callback_registered_THEN_listener_called_with_true() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }

        dispatcher.register(callback(isEnabled = true, onBack = {}))

        assertContentEquals(listOf(true), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_WHEN_disabled_callback_registered_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }

        dispatcher.register(callback(isEnabled = false, onBack = {}))

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_callback_unregistered_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        val callback = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback)
        dispatcher.addEnabledChangedListener { events += it }

        dispatcher.unregister(callback)

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_disabled_callback_registered_and_EnabledChanged_listener_added_WHEN_callback_unregistered_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        val callback = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback)
        dispatcher.addEnabledChangedListener { events += it }

        dispatcher.unregister(callback)

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_disabled_callback_registered_and_EnabledChanged_listener_added_WHEN_callback_enabled_THEN_listener_called_with_true() {
        val events = ArrayList<Boolean>()
        val callback = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback)
        dispatcher.addEnabledChangedListener { events += it }

        callback.isEnabled = true

        assertContentEquals(listOf(true), events)
    }

    @Test
    fun GIVEN_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_callback_disabled_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        val callback = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback)
        dispatcher.addEnabledChangedListener { events += it }

        callback.isEnabled = false

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_two_disabled_callback_registered_and_EnabledChanged_listener_added_WHEN_one_callback_enabled_THEN_listener_called_with_true() {
        val events = ArrayList<Boolean>()
        val callback1 = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(callback(isEnabled = false, onBack = {}))
        dispatcher.addEnabledChangedListener { events += it }

        callback1.isEnabled = true

        assertContentEquals(listOf(true), events)
    }

    @Test
    fun GIVEN_two_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_one_callback_disabled_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        val callback1 = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(callback(isEnabled = true, onBack = {}))
        dispatcher.addEnabledChangedListener { events += it }

        callback1.isEnabled = false

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_one_disabled_and_one_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_callback_enabled_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        val callback1 = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        dispatcher.addEnabledChangedListener { events += it }

        callback1.isEnabled = true

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_one_disabled_and_one_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_callback_disabled_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        val callback1 = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))
        dispatcher.addEnabledChangedListener { events += it }

        callback1.isEnabled = false

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_one_disabled_and_one_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_enabled_callback_unregistered_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        val callback1 = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))
        dispatcher.addEnabledChangedListener { events += it }

        dispatcher.unregister(callback1)

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_one_disabled_and_one_enabled_callback_registered_and_EnabledChanged_listener_added_WHEN_disabled_callback_unregistered_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        val callback1 = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        dispatcher.addEnabledChangedListener { events += it }

        dispatcher.unregister(callback1)

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_disabled_callback_registered_WHEN_callback_enabled_THEN_listener_called_with_true() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback)
        events.clear()

        callback.isEnabled = true

        assertContentEquals(listOf(true), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_enabled_callback_registered_WHEN_callback_disabled_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback)
        events.clear()

        callback.isEnabled = false

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_two_disabled_callback_registered_WHEN_one_callback_enabled_THEN_listener_called_with_true() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback1 = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(callback(isEnabled = false, onBack = {}))
        events.clear()

        callback1.isEnabled = true

        assertContentEquals(listOf(true), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_two_enabled_callback_registered_WHEN_one_callback_disabled_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback1 = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(callback(isEnabled = true, onBack = {}))
        events.clear()

        callback1.isEnabled = false

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_one_disabled_and_one_enabled_callback_registered_WHEN_callback_enabled_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback1 = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        events.clear()

        callback1.isEnabled = true

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_one_disabled_and_one_enabled_callback_registered_WHEN_callback_disabled_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback1 = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))
        events.clear()

        callback1.isEnabled = false

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_one_disabled_and_one_enabled_callback_registered_WHEN_enabled_callback_unregistered_THEN_listener_called_with_false() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback1 = callback(isEnabled = true, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = false, onBack = {}))
        events.clear()

        dispatcher.unregister(callback1)

        assertContentEquals(listOf(false), events)
    }

    @Test
    fun GIVEN_EnabledChanged_listener_added_and_one_disabled_and_one_enabled_callback_registered_WHEN_disabled_callback_unregistered_THEN_listener_not_called() {
        val events = ArrayList<Boolean>()
        dispatcher.addEnabledChangedListener { events += it }
        val callback1 = callback(isEnabled = false, onBack = {})
        dispatcher.register(callback1)
        dispatcher.register(BackCallback(isEnabled = true, onBack = {}))
        events.clear()

        dispatcher.unregister(callback1)

        assertContentEquals(emptyList(), events)
    }

    @Test
    fun GIVEN_callback_not_registered_WHEN_isRegistered_THEN_returns_false() {
        val isRegistered = dispatcher.isRegistered(callback())

        assertFalse(isRegistered)
    }

    @Test
    fun GIVEN_callback_registered_WHEN_isRegistered_for_another_callback_THEN_returns_false() {
        dispatcher.register(callback())

        val isRegistered = dispatcher.isRegistered(callback())

        assertFalse(isRegistered)
    }

    @Test
    fun GIVEN_callback_registered_WHEN_isRegistered_for_same_callback_THEN_returns_true() {
        val callback = callback()
        dispatcher.register(callback)

        val isRegistered = dispatcher.isRegistered(callback)

        assertTrue(isRegistered)
    }

    private fun callback(
        isEnabled: Boolean = true,
        priority: Int = 0,
        onBack: () -> Unit = {},
    ): BackCallback =
        BackCallback(isEnabled = isEnabled, priority = priority, onBack = onBack)

    private class LoggingCallback : BackCallback() {
        private val events: MutableList<Pair<String, BackEvent?>> = ArrayList()

        override fun onBackStarted(backEvent: BackEvent) {
            events += "start" to backEvent
        }

        override fun onBackProgressed(backEvent: BackEvent) {
            events += "progress" to backEvent
        }

        override fun onBack() {
            events += "back" to null
        }

        override fun onBackCancelled() {
            events += "cancel" to null
        }

        fun clear() {
            events.clear()
        }

        fun assertEvents(vararg events: Pair<String, BackEvent?>) {
            assertContentEquals(events.toList(), this.events)
        }
    }
}


================================================
FILE: build.gradle.kts
================================================
import com.arkivanov.gradle.AndroidConfig
import com.arkivanov.gradle.BinaryCompatibilityValidatorConfig
import com.arkivanov.gradle.PublicationConfig
import com.arkivanov.gradle.ensureUnreachableTasksDisabled
import com.arkivanov.gradle.iosCompat
import com.arkivanov.gradle.macosCompat
import com.arkivanov.gradle.setupDefaults
import com.arkivanov.gradle.setupDetekt
import com.arkivanov.gradle.tvosCompat
import com.arkivanov.gradle.watchosCompat
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        gradlePluginPortal()
        mavenCentral()
        google()
        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
    }

    dependencies {
        classpath(deps.kotlin.kotlinGradlePlug)
        classpath(deps.android.gradle)
        classpath(deps.kotlinx.binaryCompatibilityValidator)
        classpath(deps.detekt.gradleDetektPlug)
        classpath(deps.jetbrains.kotlin.serializationGradlePlug)
    }
}

plugins {
    id("com.arkivanov.gradle.setup")
}

setupDefaults(
    multiplatformConfigurator = {
        androidTarget()
        jvm()
        js {
            browser()
            nodejs()
        }
        wasmJs {
            browser()
        }
        linuxX64()
        iosCompat()
        watchosCompat()
        tvosCompat()
        macosCompat()
    },
    androidConfig = AndroidConfig(
        minSdkVersion = 15,
        compileSdkVersion = 34,
        targetSdkVersion = 34,
    ),
    publicationConfig = PublicationConfig(
        group = "com.arkivanov.essenty",
        version = deps.versions.essenty.get(),
        projectName = "Essenty",
        projectDescription = "Essential libraries for Kotlin Multiplatform",
        projectUrl = "https://github.com/arkivanov/Essenty",
        scmUrl = "scm:git:git://github.com/arkivanov/Essenty.git",
        licenseName = "The Apache License, Version 2.0",
        licenseUrl = "http://www.apache.org/licenses/LICENSE-2.0.txt",
        developerId = "arkivanov",
        developerName = "Arkadii Ivanov",
        developerEmail = "arkann1985@gmail.com",
        signingKey = System.getenv("SIGNING_KEY"),
        signingPassword = System.getenv("SIGNING_PASSWORD"),
        repositoryUrl = "https://s01.oss.sonatype.org/service/local/staging/deployByRepositoryId/${System.getenv("SONATYPE_REPOSITORY_ID")}",
        repositoryUserName = System.getenv("SONATYPE_USER_NAME"),
        repositoryPassword = System.getenv("SONATYPE_PASSWORD"),
    ),
    binaryCompatibilityValidatorConfig = BinaryCompatibilityValidatorConfig(klib = true),
)

setupDetekt()
ensureUnreachableTasksDisabled()

allprojects {
    repositories {
        mavenCentral()
        google()
    }

    afterEvaluate {
        extensions.findByType<KotlinMultiplatformExtension>()?.apply {
            sourceSets {
                all {
                    languageSettings.optIn("com.arkivanov.essenty.utils.internal.InternalEssentyApi")
                }
            }
        }
    }
}


================================================
FILE: deps.versions.toml
================================================
[versions]

essenty = "2.5.0"
kotlin = "2.1.0"
kotlinxBinaryCompatibilityValidator = "0.16.3"
kotlinxCoroutines = "1.9.0"
detektGradlePlugin = "1.23.6"
junit = "4.13.2"
androidGradle = "8.0.2"
androidxLifecycle = "2.6.2"
androidxSavedstate = "1.2.1"
androidxActivity = "1.8.1"
jetbrainsKotlinxSerialization = "1.6.3"
robolectric = "4.9.1"
reaktive = "2.1.0"

[libraries]

kotlin-kotlinGradlePlug = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinx-binaryCompatibilityValidator = { group = "org.jetbrains.kotlinx", name = "binary-compatibility-validator", version.ref = "kotlinxBinaryCompatibilityValidator" }

kotlinx-coroutinesCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
kotlinx-coroutinesTest = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }

detekt-gradleDetektPlug = { group = "io.gitlab.arturbosch.detekt", name = "detekt-gradle-plugin", version.ref = "detektGradlePlugin" }

android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradle" }

androidx-lifecycle-lifecycleCommonJava8 = { group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref = "androidxLifecycle" }
androidx-lifecycle-lifecycleRuntime = { group = "androidx.lifecycle", name = "lifecycle-runtime", version.ref = "androidxLifecycle" }
androidx-lifecycle-lifecycleViewmodelKtx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" }
androidx-savedstate-savedstateKtx = { group = "androidx.savedstate", name = "savedstate-ktx", version.ref = "androidxSavedstate" }
androidx-activity-activityKtx = { group = "androidx.activity", name = "activity-ktx", version.ref = "androidxActivity" }

jetbrains-kotlin-serializationGradlePlug = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }
jetbrains-kotlinx-kotlinxSerializationCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "jetbrainsKotlinxSerialization" }
jetbrains-kotlinx-kotlinxSerializationJson = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "jetbrainsKotlinxSerialization" }

robolectric-robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }

reaktive-reaktive = { group = "com.badoo.reaktive", name = "reaktive", version.ref = "reaktive" }


================================================
FILE: detekt.yml
================================================
complexity:
  CyclomaticComplexMethod:
    threshold: 15
    ignoreSingleWhenExpression: true
    ignoreSimpleWhenEntries: true
    ignoreNestingFunctions: false
  CognitiveComplexMethod:
    active: true
    threshold: 15
  LongParameterList:
    active: false
  TooManyFunctions:
    active: false

exceptions:
  PrintStackTrace:
    active: false
  TooGenericExceptionCaught:
    active: false

naming:
  FunctionNaming:
    excludes: ['**/test/**', '**/*Test/**']
    ignoreAnnotated: [ 'Composable' ]
  MemberNameEqualsClassName:
    active: false

style:
  ForbiddenComment:
    values: ['FIXME:', 'STOPSHIP:']
  MagicNumber:
    active: false
  MaxLineLength:
    maxLineLength: 140
    excludes: ['**/test/**', '**/*Test/**']
  ReturnCount:
    active: false


================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: gradle.properties
================================================
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2g
org.gradle.parallel=true
org.gradle.caching=true
systemProp.org.gradle.internal.publish.checksums.insecure=true
android.useAndroidX=true
android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.applyDefaultHierarchyTemplate=false

# For compatibility with Kotlin 1.9.0
android.experimental.lint.version=8.1.0


================================================
FILE: gradlew
================================================
#!/bin/sh

#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
#
#   Gradle start up script for POSIX generated by Gradle.
#
#   Important for running:
#
#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
#       noncompliant, but you have some other compliant shell such as ksh or
#       bash, then to run this script, type that shell name before the whole
#       command line, like:
#
#           ksh Gradle
#
#       Busybox and similar reduced shells will NOT work, because this script
#       requires all of these POSIX shell features:
#         * functions;
#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
#         * compound commands having a testable exit status, especially «case»;
#         * various built-in commands including «command», «set», and «ulimit».
#
#   Important for patching:
#
#   (2) This script targets any POSIX shell, so it avoids extensions provided
#       by Bash, Ksh, etc; in particular arrays are avoided.
#
#       The "traditional" practice of packing multiple parameters into a
#       space-separated string is a well documented source of bugs and security
#       problems, so this is (mostly) avoided, by progressively accumulating
#       options in "$@", and eventually passing that to Java.
#
#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
#       see the in-line comments for details.
#
#       There are tweaks for specific operating systems such as AIX, CygWin,
#       Darwin, MinGW, and NonStop.
#
#   (3) This script is generated from the Groovy template
#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
#       within the Gradle project.
#
#       You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################

# Attempt to set APP_HOME

# Resolve links: $0 may be a link
app_path=$0

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
    [ -h "$app_path" ]
do
    ls=$( ls -ld "$app_path" )
    link=${ls#*' -> '}
    case $link in             #(
      /*)   app_path=$link ;; #(
      *)    app_path=$APP_HOME$link ;;
    esac
done

# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

warn () {
    echo "$*"
} >&2

die () {
    echo
    echo "$*"
    echo
    exit 1
} >&2

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in                #(
  CYGWIN* )         cygwin=true  ;; #(
  Darwin* )         darwin=true  ;; #(
  MSYS* | MINGW* )  msys=true    ;; #(
  NONSTOP* )        nonstop=true ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD=$JAVA_HOME/jre/sh/java
    else
        JAVACMD=$JAVA_HOME/bin/java
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD=java
    if ! command -v java >/dev/null 2>&1
    then
        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
fi

# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
    case $MAX_FD in #(
      max*)
        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC3045
        MAX_FD=$( ulimit -H -n ) ||
            warn "Could not query maximum file descriptor limit"
    esac
    case $MAX_FD in  #(
      '' | soft) :;; #(
      *)
        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
        # shellcheck disable=SC3045
        ulimit -n "$MAX_FD" ||
            warn "Could not set maximum file descriptor limit to $MAX_FD"
    esac
fi

# Collect all arguments for the java command, stacking in reverse order:
#   * args from the command line
#   * the main class name
#   * -classpath
#   * -D...appname settings
#   * --module-path (only if needed)
#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.

# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )

    JAVACMD=$( cygpath --unix "$JAVACMD" )

    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    for arg do
        if
            case $arg in                                #(
              -*)   false ;;                            # don't mess with options #(
              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
                    [ -e "$t" ] ;;                      #(
              *)    false ;;
            esac
        then
            arg=$( cygpath --path --ignore --mixed "$arg" )
        fi
        # Roll the args list around exactly as many times as the number of
        # args, so each arg winds up back in the position where it started, but
        # possibly modified.
        #
        # NB: a `for` loop captures its iteration list before it begins, so
        # changing the positional parameters here affects neither the number of
        # iterations, nor the values presented in `arg`.
        shift                   # remove old arg
        set -- "$@" "$arg"      # push replacement arg
    done
fi


# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Collect all arguments for the java command;
#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
#     shell script including quotes and variable substitutions, so put them in
#     double quotes to make sure that they get re-expanded; and
#   * put everything else in single quotes, so that it's not re-expanded.

set -- \
        "-Dorg.gradle.appname=$APP_BASE_NAME" \
        -classpath "$CLASSPATH" \
        org.gradle.wrapper.GradleWrapperMain \
        "$@"

# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
    die "xargs is not available"
fi

# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
#   set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#

eval "set -- $(
        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
        xargs -n1 |
        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
        tr '\n' ' '
    )" '"$@"'

exec "$JAVACMD" "$@"


================================================
FILE: gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem      https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega


================================================
FILE: instance-keeper/.gitignore
================================================
/build


================================================
FILE: instance-keeper/api/android/instance-keeper.api
================================================
public final class com/arkivanov/essenty/instancekeeper/AndroidExtKt {
	public static final fun InstanceKeeper (Landroidx/lifecycle/ViewModelStore;Z)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
	public static synthetic fun InstanceKeeper$default (Landroidx/lifecycle/ViewModelStore;ZILjava/lang/Object;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
	public static final fun instanceKeeper (Landroidx/lifecycle/ViewModelStoreOwner;Z)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
	public static synthetic fun instanceKeeper$default (Landroidx/lifecycle/ViewModelStoreOwner;ZILjava/lang/Object;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
}

public abstract interface annotation class com/arkivanov/essenty/instancekeeper/ExperimentalInstanceKeeperApi : java/lang/annotation/Annotation {
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeper {
	public abstract fun get (Ljava/lang/Object;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
	public abstract fun put (Ljava/lang/Object;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;)V
	public abstract fun remove (Ljava/lang/Object;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance {
	public abstract fun onDestroy ()V
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance$DefaultImpls {
	public static fun onDestroy (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;)V
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeper$SimpleInstance : com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance {
	public fun <init> (Ljava/lang/Object;)V
	public final fun getInstance ()Ljava/lang/Object;
	public fun onDestroy ()V
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher : com/arkivanov/essenty/instancekeeper/InstanceKeeper {
	public abstract fun destroy ()V
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcherKt {
	public static final fun InstanceKeeperDispatcher ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher;
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeperExtKt {
	public static final fun getOrCreate (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
	public static final fun getOrCreateCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/AutoCloseable;
	public static final fun getOrCreateSimple (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
	public static final fun retainedCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/AutoCloseable;
	public static final fun retainedInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
	public static final fun retainedSimpleInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
	public static final fun retainingCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingCloseable$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingCloseable$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingSimpleInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingSimpleInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingSimpleInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingSimpleInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner {
	public abstract fun getInstanceKeeper ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
}



================================================
FILE: instance-keeper/api/instance-keeper.klib.api
================================================
// Klib ABI Dump
// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true

// Library unique name: <com.arkivanov.essenty:instance-keeper>
open annotation class com.arkivanov.essenty.instancekeeper/ExperimentalInstanceKeeperApi : kotlin/Annotation { // com.arkivanov.essenty.instancekeeper/ExperimentalInstanceKeeperApi|null[0]
    constructor <init>() // com.arkivanov.essenty.instancekeeper/ExperimentalInstanceKeeperApi.<init>|<init>(){}[0]
}

abstract interface com.arkivanov.essenty.instancekeeper/InstanceKeeper { // com.arkivanov.essenty.instancekeeper/InstanceKeeper|null[0]
    abstract fun get(kotlin/Any): com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance? // com.arkivanov.essenty.instancekeeper/InstanceKeeper.get|get(kotlin.Any){}[0]
    abstract fun put(kotlin/Any, com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance) // com.arkivanov.essenty.instancekeeper/InstanceKeeper.put|put(kotlin.Any;com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance){}[0]
    abstract fun remove(kotlin/Any): com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance? // com.arkivanov.essenty.instancekeeper/InstanceKeeper.remove|remove(kotlin.Any){}[0]

    abstract interface Instance { // com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance|null[0]
        open fun onDestroy() // com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance.onDestroy|onDestroy(){}[0]
    }

    final class <#A1: out kotlin/Any?> SimpleInstance : com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance { // com.arkivanov.essenty.instancekeeper/InstanceKeeper.SimpleInstance|null[0]
        constructor <init>(#A1) // com.arkivanov.essenty.instancekeeper/InstanceKeeper.SimpleInstance.<init>|<init>(1:0){}[0]

        final val instance // com.arkivanov.essenty.instancekeeper/InstanceKeeper.SimpleInstance.instance|{}instance[0]
            final fun <get-instance>(): #A1 // com.arkivanov.essenty.instancekeeper/InstanceKeeper.SimpleInstance.instance.<get-instance>|<get-instance>(){}[0]
    }
}

abstract interface com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher : com.arkivanov.essenty.instancekeeper/InstanceKeeper { // com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher|null[0]
    abstract fun destroy() // com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher.destroy|destroy(){}[0]
}

abstract interface com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner { // com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner|null[0]
    abstract val instanceKeeper // com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner.instanceKeeper|{}instanceKeeper[0]
        abstract fun <get-instanceKeeper>(): com.arkivanov.essenty.instancekeeper/InstanceKeeper // com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner.instanceKeeper.<get-instanceKeeper>|<get-instanceKeeper>(){}[0]
}

final fun com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher(): com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher // com.arkivanov.essenty.instancekeeper/InstanceKeeperDispatcher|InstanceKeeperDispatcher(){}[0]
final inline fun <#A: com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreate(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreate|getOrCreate@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any;kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/retainingInstance(kotlin/Any? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.instancekeeper/retainingInstance|retainingInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any?;kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedInstance(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedInstance|retainedInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any;kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainingInstance(kotlin/Any? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.instancekeeper/retainingInstance|retainingInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any?;kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreateSimple(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreateSimple|getOrCreateSimple@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/retainingSimpleInstance(kotlin/Any? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.instancekeeper/retainingSimpleInstance|retainingSimpleInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any?;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedSimpleInstance(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedSimpleInstance|retainedSimpleInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainingSimpleInstance(kotlin/Any? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.instancekeeper/retainingSimpleInstance|retainingSimpleInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any?;kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/AutoCloseable> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreateCloseable(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreateCloseable|getOrCreateCloseable@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any;kotlin.Function0<0:0>){0§<kotlin.AutoCloseable>}[0]
final inline fun <#A: kotlin/AutoCloseable> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/retainingCloseable(kotlin/Any? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.instancekeeper/retainingCloseable|retainingCloseable@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Any?;kotlin.Function0<0:0>){0§<kotlin.AutoCloseable>}[0]
final inline fun <#A: kotlin/AutoCloseable> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedCloseable(kotlin/Any, kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedCloseable|retainedCloseable@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any;kotlin.Function0<0:0>){0§<kotlin.AutoCloseable>}[0]
final inline fun <#A: kotlin/AutoCloseable> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainingCloseable(kotlin/Any? = ..., crossinline kotlin/Function0<#A>): kotlin.properties/PropertyDelegateProvider<kotlin/Any?, kotlin.properties/ReadOnlyProperty<kotlin/Any?, #A>> // com.arkivanov.essenty.instancekeeper/retainingCloseable|retainingCloseable@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Any?;kotlin.Function0<0:0>){0§<kotlin.AutoCloseable>}[0]
final inline fun <#A: reified com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreate(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreate|getOrCreate@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: reified com.arkivanov.essenty.instancekeeper/InstanceKeeper.Instance> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedInstance(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedInstance|retainedInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Function0<0:0>){0§<com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance>}[0]
final inline fun <#A: reified kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreateSimple(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreateSimple|getOrCreateSimple@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: reified kotlin/Any?> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedSimpleInstance(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedSimpleInstance|retainedSimpleInstance@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Function0<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: reified kotlin/AutoCloseable> (com.arkivanov.essenty.instancekeeper/InstanceKeeper).com.arkivanov.essenty.instancekeeper/getOrCreateCloseable(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/getOrCreateCloseable|getOrCreateCloseable@com.arkivanov.essenty.instancekeeper.InstanceKeeper(kotlin.Function0<0:0>){0§<kotlin.AutoCloseable>}[0]
final inline fun <#A: reified kotlin/AutoCloseable> (com.arkivanov.essenty.instancekeeper/InstanceKeeperOwner).com.arkivanov.essenty.instancekeeper/retainedCloseable(kotlin/Function0<#A>): #A // com.arkivanov.essenty.instancekeeper/retainedCloseable|retainedCloseable@com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner(kotlin.Function0<0:0>){0§<kotlin.AutoCloseable>}[0]


================================================
FILE: instance-keeper/api/jvm/instance-keeper.api
================================================
public abstract interface annotation class com/arkivanov/essenty/instancekeeper/ExperimentalInstanceKeeperApi : java/lang/annotation/Annotation {
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeper {
	public abstract fun get (Ljava/lang/Object;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
	public abstract fun put (Ljava/lang/Object;Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;)V
	public abstract fun remove (Ljava/lang/Object;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance {
	public abstract fun onDestroy ()V
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance$DefaultImpls {
	public static fun onDestroy (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;)V
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeper$SimpleInstance : com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance {
	public fun <init> (Ljava/lang/Object;)V
	public final fun getInstance ()Ljava/lang/Object;
	public fun onDestroy ()V
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher : com/arkivanov/essenty/instancekeeper/InstanceKeeper {
	public abstract fun destroy ()V
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcherKt {
	public static final fun InstanceKeeperDispatcher ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher;
}

public final class com/arkivanov/essenty/instancekeeper/InstanceKeeperExtKt {
	public static final fun getOrCreate (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
	public static final fun getOrCreateCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/AutoCloseable;
	public static final fun getOrCreateSimple (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
	public static final fun retainedCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/AutoCloseable;
	public static final fun retainedInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
	public static final fun retainedSimpleInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
	public static final fun retainingCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingCloseable (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingCloseable$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingCloseable$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingSimpleInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static final fun retainingSimpleInstance (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingSimpleInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
	public static synthetic fun retainingSimpleInstance$default (Lcom/arkivanov/essenty/instancekeeper/InstanceKeeperOwner;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider;
}

public abstract interface class com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner {
	public abstract fun getInstanceKeeper ()Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper;
}



================================================
FILE: instance-keeper/build.gradle.kts
================================================
import com.arkivanov.gradle.bundle
import com.arkivanov.gradle.setupBinaryCompatibilityValidator
import com.arkivanov.gradle.setupMultiplatform
import com.arkivanov.gradle.setupPublication
import com.arkivanov.gradle.setupSourceSets

plugins {
    id("kotlin-multiplatform")
    id("com.android.library")
    id("com.arkivanov.gradle.setup")
}

setupMultiplatform()
setupPublication()
setupBinaryCompatibilityValidator()

android {
    namespace = "com.arkivanov.essenty.instancekeeper"
}

kotlin {
    setupSourceSets {
        val android by bundle()

        common.main.dependencies {
            implementation(project(":utils-internal"))
        }

        android.main.dependencies {
            implementation(deps.androidx.lifecycle.lifecycleViewmodelKtx)
        }
    }
}


================================================
FILE: instance-keeper/src/androidMain/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest/>


================================================
FILE: instance-keeper/src/androidMain/kotlin/com/arkivanov/essenty/instancekeeper/AndroidExt.kt
================================================
package com.arkivanov.essenty.instancekeeper

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.get

/**
 * Creates a new instance of [InstanceKeeper] and attaches it to the provided AndroidX [ViewModelStore].
 *
 * @param discardRetainedInstances a flag indicating whether any previously retained instances should be
 * discarded and destroyed or not, default value is `false`.
 */
fun InstanceKeeper(
    viewModelStore: ViewModelStore,
    discardRetainedInstances: Boolean = false,
): InstanceKeeper =
    ViewModelProvider(
        viewModelStore,
        object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(modelClass: Class<T>): T = InstanceKeeperViewModel() as T
        }
    )
        .get<InstanceKeeperViewModel>()
        .apply {
            if (discardRetainedInstances) {
                recreate()
            }
        }
        .instanceKeeperDispatcher

/**
 * Creates a new instance of [InstanceKeeper] and attaches it to the AndroidX [ViewModelStore].
 *
 * @param discardRetainedInstances a flag indicating whether any previously retained instances should be
 * discarded and destroyed or not, default value is `false`.
 */
fun ViewModelStoreOwner.instanceKeeper(discardRetainedInstances: Boolean = false): InstanceKeeper =
    InstanceKeeper(viewModelStore = viewModelStore, discardRetainedInstances = discardRetainedInstances)

internal class InstanceKeeperViewModel : ViewModel() {
    var instanceKeeperDispatcher: InstanceKeeperDispatcher = InstanceKeeperDispatcher()
        private set

    override fun onCleared() {
        instanceKeeperDispatcher.destroy()
    }

    fun recreate() {
        instanceKeeperDispatcher.destroy()
        instanceKeeperDispatcher = InstanceKeeperDispatcher()
    }
}


================================================
FILE: instance-keeper/src/androidUnitTest/kotlin/com/arkivanov/essenty/instancekeeper/AndroidInstanceKeeperTest.kt
================================================
package com.arkivanov.essenty.instancekeeper

import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import kotlin.test.Test
import kotlin.test.assertNotSame
import kotlin.test.assertSame
import kotlin.test.assertTrue

@Suppress("TestFunctionName")
class AndroidInstanceKeeperTest {

    @Test
    fun retains_instances() {
        val owner = TestOwner()
        var instanceKeeper = owner.instanceKeeper()
        val instance1 = instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)

        instanceKeeper = owner.instanceKeeper()
        val instance2 = instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)

        assertSame(instance1, instance2)
    }

    @Test
    fun GIVEN_discardRetainedInstances_is_true_on_restore_THEN_instances_not_retained() {
        val owner = TestOwner()
        var instanceKeeper = owner.instanceKeeper()
        val instance1 = instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)

        instanceKeeper = owner.instanceKeeper(discardRetainedInstances = true)
        val instance2 = instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)

        assertNotSame(instance1, instance2)
    }

    @Test
    fun GIVEN_discardRetainedInstances_is_true_on_restore_THEN_old_instances_destroyed() {
        val owner = TestOwner()
        val instanceKeeper = owner.instanceKeeper()
        val instance1 = instanceKeeper.getOrCreate(key = "key", factory = ::TestInstance)

        owner.instanceKeeper(discardRetainedInstances = true)

        assertTrue(instance1.isDestroyed)
    }

    private class TestOwner : ViewModelStoreOwner {
        override val viewModelStore: ViewModelStore = ViewModelStore()
    }

    private class TestInstance : InstanceKeeper.Instance {
        var isDestroyed: Boolean = false

        override fun onDestroy() {
            isDestroyed = true
        }
    }
}


================================================
FILE: instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/DefaultInstanceKeeperDispatcher.kt
================================================
package com.arkivanov.essenty.instancekeeper

import com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance

internal class DefaultInstanceKeeperDispatcher : InstanceKeeperDispatcher {

    private val map = HashMap<Any, Instance>()
    private var isDestroyed = false

    override fun get(key: Any): Instance? =
        map[key]

    override fun put(key: Any, instance: Instance) {
        check(key !in map) { "Another instance is already associated with the key: $key" }

        map[key] = instance

        if (isDestroyed) {
            instance.onDestroy()
        }
    }

    override fun remove(key: Any): Instance? =
        map.remove(key)

    override fun destroy() {
        if (!isDestroyed) {
            isDestroyed = true
            map.values.toList().forEach(Instance::onDestroy)
        }
    }
}


================================================
FILE: instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/ExperimentalInstanceKeeperApi.kt
================================================
package com.arkivanov.essenty.instancekeeper

/**
 * Marks experimental API in Essenty. An experimental API can be changed or removed at any time.
 */
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalInstanceKeeperApi


================================================
FILE: instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeper.kt
================================================
package com.arkivanov.essenty.instancekeeper

/**
 * A generic keyed store of [Instance] objects. Instances are destroyed at the end of the
 * [InstanceKeeper]'s scope, which is typically tied to the scope of a back stack entry.
 * E.g. instances are retained over Android configuration changes, and destroyed when the
 * corresponding back stack entry is popped.
 */
interface InstanceKeeper {

    /**
     * Returns an instance with the given [key], or `null` if no instance with the given key exists.
     */
    fun get(key: Any): Instance?

    /**
     * Stores the given [instance] with the given [key]. Throws [IllegalStateException] if another
     * instance is already registered with the given [key].
     */
    fun put(key: Any, instance: Instance)

    /**
     * Removes an instance with the given [key]. This does not destroy the instance.
     */
    fun remove(key: Any): Instance?

    /**
     * Represents a destroyable instance.
     */
    interface Instance {

        /**
         * Called at the end of the [InstanceKeeper]'s scope.
         */
        fun onDestroy() {}
    }

    /**
     * Are simple [Instance] wrapper for cases when destroying is not required.
     */
    class SimpleInstance<out T>(val instance: T) : Instance
}


================================================
FILE: instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher.kt
================================================
package com.arkivanov.essenty.instancekeeper

import kotlin.js.JsName

/**
 * Represents a destroyable [InstanceKeeper].
 */
interface InstanceKeeperDispatcher : InstanceKeeper {

    /**
     * Destroys all existing instances. Instances are not cleared, so that they can be
     * accessed later. Any new instances will be immediately destroyed.
     */
    fun destroy()
}

/**
 * Creates a default implementation of [InstanceKeeperDispatcher].
 */
@JsName("instanceKeeperDispatcher")
fun InstanceKeeperDispatcher(): InstanceKeeperDispatcher = DefaultInstanceKeeperDispatcher()


================================================
FILE: instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperExt.kt
================================================
package com.arkivanov.essenty.instancekeeper

import com.arkivanov.essenty.instancekeeper.InstanceKeeper.SimpleInstance
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.typeOf

/**
 * Returns a previously stored [InstanceKeeper.Instance] of type [T] with the given key,
 * or creates and stores a new one if it doesn't exist.
 */
inline fun <T : InstanceKeeper.Instance> InstanceKeeper.getOrCreate(key: Any, factory: () -> T): T {
    @Suppress("UNCHECKED_CAST") // Assuming the type per key is always the same
    var instance: T? = get(key) as T?
    if (instance == null) {
        instance = factory()
        put(key, instance)
    }

    return instance
}

/**
 * Returns a previously stored [InstanceKeeper.Instance] of type [T],
 * or creates and stores a new one if it doesn't exist. Uses `typeOf<T>()` as key.
 *
 * Deprecated. Using `getOrCreate` without the `key` parameter may crash when [T]
 * refers to a type with type (generic) parameters (e.g. a `StateFlow>`) and R8 Full Mode enabled.
 * See [KT-42913](https://youtrack.jetbrains.com/issue/KT-42913) for more information.
 */
@Deprecated(
    message = "Using getOrCreate without the key parameter is unsafe. Please use the other variant.",
    replaceWith = ReplaceWith(
        expression = "getOrCreate(key = , factory = factory)",
    )
)
inline fun <reified T : InstanceKeeper.Instance> InstanceKeeper.getOrCreate(factory: () -> T): T =
    getOrCreate(key = typeOf<T>(), factory = factory)

/**
 * Returns a previously stored [AutoCloseable] instance of type [T] with the given [key],
 * or creates and stores a new one if it doesn't exist.
 *
 * @param key a key to store and retrieve the instance.
 * @param factory a function creating a new instance of type [T].
 */
inline fun <T : AutoCloseable> InstanceKeeper.getOrCreateCloseable(key: Any, factory: () -> T): T =
    getOrCreate(key = key) {
        val instance = factory()

        object : InstanceKeeper.Instance {
            val instance: T = instance

            override fun onDestroy() {
                instance.close()
            }
        }
    }.instance

/**
 * Returns a previously stored [AutoCloseable] instance of type [T],
 * or creates and stores a new one if it doesn't exist. Uses `typeOf<T>()` as key.
 *
 * Deprecated. Using `getOrCreateCloseable` without the `key` parameter may crash when [T]
 * refers to a type with type (generic) parameters (e.g. a `StateFlow>`) and R8 Full Mode enabled.
 * See [KT-42913](https://youtrack.jetbrains.com/issue/KT-42913) for more information.
 *
 * @param factory a function creating a new instance of type [T].
 */
@Deprecated(
    message = "Using getOrCreateCloseable without the key parameter is unsafe. Please use the other variant.",
    replaceWith = ReplaceWith(
        expression = "getOrCreateCloseable(key = , factory = factory)",
    )
)
inline fun <reified T : AutoCloseable> InstanceKeeper.getOrCreateCloseable(factory: () -> T): T =
    getOrCreateCloseable(key = typeOf<T>(), factory = factory)

/**
 * A convenience function for [InstanceKeeper.getOrCreate].
 */
inline fun <T : InstanceKeeper.Instance> InstanceKeeperOwner.retainedInstance(key: Any, factory: () -> T): T =
    instanceKeeper.getOrCreate(key = key, factory = factory)

/**
 * A convenience function for [InstanceKeeper.getOrCreate].
 *
 * Deprecated. Using `retainedInstance` without the `key` parameter may crash when [T]
 * refers to a type with type (generic) parameters (e.g. a `StateFlow>`) and R8 Full Mode enabled.
 * See [KT-42913](https://youtrack.jetbrains.com/issue/KT-42913) for more information.
 */
@Deprecated(
    message = "Using retainedInstance without the key parameter is unsafe. Please use the other variant.",
    replaceWith = ReplaceWith(
        expression = "retainedInstance(key = , factory = factory)",
    )
)
inline fun <reified T : InstanceKeeper.Instance> InstanceKeeperOwner.retainedInstance(factory: () -> T): T =
    instanceKeeper.getOrCreate(key = typeOf<T>(), factory = factory)

/**
 * A convenience function for [InstanceKeeper.getOrCreateCloseable].
 */
inline fun <T : AutoCloseable> InstanceKeeperOwner.retainedCloseable(key: Any, factory: () -> T): T =
    instanceKeeper.getOrCreateCloseable(key = key, factory = factory)

/**
 * A convenience function for [InstanceKeeper.getOrCreateCloseable].
 *
 * Deprecated. Using `retainedCloseable` without the `key` parameter may crash when [T]
 * refers to a type with type (generic) parameters (e.g. a `StateFlow>`) and R8 Full Mode enabled.
 * See [KT-42913](https://youtrack.jetbrains.com/issue/KT-42913) for more information.
 */
@Deprecated(
    message = "Using retainedCloseable without the key parameter is unsafe. Please use the other variant.",
    replaceWith = ReplaceWith(
        expression = "retainedCloseable(key = , factory = factory)",
    )
)
inline fun <reified T : AutoCloseable> InstanceKeeperOwner.retainedCloseable(factory: () -> T): T =
    instanceKeeper.getOrCreateCloseable(key = typeOf<T>(), factory = factory)

/**
 * Returns a previously stored instance of type [T] with the given key,
 * or creates and stores a new one if it doesn't exist.
 *
 * This overload is for simple cases when instance destroying is not required.
 */
inline fun <T> InstanceKeeper.getOrCreateSimple(key: Any, factory: () -> T): T =
    getOrCreate(key = key) { SimpleInstance(factory()) }
        .instance

/**
 * Returns a previously stored instance of type [T],
 * or creates and stores a new one if it doesn't exist. Uses `typeOf<T>()` as key.
 *
 * This overload is for simple cases when instance destroying is not required.
 *
 * Deprecated. Using `getOrCreateSimple` without the `key` parameter may crash when [T]
 * refers to a type with type (generic) parameters (e.g. a `StateFlow>`) and R8 Full Mode enabled.
 * See [KT-42913](https://youtrack.jetbrains.com/issue/KT-42913) for more information.
 */
@Deprecated(
    message = "Using getOrCreateSimple without the key parameter is unsafe. Please use the other variant.",
    replaceWith = ReplaceWith(
        expression = "getOrCreateSimple(key = , factory = factory)",
    )
)
inline fun <reified T> InstanceKeeper.getOrCreateSimple(factory: () -> T): T =
    getOrCreateSimple(key = typeOf<T>(), factory = factory)

/**
 * A convenience function for [InstanceKeeper.getOrCreateSimple].
 */
inline fun <T> InstanceKeeperOwner.retainedSimpleInstance(key: Any, factory: () -> T): T =
    instanceKeeper.getOrCreateSimple(key = key, factory = factory)

/**
 * A convenience function for [InstanceKeeper.getOrCreateSimple].
 *
 * Deprecated. Using `retainedSimpleInstance` without the `key` parameter may crash when [T]
 * refers to a type with type (generic) parameters (e.g. a `StateFlow>`) and R8 Full Mode enabled.
 * See [KT-42913](https://youtrack.jetbrains.com/issue/KT-42913) for more information.
 */
@Deprecated(
    message = "Using retainedSimpleInstance without the key parameter is unsafe. Please use the other variant.",
    replaceWith = ReplaceWith(
        expression = "retainedSimpleInstance(key = , factory = factory)",
    )
)
inline fun <reified T> InstanceKeeperOwner.retainedSimpleInstance(factory: () -> T): T =
    instanceKeeper.getOrCreateSimple(key = typeOf<T>(), factory = factory)

/**
 * Helper function for creating a
 * [delegated property]((https://kotlinlang.org/docs/delegated-properties.html)) whose value is
 * automatically retained using [InstanceKeeper].
 *
 * Example:
 *
 * ```
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
 * import com.arkivanov.essenty.instancekeeper.retainingInstance
 *
 * class SomeLogic(override val instanceKeeper: InstanceKeeper) : InstanceKeeperOwner {
 *     private val viewModel by retainingInstance { RetainedViewModel() }
 *
 *     private class RetainedViewModel : InstanceKeeper.Instance {
 *         // ...
 *     }
 * }
 * ```
 *
 * @param key an optional key for storing and retrieving the instance. If not provided, then the
 * property name is used as a key.
 * @param factory a function creating a new instance of type [T].
 * @param T a type of the property, extends [InstanceKeeper.Instance].
 * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
 * @see getOrCreate
 */
@ExperimentalInstanceKeeperApi
inline fun <T : InstanceKeeper.Instance> InstanceKeeper.retainingInstance(
    key: Any? = null,
    crossinline factory: () -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
    PropertyDelegateProvider { _, property ->
        val instance = getOrCreate(key = key ?: "RETAINING_INSTANCE_${property.name}", factory = factory)
        ReadOnlyProperty { _, _ -> instance }
    }

/**
 * Helper function for creating a
 * [delegated property]((https://kotlinlang.org/docs/delegated-properties.html)) whose value is
 * automatically retained using [InstanceKeeper].
 *
 * Example:
 *
 * ```
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
 * import com.arkivanov.essenty.instancekeeper.retainingInstance
 *
 * class SomeLogic(override val instanceKeeper: InstanceKeeper) : InstanceKeeperOwner {
 *     private val viewModel by retainingInstance { RetainedViewModel() }
 *
 *     private class RetainedViewModel : InstanceKeeper.Instance {
 *         // ...
 *     }
 * }
 * ```
 *
 * @param key an optional key for storing and retrieving the instance. If not provided, then the
 * property name is used as a key.
 * @param factory a function creating a new instance of type [T].
 * @param T a type of the property, extends [InstanceKeeper.Instance].
 * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
 * @see getOrCreate
 */
@ExperimentalInstanceKeeperApi
inline fun <T : InstanceKeeper.Instance> InstanceKeeperOwner.retainingInstance(
    key: Any? = null,
    crossinline factory: () -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
    instanceKeeper.retainingInstance(key = key, factory = factory)

/**
 * Helper function for creating a
 * [delegated property]((https://kotlinlang.org/docs/delegated-properties.html)) whose value is
 * automatically retained using [InstanceKeeper].
 *
 * This overload is for simple cases when instance destroying is not required.
 *
 * Example:
 *
 * ```
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
 * import com.arkivanov.essenty.instancekeeper.retainingSimpleInstance
 *
 * class SomeLogic(override val instanceKeeper: InstanceKeeper) : InstanceKeeperOwner {
 *     private val viewModel by retainingSimpleInstance { RetainedClass() }
 *
 *     private class RetainedClass {
 *         // ...
 *     }
 * }
 * ```
 *
 * @param key an optional key for storing and retrieving the instance. If not provided, then the
 * property name is used as a key.
 * @param factory a function creating a new instance of type [T].
 * @param T a type of the property.
 * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
 * @see getOrCreateSimple
 */
@ExperimentalInstanceKeeperApi
inline fun <T> InstanceKeeper.retainingSimpleInstance(
    key: Any? = null,
    crossinline factory: () -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
    PropertyDelegateProvider { _, property ->
        val instance = getOrCreateSimple(key = key ?: "RETAINING_SIMPLE_INSTANCE_${property.name}", factory = factory)
        ReadOnlyProperty { _, _ -> instance }
    }

/**
 * Helper function for creating a
 * [delegated property]((https://kotlinlang.org/docs/delegated-properties.html)) whose value is
 * automatically retained using [InstanceKeeper].
 *
 * This overload is for simple cases when instance destroying is not required.
 *
 * Example:
 *
 * ```
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
 * import com.arkivanov.essenty.instancekeeper.retainingSimpleInstance
 *
 * class SomeLogic(override val instanceKeeper: InstanceKeeper) : InstanceKeeperOwner {
 *     private val viewModel by retainingSimpleInstance { RetainedClass() }
 *
 *     private class RetainedClass {
 *         // ...
 *     }
 * }
 * ```
 *
 * @param key an optional key for storing and retrieving the instance. If not provided, then the
 * property name is used as a key.
 * @param factory a function creating a new instance of type [T].
 * @param T a type of the property.
 * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
 * @see getOrCreateSimple
 */
@ExperimentalInstanceKeeperApi
inline fun <T> InstanceKeeperOwner.retainingSimpleInstance(
    key: Any? = null,
    crossinline factory: () -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
    instanceKeeper.retainingSimpleInstance(key = key, factory = factory)

/**
 * Helper function for creating a
 * [delegated property]((https://kotlinlang.org/docs/delegated-properties.html)) whose value is
 * automatically retained using [InstanceKeeper].
 *
 * This overload is for simple cases when instance destroying is not required.
 *
 * Example:
 *
 * ```
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
 * import com.arkivanov.essenty.instancekeeper.retainingSimpleInstance
 *
 * class SomeLogic(override val instanceKeeper: InstanceKeeper) : InstanceKeeperOwner {
 *     private val viewModel by retainingSimpleInstance { RetainedViewModel() }
 *
 *     private class RetainedViewModel : AutoCloseable {
 *         // ...
 *     }
 * }
 * ```
 *
 * @param key an optional key for storing and retrieving the instance. If not provided, then the
 * property name is used as a key.
 * @param factory a function creating a new instance of type [T].
 * @param T a type of the property, extends [AutoCloseable].
 * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
 * @see getOrCreateCloseable
 */
@ExperimentalInstanceKeeperApi
inline fun <T : AutoCloseable> InstanceKeeper.retainingCloseable(
    key: Any? = null,
    crossinline factory: () -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
    PropertyDelegateProvider { _, property ->
        val instance = getOrCreateCloseable(key = key ?: "RETAINING_CLOSEABLE_${property.name}", factory = factory)
        ReadOnlyProperty { _, _ -> instance }
    }

/**
 * Helper function for creating a
 * [delegated property]((https://kotlinlang.org/docs/delegated-properties.html)) whose value is
 * automatically retained using [InstanceKeeper].
 *
 * This overload is for simple cases when instance destroying is not required.
 *
 * Example:
 *
 * ```
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeper
 * import com.arkivanov.essenty.instancekeeper.InstanceKeeperOwner
 * import com.arkivanov.essenty.instancekeeper.retainingSimpleInstance
 *
 * class SomeLogic(override val instanceKeeper: InstanceKeeper) : InstanceKeeperOwner {
 *     private val viewModel by retainingSimpleInstance { RetainedViewModel() }
 *
 *     private class RetainedViewModel : AutoCloseable {
 *         // ...
 *     }
 * }
 * ```
 *
 * @param key an optional key for storing and retrieving the instance. If not provided, then the
 * property name is used as a key.
 * @param factory a function creating a new instance of type [T].
 * @param T a type of the property, extends [AutoCloseable].
 * @return [PropertyDelegateProvider] of type [T], typically used to define a delegated property.
 * @see getOrCreateCloseable
 */
@ExperimentalInstanceKeeperApi
inline fun <T : AutoCloseable> InstanceKeeperOwner.retainingCloseable(
    key: Any? = null,
    crossinline factory: () -> T,
): PropertyDelegateProvider<Any?, ReadOnlyProperty<Any?, T>> =
    instanceKeeper.retainingCloseable(key = key, factory = factory)


================================================
FILE: instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner.kt
================================================
package com.arkivanov.essenty.instancekeeper

/**
 * Represents a holder of [InstanceKeeper].
 */
interface InstanceKeeperOwner {

    val instanceKeeper: InstanceKeeper
}


================================================
FILE: instance-keeper/src/commonTest/kotlin/com/arkivanov/essenty/instancekeeper/DefaultInstanceKeeperDispatcherTest.kt
================================================
package com.arkivanov.essenty.instancekeeper

import kotlin.test.Test
import kotlin.test.assertFails
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue

@Suppress("TestFunctionName")
class DefaultInstanceKeeperDispatcherTest {

    @Test
    fun GIVEN_no_instance_put_WHEN_get_THEN_returns_null() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        dispatcher.put(key = "key1", instance = TestInstance())

        val returnedInstance = dispatcher.get(key = "key2")

        assertNull(returnedInstance)
    }

    @Test
    fun GIVEN_instance_put_WHEN_get_THEN_returns_instance() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance1 = TestInstance()
        val instance2 = TestInstance()
        dispatcher.put(key = "key1", instance = instance1)
        dispatcher.put(key = "key2", instance = instance2)

        val returnedInstance1 = dispatcher.get(key = "key1")
        val returnedInstance2 = dispatcher.get(key = "key2")

        assertSame(instance1, returnedInstance1)
        assertSame(instance2, returnedInstance2)
    }

    @Test
    fun GIVEN_instance_put_WHEN_put_with_same_key_THEN_throws_exception() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        dispatcher.put(key = "key", instance = TestInstance())

        assertFails {
            dispatcher.put(key = "key", instance = TestInstance())
        }
    }

    @Test
    fun GIVEN_instance_with_same_key_put_twice_WHEN_get_THEN_returns_original_instance() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.put(key = "key", instance = instance)
        try {
            dispatcher.put(key = "key", instance = TestInstance())
        } catch (ignored: Exception) {
        }

        val returnedInstance = dispatcher.get(key = "key")

        assertSame(instance, returnedInstance)
    }

    @Test
    fun GIVEN_instances_put_WHEN_destroy_THEN_instances_destroyed() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance1 = TestInstance()
        val instance2 = TestInstance()
        dispatcher.put(key = "key1", instance = instance1)
        dispatcher.put(key = "key2", instance = instance2)

        dispatcher.destroy()

        assertTrue(instance1.isDestroyed)
        assertTrue(instance2.isDestroyed)
    }

    @Test
    fun GIVEN_instance_put_and_destroyed_WHEN_get_THEN_returns_instance() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.put(key = "key", instance = instance)
        dispatcher.destroy()

        val returnedInstance = dispatcher.get(key = "key")

        assertSame(instance, returnedInstance)
    }

    @Test
    fun GIVEN_destroyed_WHEN_put_THEN_does_not_throw() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.destroy()

        dispatcher.put(key = "key", instance = instance)
    }

    @Test
    fun GIVEN_destroyed_and_put_WHEN_get_THEN_returns_instance() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.destroy()
        dispatcher.put(key = "key", instance = instance)

        val returnedInstance = dispatcher.get(key = "key")

        assertSame(instance, returnedInstance)
    }

    @Test
    fun GIVEN_not_empty_and_destroyed_WHEN_destroy_THEN_instance_is_not_destroyed_second_time() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.put(key = "key", instance = instance)
        dispatcher.destroy()
        instance.isDestroyed = false

        dispatcher.destroy()

        assertFalse(instance.isDestroyed)
    }

    @Test
    fun GEVEN_instance_not_put_WHEN_remove_THEN_returns_null() {
        val dispatcher = DefaultInstanceKeeperDispatcher()

        val returnedInstance = dispatcher.remove(key = "key")

        assertNull(returnedInstance)
    }

    @Test
    fun GEVEN_instance_put_WHEN_remove_THEN_returns_instance() {
        val instance = TestInstance()
        val dispatcher = DefaultInstanceKeeperDispatcher()
        dispatcher.put("key", instance)

        val returnedInstance = dispatcher.remove(key = "key")

        assertSame(instance, returnedInstance)
    }

    @Test
    fun GEVEN_instance_removed_WHEN_remove_THEN_returns_null() {
        val instance = TestInstance()
        val dispatcher = DefaultInstanceKeeperDispatcher()
        dispatcher.put("key", instance)
        dispatcher.remove("key")

        val returnedInstance = dispatcher.remove(key = "key")

        assertNull(returnedInstance)
    }

    @Test
    fun GIVEN_instance_put_and_destroyed_WHEN_remove_THEN_returns_instance() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.put(key = "key", instance = instance)
        dispatcher.destroy()

        val returnedInstance = dispatcher.remove(key = "key")

        assertSame(instance, returnedInstance)
    }

    @Test
    fun GIVEN_instance_put_and_destroyed_and_removed_WHEN_remove_THEN_returns_null() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        val instance = TestInstance()
        dispatcher.put(key = "key", instance = instance)
        dispatcher.destroy()
        dispatcher.remove(key = "key")

        val returnedInstance = dispatcher.remove(key = "key")

        assertNull(returnedInstance)
    }

    @Test
    fun GIVEN_instance_put_WHEN_destroy_and_instance_calls_get_in_onDestroy_THEN_returns_null() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        var returnedInstance: Any? = Any()
        dispatcher.put(key = "key", instance = TestInstance(onDestroy = { returnedInstance = null }))

        dispatcher.destroy()

        assertNull(returnedInstance)
    }

    @Test
    fun GIVEN_instances_put_WHEN_destroy_and_instance_calls_remove_in_onDestroy_THEN_does_not_throw() {
        val dispatcher = DefaultInstanceKeeperDispatcher()
        dispatcher.put(key = "key1", instance = TestInstance(onDestroy = { dispatcher.remove(key = "key1") }))
        dispatcher.put(key = "key2", instance = TestInstance(onDestroy = { dispatcher.remove(key = "key2") }))

        dispatcher.destroy()
    }

    private class TestInstance(
        private val onDestroy: () -> Unit = {},
    ) : InstanceKeeper.Instance {
        var isDestroyed: Boolean = false

        override fun onDestroy() {
            isDestroyed = true
            onDestroy.invoke()
        }
    }
}


================================================
FILE: instance-keeper/src/commonTest/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperExtTest.kt
================================================
package com.arkivanov.essenty.instancekeeper

import kotlin.test.Test
import kotlin.test.assertNotSame
import kotlin.test.assertSame
Download .txt
gitextract_dhrtofoi/

├── .editorconfig
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── build.yml
│       └── publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── back-handler/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── back-handler.api
│   │   ├── back-handler.klib.api
│   │   └── jvm/
│   │       └── back-handler.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── backhandler/
│       │                       └── AndroidBackHandler.kt
│       ├── androidUnitTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── backhandler/
│       │                       ├── AndroidBackHandlerTest.kt
│       │                       ├── AndroidBackHandlerWithLifecycleTest.kt
│       │                       └── OnBackPressedCallbackAdapterTest.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── backhandler/
│       │                       ├── BackCallback.kt
│       │                       ├── BackDispatcher.kt
│       │                       ├── BackEvent.kt
│       │                       ├── BackHandler.kt
│       │                       ├── BackHandlerOwner.kt
│       │                       ├── DefaultBackDispatcher.kt
│       │                       └── Utils.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── backhandler/
│                               └── DefaultBackDispatcherTest.kt
├── build.gradle.kts
├── deps.versions.toml
├── detekt.yml
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── instance-keeper/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── instance-keeper.api
│   │   ├── instance-keeper.klib.api
│   │   └── jvm/
│   │       └── instance-keeper.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── instancekeeper/
│       │                       └── AndroidExt.kt
│       ├── androidUnitTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── instancekeeper/
│       │                       └── AndroidInstanceKeeperTest.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── instancekeeper/
│       │                       ├── DefaultInstanceKeeperDispatcher.kt
│       │                       ├── ExperimentalInstanceKeeperApi.kt
│       │                       ├── InstanceKeeper.kt
│       │                       ├── InstanceKeeperDispatcher.kt
│       │                       ├── InstanceKeeperExt.kt
│       │                       └── InstanceKeeperOwner.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── instancekeeper/
│                               ├── DefaultInstanceKeeperDispatcherTest.kt
│                               └── InstanceKeeperExtTest.kt
├── lifecycle/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── lifecycle.api
│   │   ├── jvm/
│   │   │   └── lifecycle.api
│   │   └── lifecycle.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── AndroidExt.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       ├── Lifecycle.kt
│       │                       ├── LifecycleExt.kt
│       │                       ├── LifecycleOwner.kt
│       │                       ├── LifecycleRegistry.kt
│       │                       ├── LifecycleRegistryExt.kt
│       │                       └── LifecycleRegistryImpl.kt
│       ├── commonTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       ├── LifecycleExtTest.kt
│       │                       └── LifecycleRegistryTest.kt
│       ├── itvosMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── ApplicationLifecycle.kt
│       └── itvosTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── lifecycle/
│                               ├── ApplicationLifecyclePlatformTest.kt
│                               └── ApplicationLifecycleTest.kt
├── lifecycle-coroutines/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── lifecycle-coroutines.api
│   │   ├── jvm/
│   │   │   └── lifecycle-coroutines.api
│   │   └── lifecycle-coroutines.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── coroutines/
│       │                           ├── CoroutineScopeWithLifecycle.kt
│       │                           ├── DispatchersExt.kt
│       │                           ├── FlowWithLifecycle.kt
│       │                           └── RepeatOnLifecycle.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── lifecycle/
│                               └── coroutines/
│                                   ├── CoroutineScopeWithLifecycleTest.kt
│                                   ├── DispatchersExtTest.kt
│                                   └── LifecycleCoroutinesExtTest.kt
├── lifecycle-reaktive/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── lifecycle-reaktive.api
│   │   ├── jvm/
│   │   │   └── lifecycle-reaktive.api
│   │   └── lifecycle-reaktive.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── lifecycle/
│       │                       └── reaktive/
│       │                           └── DisposableWithLifecycle.kt
│       └── commonTest/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── lifecycle/
│                               └── reaktive/
│                                   └── DisposableWithLifecycleTest.kt
├── settings.gradle.kts
├── state-keeper/
│   ├── .gitignore
│   ├── api/
│   │   ├── android/
│   │   │   └── state-keeper.api
│   │   ├── jvm/
│   │   │   └── state-keeper.api
│   │   └── state-keeper.klib.api
│   ├── build.gradle.kts
│   └── src/
│       ├── androidMain/
│       │   ├── AndroidManifest.xml
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── AndroidExt.kt
│       │                       ├── BundleExt.kt
│       │                       └── PersistableBundleExt.kt
│       ├── androidUnitTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── AndroidStateKeeperTest.kt
│       │                       ├── BundleExtTest.kt
│       │                       └── TestUtils.android.kt
│       ├── commonMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── DefaultStateKeeperDispatcher.kt
│       │                       ├── ExperimentalStateKeeperApi.kt
│       │                       ├── PolymorphicSerializer.kt
│       │                       ├── SerializableContainer.kt
│       │                       ├── StateKeeper.kt
│       │                       ├── StateKeeperDispatcher.kt
│       │                       ├── StateKeeperExt.kt
│       │                       ├── StateKeeperOwner.kt
│       │                       ├── Utils.kt
│       │                       └── base64/
│       │                           ├── Decoder.kt
│       │                           ├── Dictionaries.kt
│       │                           ├── Encoder.kt
│       │                           └── README.md
│       ├── commonTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       ├── CodingTest.kt
│       │                       ├── DefaultStateKeeperDispatcherTest.kt
│       │                       ├── PolymorphicSerializerTest.kt
│       │                       ├── SerializableContainerTest.kt
│       │                       ├── SerializableData.kt
│       │                       ├── StateKeeperExtTest.kt
│       │                       ├── TestUtils.kt
│       │                       └── base64/
│       │                           ├── Base64ImplTest.kt
│       │                           └── README.md
│       ├── javaMain/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       └── Utils.java.kt
│       ├── jsTest/
│       │   └── kotlin/
│       │       └── com/
│       │           └── arkivanov/
│       │               └── essenty/
│       │                   └── statekeeper/
│       │                       └── DefaultStateKeeperDispatcherJsTest.kt
│       └── nonJavaMain/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── statekeeper/
│                               └── Utils.kt
├── state-keeper-benchmarks/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       ├── main/
│       │   └── res/
│       │       └── AndroidManifest.xml
│       └── test/
│           └── kotlin/
│               └── com/
│                   └── arkivanov/
│                       └── essenty/
│                           └── statekeeper/
│                               └── benchmarks/
│                                   └── Benchmarks.kt
├── tools/
│   └── check-publication/
│       ├── .gitignore
│       ├── build.gradle.kts
│       └── src/
│           ├── androidMain/
│           │   └── AndroidManifest.xml
│           └── commonMain/
│               └── kotlin/
│                   └── com/
│                       └── arkivanov/
│                           └── essenty/
│                               └── tools/
│                                   └── checkpublication/
│                                       └── Dummy.kt
└── utils-internal/
    ├── .gitignore
    ├── build.gradle.kts
    └── src/
        ├── androidMain/
        │   └── AndroidManifest.xml
        └── commonMain/
            └── kotlin/
                └── com/
                    └── arkivanov/
                        └── essenty/
                            └── utils/
                                └── internal/
                                    ├── ExperimentalEssentyApi.kt
                                    └── InternalEssentyApi.kt
Condensed preview — 137 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (426K chars).
[
  {
    "path": ".editorconfig",
    "chars": 3924,
    "preview": "root = true\n\n[*]\ninsert_final_newline = true\n\n[{*.kt, *.kts}]\nmax_line_length = 140\nij_kotlin_packages_to_use_import_on_"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 116,
    "preview": "# These are supported funding model platforms\n\ngithub: arkivanov\ncustom: [\"https://www.buymeacoffee.com/arkivanov\"]\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 1021,
    "preview": "name: Build\n\non:\n  pull_request:\n    paths-ignore:\n      - 'docs/**'\n\njobs:\n  linux-build:\n    name: Build on Linux\n    "
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 2249,
    "preview": "name: Publish\n\non:\n  workflow_dispatch:\n\njobs:\n  create-staging-repository:\n    runs-on: ubuntu-latest\n    name: Create "
  },
  {
    "path": ".gitignore",
    "chars": 62,
    "preview": "*.iml\n.gradle\nlocal.properties\n.idea\n/build\n.DS_Store\n.kotlin\n"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 21743,
    "preview": "[![Maven Central](https://img.shields.io/maven-central/v/com.arkivanov.essenty/lifecycle?color=blue)](https://search.mav"
  },
  {
    "path": "back-handler/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "back-handler/api/android/back-handler.api",
    "chars": 5241,
    "preview": "public final class com/arkivanov/essenty/backhandler/AndroidBackHandlerKt {\n\tpublic static final fun BackHandler (Landro"
  },
  {
    "path": "back-handler/api/back-handler.klib.api",
    "chars": 11115,
    "preview": "// Klib ABI Dump\n// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSi"
  },
  {
    "path": "back-handler/api/jvm/back-handler.api",
    "chars": 4584,
    "preview": "public abstract class com/arkivanov/essenty/backhandler/BackCallback {\n\tpublic static final field Companion Lcom/arkivan"
  },
  {
    "path": "back-handler/build.gradle.kts",
    "chars": 769,
    "preview": "import com.arkivanov.gradle.bundle\nimport com.arkivanov.gradle.setupBinaryCompatibilityValidator\nimport com.arkivanov.gr"
  },
  {
    "path": "back-handler/src/androidMain/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest/>\n"
  },
  {
    "path": "back-handler/src/androidMain/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandler.kt",
    "chars": 2914,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport androidx.activity.BackEventCompat\nimport androidx.activity.OnBackPress"
  },
  {
    "path": "back-handler/src/androidUnitTest/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandlerTest.kt",
    "chars": 9436,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport androidx.activity.BackEventCompat\nimport androidx.activity.BackEventCo"
  },
  {
    "path": "back-handler/src/androidUnitTest/kotlin/com/arkivanov/essenty/backhandler/AndroidBackHandlerWithLifecycleTest.kt",
    "chars": 2251,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport androidx.activity.OnBackPressedDispatcher\nimport androidx.lifecycle.Li"
  },
  {
    "path": "back-handler/src/androidUnitTest/kotlin/com/arkivanov/essenty/backhandler/OnBackPressedCallbackAdapterTest.kt",
    "chars": 7384,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport androidx.activity.BackEventCompat\nimport kotlin.test.Test\nimport kotli"
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackCallback.kt",
    "chars": 2638,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport kotlin.properties.Delegates\n\n/**\n * A callback for back button handlin"
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackDispatcher.kt",
    "chars": 1888,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport kotlin.js.JsName\n\n/**\n * Provides a way to manually trigger back butto"
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackEvent.kt",
    "chars": 779,
    "preview": "package com.arkivanov.essenty.backhandler\n\n/**\n * Represents an event of the predictive back gesture.\n *\n * @param progr"
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackHandler.kt",
    "chars": 509,
    "preview": "package com.arkivanov.essenty.backhandler\n\n/**\n * A handler for back button presses.\n */\ninterface BackHandler {\n\n    /*"
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/BackHandlerOwner.kt",
    "chars": 229,
    "preview": "package com.arkivanov.essenty.backhandler\n\n/**\n * Represents a holder of [BackHandler]. It may be implemented by an arbi"
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/DefaultBackDispatcher.kt",
    "chars": 2923,
    "preview": "package com.arkivanov.essenty.backhandler\n\ninternal class DefaultBackDispatcher : BackDispatcher {\n\n    private var set "
  },
  {
    "path": "back-handler/src/commonMain/kotlin/com/arkivanov/essenty/backhandler/Utils.kt",
    "chars": 189,
    "preview": "package com.arkivanov.essenty.backhandler\n\ninternal fun Iterable<BackCallback>.findMostImportant(): BackCallback? =\n    "
  },
  {
    "path": "back-handler/src/commonTest/kotlin/com/arkivanov/essenty/backhandler/DefaultBackDispatcherTest.kt",
    "chars": 26373,
    "preview": "package com.arkivanov.essenty.backhandler\n\nimport kotlin.test.Test\nimport kotlin.test.assertContentEquals\nimport kotlin."
  },
  {
    "path": "build.gradle.kts",
    "chars": 3114,
    "preview": "import com.arkivanov.gradle.AndroidConfig\nimport com.arkivanov.gradle.BinaryCompatibilityValidatorConfig\nimport com.arki"
  },
  {
    "path": "deps.versions.toml",
    "chars": 2504,
    "preview": "[versions]\n\nessenty = \"2.5.0\"\nkotlin = \"2.1.0\"\nkotlinxBinaryCompatibilityValidator = \"0.16.3\"\nkotlinxCoroutines = \"1.9.0"
  },
  {
    "path": "detekt.yml",
    "chars": 767,
    "preview": "complexity:\n  CyclomaticComplexMethod:\n    threshold: 15\n    ignoreSingleWhenExpression: true\n    ignoreSimpleWhenEntrie"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 252,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradle.properties",
    "chars": 387,
    "preview": "kotlin.code.style=official\norg.gradle.jvmargs=-Xmx2g\norg.gradle.parallel=true\norg.gradle.caching=true\nsystemProp.org.gra"
  },
  {
    "path": "gradlew",
    "chars": 8504,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "gradlew.bat",
    "chars": 2868,
    "preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
  },
  {
    "path": "instance-keeper/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "instance-keeper/api/android/instance-keeper.api",
    "chars": 6178,
    "preview": "public final class com/arkivanov/essenty/instancekeeper/AndroidExtKt {\n\tpublic static final fun InstanceKeeper (Landroid"
  },
  {
    "path": "instance-keeper/api/instance-keeper.klib.api",
    "chars": 11392,
    "preview": "// Klib ABI Dump\n// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSi"
  },
  {
    "path": "instance-keeper/api/jvm/instance-keeper.api",
    "chars": 5504,
    "preview": "public abstract interface annotation class com/arkivanov/essenty/instancekeeper/ExperimentalInstanceKeeperApi : java/lan"
  },
  {
    "path": "instance-keeper/build.gradle.kts",
    "chars": 783,
    "preview": "import com.arkivanov.gradle.bundle\nimport com.arkivanov.gradle.setupBinaryCompatibilityValidator\nimport com.arkivanov.gr"
  },
  {
    "path": "instance-keeper/src/androidMain/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest/>\n"
  },
  {
    "path": "instance-keeper/src/androidMain/kotlin/com/arkivanov/essenty/instancekeeper/AndroidExt.kt",
    "chars": 1948,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelPro"
  },
  {
    "path": "instance-keeper/src/androidUnitTest/kotlin/com/arkivanov/essenty/instancekeeper/AndroidInstanceKeeperTest.kt",
    "chars": 1917,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport androidx.lifecycle.ViewModelStore\nimport androidx.lifecycle.ViewMod"
  },
  {
    "path": "instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/DefaultInstanceKeeperDispatcher.kt",
    "chars": 828,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance\n\ninter"
  },
  {
    "path": "instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/ExperimentalInstanceKeeperApi.kt",
    "chars": 348,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\n/**\n * Marks experimental API in Essenty. An experimental API can be chang"
  },
  {
    "path": "instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeper.kt",
    "chars": 1265,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\n/**\n * A generic keyed store of [Instance] objects. Instances are destroye"
  },
  {
    "path": "instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperDispatcher.kt",
    "chars": 580,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport kotlin.js.JsName\n\n/**\n * Represents a destroyable [InstanceKeeper]."
  },
  {
    "path": "instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperExt.kt",
    "chars": 16126,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport com.arkivanov.essenty.instancekeeper.InstanceKeeper.SimpleInstance\n"
  },
  {
    "path": "instance-keeper/src/commonMain/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperOwner.kt",
    "chars": 172,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\n/**\n * Represents a holder of [InstanceKeeper].\n */\ninterface InstanceKeep"
  },
  {
    "path": "instance-keeper/src/commonTest/kotlin/com/arkivanov/essenty/instancekeeper/DefaultInstanceKeeperDispatcherTest.kt",
    "chars": 6683,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport kotlin.test.Test\nimport kotlin.test.assertFails\nimport kotlin.test."
  },
  {
    "path": "instance-keeper/src/commonTest/kotlin/com/arkivanov/essenty/instancekeeper/InstanceKeeperExtTest.kt",
    "chars": 4217,
    "preview": "package com.arkivanov.essenty.instancekeeper\n\nimport kotlin.test.Test\nimport kotlin.test.assertNotSame\nimport kotlin.tes"
  },
  {
    "path": "lifecycle/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "lifecycle/api/android/lifecycle.api",
    "chars": 7493,
    "preview": "public final class com/arkivanov/essenty/lifecycle/AndroidExtKt {\n\tpublic static final fun asEssentyLifecycle (Landroidx"
  },
  {
    "path": "lifecycle/api/jvm/lifecycle.api",
    "chars": 7181,
    "preview": "public abstract interface class com/arkivanov/essenty/lifecycle/Lifecycle {\n\tpublic abstract fun getState ()Lcom/arkivan"
  },
  {
    "path": "lifecycle/api/lifecycle.klib.api",
    "chars": 11195,
    "preview": "// Klib ABI Dump\n// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSi"
  },
  {
    "path": "lifecycle/build.gradle.kts",
    "chars": 989,
    "preview": "import com.arkivanov.gradle.bundle\nimport com.arkivanov.gradle.dependsOn\nimport com.arkivanov.gradle.setupBinaryCompatib"
  },
  {
    "path": "lifecycle/src/androidMain/AndroidManifest.xml",
    "chars": 52,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest />\n"
  },
  {
    "path": "lifecycle/src/androidMain/kotlin/com/arkivanov/essenty/lifecycle/AndroidExt.kt",
    "chars": 2573,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport androidx.lifecycle.DefaultLifecycleObserver\nimport androidx.lifecycle.Li"
  },
  {
    "path": "lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/Lifecycle.kt",
    "chars": 1271,
    "preview": "package com.arkivanov.essenty.lifecycle\n\n/**\n * A holder of [Lifecycle.State] that can be observed for changes.\n *\n * Po"
  },
  {
    "path": "lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleExt.kt",
    "chars": 5133,
    "preview": "package com.arkivanov.essenty.lifecycle\n\n/**\n * A convenience method for [Lifecycle.subscribe].\n */\nfun Lifecycle.subscr"
  },
  {
    "path": "lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleOwner.kt",
    "chars": 147,
    "preview": "package com.arkivanov.essenty.lifecycle\n\n/**\n * Represents a holder of [Lifecycle].\n */\ninterface LifecycleOwner {\n\n    "
  },
  {
    "path": "lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistry.kt",
    "chars": 686,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport kotlin.js.JsName\n\n/**\n * Represents [Lifecycle] and [Lifecycle.Callbacks"
  },
  {
    "path": "lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistryExt.kt",
    "chars": 1788,
    "preview": "package com.arkivanov.essenty.lifecycle\n\n/**\n * Drives the state of the [Lifecycle] forward to [Lifecycle.State.CREATED]"
  },
  {
    "path": "lifecycle/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistryImpl.kt",
    "chars": 1994,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle.Callbacks\nimport com.arkivanov"
  },
  {
    "path": "lifecycle/src/commonTest/kotlin/com/arkivanov/essenty/lifecycle/LifecycleExtTest.kt",
    "chars": 5493,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle.Callbacks\nimport com.arkivanov"
  },
  {
    "path": "lifecycle/src/commonTest/kotlin/com/arkivanov/essenty/lifecycle/LifecycleRegistryTest.kt",
    "chars": 3826,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\n\n@Suppress(\"TestFunctio"
  },
  {
    "path": "lifecycle/src/itvosMain/kotlin/com/arkivanov/essenty/lifecycle/ApplicationLifecycle.kt",
    "chars": 4592,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport platform.Foundation.NSNotification\nimport platform.Foundation.NSNotifica"
  },
  {
    "path": "lifecycle/src/itvosTest/kotlin/com/arkivanov/essenty/lifecycle/ApplicationLifecyclePlatformTest.kt",
    "chars": 1294,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport platform.Foundation.NSNotificationCenter\nimport platform.UIKit.UIApplica"
  },
  {
    "path": "lifecycle/src/itvosTest/kotlin/com/arkivanov/essenty/lifecycle/ApplicationLifecycleTest.kt",
    "chars": 9789,
    "preview": "package com.arkivanov.essenty.lifecycle\n\nimport platform.Foundation.NSNotification\nimport platform.Foundation.NSNotifica"
  },
  {
    "path": "lifecycle-coroutines/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "lifecycle-coroutines/api/android/lifecycle-coroutines.api",
    "chars": 2867,
    "preview": "public final class com/arkivanov/essenty/lifecycle/coroutines/CoroutineScopeWithLifecycleKt {\n\tpublic static final fun c"
  },
  {
    "path": "lifecycle-coroutines/api/jvm/lifecycle-coroutines.api",
    "chars": 2867,
    "preview": "public final class com/arkivanov/essenty/lifecycle/coroutines/CoroutineScopeWithLifecycleKt {\n\tpublic static final fun c"
  },
  {
    "path": "lifecycle-coroutines/api/lifecycle-coroutines.klib.api",
    "chars": 3409,
    "preview": "// Klib ABI Dump\n// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSi"
  },
  {
    "path": "lifecycle-coroutines/build.gradle.kts",
    "chars": 753,
    "preview": "import com.arkivanov.gradle.setupBinaryCompatibilityValidator\nimport com.arkivanov.gradle.setupMultiplatform\nimport com."
  },
  {
    "path": "lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/CoroutineScopeWithLifecycle.kt",
    "chars": 1264,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle\nimport com.arkivano"
  },
  {
    "path": "lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/DispatchersExt.kt",
    "chars": 587,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport kotlinx.coroutines.MainCoroutineDispatcher\nimport kotlin.conc"
  },
  {
    "path": "lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/FlowWithLifecycle.kt",
    "chars": 1798,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle\nimport kotlinx.coro"
  },
  {
    "path": "lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/RepeatOnLifecycle.kt",
    "chars": 5202,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle\nimport com.arkivano"
  },
  {
    "path": "lifecycle-coroutines/src/commonTest/kotlin/com/arkivanov/essenty/lifecycle/coroutines/CoroutineScopeWithLifecycleTest.kt",
    "chars": 1380,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport com.arkivanov.essenty.lifecycle.LifecycleRegistry\nimport com."
  },
  {
    "path": "lifecycle-coroutines/src/commonTest/kotlin/com/arkivanov/essenty/lifecycle/coroutines/DispatchersExtTest.kt",
    "chars": 1383,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Expe"
  },
  {
    "path": "lifecycle-coroutines/src/commonTest/kotlin/com/arkivanov/essenty/lifecycle/coroutines/LifecycleCoroutinesExtTest.kt",
    "chars": 5473,
    "preview": "package com.arkivanov.essenty.lifecycle.coroutines\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle\nimport com.arkivano"
  },
  {
    "path": "lifecycle-reaktive/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "lifecycle-reaktive/api/android/lifecycle-reaktive.api",
    "chars": 405,
    "preview": "public final class com/arkivanov/essenty/lifecycle/reaktive/DisposableWithLifecycleKt {\n\tpublic static final fun disposa"
  },
  {
    "path": "lifecycle-reaktive/api/jvm/lifecycle-reaktive.api",
    "chars": 405,
    "preview": "public final class com/arkivanov/essenty/lifecycle/reaktive/DisposableWithLifecycleKt {\n\tpublic static final fun disposa"
  },
  {
    "path": "lifecycle-reaktive/api/lifecycle-reaktive.klib.api",
    "chars": 1029,
    "preview": "// Klib ABI Dump\n// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSi"
  },
  {
    "path": "lifecycle-reaktive/build.gradle.kts",
    "chars": 644,
    "preview": "import com.arkivanov.gradle.setupBinaryCompatibilityValidator\nimport com.arkivanov.gradle.setupMultiplatform\nimport com."
  },
  {
    "path": "lifecycle-reaktive/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/reaktive/DisposableWithLifecycle.kt",
    "chars": 803,
    "preview": "package com.arkivanov.essenty.lifecycle.reaktive\n\nimport com.arkivanov.essenty.lifecycle.Lifecycle\nimport com.arkivanov."
  },
  {
    "path": "lifecycle-reaktive/src/commonTest/kotlin/com/arkivanov/essenty/lifecycle/reaktive/DisposableWithLifecycleTest.kt",
    "chars": 1250,
    "preview": "package com.arkivanov.essenty.lifecycle.reaktive\n\nimport com.arkivanov.essenty.lifecycle.LifecycleRegistry\nimport com.ar"
  },
  {
    "path": "settings.gradle.kts",
    "chars": 936,
    "preview": "dependencyResolutionManagement {\n    versionCatalogs {\n        create(\"deps\") {\n            from(files(\"deps.versions.to"
  },
  {
    "path": "state-keeper/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "state-keeper/api/android/state-keeper.api",
    "chars": 7721,
    "preview": "public final class com/arkivanov/essenty/statekeeper/AndroidExtKt {\n\tpublic static final fun StateKeeper (Landroidx/save"
  },
  {
    "path": "state-keeper/api/jvm/state-keeper.api",
    "chars": 4787,
    "preview": "public abstract interface annotation class com/arkivanov/essenty/statekeeper/ExperimentalStateKeeperApi : java/lang/anno"
  },
  {
    "path": "state-keeper/api/state-keeper.klib.api",
    "chars": 7651,
    "preview": "// Klib ABI Dump\n// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxX64, macosArm64, macosX64, tvosArm64, tvosSi"
  },
  {
    "path": "state-keeper/build.gradle.kts",
    "chars": 1393,
    "preview": "import com.arkivanov.gradle.bundle\nimport com.arkivanov.gradle.dependsOn\nimport com.arkivanov.gradle.setupBinaryCompatib"
  },
  {
    "path": "state-keeper/src/androidMain/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest/>\n"
  },
  {
    "path": "state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/AndroidExt.kt",
    "chars": 3905,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport android.os.Bundle\nimport androidx.savedstate.SavedStateRegistry\nimport"
  },
  {
    "path": "state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/BundleExt.kt",
    "chars": 2761,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelabl"
  },
  {
    "path": "state-keeper/src/androidMain/kotlin/com/arkivanov/essenty/statekeeper/PersistableBundleExt.kt",
    "chars": 2512,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport android.os.Build\nimport android.os.Bundle\nimport android.os.Persistabl"
  },
  {
    "path": "state-keeper/src/androidUnitTest/kotlin/com/arkivanov/essenty/statekeeper/AndroidStateKeeperTest.kt",
    "chars": 4008,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport androidx.lifecycle.L"
  },
  {
    "path": "state-keeper/src/androidUnitTest/kotlin/com/arkivanov/essenty/statekeeper/BundleExtTest.kt",
    "chars": 1786,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport android.os.Bundle\nimport kotlinx.serialization.Serializable\nimport org"
  },
  {
    "path": "state-keeper/src/androidUnitTest/kotlin/com/arkivanov/essenty/statekeeper/TestUtils.android.kt",
    "chars": 430,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport android.os.Bundle\nimport android.os.Parcel\n\ninternal fun Bundle.parcel"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/DefaultStateKeeperDispatcher.kt",
    "chars": 2033,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.DeserializationStrategy\nimport kotlinx.serializa"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/ExperimentalStateKeeperApi.kt",
    "chars": 342,
    "preview": "package com.arkivanov.essenty.statekeeper\n\n/**\n * Marks experimental API in Essenty. An experimental API can be changed "
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/PolymorphicSerializer.kt",
    "chars": 3846,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.ExperimentalSerializationApi\nimport kotlinx.seri"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/SerializableContainer.kt",
    "chars": 3516,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport com.arkivanov.essenty.statekeeper.base64.base64ToByteArray\nimport com."
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeper.kt",
    "chars": 1299,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.DeserializationStrategy\nimport kotlinx.serializa"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperDispatcher.kt",
    "chars": 587,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlin.js.JsName\n\n/**\n * Represents a savable [StateKeeper].\n */\ninter"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExt.kt",
    "chars": 7272,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.Seriali"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperOwner.kt",
    "chars": 157,
    "preview": "package com.arkivanov.essenty.statekeeper\n\n/**\n * Represents a holder of [StateKeeper].\n */\ninterface StateKeeperOwner {"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/Utils.kt",
    "chars": 447,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.DeserializationStrategy\nimport kotlinx.serializa"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/base64/Decoder.kt",
    "chars": 1855,
    "preview": "package com.arkivanov.essenty.statekeeper.base64\n\ninternal fun String.base64ToByteArray(): ByteArray =\n    decode(this)\n"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/base64/Dictionaries.kt",
    "chars": 274,
    "preview": "package com.arkivanov.essenty.statekeeper.base64\n\ninternal val dictionary: CharArray = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/base64/Encoder.kt",
    "chars": 1429,
    "preview": "package com.arkivanov.essenty.statekeeper.base64\n\ninternal fun ByteArray.toBase64(): String =\n    encode(this)\n\ninternal"
  },
  {
    "path": "state-keeper/src/commonMain/kotlin/com/arkivanov/essenty/statekeeper/base64/README.md",
    "chars": 249,
    "preview": "The content of this package was copied from https://github.com/cy6erGn0m/kotlinx.serialization/tree/cy/base64/formats/ba"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/CodingTest.kt",
    "chars": 365,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\n\nclass CodingTest {\n\n"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/DefaultStateKeeperDispatcherTest.kt",
    "chars": 4149,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.Serializable\nimport kotlin.test.Test\nimport kotl"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/PolymorphicSerializerTest.kt",
    "chars": 1371,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.ExperimentalSerializationApi\nimport kotlinx.seri"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/SerializableContainerTest.kt",
    "chars": 6047,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.Serializable\nimport kotlin.test.Test\nimport kotl"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/SerializableData.kt",
    "chars": 19690,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.KSerializer\nimport kotlinx.serialization.Seriali"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/StateKeeperExtTest.kt",
    "chars": 3660,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.builtins.nullable\nimport kotlinx.serialization.b"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/TestUtils.kt",
    "chars": 399,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.KSerializer\n\ninternal fun <T : Any> T.serializeA"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/base64/Base64ImplTest.kt",
    "chars": 1620,
    "preview": "package com.arkivanov.essenty.statekeeper.base64\n\nimport kotlin.test.Test\nimport kotlin.test.assertEquals\n\nclass Base64I"
  },
  {
    "path": "state-keeper/src/commonTest/kotlin/com/arkivanov/essenty/statekeeper/base64/README.md",
    "chars": 244,
    "preview": "The content of this package was copied from https://github.com/cy6erGn0m/kotlinx.serialization/tree/cy/base64/formats/ba"
  },
  {
    "path": "state-keeper/src/javaMain/kotlin/com/arkivanov/essenty/statekeeper/Utils.java.kt",
    "chars": 1390,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.DeserializationStrategy\nimport kotlinx.serializa"
  },
  {
    "path": "state-keeper/src/jsTest/kotlin/com/arkivanov/essenty/statekeeper/DefaultStateKeeperDispatcherJsTest.kt",
    "chars": 551,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlin.test.Test\nimport kotlin.test.assertTrue\n\n@Suppress(\"TestFunctio"
  },
  {
    "path": "state-keeper/src/nonJavaMain/kotlin/com/arkivanov/essenty/statekeeper/Utils.kt",
    "chars": 496,
    "preview": "package com.arkivanov.essenty.statekeeper\n\nimport kotlinx.serialization.DeserializationStrategy\nimport kotlinx.serializa"
  },
  {
    "path": "state-keeper-benchmarks/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "state-keeper-benchmarks/build.gradle.kts",
    "chars": 593,
    "preview": "import com.arkivanov.gradle.setupAndroidLibrary\n\nplugins {\n    id(\"kotlin-android\")\n    id(\"com.android.library\")\n    id"
  },
  {
    "path": "state-keeper-benchmarks/src/main/res/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest/>\n"
  },
  {
    "path": "state-keeper-benchmarks/src/test/kotlin/com/arkivanov/essenty/statekeeper/benchmarks/Benchmarks.kt",
    "chars": 3701,
    "preview": "@file:Suppress(\"INVISIBLE_MEMBER\", \"INVISIBLE_REFERENCE\")\n\npackage com.arkivanov.essenty.statekeeper.benchmarks\n\nimport "
  },
  {
    "path": "tools/check-publication/.gitignore",
    "chars": 45,
    "preview": "*.iml\n.gradle\n/local.properties\n.idea\n/build\n"
  },
  {
    "path": "tools/check-publication/build.gradle.kts",
    "chars": 1114,
    "preview": "import com.arkivanov.gradle.setupMultiplatform\nimport com.arkivanov.gradle.setupSourceSets\n\nplugins {\n    id(\"kotlin-mul"
  },
  {
    "path": "tools/check-publication/src/androidMain/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest/>\n"
  },
  {
    "path": "tools/check-publication/src/commonMain/kotlin/com/arkivanov/essenty/tools/checkpublication/Dummy.kt",
    "chars": 83,
    "preview": "package com.arkivanov.essenty.tools.checkpublication\n\nfun dummy() {\n    // no-op\n}\n"
  },
  {
    "path": "utils-internal/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "utils-internal/build.gradle.kts",
    "chars": 312,
    "preview": "import com.arkivanov.gradle.setupMultiplatform\nimport com.arkivanov.gradle.setupPublication\n\nplugins {\n    id(\"kotlin-mu"
  },
  {
    "path": "utils-internal/src/androidMain/AndroidManifest.xml",
    "chars": 51,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest/>\n"
  },
  {
    "path": "utils-internal/src/commonMain/kotlin/com/arkivanov/essenty/utils/internal/ExperimentalEssentyApi.kt",
    "chars": 341,
    "preview": "package com.arkivanov.essenty.utils.internal\n\n/**\n * Marks experimental API in Essenty. An experimental API can be chang"
  },
  {
    "path": "utils-internal/src/commonMain/kotlin/com/arkivanov/essenty/utils/internal/InternalEssentyApi.kt",
    "chars": 313,
    "preview": "package com.arkivanov.essenty.utils.internal\n\n@RequiresOptIn(message = \"This API is internal, please don't use it.\", lev"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the arkivanov/Essenty GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 137 files (391.7 KB), approximately 100.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!