[
  {
    "path": ".allstar/binary_artifacts.yaml",
    "content": "# Exemption reason: This repo uses binary artifacts to ship gradle.jar for users. It does not allow any others.\n# Exemption timeframe: permanent\noptConfig:\n  optOut: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/adaptive-bug-report.md",
    "content": "---\nname: Adaptive bug report\nabout: Create a report about adaptive\ntitle: \"[Adaptive]\"\nlabels: adaptive\nassignees: alexvanyo\n\n---\n\n**Description**\n\n**Steps to reproduce**\n\n**Expected behavior** \n\n**Additional context**\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Describe the bug\nA clear and concise description of what the bug is.\n\n## To Reproduce\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n## Expected behavior\nA clear and concise description of what you expected to happen.\n\n## Screenshots?\nIf applicable, add screenshots to help explain your problem.\n\n## Environment:\n - Android OS version: [e.g. Android 5.0]\n - Device: [e.g. Emulator, Google Pixel 4]\n - Accompanist version: [e.g. v0.X]\n\n## Additional context\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/general-bug-report.md",
    "content": "---\nname: General Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Describe the bug\nA clear and concise description of what the bug is.\n\n## To Reproduce (if applicable)\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n## Expected behavior (if applicable)\nA clear and concise description of what you expected to happen.\n\n## Screenshots? (if applicable)\nIf applicable, add screenshots to help explain your problem.\n\n## Environment: (if applicable)\n - Android OS version: [e.g. Android 5.0]\n - Device: [e.g. Emulator, Google Pixel 4]\n - Accompanist version: [e.g. v0.X]\n\n## Additional context\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/general-other-bug-report.md",
    "content": "---\nname: General/Other bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Describe the bug\nA clear and concise description of what the bug is.\n\n## To Reproduce (if applicable)\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n## Expected behavior (if applicable)\nA clear and concise description of what you expected to happen.\n\n## Screenshots? (if applicable)\nIf applicable, add screenshots to help explain your problem.\n\n## Environment: (if applicable)\n - Android OS version: [e.g. Android 5.0]\n - Device: [e.g. Emulator, Google Pixel 4]\n - Accompanist version: [e.g. v0.X]\n\n## Additional context\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/navigation-material-bug-report.md",
    "content": "---\nname: Navigation Material bug report\nabout: Create a report to help us improve\ntitle: \"[Navigation Material] \"\nlabels: ''\nassignees: jossiwolf\n\n---\n\n**Description**\n\n**Steps to reproduce**\n\n**Expected behavior** \n\n**Additional context**\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/permissions-bug-report.md",
    "content": "---\nname: Permissions bug report\nabout: Create a report to help us improve\ntitle: \"[Permissions] \"\nlabels: ''\nassignees: bentrengrove\n\n---\n\n**Description**\n\n**Steps to reproduce**\n\n**Expected behavior** \n\n**Additional context**\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/testharness-bug-report.md",
    "content": "---\nname: Test harness bug report\nabout: Create a report about test harness\ntitle: \"[Test Harness]\"\nlabels: testharness\nassignees: alexvanyo\n\n---\n\n**Description**\n\n**Steps to reproduce**\n\n**Expected behavior** \n\n**Additional context**\n"
  },
  {
    "path": ".github/auto-merge.yml",
    "content": "# Config for github.com/bobvanderlinden/probot-auto-merge\nminApprovals:\n  COLLABORATOR: 1\nrequiredLabels:\n  - automerge\nmergeMethod: merge\nreportStatus: true"
  },
  {
    "path": ".github/ci-gradle.properties",
    "content": "#\n# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Turn Gradle daemon off due to https://github.com/Kotlin/dokka/issues/1405\norg.gradle.daemon=false\n\norg.gradle.parallel=true\norg.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError\norg.gradle.workers.max=2\n\nkotlin.compiler.execution.strategy=in-process\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "### Please add the library name to the PR title. Example: \"[Insets] Fixes typo\" ###\n"
  },
  {
    "path": ".github/release-drafter.yml",
    "content": "name-template: 'v$NEXT_PATCH_VERSION 🌈'\ntag-template: 'v$NEXT_PATCH_VERSION'\ntemplate: |\n  ## What’s Changed\n\n  $CHANGES"
  },
  {
    "path": ".github/workflows/automerger.yml",
    "content": "name: main to snapshot auto merger\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  automerge:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: '0' # 0 == fetch all history history\n          ref: 'snapshot'\n          token: ${{ secrets.AUTOMERGE_PAT }}\n\n      - run: |\n          git config user.name github-actions\n          git config user.email github-actions@github.com\n          git fetch origin\n          git merge origin/main --no-edit\n          git push\n"
  },
  {
    "path": ".github/workflows/build-snapshot.yml",
    "content": "name: Build & test (snapshot)\n\non:\n  push:\n    branches:\n      - snapshot\n    paths-ignore:\n      - '**.md'\n  pull_request:\n    branches:\n      - snapshot\n  workflow_dispatch:\n\njobs:\n  build:\n    # Skip build if head commit contains 'skip ci'\n    if: \"!contains(github.event.head_commit.message, 'skip ci')\"\n\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          # Fetch expanded history, which is needed for affected module detection\n          fetch-depth: '500'\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: set up JDK\n        uses: actions/setup-java@v1\n        with:\n          java-version: 17\n\n      - name: Decrypt secrets\n        run: release/signing-setup.sh ${{ secrets.ENCRYPT_KEY }}\n\n      - name: Generate cache key\n        run: ./checksum.sh checksum.txt\n\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.gradle/caches/modules-*\n            ~/.gradle/caches/jars-*\n            ~/.gradle/caches/build-cache-*\n          key: gradle-${{ hashFiles('checksum.txt') }}\n\n      - name: Build\n        run: |\n          ./gradlew --scan --stacktrace \\\n              spotlessCheck \\\n              assemble \\\n              metalavaCheckCompatibilityRelease \\\n              lintDebug\n\n      - name: Unit Tests\n        run: |\n          ./scripts/run-tests.sh \\\n              --unit-tests \\\n              --run-affected \\\n              --affected-base-ref=$BASE_REF\n\n      - name: Upload test results\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: test-results-robolectric\n          path: |\n            **/build/test-results/*\n            **/build/reports/*\n\n      - name: Clean secrets\n        if: always()\n        run: release/signing-cleanup.sh\n\n  test:\n    runs-on: macos-latest\n    needs: build\n    timeout-minutes: 50\n\n    strategy:\n      # Allow tests to continue on other devices if they fail on one device.\n      fail-fast: false\n      matrix:\n        api-level: [ 22, 26, 29, 31, 32 ]\n        shard: [ 0, 1 ] # Need to update shard-count below if this changes\n\n    env:\n      TERM: dumb\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          # Fetch expanded history, which is needed for affected module detection\n          fetch-depth: '500'\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: set up JDK\n        uses: actions/setup-java@v1\n        with:\n          java-version: 17\n\n      - name: Decrypt secrets\n        run: release/signing-setup.sh ${{ secrets.ENCRYPT_KEY }}\n\n      - name: Generate cache key\n        run: ./checksum.sh checksum.txt\n\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.gradle/caches/modules-*\n            ~/.gradle/caches/jars-*\n            ~/.gradle/caches/build-cache-*\n          key: gradle-${{ hashFiles('checksum.txt') }}\n\n      # Determine what emulator image to use. We run all API 28+ emulators using\n      # the google_apis image\n      - name: Determine emulator target\n        id: determine-target\n        env:\n          API_LEVEL: ${{ matrix.api-level }}\n        run: |\n          TARGET=\"default\"\n          if [ \"$API_LEVEL\" -ge \"28\" ]; then\n            TARGET=\"google_apis\"\n          fi\n          echo \"TARGET=$TARGET\" >> $GITHUB_OUTPUT\n\n      - name: Run tests\n        uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: ${{ matrix.api-level }}\n          target: ${{ steps.determine-target.outputs.TARGET }}\n          profile: Galaxy Nexus\n          script: ./scripts/run-tests.sh --log-file=logcat.txt --run-affected --affected-base-ref=$BASE_REF --shard-index=${{ matrix.shard }} --shard-count=2\n\n      - name: Clean secrets\n        if: always()\n        run: release/signing-cleanup.sh\n\n      - name: Upload logs\n        if: always()\n        uses: actions/upload-artifact@v2\n        with:\n          name: logs-${{ matrix.api-level }}-${{ steps.determine-target.outputs.TARGET }}-${{ matrix.shard }}\n          path: logcat.txt\n\n      - name: Upload test results\n        if: always()\n        uses: actions/upload-artifact@v2\n        with:\n          name: test-results-${{ matrix.api-level }}-${{ steps.determine-target.outputs.TARGET }}-${{ matrix.shard }}\n          path: |\n            **/build/reports/*\n            **/build/outputs/*/connected/*\n\n  deploy:\n    if: github.event_name == 'push' # only deploy for pushed commits (not PRs)\n\n    runs-on: ubuntu-latest\n    needs: [ build, test ]\n    timeout-minutes: 30\n    env:\n      TERM: dumb\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: set up JDK\n        uses: actions/setup-java@v1\n        with:\n          java-version: 17\n\n      - name: Decrypt secrets\n        run: release/signing-setup.sh ${{ secrets.ENCRYPT_KEY }}\n\n      - name: Generate cache key\n        run: ./checksum.sh checksum.txt\n\n      - uses: actions/cache@v2\n        with:\n          path: |\n            ~/.gradle/caches/modules-*\n            ~/.gradle/caches/jars-*\n            ~/.gradle/caches/build-cache-*\n          key: gradle-${{ hashFiles('checksum.txt') }}\n\n      - name: Deploy to Sonatype\n        run: ./gradlew publish --no-parallel --stacktrace --no-configuration-cache\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}\n\n      - name: Clean secrets\n        if: always()\n        run: release/signing-cleanup.sh\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build & test\n\non:\n  push:\n    branches:\n      - main\n      - compose-1.0\n      - compose-1.1\n      - compose-1.2\n      - compose-1.3\n      - compose-1.4\n      - compose-1.5\n      - compose-1.6\n    paths-ignore:\n      - '**.md'\n  pull_request:\n\njobs:\n  build:\n    # Skip build if head commit contains 'skip ci'\n    if: \"!contains(github.event.head_commit.message, 'skip ci')\"\n\n    runs-on: ubuntu-latest\n    timeout-minutes: 45\n\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          # Fetch expanded history, which is needed for affected module detection\n          fetch-depth: '500'\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: Setup java\n        uses: actions/setup-java@v3\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: Decrypt secrets\n        run: release/signing-setup.sh ${{ secrets.ENCRYPT_KEY }}\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n        \n      - name: Build\n        run: |\n          ./gradlew --scan --stacktrace \\\n              spotlessCheck \\\n              assemble \\\n              metalavaCheckCompatibilityRelease \\\n              lintDebug\n\n      - name: Unit Tests\n        run: |\n          ./scripts/run-tests.sh \\\n              --unit-tests \\\n              --run-affected \\\n              --affected-base-ref=$BASE_REF\n\n      - name: Upload test results\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: test-results-robolectric\n          path: |\n            **/build/test-results/*\n            **/build/reports/*\n\n      - name: Clean secrets\n        if: always()\n        run: release/signing-cleanup.sh\n\n  test:\n    runs-on: ubuntu-latest\n    needs: build\n    timeout-minutes: 70\n\n    strategy:\n      # Allow tests to continue on other devices if they fail on one device.\n      fail-fast: false\n      matrix:\n        api-level: [ 22, 26, 30 ]\n        shard: [ 0, 1 ] # Need to update shard-count below if this changes\n\n    env:\n      TERM: dumb\n\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          # Fetch expanded history, which is needed for affected module detection\n          fetch-depth: '500'\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: Enable KVM\n        run: |\n          echo 'KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0666\", OPTIONS+=\"static_node=kvm\"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules\n          sudo udevadm control --reload-rules\n          sudo udevadm trigger --name-match=kvm\n\n      - name: Setup java\n        uses: actions/setup-java@v3\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: Decrypt secrets\n        run: release/signing-setup.sh ${{ secrets.ENCRYPT_KEY }}\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n\n      # Determine what emulator image to use. We run all API 28+ emulators using\n      # the google_apis image\n      - name: Determine emulator target\n        id: determine-target\n        env:\n          API_LEVEL: ${{ matrix.api-level }}\n        run: |\n          TARGET=\"default\"\n          if [ \"$API_LEVEL\" -ge \"28\" ]; then\n            TARGET=\"google_apis\"\n          fi\n          echo \"TARGET=$TARGET\" >> $GITHUB_OUTPUT\n      - name: Determine emulator arch\n        id: determine-arch\n        env: \n          API_LEVEL: ${{ matrix.api-level }}\n        run: |\n          ARCH=\"x86\"\n          if [ \"$API_LEVEL\" -ge \"29\" ]; then\n            ARCH=\"x86_64\"\n          fi\n          echo \"ARCH=$ARCH\" >> $GITHUB_OUTPUT\n      - name: Run tests\n        uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: ${{ matrix.api-level }}\n          arch: ${{ steps.determine-arch.outputs.ARCH }}\n          target: ${{ steps.determine-target.outputs.TARGET }}\n          profile: Galaxy Nexus\n          script: ./scripts/run-tests.sh --log-file=logcat.txt --run-affected --affected-base-ref=$BASE_REF --shard-index=${{ matrix.shard }} --shard-count=2\n\n      - name: Clean secrets\n        if: always()\n        run: release/signing-cleanup.sh\n\n      - name: Upload logs\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: logs-${{ matrix.api-level }}-${{ steps.determine-target.outputs.TARGET }}-${{ matrix.shard }}\n          path: logcat.txt\n\n      - name: Upload test results\n        if: always()\n        uses: actions/upload-artifact@v4\n        with:\n          name: test-results-${{ matrix.api-level }}-${{ steps.determine-target.outputs.TARGET }}-${{ matrix.shard }}\n          path: |\n            **/build/reports/*\n            **/build/outputs/*/connected/*\n\n  deploy:\n    if: github.event_name == 'push' # only deploy for pushed commits (not PRs)\n\n    runs-on: ubuntu-latest\n    needs: [ build, test ]\n    timeout-minutes: 30\n    env:\n      TERM: dumb\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: Setup java\n        uses: actions/setup-java@v3\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: Decrypt secrets\n        run: release/signing-setup.sh ${{ secrets.ENCRYPT_KEY }}\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n\n      - name: Deploy to Sonatype\n        run: ./gradlew publish --no-parallel --stacktrace --no-configuration-cache\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}\n\n      - name: Clean secrets\n        if: always()\n        run: release/signing-cleanup.sh\n"
  },
  {
    "path": ".github/workflows/issues-stale.yml",
    "content": "name: 'Close stale issues and PRs'\non:\n  schedule:\n    - cron: '15 3 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/stale@v3\n        with:\n          stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'\n          days-before-stale: 45\n          days-before-close: 5\n          exempt-all-pr-milestones: true\n          exempt-issue-labels: 'waiting for info,waiting on dependency'\n"
  },
  {
    "path": ".github/workflows/publish-docs.yml",
    "content": "name: Publish docs\n\non:\n  push:\n    tags:\n      - v*\n  workflow_dispatch:\n\njobs:\n  deploy_docs:\n    runs-on: ubuntu-latest\n    env:\n      TERM: dumb\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - name: Copy CI gradle.properties\n        run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties\n\n      - name: Setup java\n        uses: actions/setup-java@v3\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v2\n\n      - name: Setup Python\n        uses: actions/setup-python@v4\n        with:\n          python-version: '3.x'\n\n      - name: Install dependencies\n        run: |\n          python3 -m pip install --upgrade pip\n          python3 -m pip install mkdocs-material==\"9.*\"\n\n      - name: Generate docs\n        run: ./generate_docs.sh\n\n      - name: Build site\n        run: mkdocs build\n\n      - name: Deploy\n        uses: peaceiris/actions-gh-pages@v3\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: ./site\n"
  },
  {
    "path": ".github/workflows/update-release.yml",
    "content": "name: Update release\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  update_draft_release:\n    runs-on: ubuntu-latest\n    steps:\n      # pin directly to 6.0.0 because we don't want to update without knowledge\n      - uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Gradle\n.gradle\nbuild/\n\ncaptures\n\n/local.properties\n\n# IntelliJ .idea folder\n.idea/workspace.xml\n.idea/libraries\n.idea/caches\n.idea/navEditor.xml\n.idea/tasks.xml\n.idea/modules.xml\n.idea/compiler.xml\n.idea/jarRepositories.xml\n.idea/deploymentTargetDropDown.xml\n.idea/misc.xml\n.idea/androidTestResultsUserPreferences.xml\n.idea/deploymentTargetSelector.xml\ngradle.xml\n*.iml\n\n# General\n.DS_Store\n.externalNativeBuild\n\n# Do not commit plain-text signing info\nrelease/*.properties\nrelease/*.gpg\n\n# VS Code config\norg.eclipse.buildship.core.prefs\n.classpath\n.project\n\n# Temporary API docs\ndocs/api\npackage-list-coil-base\n\n# Mkdocs temporary serving folder\ndocs-gen\nsite\n*.bak\n\n# Lint reports\nlint-report.*\n"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <JetCodeStyleSettings>\n      <option name=\"NAME_COUNT_TO_USE_STAR_IMPORT\" value=\"99\" />\n      <option name=\"NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS\" value=\"99\" />\n      <option name=\"CONTINUATION_INDENT_IN_PARAMETER_LISTS\" value=\"true\" />\n      <option name=\"CONTINUATION_INDENT_IN_ARGUMENT_LISTS\" value=\"true\" />\n      <option name=\"CONTINUATION_INDENT_FOR_EXPRESSION_BODIES\" value=\"true\" />\n      <option name=\"CONTINUATION_INDENT_FOR_CHAINED_CALLS\" value=\"true\" />\n      <option name=\"CONTINUATION_INDENT_IN_SUPERTYPE_LISTS\" value=\"true\" />\n      <option name=\"CONTINUATION_INDENT_IN_IF_CONDITIONS\" value=\"true\" />\n      <option name=\"CONTINUATION_INDENT_IN_ELVIS\" value=\"true\" />\n      <option name=\"WRAP_EXPRESSION_BODY_FUNCTIONS\" value=\"0\" />\n      <option name=\"IF_RPAREN_ON_NEW_LINE\" value=\"false\" />\n      <option name=\"CODE_STYLE_DEFAULTS\" value=\"KOTLIN_OFFICIAL\" />\n    </JetCodeStyleSettings>\n    <codeStyleSettings language=\"XML\">\n      <indentOptions>\n        <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n      </indentOptions>\n      <arrangement>\n        <rules>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>xmlns:android</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>xmlns:.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*:id</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*:name</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>name</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>style</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>^$</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>ANDROID_ATTRIBUTE_ORDER</order>\n            </rule>\n          </section>\n          <section>\n            <rule>\n              <match>\n                <AND>\n                  <NAME>.*</NAME>\n                  <XML_ATTRIBUTE />\n                  <XML_NAMESPACE>.*</XML_NAMESPACE>\n                </AND>\n              </match>\n              <order>BY_NAME</order>\n            </rule>\n          </section>\n        </rules>\n      </arrangement>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"kotlin\">\n      <option name=\"CODE_STYLE_DEFAULTS\" value=\"KOTLIN_OFFICIAL\" />\n      <indentOptions>\n        <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n      </indentOptions>\n    </codeStyleSettings>\n  </code_scheme>\n</component>"
  },
  {
    "path": ".idea/codeStyles/codeStyleConfig.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <state>\n    <option name=\"USE_PER_PROJECT_SETTINGS\" value=\"true\" />\n  </state>\n</component>\n"
  },
  {
    "path": ".idea/copyright/AOSP.xml",
    "content": "<component name=\"CopyrightManager\">\n  <copyright>\n    <option name=\"notice\" value=\"Copyright &amp;#36;today.year The Android Open Source Project&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10;     https://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License.\" />\n    <option name=\"myName\" value=\"AOSP\" />\n  </copyright>\n</component>"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "content": "<component name=\"CopyrightManager\">\n  <settings default=\"AOSP\" />\n</component>"
  },
  {
    "path": ".idea/inspectionProfiles/ktlint.xml",
    "content": "<component name=\"InspectionProjectProfileManager\">\n  <profile version=\"1.0\">\n    <option name=\"myName\" value=\"ktlint\" />\n    <inspection_tool class=\"KotlinUnusedImport\" enabled=\"true\" level=\"ERROR\" enabled_by_default=\"true\" />\n    <inspection_tool class=\"RedundantSemicolon\" enabled=\"true\" level=\"ERROR\" enabled_by_default=\"true\" />\n  </profile>\n</component>\n"
  },
  {
    "path": ".idea/inspectionProfiles/profiles_settings.xml",
    "content": "<component name=\"InspectionProjectProfileManager\">\n  <settings>\n    <option name=\"PROJECT_PROFILE\" value=\"ktlint\" />\n    <version value=\"1.0\" />\n  </settings>\n</component>\n"
  },
  {
    "path": ".idea/kotlinScripting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"KotlinScriptingSettings\">\n    <option name=\"isAutoReloadEnabled\" value=\"true\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/kotlinc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Kotlin2JvmCompilerArguments\">\n    <option name=\"jvmTarget\" value=\"1.8\" />\n  </component>\n  <component name=\"KotlinJpsPluginSettings\">\n    <option name=\"version\" value=\"2.0.20\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer\" />\n        <option value=\"com.intellij.execution.junit.AllInPackageConfigurationProducer\" />\n        <option value=\"com.intellij.execution.junit.PatternConfigurationProducer\" />\n        <option value=\"com.intellij.execution.junit.TestInClassConfigurationProducer\" />\n        <option value=\"com.intellij.execution.junit.UniqueIdConfigurationProducer\" />\n        <option value=\"com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer\" />\n        <option value=\"org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer\" />\n        <option value=\"org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "ASSETS_LICENSE.txt",
    "content": "All font files are licensed under the SIL OPEN FONT LICENSE license. All other files are licensed under the Apache 2 license.\n\n\nSIL OPEN FONT LICENSE\nVersion 1.1 - 26 February 2007\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded,\nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting — in part or in whole — any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE."
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guidelines you need to follow.\n\n## New Features/Libraries\n\nBefore contributing large new features and/or libraries please start a discussion \nwith us first via GitHub Issues and check that we can support it.\nWe are unable to support all new features, even though we wish we could! If we \nare unable to support adding your feature, we always encourage you to open source it \nin your own repository to help the Compose community grow.\n\n## Contributor License Agreement\n\nContributions to this project must be accompanied by a Contributor License\nAgreement. You (or your employer) retain the copyright to your contribution,\nthis simply gives us permission to use and redistribute your contributions as\npart of the project. Head over to <https://cla.developers.google.com/> to see\nyour current agreements on file or to sign a new one.\n\nYou generally only need to submit a CLA once, so if you've already submitted one\n(even if it was for a different project), you probably don't need to do it\nagain.\n\n## Code Reviews\n\nAll submissions, including submissions by project members, require review. We\nuse GitHub pull requests for this purpose. Consult\n[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more\ninformation on using pull requests.\n\n## API Changes\n\nIf you are changing any public APIs, you need to run `./gradlew metalavaGenerateSignatureRelease` which will \nupdate the API signatures.\n\n## Formatting \n\nTo apply formatting, we use spotless. Run `./gradlew :pager:spotlessApply` to format the code according \nto the spec.\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "![Accompanist logo](docs/header.png)\n\nAccompanist is a group of libraries that aim to supplement [Jetpack Compose][compose] with features that are commonly required by developers but not yet available.\n\nAccompanist is a labs like environment for new Compose APIs. We use it to help fill known gaps in the Compose toolkit, experiment with new APIs and to gather insight into the development experience of developing a Compose library. The goal of these libraries is to upstream them into the official toolkit, at which point they will be deprecated and removed from Accompanist.\n\nFor more details like, why does this library exist? Why is it not part of AndroidX? Will you be releasing more libraries? Check out our [Accompanist FAQ](https://medium.com/p/b55117b02712).\n\n## Compose versions\n\nEach [release](https://github.com/google/accompanist/releases) outlines what version of the Compose UI libraries it depends on. We are currently releasing multiple versions of Accompanist for the different versions of Compose:\n\n<table>\n <tr>\n  <td>Compose 1.0 (1.0.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.20\"></td>\n </tr>\n <tr>\n  <td>Compose 1.1 (1.1.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.23\"></td>\n </tr>\n <tr>\n  <td>Compose UI 1.2 (1.2.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.25\"></td>\n </tr>\n <tr>\n  <td>Compose UI 1.3 (1.3.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.28\"></td>\n </tr>\n <tr>\n  <td>Compose UI 1.4 (1.4.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.30\"></td>\n </tr>\n <tr>\n  <td>Compose UI 1.5 (1.5.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.32\"></td>\n </tr>\n <tr>\n  <td>Compose UI 1.6 (1.6.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.34\"></td>\n </tr>\n  <tr>\n  <td>Compose UI 1.7+ (1.7.x)</td><td><img alt=\"Maven Central\" src=\"https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions?versionPrefix=0.37\"></td>\n </tr>\n</table>\n\nFor stable versions of Compose, we use the latest *stable* version of the Compose compiler. For non-stable versions (alpha, beta, etc), we use the latest compiler at the time of release.\n\n> :warning: **Ensure you are using the Accompanist version that matches with your Compose UI version**: If you upgrade Accompanist, it will upgrade your Compose libraries version via transitive dependencies.\n\n## Libraries\n\n### 📫 [Permissions](./permissions/)\nA library that provides [Android runtime permissions][runtimepermissions] support for Jetpack Compose.\n\n### 🖌️ [Drawable Painter](./drawablepainter/)\nA library which provides a way to use Android Drawables as Jetpack Compose Painters.\n\n### 📜 [Adaptive](./adaptive/)\nA library providing a collection of utilities for adaptive layouts.\n\n### 🧭✨[Navigation-Animation](./navigation-animation/) (Deprecated & Removed)\nSee our [Migration Guide](https://google.github.io/accompanist/navigation-animation/) for migrating to using built in support for animations in Jetpack Navigation Compose. \n\n### 🧭🎨️ [Navigation-Material](./navigation-material/) (Deprecated & Removed)\nSee our [Migration Guide](https://google.github.io/accompanist/navigation-material/) for migrating to using built in material-navigation support. \n\n### 🍫 [System UI Controller](./systemuicontroller/) (Deprecated & Removed)\nWe recommend migrating to edge to edge. See our [Migration Guide](https://google.github.io/accompanist/systemuicontroller/) for more details.\n\n---\n\n## Future?\n\nAny of the features available in this group of libraries may become obsolete in the future, at which point they will (probably) become deprecated. \n\nWe will aim to provide a migration path (where possible), to whatever supersedes the functionality.\n\n## Snapshots\n\nSnapshots of the current development version of Accompanist are available, which track the latest commit. See [here](docs/using-snapshot-version.md) for more information. \n\n---\n\n### Why the name?\n\nThe library is all about adding some utilities around Compose. Music composing is done by a\ncomposer, and since this library is about supporting composition, the supporting role of an [accompanist](https://en.wikipedia.org/wiki/Accompaniment) felt like a good name.\n\n## Contributions\n\nPlease contribute! We will gladly review any pull requests.\nMake sure to read the [Contributing](CONTRIBUTING.md) page first though.\n\n## License\n\n```\nCopyright 2020 The Android Open Source Project\n \nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n[appcompat]: https://developer.android.com/jetpack/androidx/releases/appcompat\n[compose]: https://developer.android.com/jetpack/compose\n[snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/\n[mdc]: https://github.com/material-components/material-components-android\n[windowinsets]: https://developer.android.com/reference/kotlin/android/view/WindowInsets\n[viewpager]: https://developer.android.com/reference/kotlin/androidx/viewpager/widget/ViewPager\n[runtimepermissions]: https://developer.android.com/guide/topics/permissions/overview\n"
  },
  {
    "path": "adaptive/README.md",
    "content": "# Adaptive utilities for Jetpack Compose\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-adaptive)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nFor more information, visit the documentation: https://google.github.io/accompanist/adaptive\n\n## Download\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-adaptive:<version>\"\n}\n```\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n  [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-adaptive/"
  },
  {
    "path": "adaptive/api/current.api",
    "content": "// Signature format: 4.0\npackage com.google.accompanist.adaptive {\n\n  public final class DisplayFeaturesKt {\n    method @androidx.compose.runtime.Composable public static java.util.List<androidx.window.layout.DisplayFeature> calculateDisplayFeatures(android.app.Activity activity);\n  }\n\n  public final class FoldAwareColumnKt {\n    method @androidx.compose.runtime.Composable public static void FoldAwareColumn(java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.PaddingValues foldPadding, optional androidx.compose.ui.Alignment.Horizontal horizontalAlignment, kotlin.jvm.functions.Function1<? super com.google.accompanist.adaptive.FoldAwareColumnScope,kotlin.Unit> content);\n  }\n\n  @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FoldAwareColumnScope {\n    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment);\n    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.VerticalAlignmentLine alignmentLine);\n    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.Measured,java.lang.Integer> alignmentLineBlock);\n    method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier ignoreFold(androidx.compose.ui.Modifier);\n  }\n\n  @kotlin.jvm.JvmInline public final value class FoldAwareConfiguration {\n    field public static final com.google.accompanist.adaptive.FoldAwareConfiguration.Companion Companion;\n  }\n\n  public static final class FoldAwareConfiguration.Companion {\n    method public int getAllFolds();\n    method public int getHorizontalFoldsOnly();\n    method public int getVerticalFoldsOnly();\n    property public final int AllFolds;\n    property public final int HorizontalFoldsOnly;\n    property public final int VerticalFoldsOnly;\n  }\n\n  public final class SplitResult {\n    ctor public SplitResult(androidx.compose.foundation.gestures.Orientation gapOrientation, androidx.compose.ui.geometry.Rect gapBounds);\n    method public androidx.compose.ui.geometry.Rect getGapBounds();\n    method public androidx.compose.foundation.gestures.Orientation getGapOrientation();\n    property public final androidx.compose.ui.geometry.Rect gapBounds;\n    property public final androidx.compose.foundation.gestures.Orientation gapOrientation;\n  }\n\n  public final class TwoPaneKt {\n    method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(float splitOffset, optional boolean offsetFromStart, optional float gapWidth);\n    method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(float splitFraction, optional float gapWidth);\n    method @androidx.compose.runtime.Composable public static void TwoPane(kotlin.jvm.functions.Function0<kotlin.Unit> first, kotlin.jvm.functions.Function0<kotlin.Unit> second, com.google.accompanist.adaptive.TwoPaneStrategy strategy, java.util.List<? extends androidx.window.layout.DisplayFeature> displayFeatures, optional androidx.compose.ui.Modifier modifier, optional int foldAwareConfiguration);\n    method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(float splitOffset, optional boolean offsetFromTop, optional float gapHeight);\n    method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(float splitFraction, optional float gapHeight);\n  }\n\n  public fun interface TwoPaneStrategy {\n    method public com.google.accompanist.adaptive.SplitResult calculateSplitResult(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates);\n  }\n\n}\n\n"
  },
  {
    "path": "adaptive/build.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n    alias(libs.plugins.accompanist.android.library)\n    alias(libs.plugins.accompanist.android.library.compose)\n    alias(libs.plugins.accompanist.android.library.published)\n}\n\nandroid {\n    namespace = \"com.google.accompanist.adaptive\"\n\n    sourceSets {\n        named(\"test\") {\n            java.srcDirs(\"src/sharedTest/kotlin\")\n            res.srcDirs(\"src/sharedTest/res\")\n        }\n        named(\"androidTest\") {\n            java.srcDirs(\"src/sharedTest/kotlin\")\n            res.srcDirs(\"src/sharedTest/res\")\n        }\n    }\n}\n\ndependencies {\n    api(libs.compose.foundation.foundation)\n    api(libs.compose.ui.ui)\n    api(libs.androidx.window)\n    \n    implementation(libs.kotlin.coroutines.android)\n    implementation(libs.compose.ui.util)\n\n    // ======================\n    // Test dependencies\n    // ======================\n\n    androidTestImplementation(project(\":internal-testutils\"))\n    testImplementation(project(\":internal-testutils\"))\n\n    androidTestImplementation(libs.junit)\n    testImplementation(libs.junit)\n\n    androidTestImplementation(libs.truth)\n    testImplementation(libs.truth)\n\n    androidTestImplementation(libs.compose.ui.test.junit4)\n    testImplementation(libs.compose.ui.test.junit4)\n\n    androidTestImplementation(libs.compose.ui.test.manifest)\n    testImplementation(libs.compose.ui.test.manifest)\n\n    androidTestImplementation(libs.androidx.test.runner)\n    testImplementation(libs.androidx.test.runner)\n\n    androidTestImplementation(libs.androidx.window.testing)\n    testImplementation(libs.androidx.window.testing)\n\n    testImplementation(libs.robolectric)\n}\n"
  },
  {
    "path": "adaptive/gradle.properties",
    "content": "POM_ARTIFACT_ID=accompanist-adaptive\nPOM_NAME=Accompanist Adaptive library\nPOM_PACKAGING=aar"
  },
  {
    "path": "adaptive/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ Copyright 2022 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest />\n"
  },
  {
    "path": "adaptive/src/main/java/com/google/accompanist/adaptive/DisplayFeatures.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport android.app.Activity\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.produceState\nimport androidx.compose.runtime.remember\nimport androidx.window.layout.DisplayFeature\nimport androidx.window.layout.WindowInfoTracker\n\n/**\n * Calculates the list of [DisplayFeature]s from the given [activity].\n */\n@Composable\npublic fun calculateDisplayFeatures(activity: Activity): List<DisplayFeature> {\n    val windowLayoutInfo = remember(activity) {\n        WindowInfoTracker.getOrCreate(activity).windowLayoutInfo(activity)\n    }\n    val displayFeatures by produceState(\n        initialValue = emptyList<DisplayFeature>(),\n        key1 = windowLayoutInfo\n    ) {\n        windowLayoutInfo.collect { info ->\n            value = info.displayFeatures\n        }\n    }\n\n    return displayFeatures\n}\n"
  },
  {
    "path": "adaptive/src/main/java/com/google/accompanist/adaptive/FoldAwareColumn.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport android.util.Range\nimport androidx.annotation.VisibleForTesting\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.ExperimentalComposeUiApi\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.layout.IntrinsicMeasurable\nimport androidx.compose.ui.layout.IntrinsicMeasureScope\nimport androidx.compose.ui.layout.Layout\nimport androidx.compose.ui.layout.LayoutCoordinates\nimport androidx.compose.ui.layout.Measurable\nimport androidx.compose.ui.layout.MeasurePolicy\nimport androidx.compose.ui.layout.MeasureResult\nimport androidx.compose.ui.layout.MeasureScope\nimport androidx.compose.ui.layout.ParentDataModifier\nimport androidx.compose.ui.layout.Placeable\nimport androidx.compose.ui.layout.boundsInRoot\nimport androidx.compose.ui.layout.boundsInWindow\nimport androidx.compose.ui.layout.findRootCoordinates\nimport androidx.compose.ui.platform.InspectorInfo\nimport androidx.compose.ui.platform.InspectorValueInfo\nimport androidx.compose.ui.unit.Constraints\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.LayoutDirection\nimport androidx.window.layout.DisplayFeature\nimport androidx.window.layout.FoldingFeature\nimport kotlin.math.roundToInt\n\n/**\n * A simplified version of [Column] that places children in a fold-aware manner.\n *\n * The layout starts placing children from the top of the available space. If there is a horizontal\n * [separating](https://developer.android.com/reference/kotlin/androidx/window/layout/FoldingFeature#isSeparating())\n * fold present in the window, then the layout will check to see if any children overlap the fold.\n * If a child would overlap the fold in its current position, then the layout will increase its\n * y coordinate so that the child is now placed below the fold, and any subsequent children will\n * also be placed below the fold.\n *\n *\n * @param displayFeatures a list of display features the device currently has\n * @param modifier an optional modifier for the layout\n * @param foldPadding the optional padding to add around a fold\n * @param horizontalAlignment the horizontal alignment of the layout's children.\n */\n@Composable\npublic fun FoldAwareColumn(\n    displayFeatures: List<DisplayFeature>,\n    modifier: Modifier = Modifier,\n    foldPadding: PaddingValues = PaddingValues(),\n    horizontalAlignment: Alignment.Horizontal = Alignment.Start,\n    content: @Composable FoldAwareColumnScope.() -> Unit,\n) {\n    Layout(\n        modifier = modifier,\n        measurePolicy = foldAwareColumnMeasurePolicy(\n            verticalArrangement = Arrangement.Top,\n            horizontalAlignment = horizontalAlignment,\n            fold = {\n                // Extract folding feature if horizontal and separating\n                displayFeatures.find {\n                    it is FoldingFeature && it.orientation == FoldingFeature.Orientation.HORIZONTAL &&\n                        it.isSeparating\n                } as FoldingFeature?\n            },\n            foldPadding = foldPadding,\n        ),\n        content = { FoldAwareColumnScopeInstance.content() }\n    )\n}\n\n/**\n * FoldAwareColumn version of [rowColumnMeasurePolicy] that uses [FoldAwareColumnMeasurementHelper.foldAwarePlaceHelper]\n * method instead of [RowColumnMeasurementHelper.placeHelper]\n */\n// TODO: change from internal to private once metalava issue is solved https://issuetracker.google.com/issues/271539608\n@Composable\ninternal fun foldAwareColumnMeasurePolicy(\n    verticalArrangement: Arrangement.Vertical,\n    horizontalAlignment: Alignment.Horizontal,\n    fold: () -> FoldingFeature?,\n    foldPadding: PaddingValues\n) = remember(verticalArrangement, horizontalAlignment, fold, foldPadding) {\n\n    val orientation = LayoutOrientation.Vertical\n    val arrangement: (Int, IntArray, LayoutDirection, Density, IntArray) -> Unit =\n        { totalSize, size, _, density, outPosition ->\n            with(verticalArrangement) { density.arrange(totalSize, size, outPosition) }\n        }\n    val arrangementSpacing = verticalArrangement.spacing\n    val crossAxisAlignment = CrossAxisAlignment.horizontal(horizontalAlignment)\n    val crossAxisSize = SizeMode.Wrap\n\n    object : MeasurePolicy {\n        override fun MeasureScope.measure(\n            measurables: List<Measurable>,\n            constraints: Constraints\n        ): MeasureResult {\n            val placeables = arrayOfNulls<Placeable?>(measurables.size)\n            val rowColumnMeasureHelper =\n                FoldAwareColumnMeasurementHelper(\n                    orientation,\n                    arrangement,\n                    arrangementSpacing,\n                    crossAxisSize,\n                    crossAxisAlignment,\n                    measurables,\n                    placeables\n                )\n\n            val measureResult = rowColumnMeasureHelper\n                .measureWithoutPlacing(\n                    this,\n                    constraints, 0, measurables.size\n                )\n\n            val layoutWidth: Int\n            val layoutHeight: Int\n            if (orientation == LayoutOrientation.Horizontal) {\n                layoutWidth = measureResult.mainAxisSize\n                layoutHeight = measureResult.crossAxisSize\n            } else {\n                layoutWidth = measureResult.crossAxisSize\n                layoutHeight = measureResult.mainAxisSize\n            }\n\n            // Calculate fold bounds in pixels (including any added fold padding)\n            val foldBoundsPx = with(density) {\n                val topPaddingPx = foldPadding.calculateTopPadding().roundToPx()\n                val bottomPaddingPx = foldPadding.calculateBottomPadding().roundToPx()\n\n                fold()?.bounds?.let {\n                    Rect(\n                        left = it.left.toFloat(),\n                        top = it.top.toFloat() - topPaddingPx,\n                        right = it.right.toFloat(),\n                        bottom = it.bottom.toFloat() + bottomPaddingPx\n                    )\n                }\n            }\n\n            // We only know how much padding is added inside the placement scope, so just add fold height\n            // and height of the largest child when laying out to cover the maximum possible height\n            val heightPadding = foldBoundsPx?.let { bounds ->\n                val largestChildHeight = rowColumnMeasureHelper.placeables.maxOfOrNull {\n                    if ((it?.parentData as? RowColumnParentData)?.ignoreFold == true) {\n                        0\n                    } else {\n                        it?.height ?: 0\n                    }\n                } ?: 0\n                bounds.height.roundToInt() + largestChildHeight\n            } ?: 0\n            val paddedLayoutHeight = layoutHeight + heightPadding\n\n            return layout(layoutWidth, paddedLayoutHeight) {\n                rowColumnMeasureHelper.foldAwarePlaceHelper(\n                    this,\n                    measureResult,\n                    0,\n                    layoutDirection,\n                    foldBoundsPx\n                )\n            }\n        }\n\n        override fun IntrinsicMeasureScope.minIntrinsicWidth(\n            measurables: List<IntrinsicMeasurable>,\n            height: Int\n        ) = MinIntrinsicWidthMeasureBlock(orientation)(\n            measurables,\n            height,\n            arrangementSpacing.roundToPx()\n        )\n\n        override fun IntrinsicMeasureScope.minIntrinsicHeight(\n            measurables: List<IntrinsicMeasurable>,\n            width: Int\n        ) = MinIntrinsicHeightMeasureBlock(orientation)(\n            measurables,\n            width,\n            arrangementSpacing.roundToPx()\n        )\n\n        override fun IntrinsicMeasureScope.maxIntrinsicWidth(\n            measurables: List<IntrinsicMeasurable>,\n            height: Int\n        ) = MaxIntrinsicWidthMeasureBlock(orientation)(\n            measurables,\n            height,\n            arrangementSpacing.roundToPx()\n        )\n\n        override fun IntrinsicMeasureScope.maxIntrinsicHeight(\n            measurables: List<IntrinsicMeasurable>,\n            width: Int\n        ) = MaxIntrinsicHeightMeasureBlock(orientation)(\n            measurables,\n            width,\n            arrangementSpacing.roundToPx()\n        )\n    }\n}\n\n/**\n * Inherits from [RowColumnMeasurementHelper] to place children in a fold-aware manner\n */\nprivate class FoldAwareColumnMeasurementHelper(\n    orientation: LayoutOrientation,\n    arrangement: (Int, IntArray, LayoutDirection, Density, IntArray) -> Unit,\n    arrangementSpacing: Dp,\n    crossAxisSize: SizeMode,\n    crossAxisAlignment: CrossAxisAlignment,\n    measurables: List<Measurable>,\n    placeables: Array<Placeable?>\n) : RowColumnMeasurementHelper(\n    orientation,\n    arrangement,\n    arrangementSpacing,\n    crossAxisSize,\n    crossAxisAlignment,\n    measurables,\n    placeables\n) {\n    /**\n     * Copy of [placeHelper] that has been modified for FoldAwareColumn implementation\n     */\n    @OptIn(ExperimentalComposeUiApi::class)\n    fun foldAwarePlaceHelper(\n        placeableScope: Placeable.PlacementScope,\n        measureResult: RowColumnMeasureHelperResult,\n        crossAxisOffset: Int,\n        layoutDirection: LayoutDirection,\n        foldBoundsPx: Rect?\n    ) {\n        with(placeableScope) {\n            val layoutBounds = coordinates!!.trueBoundsInWindow()\n\n            var placeableY = 0\n\n            for (i in measureResult.startIndex until measureResult.endIndex) {\n                val placeable = placeables[i]!!\n                val mainAxisPositions = measureResult.mainAxisPositions\n                val crossAxisPosition = getCrossAxisPosition(\n                    placeable,\n                    (measurables[i].parentData as? RowColumnParentData),\n                    measureResult.crossAxisSize,\n                    layoutDirection,\n                    measureResult.beforeCrossAxisAlignmentLine\n                ) + crossAxisOffset\n                if (orientation == LayoutOrientation.Horizontal) {\n                    placeable.place(\n                        mainAxisPositions[i - measureResult.startIndex],\n                        crossAxisPosition\n                    )\n                } else {\n                    val relativeBounds = Rect(\n                        left = 0f,\n                        top = placeableY.toFloat(),\n                        right = placeable.width.toFloat(),\n                        bottom = (placeableY + placeable.height).toFloat()\n                    )\n                    val absoluteBounds =\n                        relativeBounds.translate(layoutBounds.left, layoutBounds.top)\n\n                    // If placeable overlaps fold, push placeable below\n                    if (foldBoundsPx?.overlapsVertically(absoluteBounds) == true &&\n                        (placeable.parentData as? RowColumnParentData)?.ignoreFold != true\n                    ) {\n                        placeableY = (foldBoundsPx.bottom - layoutBounds.top).toInt()\n                    }\n\n                    placeable.place(crossAxisPosition, placeableY)\n\n                    placeableY += placeable.height\n                }\n            }\n        }\n    }\n}\n\n/**\n * Copy of original [LayoutCoordinates.boundsInWindow], but without the nonzero dimension check.\n *\n * Instead of returning [Rect.Zero] for a layout with zero width/height, this method will still\n * return a Rect with the layout's bounds.\n */\n@VisibleForTesting\ninternal fun LayoutCoordinates.trueBoundsInWindow(): Rect {\n    val root = findRootCoordinates()\n    val bounds = boundsInRoot()\n    val rootWidth = root.size.width.toFloat()\n    val rootHeight = root.size.height.toFloat()\n\n    val boundsLeft = bounds.left.coerceIn(0f, rootWidth)\n    val boundsTop = bounds.top.coerceIn(0f, rootHeight)\n    val boundsRight = bounds.right.coerceIn(0f, rootWidth)\n    val boundsBottom = bounds.bottom.coerceIn(0f, rootHeight)\n\n    val topLeft = root.localToWindow(Offset(boundsLeft, boundsTop))\n    val topRight = root.localToWindow(Offset(boundsRight, boundsTop))\n    val bottomRight = root.localToWindow(Offset(boundsRight, boundsBottom))\n    val bottomLeft = root.localToWindow(Offset(boundsLeft, boundsBottom))\n\n    val left = minOf(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)\n    val top = minOf(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)\n    val right = maxOf(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)\n    val bottom = maxOf(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)\n\n    return Rect(left, top, right, bottom)\n}\n\n/**\n * Checks if the vertical ranges of the two Rects overlap (inclusive)\n */\nprivate fun Rect.overlapsVertically(other: Rect): Boolean {\n    val verticalRange = Range(top, bottom)\n    val otherVerticalRange = Range(other.top, other.bottom)\n    return verticalRange.overlaps(otherVerticalRange)\n}\n\n/**\n * Inclusive check to see if the given float ranges overlap\n */\nprivate fun Range<Float>.overlaps(other: Range<Float>): Boolean {\n    return (lower >= other.lower && lower <= other.upper) || (upper >= other.lower && upper <= other.upper)\n}\n\n/**\n * Copy of [RowColumnParentData] that has been modified to include the new ignoreFold field.\n */\ninternal data class RowColumnParentData(\n    var weight: Float = 0f,\n    var fill: Boolean = true,\n    var crossAxisAlignment: CrossAxisAlignment? = null,\n    var ignoreFold: Boolean = false\n)\n\ninternal class IgnoreFoldModifier(\n    inspectorInfo: InspectorInfo.() -> Unit\n) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {\n    override fun Density.modifyParentData(parentData: Any?) =\n        ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {\n            it.ignoreFold = true\n        }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        return other is IgnoreFoldModifier\n    }\n\n    override fun hashCode(): Int {\n        return 0\n    }\n\n    override fun toString(): String =\n        \"IgnoreFoldModifier(ignoreFold=true)\"\n}\n"
  },
  {
    "path": "adaptive/src/main/java/com/google/accompanist/adaptive/FoldAwareColumnScope.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.ColumnScope\nimport androidx.compose.foundation.layout.LayoutScopeMarker\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.Stable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.layout.Measured\nimport androidx.compose.ui.layout.VerticalAlignmentLine\nimport androidx.compose.ui.platform.debugInspectorInfo\n\n/**\n * Copy of [ColumnScope] that excludes the weight Modifier attribute.\n *\n * Also adds a new [ignoreFold] Modifier attribute.\n */\n@LayoutScopeMarker\n@Immutable\npublic interface FoldAwareColumnScope {\n    /**\n     * Ignore the fold when placing this child within the [FoldAwareColumn].\n     */\n    @Stable\n    public fun Modifier.ignoreFold(): Modifier\n\n    /**\n     * Align the element horizontally within the [Column]. This alignment will have priority over\n     * the [Column]'s `horizontalAlignment` parameter.\n     *\n     * Example usage:\n     * @sample androidx.compose.foundation.layout.samples.SimpleAlignInColumn\n     */\n    @Stable\n    public fun Modifier.align(alignment: Alignment.Horizontal): Modifier\n\n    /**\n     * Position the element horizontally such that its [alignmentLine] aligns with sibling elements\n     * also configured to [alignBy]. [alignBy] is a form of [align],\n     * so both modifiers will not work together if specified for the same layout.\n     * Within a [Column], all components with [alignBy] will align horizontally using\n     * the specified [VerticalAlignmentLine]s or values provided using the other\n     * [alignBy] overload, forming a sibling group.\n     * At least one element of the sibling group will be placed as it had [Alignment.Start] align\n     * in [Column], and the alignment of the other siblings will be then determined such that\n     * the alignment lines coincide. Note that if only one element in a [Column] has the\n     * [alignBy] modifier specified the element will be positioned\n     * as if it had [Alignment.Start] align.\n     *\n     * Example usage:\n     * @sample androidx.compose.foundation.layout.samples.SimpleRelativeToSiblingsInColumn\n     */\n    @Stable\n    public fun Modifier.alignBy(alignmentLine: VerticalAlignmentLine): Modifier\n\n    /**\n     * Position the element horizontally such that the alignment line for the content as\n     * determined by [alignmentLineBlock] aligns with sibling elements also configured to\n     * [alignBy]. [alignBy] is a form of [align], so both modifiers\n     * will not work together if specified for the same layout.\n     * Within a [Column], all components with [alignBy] will align horizontally using\n     * the specified [VerticalAlignmentLine]s or values obtained from [alignmentLineBlock],\n     * forming a sibling group.\n     * At least one element of the sibling group will be placed as it had [Alignment.Start] align\n     * in [Column], and the alignment of the other siblings will be then determined such that\n     * the alignment lines coincide. Note that if only one element in a [Column] has the\n     * [alignBy] modifier specified the element will be positioned\n     * as if it had [Alignment.Start] align.\n     *\n     * Example usage:\n     * @sample androidx.compose.foundation.layout.samples.SimpleRelativeToSiblings\n     */\n    @Stable\n    public fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int): Modifier\n}\n\ninternal object FoldAwareColumnScopeInstance : FoldAwareColumnScope {\n    @Stable\n    override fun Modifier.ignoreFold() = this.then(\n        IgnoreFoldModifier(\n            inspectorInfo = debugInspectorInfo {\n                name = \"ignoreFold\"\n                value = true\n            }\n        )\n    )\n\n    @Stable\n    override fun Modifier.align(alignment: Alignment.Horizontal) = this.then(\n        HorizontalAlignModifier(\n            horizontal = alignment,\n            inspectorInfo = debugInspectorInfo {\n                name = \"align\"\n                value = alignment\n            }\n        )\n    )\n\n    @Stable\n    override fun Modifier.alignBy(alignmentLine: VerticalAlignmentLine) = this.then(\n        SiblingsAlignedModifier.WithAlignmentLine(\n            alignmentLine = alignmentLine,\n            inspectorInfo = debugInspectorInfo {\n                name = \"alignBy\"\n                value = alignmentLine\n            }\n        )\n    )\n\n    @Stable\n    override fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int) = this.then(\n        SiblingsAlignedModifier.WithAlignmentLineBlock(\n            block = alignmentLineBlock,\n            inspectorInfo = debugInspectorInfo {\n                name = \"alignBy\"\n                value = alignmentLineBlock\n            }\n        )\n    )\n}\n"
  },
  {
    "path": "adaptive/src/main/java/com/google/accompanist/adaptive/RowColumnImpl.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.Stable\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.layout.AlignmentLine\nimport androidx.compose.ui.layout.IntrinsicMeasurable\nimport androidx.compose.ui.layout.Measured\nimport androidx.compose.ui.layout.ParentDataModifier\nimport androidx.compose.ui.layout.Placeable\nimport androidx.compose.ui.platform.InspectorInfo\nimport androidx.compose.ui.platform.InspectorValueInfo\nimport androidx.compose.ui.unit.Constraints\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.LayoutDirection\nimport androidx.compose.ui.util.fastForEach\nimport com.google.accompanist.adaptive.LayoutOrientation.Horizontal\nimport com.google.accompanist.adaptive.LayoutOrientation.Vertical\nimport kotlin.math.max\nimport kotlin.math.min\nimport kotlin.math.roundToInt\n\n/**\n * Copied from:\n * RowColumnImpl.kt\n * https://android-review.googlesource.com/c/platform/frameworks/support/+/2260390/27/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt\n *\n * The only changes were updating access modifiers and removing unused code\n */\n\n/**\n * [Row] will be [Horizontal], [Column] is [Vertical].\n */\ninternal enum class LayoutOrientation {\n    Horizontal,\n    Vertical\n}\n\n/**\n * Used to specify the alignment of a layout's children, in cross axis direction.\n */\n@Immutable\ninternal sealed class CrossAxisAlignment {\n    /**\n     * Aligns to [size]. If this is a vertical alignment, [layoutDirection] should be\n     * [LayoutDirection.Ltr].\n     *\n     * @param size The remaining space (total size - content size) in the container.\n     * @param layoutDirection The layout direction of the content if horizontal or\n     * [LayoutDirection.Ltr] if vertical.\n     * @param placeable The item being aligned.\n     * @param beforeCrossAxisAlignmentLine The space before the cross-axis alignment line if\n     * an alignment line is being used or 0 if no alignment line is being used.\n     */\n    internal abstract fun align(\n        size: Int,\n        layoutDirection: LayoutDirection,\n        placeable: Placeable,\n        beforeCrossAxisAlignmentLine: Int\n    ): Int\n\n    /**\n     * Returns `true` if this is [Relative].\n     */\n    internal open val isRelative: Boolean\n        get() = false\n\n    /**\n     * Returns the alignment line position relative to the left/top of the space or `null` if\n     * this alignment doesn't rely on alignment lines.\n     */\n    internal open fun calculateAlignmentLinePosition(placeable: Placeable): Int? = null\n\n    companion object {\n        /**\n         * Place children such that their center is in the middle of the cross axis.\n         */\n        @Stable\n        val Center: CrossAxisAlignment = CenterCrossAxisAlignment\n\n        /**\n         * Place children such that their start edge is aligned to the start edge of the cross\n         * axis. TODO(popam): Consider rtl directionality.\n         */\n        @Stable\n        val Start: CrossAxisAlignment = StartCrossAxisAlignment\n\n        /**\n         * Place children such that their end edge is aligned to the end edge of the cross\n         * axis. TODO(popam): Consider rtl directionality.\n         */\n        @Stable\n        val End: CrossAxisAlignment = EndCrossAxisAlignment\n\n        /**\n         * Align children by their baseline.\n         */\n        fun AlignmentLine(alignmentLine: AlignmentLine): CrossAxisAlignment =\n            AlignmentLineCrossAxisAlignment(AlignmentLineProvider.Value(alignmentLine))\n\n        /**\n         * Align children relative to their siblings using the alignment line provided as a\n         * parameter using [AlignmentLineProvider].\n         */\n        internal fun Relative(alignmentLineProvider: AlignmentLineProvider): CrossAxisAlignment =\n            AlignmentLineCrossAxisAlignment(alignmentLineProvider)\n\n        /**\n         * Align children with vertical alignment.\n         */\n        internal fun vertical(vertical: Alignment.Vertical): CrossAxisAlignment =\n            VerticalCrossAxisAlignment(vertical)\n\n        /**\n         * Align children with horizontal alignment.\n         */\n        internal fun horizontal(horizontal: Alignment.Horizontal): CrossAxisAlignment =\n            HorizontalCrossAxisAlignment(horizontal)\n    }\n\n    private object CenterCrossAxisAlignment : CrossAxisAlignment() {\n        override fun align(\n            size: Int,\n            layoutDirection: LayoutDirection,\n            placeable: Placeable,\n            beforeCrossAxisAlignmentLine: Int\n        ): Int {\n            return size / 2\n        }\n    }\n\n    private object StartCrossAxisAlignment : CrossAxisAlignment() {\n        override fun align(\n            size: Int,\n            layoutDirection: LayoutDirection,\n            placeable: Placeable,\n            beforeCrossAxisAlignmentLine: Int\n        ): Int {\n            return if (layoutDirection == LayoutDirection.Ltr) 0 else size\n        }\n    }\n\n    private object EndCrossAxisAlignment : CrossAxisAlignment() {\n        override fun align(\n            size: Int,\n            layoutDirection: LayoutDirection,\n            placeable: Placeable,\n            beforeCrossAxisAlignmentLine: Int\n        ): Int {\n            return if (layoutDirection == LayoutDirection.Ltr) size else 0\n        }\n    }\n\n    private class AlignmentLineCrossAxisAlignment(\n        val alignmentLineProvider: AlignmentLineProvider\n    ) : CrossAxisAlignment() {\n        override val isRelative: Boolean\n            get() = true\n\n        override fun calculateAlignmentLinePosition(placeable: Placeable): Int {\n            return alignmentLineProvider.calculateAlignmentLinePosition(placeable)\n        }\n\n        override fun align(\n            size: Int,\n            layoutDirection: LayoutDirection,\n            placeable: Placeable,\n            beforeCrossAxisAlignmentLine: Int\n        ): Int {\n            val alignmentLinePosition =\n                alignmentLineProvider.calculateAlignmentLinePosition(placeable)\n            return if (alignmentLinePosition != AlignmentLine.Unspecified) {\n                val line = beforeCrossAxisAlignmentLine - alignmentLinePosition\n                if (layoutDirection == LayoutDirection.Rtl) {\n                    size - line\n                } else {\n                    line\n                }\n            } else {\n                0\n            }\n        }\n    }\n\n    private class VerticalCrossAxisAlignment(\n        val vertical: Alignment.Vertical\n    ) : CrossAxisAlignment() {\n        override fun align(\n            size: Int,\n            layoutDirection: LayoutDirection,\n            placeable: Placeable,\n            beforeCrossAxisAlignmentLine: Int\n        ): Int {\n            return vertical.align(0, size)\n        }\n    }\n\n    private class HorizontalCrossAxisAlignment(\n        val horizontal: Alignment.Horizontal\n    ) : CrossAxisAlignment() {\n        override fun align(\n            size: Int,\n            layoutDirection: LayoutDirection,\n            placeable: Placeable,\n            beforeCrossAxisAlignmentLine: Int\n        ): Int {\n            return horizontal.align(0, size, layoutDirection)\n        }\n    }\n}\n\n/**\n * Box [Constraints], but which abstract away width and height in favor of main axis and cross axis.\n */\ninternal data class OrientationIndependentConstraints(\n    val mainAxisMin: Int,\n    val mainAxisMax: Int,\n    val crossAxisMin: Int,\n    val crossAxisMax: Int\n) {\n    constructor(c: Constraints, orientation: LayoutOrientation) : this(\n        if (orientation === Horizontal) c.minWidth else c.minHeight,\n        if (orientation === Horizontal) c.maxWidth else c.maxHeight,\n        if (orientation === Horizontal) c.minHeight else c.minWidth,\n        if (orientation === Horizontal) c.maxHeight else c.maxWidth\n    )\n\n    // Creates a new instance with the same main axis constraints and maximum tight cross axis.\n    fun stretchCrossAxis() = OrientationIndependentConstraints(\n        mainAxisMin,\n        mainAxisMax,\n        if (crossAxisMax != Constraints.Infinity) crossAxisMax else crossAxisMin,\n        crossAxisMax\n    )\n\n    // Given an orientation, resolves the current instance to traditional constraints.\n    fun toBoxConstraints(orientation: LayoutOrientation) =\n        if (orientation === Horizontal) {\n            Constraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)\n        } else {\n            Constraints(crossAxisMin, crossAxisMax, mainAxisMin, mainAxisMax)\n        }\n\n    // Given an orientation, resolves the max width constraint this instance represents.\n    fun maxWidth(orientation: LayoutOrientation) =\n        if (orientation === Horizontal) {\n            mainAxisMax\n        } else {\n            crossAxisMax\n        }\n\n    // Given an orientation, resolves the max height constraint this instance represents.\n    fun maxHeight(orientation: LayoutOrientation) =\n        if (orientation === Horizontal) {\n            crossAxisMax\n        } else {\n            mainAxisMax\n        }\n}\n\ninternal val IntrinsicMeasurable.rowColumnParentData: RowColumnParentData?\n    get() = parentData as? RowColumnParentData\n\ninternal val RowColumnParentData?.weight: Float\n    get() = this?.weight ?: 0f\n\ninternal val RowColumnParentData?.fill: Boolean\n    get() = this?.fill ?: true\n\ninternal val RowColumnParentData?.crossAxisAlignment: CrossAxisAlignment?\n    get() = this?.crossAxisAlignment\n\ninternal val RowColumnParentData?.isRelative: Boolean\n    get() = this.crossAxisAlignment?.isRelative ?: false\n\ninternal fun MinIntrinsicWidthMeasureBlock(orientation: LayoutOrientation) =\n    if (orientation == Horizontal) {\n        IntrinsicMeasureBlocks.HorizontalMinWidth\n    } else {\n        IntrinsicMeasureBlocks.VerticalMinWidth\n    }\n\ninternal fun MinIntrinsicHeightMeasureBlock(orientation: LayoutOrientation) =\n    if (orientation == Horizontal) {\n        IntrinsicMeasureBlocks.HorizontalMinHeight\n    } else {\n        IntrinsicMeasureBlocks.VerticalMinHeight\n    }\n\ninternal fun MaxIntrinsicWidthMeasureBlock(orientation: LayoutOrientation) =\n    if (orientation == Horizontal) {\n        IntrinsicMeasureBlocks.HorizontalMaxWidth\n    } else {\n        IntrinsicMeasureBlocks.VerticalMaxWidth\n    }\n\ninternal fun MaxIntrinsicHeightMeasureBlock(orientation: LayoutOrientation) =\n    if (orientation == Horizontal) {\n        IntrinsicMeasureBlocks.HorizontalMaxHeight\n    } else {\n        IntrinsicMeasureBlocks.VerticalMaxHeight\n    }\n\ninternal object IntrinsicMeasureBlocks {\n    val HorizontalMinWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableHeight, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { h -> minIntrinsicWidth(h) },\n                { w -> maxIntrinsicHeight(w) },\n                availableHeight,\n                mainAxisSpacing,\n                Horizontal,\n                Horizontal\n            )\n        }\n    val VerticalMinWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableHeight, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { h -> minIntrinsicWidth(h) },\n                { w -> maxIntrinsicHeight(w) },\n                availableHeight,\n                mainAxisSpacing,\n                Vertical,\n                Horizontal\n            )\n        }\n    val HorizontalMinHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableWidth, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { w -> minIntrinsicHeight(w) },\n                { h -> maxIntrinsicWidth(h) },\n                availableWidth,\n                mainAxisSpacing,\n                Horizontal,\n                Vertical\n            )\n        }\n    val VerticalMinHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableWidth, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { w -> minIntrinsicHeight(w) },\n                { h -> maxIntrinsicWidth(h) },\n                availableWidth,\n                mainAxisSpacing,\n                Vertical,\n                Vertical\n            )\n        }\n    val HorizontalMaxWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableHeight, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { h -> maxIntrinsicWidth(h) },\n                { w -> maxIntrinsicHeight(w) },\n                availableHeight,\n                mainAxisSpacing,\n                Horizontal,\n                Horizontal\n            )\n        }\n    val VerticalMaxWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableHeight, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { h -> maxIntrinsicWidth(h) },\n                { w -> maxIntrinsicHeight(w) },\n                availableHeight,\n                mainAxisSpacing,\n                Vertical,\n                Horizontal\n            )\n        }\n    val HorizontalMaxHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableWidth, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { w -> maxIntrinsicHeight(w) },\n                { h -> maxIntrinsicWidth(h) },\n                availableWidth,\n                mainAxisSpacing,\n                Horizontal,\n                Vertical\n            )\n        }\n    val VerticalMaxHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =\n        { measurables, availableWidth, mainAxisSpacing ->\n            intrinsicSize(\n                measurables,\n                { w -> maxIntrinsicHeight(w) },\n                { h -> maxIntrinsicWidth(h) },\n                availableWidth,\n                mainAxisSpacing,\n                Vertical,\n                Vertical\n            )\n        }\n}\n\nprivate fun intrinsicSize(\n    children: List<IntrinsicMeasurable>,\n    intrinsicMainSize: IntrinsicMeasurable.(Int) -> Int,\n    intrinsicCrossSize: IntrinsicMeasurable.(Int) -> Int,\n    crossAxisAvailable: Int,\n    mainAxisSpacing: Int,\n    layoutOrientation: LayoutOrientation,\n    intrinsicOrientation: LayoutOrientation\n) = if (layoutOrientation == intrinsicOrientation) {\n    intrinsicMainAxisSize(children, intrinsicMainSize, crossAxisAvailable, mainAxisSpacing)\n} else {\n    intrinsicCrossAxisSize(\n        children,\n        intrinsicCrossSize,\n        intrinsicMainSize,\n        crossAxisAvailable,\n        mainAxisSpacing\n    )\n}\n\nprivate fun intrinsicMainAxisSize(\n    children: List<IntrinsicMeasurable>,\n    mainAxisSize: IntrinsicMeasurable.(Int) -> Int,\n    crossAxisAvailable: Int,\n    mainAxisSpacing: Int\n): Int {\n    var weightUnitSpace = 0\n    var fixedSpace = 0\n    var totalWeight = 0f\n    children.fastForEach { child ->\n        val weight = child.rowColumnParentData.weight\n        val size = child.mainAxisSize(crossAxisAvailable)\n        if (weight == 0f) {\n            fixedSpace += size\n        } else if (weight > 0f) {\n            totalWeight += weight\n            weightUnitSpace = max(weightUnitSpace, (size / weight).roundToInt())\n        }\n    }\n    return (weightUnitSpace * totalWeight).roundToInt() + fixedSpace +\n        (children.size - 1) * mainAxisSpacing\n}\n\nprivate fun intrinsicCrossAxisSize(\n    children: List<IntrinsicMeasurable>,\n    mainAxisSize: IntrinsicMeasurable.(Int) -> Int,\n    crossAxisSize: IntrinsicMeasurable.(Int) -> Int,\n    mainAxisAvailable: Int,\n    mainAxisSpacing: Int\n): Int {\n    var fixedSpace = min((children.size - 1) * mainAxisSpacing, mainAxisAvailable)\n    var crossAxisMax = 0\n    var totalWeight = 0f\n    children.fastForEach { child ->\n        val weight = child.rowColumnParentData.weight\n        if (weight == 0f) {\n            // Ask the child how much main axis space it wants to occupy. This cannot be more\n            // than the remaining available space.\n            val mainAxisSpace = min(\n                child.mainAxisSize(Constraints.Infinity),\n                mainAxisAvailable - fixedSpace\n            )\n            fixedSpace += mainAxisSpace\n            // Now that the assigned main axis space is known, ask about the cross axis space.\n            crossAxisMax = max(crossAxisMax, child.crossAxisSize(mainAxisSpace))\n        } else if (weight > 0f) {\n            totalWeight += weight\n        }\n    }\n\n    // For weighted children, calculate how much main axis space weight=1 would represent.\n    val weightUnitSpace = if (totalWeight == 0f) {\n        0\n    } else if (mainAxisAvailable == Constraints.Infinity) {\n        Constraints.Infinity\n    } else {\n        (max(mainAxisAvailable - fixedSpace, 0) / totalWeight).roundToInt()\n    }\n\n    children.fastForEach { child ->\n        val weight = child.rowColumnParentData.weight\n        // Now the main axis for weighted children is known, so ask about the cross axis space.\n        if (weight > 0f) {\n            crossAxisMax = max(\n                crossAxisMax,\n                child.crossAxisSize(\n                    if (weightUnitSpace != Constraints.Infinity) {\n                        (weightUnitSpace * weight).roundToInt()\n                    } else {\n                        Constraints.Infinity\n                    }\n                )\n            )\n        }\n    }\n    return crossAxisMax\n}\n\ninternal class LayoutWeightImpl(\n    val weight: Float,\n    val fill: Boolean,\n    inspectorInfo: InspectorInfo.() -> Unit\n) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {\n    override fun Density.modifyParentData(parentData: Any?) =\n        ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {\n            it.weight = weight\n            it.fill = fill\n        }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        val otherModifier = other as? LayoutWeightImpl ?: return false\n        return weight == otherModifier.weight &&\n            fill == otherModifier.fill\n    }\n\n    override fun hashCode(): Int {\n        var result = weight.hashCode()\n        result = 31 * result + fill.hashCode()\n        return result\n    }\n\n    override fun toString(): String =\n        \"LayoutWeightImpl(weight=$weight, fill=$fill)\"\n}\n\ninternal sealed class SiblingsAlignedModifier(\n    inspectorInfo: InspectorInfo.() -> Unit\n) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {\n    abstract override fun Density.modifyParentData(parentData: Any?): Any?\n\n    internal class WithAlignmentLineBlock(\n        val block: (Measured) -> Int,\n        inspectorInfo: InspectorInfo.() -> Unit\n    ) : SiblingsAlignedModifier(inspectorInfo) {\n        override fun Density.modifyParentData(parentData: Any?): Any {\n            return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {\n                it.crossAxisAlignment =\n                    CrossAxisAlignment.Relative(AlignmentLineProvider.Block(block))\n            }\n        }\n\n        override fun equals(other: Any?): Boolean {\n            if (this === other) return true\n            val otherModifier = other as? WithAlignmentLineBlock ?: return false\n            return block == otherModifier.block\n        }\n\n        override fun hashCode(): Int = block.hashCode()\n\n        override fun toString(): String = \"WithAlignmentLineBlock(block=$block)\"\n    }\n\n    internal class WithAlignmentLine(\n        val alignmentLine: AlignmentLine,\n        inspectorInfo: InspectorInfo.() -> Unit\n    ) : SiblingsAlignedModifier(inspectorInfo) {\n        override fun Density.modifyParentData(parentData: Any?): Any {\n            return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {\n                it.crossAxisAlignment =\n                    CrossAxisAlignment.Relative(AlignmentLineProvider.Value(alignmentLine))\n            }\n        }\n\n        override fun equals(other: Any?): Boolean {\n            if (this === other) return true\n            val otherModifier = other as? WithAlignmentLine ?: return false\n            return alignmentLine == otherModifier.alignmentLine\n        }\n\n        override fun hashCode(): Int = alignmentLine.hashCode()\n\n        override fun toString(): String = \"WithAlignmentLine(line=$alignmentLine)\"\n    }\n}\n\ninternal class HorizontalAlignModifier(\n    val horizontal: Alignment.Horizontal,\n    inspectorInfo: InspectorInfo.() -> Unit\n) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {\n    override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {\n        return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {\n            it.crossAxisAlignment = CrossAxisAlignment.horizontal(horizontal)\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        val otherModifier = other as? HorizontalAlignModifier ?: return false\n        return horizontal == otherModifier.horizontal\n    }\n\n    override fun hashCode(): Int = horizontal.hashCode()\n\n    override fun toString(): String =\n        \"HorizontalAlignModifier(horizontal=$horizontal)\"\n}\n\ninternal class VerticalAlignModifier(\n    val vertical: Alignment.Vertical,\n    inspectorInfo: InspectorInfo.() -> Unit\n) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {\n    override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {\n        return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {\n            it.crossAxisAlignment = CrossAxisAlignment.vertical(vertical)\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        val otherModifier = other as? VerticalAlignModifier ?: return false\n        return vertical == otherModifier.vertical\n    }\n\n    override fun hashCode(): Int = vertical.hashCode()\n\n    override fun toString(): String =\n        \"VerticalAlignModifier(vertical=$vertical)\"\n}\n\n/**\n * Provides the alignment line.\n */\ninternal sealed class AlignmentLineProvider {\n    abstract fun calculateAlignmentLinePosition(placeable: Placeable): Int\n    data class Block(val lineProviderBlock: (Measured) -> Int) : AlignmentLineProvider() {\n        override fun calculateAlignmentLinePosition(\n            placeable: Placeable\n        ): Int {\n            return lineProviderBlock(placeable)\n        }\n    }\n\n    data class Value(val alignmentLine: AlignmentLine) : AlignmentLineProvider() {\n        override fun calculateAlignmentLinePosition(placeable: Placeable): Int {\n            return placeable[alignmentLine]\n        }\n    }\n}\n\n/**\n * Used to specify how a layout chooses its own size when multiple behaviors are possible.\n */\n// TODO(popam): remove this when Flow is reworked\ninternal enum class SizeMode {\n    /**\n     * Minimize the amount of free space by wrapping the children,\n     * subject to the incoming layout constraints.\n     */\n    Wrap,\n\n    /**\n     * Maximize the amount of free space by expanding to fill the available space,\n     * subject to the incoming layout constraints.\n     */\n    Expand\n}\n"
  },
  {
    "path": "adaptive/src/main/java/com/google/accompanist/adaptive/RowColumnMeasurementHelper.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport androidx.compose.ui.layout.AlignmentLine\nimport androidx.compose.ui.layout.Measurable\nimport androidx.compose.ui.layout.MeasureScope\nimport androidx.compose.ui.layout.Placeable\nimport androidx.compose.ui.unit.Constraints\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.LayoutDirection\nimport kotlin.math.max\nimport kotlin.math.min\nimport kotlin.math.roundToInt\nimport kotlin.math.sign\n\n/**\n * Copied from:\n * RowColumnMeasurementHelper.kt\n * https://android-review.googlesource.com/c/platform/frameworks/support/+/2260390/27/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnMeasurementHelper.kt\n *\n * The only changes were updating access modifiers and making RowColumnMeasurementHelper an open class\n */\n\n/**\n * This is a data class that holds the determined width, height of a row,\n * and information on how to retrieve main axis and cross axis positions.\n */\ninternal class RowColumnMeasureHelperResult(\n    val crossAxisSize: Int,\n    val mainAxisSize: Int,\n    val startIndex: Int,\n    val endIndex: Int,\n    val beforeCrossAxisAlignmentLine: Int,\n    val mainAxisPositions: IntArray,\n)\n\n/**\n * RowColumnMeasurementHelper\n * Measures the row and column without placing, useful for reusing row/column logic\n */\ninternal open class RowColumnMeasurementHelper(\n    val orientation: LayoutOrientation,\n    val arrangement: (Int, IntArray, LayoutDirection, Density, IntArray) -> Unit,\n    val arrangementSpacing: Dp,\n    val crossAxisSize: SizeMode,\n    val crossAxisAlignment: CrossAxisAlignment,\n    val measurables: List<Measurable>,\n    val placeables: Array<Placeable?>\n) {\n\n    private val rowColumnParentData = Array(measurables.size) {\n        measurables[it].rowColumnParentData\n    }\n\n    fun Placeable.mainAxisSize() =\n        if (orientation == LayoutOrientation.Horizontal) width else height\n\n    fun Placeable.crossAxisSize() =\n        if (orientation == LayoutOrientation.Horizontal) height else width\n\n    /**\n     * Measures the row and column without placing, useful for reusing row/column logic\n     *\n     * @param measureScope The measure scope to retrieve density\n     * @param constraints The desired constraints for the startIndex and endIndex\n     * can hold null items if not measured.\n     * @param startIndex The startIndex (inclusive) when examining measurables, placeable\n     * and parentData\n     * @param endIndex The ending index (exclusive) when examinning measurable, placeable\n     * and parentData\n     */\n    fun measureWithoutPlacing(\n        measureScope: MeasureScope,\n        constraints: Constraints,\n        startIndex: Int,\n        endIndex: Int\n    ): RowColumnMeasureHelperResult {\n        @Suppress(\"NAME_SHADOWING\")\n        val constraints = OrientationIndependentConstraints(constraints, orientation)\n        val arrangementSpacingPx = with(measureScope) {\n            arrangementSpacing.roundToPx()\n        }\n\n        var totalWeight = 0f\n        var fixedSpace = 0\n        var crossAxisSpace = 0\n        var weightChildrenCount = 0\n\n        var anyAlignBy = false\n        val subSize = endIndex - startIndex\n\n        // First measure children with zero weight.\n        var spaceAfterLastNoWeight = 0\n        for (i in startIndex until endIndex) {\n            val child = measurables[i]\n            val parentData = rowColumnParentData[i]\n            val weight = parentData.weight\n\n            if (weight > 0f) {\n                totalWeight += weight\n                ++weightChildrenCount\n            } else {\n                val mainAxisMax = constraints.mainAxisMax\n                val placeable = placeables[i] ?: child.measure(\n                    // Ask for preferred main axis size.\n                    constraints.copy(\n                        mainAxisMin = 0,\n                        mainAxisMax = if (mainAxisMax == Constraints.Infinity) {\n                            Constraints.Infinity\n                        } else {\n                            mainAxisMax - fixedSpace\n                        },\n                        crossAxisMin = 0\n                    ).toBoxConstraints(orientation)\n                )\n                spaceAfterLastNoWeight = min(\n                    arrangementSpacingPx,\n                    mainAxisMax - fixedSpace - placeable.mainAxisSize()\n                )\n                fixedSpace += placeable.mainAxisSize() + spaceAfterLastNoWeight\n                crossAxisSpace = max(crossAxisSpace, placeable.crossAxisSize())\n                anyAlignBy = anyAlignBy || parentData.isRelative\n                placeables[i] = placeable\n            }\n        }\n\n        var weightedSpace = 0\n        if (weightChildrenCount == 0) {\n            // fixedSpace contains an extra spacing after the last non-weight child.\n            fixedSpace -= spaceAfterLastNoWeight\n        } else {\n            // Measure the rest according to their weights in the remaining main axis space.\n            val targetSpace =\n                if (totalWeight > 0f && constraints.mainAxisMax != Constraints.Infinity) {\n                    constraints.mainAxisMax\n                } else {\n                    constraints.mainAxisMin\n                }\n            val remainingToTarget =\n                targetSpace - fixedSpace - arrangementSpacingPx * (weightChildrenCount - 1)\n\n            val weightUnitSpace = if (totalWeight > 0) remainingToTarget / totalWeight else 0f\n            var remainder = remainingToTarget - (startIndex until endIndex).sumOf {\n                (weightUnitSpace * rowColumnParentData[it].weight).roundToInt()\n            }\n\n            for (i in startIndex until endIndex) {\n                if (placeables[i] == null) {\n                    val child = measurables[i]\n                    val parentData = rowColumnParentData[i]\n                    val weight = parentData.weight\n                    check(weight > 0) { \"All weights <= 0 should have placeables\" }\n                    // After the weightUnitSpace rounding, the total space going to be occupied\n                    // can be smaller or larger than remainingToTarget. Here we distribute the\n                    // loss or gain remainder evenly to the first children.\n                    val remainderUnit = remainder.sign\n                    remainder -= remainderUnit\n                    val childMainAxisSize = max(\n                        0,\n                        (weightUnitSpace * weight).roundToInt() + remainderUnit\n                    )\n                    val placeable = child.measure(\n                        OrientationIndependentConstraints(\n                            if (parentData.fill && childMainAxisSize != Constraints.Infinity) {\n                                childMainAxisSize\n                            } else {\n                                0\n                            },\n                            childMainAxisSize,\n                            0,\n                            constraints.crossAxisMax\n                        ).toBoxConstraints(orientation)\n                    )\n                    weightedSpace += placeable.mainAxisSize()\n                    crossAxisSpace = max(crossAxisSpace, placeable.crossAxisSize())\n                    anyAlignBy = anyAlignBy || parentData.isRelative\n                    placeables[i] = placeable\n                }\n            }\n            weightedSpace = (weightedSpace + arrangementSpacingPx * (weightChildrenCount - 1))\n                .coerceAtMost(constraints.mainAxisMax - fixedSpace)\n        }\n\n        var beforeCrossAxisAlignmentLine = 0\n        var afterCrossAxisAlignmentLine = 0\n        if (anyAlignBy) {\n            for (i in startIndex until endIndex) {\n                val placeable = placeables[i]!!\n                val parentData = rowColumnParentData[i]\n                val alignmentLinePosition = parentData.crossAxisAlignment\n                    ?.calculateAlignmentLinePosition(placeable)\n                if (alignmentLinePosition != null) {\n                    beforeCrossAxisAlignmentLine = max(\n                        beforeCrossAxisAlignmentLine,\n                        alignmentLinePosition.let {\n                            if (it != AlignmentLine.Unspecified) it else 0\n                        }\n                    )\n                    afterCrossAxisAlignmentLine = max(\n                        afterCrossAxisAlignmentLine,\n                        placeable.crossAxisSize() -\n                            (\n                                alignmentLinePosition.let {\n                                    if (it != AlignmentLine.Unspecified) {\n                                        it\n                                    } else {\n                                        placeable.crossAxisSize()\n                                    }\n                                }\n                                )\n                    )\n                }\n            }\n        }\n\n        // Compute the Row or Column size and position the children.\n        val mainAxisLayoutSize = max(fixedSpace + weightedSpace, constraints.mainAxisMin)\n        val crossAxisLayoutSize = if (constraints.crossAxisMax != Constraints.Infinity &&\n            crossAxisSize == SizeMode.Expand\n        ) {\n            constraints.crossAxisMax\n        } else {\n            max(\n                crossAxisSpace,\n                max(\n                    constraints.crossAxisMin,\n                    beforeCrossAxisAlignmentLine + afterCrossAxisAlignmentLine\n                )\n            )\n        }\n        val mainAxisPositions = IntArray(subSize) { 0 }\n        val childrenMainAxisSize = IntArray(subSize) { index ->\n            placeables[index + startIndex]!!.mainAxisSize()\n        }\n\n        return RowColumnMeasureHelperResult(\n            mainAxisSize = mainAxisLayoutSize,\n            crossAxisSize = crossAxisLayoutSize,\n            startIndex = startIndex,\n            endIndex = endIndex,\n            beforeCrossAxisAlignmentLine = beforeCrossAxisAlignmentLine,\n            mainAxisPositions = mainAxisPositions(\n                mainAxisLayoutSize,\n                childrenMainAxisSize,\n                mainAxisPositions,\n                measureScope\n            )\n        )\n    }\n\n    private fun mainAxisPositions(\n        mainAxisLayoutSize: Int,\n        childrenMainAxisSize: IntArray,\n        mainAxisPositions: IntArray,\n        measureScope: MeasureScope\n    ): IntArray {\n        arrangement(\n            mainAxisLayoutSize,\n            childrenMainAxisSize,\n            measureScope.layoutDirection,\n            measureScope,\n            mainAxisPositions\n        )\n        return mainAxisPositions\n    }\n\n    protected fun getCrossAxisPosition(\n        placeable: Placeable,\n        parentData: RowColumnParentData?,\n        crossAxisLayoutSize: Int,\n        layoutDirection: LayoutDirection,\n        beforeCrossAxisAlignmentLine: Int\n    ): Int {\n        val childCrossAlignment = parentData?.crossAxisAlignment ?: crossAxisAlignment\n        return childCrossAlignment.align(\n            size = crossAxisLayoutSize - placeable.crossAxisSize(),\n            layoutDirection = if (orientation == LayoutOrientation.Horizontal) {\n                LayoutDirection.Ltr\n            } else {\n                layoutDirection\n            },\n            placeable = placeable,\n            beforeCrossAxisAlignmentLine = beforeCrossAxisAlignmentLine\n        )\n    }\n\n    fun placeHelper(\n        placeableScope: Placeable.PlacementScope,\n        measureResult: RowColumnMeasureHelperResult,\n        crossAxisOffset: Int,\n        layoutDirection: LayoutDirection,\n    ) {\n        with(placeableScope) {\n            for (i in measureResult.startIndex until measureResult.endIndex) {\n                val placeable = placeables[i]\n                placeable!!\n                val mainAxisPositions = measureResult.mainAxisPositions\n                val crossAxisPosition = getCrossAxisPosition(\n                    placeable,\n                    (measurables[i].parentData as? RowColumnParentData),\n                    measureResult.crossAxisSize,\n                    layoutDirection,\n                    measureResult.beforeCrossAxisAlignmentLine\n                ) + crossAxisOffset\n                if (orientation == LayoutOrientation.Horizontal) {\n                    placeable.place(\n                        mainAxisPositions[i - measureResult.startIndex],\n                        crossAxisPosition\n                    )\n                } else {\n                    placeable.place(\n                        crossAxisPosition,\n                        mainAxisPositions[i - measureResult.startIndex]\n                    )\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "adaptive/src/main/java/com/google/accompanist/adaptive/TwoPane.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport androidx.compose.foundation.gestures.Orientation\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.wrapContentSize\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.ExperimentalComposeUiApi\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.toComposeRect\nimport androidx.compose.ui.layout.Layout\nimport androidx.compose.ui.layout.LayoutCoordinates\nimport androidx.compose.ui.layout.boundsInWindow\nimport androidx.compose.ui.layout.layoutId\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.LayoutDirection\nimport androidx.compose.ui.unit.constrain\nimport androidx.compose.ui.unit.constrainHeight\nimport androidx.compose.ui.unit.constrainWidth\nimport androidx.compose.ui.unit.dp\nimport androidx.window.layout.DisplayFeature\nimport androidx.window.layout.FoldingFeature\nimport kotlin.math.roundToInt\n\n/**\n * A layout that places two different pieces of content defined by the [first] and [second]\n * slots where the arrangement, sizes and separation behaviour is controlled by [TwoPaneStrategy].\n *\n * [TwoPane] is fold and hinges aware using the provided the [displayFeatures] (which should\n * normally be calculated via [calculateDisplayFeatures]). The layout will be adapted to properly\n * separate [first] and [second] panes so they don't interfere with hardware hinges (vertical or\n * horizontal) as specified in [displayFeatures], or respect folds when needed (for example, when\n * foldable is half-folded (90-degree fold AKA tabletop) the split will become on the bend).\n *\n * To only be aware of folds with a specific orientation, pass in an alternate\n * [foldAwareConfiguration] to only adjust for vertical or horizontal folds.\n *\n * The [TwoPane] layout will always place both [first] and [second], based on the provided\n * [strategy] and window environment. If you instead only want to place one or the other,\n * that should be controlled at a higher level and not calling [TwoPane] if placing both is not\n * desired.\n *\n * @param first the first content of the layout, a left-most in LTR, a right-most in RTL and\n * top-most in a vertical split based on the [SplitResult] of [TwoPaneStrategy.calculateSplitResult]\n * @param second the second content of the layout, a right-most in the LTR, a left-most in the RTL\n * and the bottom-most in a vertical split based on the [SplitResult] of\n * [TwoPaneStrategy.calculateSplitResult]\n * @param strategy strategy of the two pane that controls the arrangement of the layout\n * @param displayFeatures the list of known display features to automatically avoid\n * @param foldAwareConfiguration the types of display features to automatically avoid\n * @param modifier an optional modifier for the layout\n */\n@Composable\npublic fun TwoPane(\n    first: @Composable () -> Unit,\n    second: @Composable () -> Unit,\n    strategy: TwoPaneStrategy,\n    displayFeatures: List<DisplayFeature>,\n    modifier: Modifier = Modifier,\n    foldAwareConfiguration: FoldAwareConfiguration = FoldAwareConfiguration.AllFolds,\n) {\n    TwoPane(\n        first = first,\n        second = second,\n        strategy = when (foldAwareConfiguration) {\n            FoldAwareConfiguration.HorizontalFoldsOnly -> {\n                VerticalTwoPaneStrategy(\n                    displayFeatures = displayFeatures,\n                    defaultStrategy = strategy,\n                )\n            }\n            FoldAwareConfiguration.VerticalFoldsOnly -> {\n                HorizontalTwoPaneStrategy(\n                    displayFeatures = displayFeatures,\n                    defaultStrategy = strategy,\n                )\n            }\n            FoldAwareConfiguration.AllFolds -> {\n                TwoPaneStrategy(\n                    displayFeatures = displayFeatures,\n                    defaultStrategy = strategy,\n                )\n            }\n            else -> error(\"Unknown FoldAware value!\")\n        },\n        modifier = modifier,\n    )\n}\n\n@OptIn(ExperimentalComposeUiApi::class)\n@Composable\ninternal fun TwoPane(\n    first: @Composable () -> Unit,\n    second: @Composable () -> Unit,\n    strategy: TwoPaneStrategy,\n    modifier: Modifier = Modifier\n) {\n    val density = LocalDensity.current\n    Layout(\n        modifier = modifier.wrapContentSize(),\n        content = {\n            Box(Modifier.layoutId(\"first\")) {\n                first()\n            }\n            Box(Modifier.layoutId(\"second\")) {\n                second()\n            }\n        }\n    ) { measurable, constraints ->\n        val firstMeasurable = measurable.find { it.layoutId == \"first\" }!!\n        val secondMeasurable = measurable.find { it.layoutId == \"second\" }!!\n\n        layout(constraints.maxWidth, constraints.maxHeight) {\n            val splitResult = strategy.calculateSplitResult(\n                density = density,\n                layoutDirection = layoutDirection,\n                layoutCoordinates = coordinates ?: return@layout\n            )\n\n            val gapOrientation = splitResult.gapOrientation\n            val gapBounds = splitResult.gapBounds\n\n            val gapLeft = constraints.constrainWidth(gapBounds.left.roundToInt())\n            val gapRight = constraints.constrainWidth(gapBounds.right.roundToInt())\n            val gapTop = constraints.constrainHeight(gapBounds.top.roundToInt())\n            val gapBottom = constraints.constrainHeight(gapBounds.bottom.roundToInt())\n            val firstConstraints =\n                if (gapOrientation == Orientation.Vertical) {\n                    val width = when (layoutDirection) {\n                        LayoutDirection.Ltr -> gapLeft\n                        LayoutDirection.Rtl -> constraints.maxWidth - gapRight\n                    }\n\n                    constraints.copy(minWidth = width, maxWidth = width)\n                } else {\n                    constraints.copy(minHeight = gapTop, maxHeight = gapTop)\n                }\n            val secondConstraints =\n                if (gapOrientation == Orientation.Vertical) {\n                    val width = when (layoutDirection) {\n                        LayoutDirection.Ltr -> constraints.maxWidth - gapRight\n                        LayoutDirection.Rtl -> gapLeft\n                    }\n                    constraints.copy(minWidth = width, maxWidth = width)\n                } else {\n                    val height = constraints.maxHeight - gapBottom\n                    constraints.copy(minHeight = height, maxHeight = height)\n                }\n            val firstPlaceable = firstMeasurable.measure(constraints.constrain(firstConstraints))\n            val secondPlaceable = secondMeasurable.measure(constraints.constrain(secondConstraints))\n\n            firstPlaceable.placeRelative(0, 0)\n            val detailOffsetX =\n                if (gapOrientation == Orientation.Vertical) {\n                    constraints.maxWidth - secondPlaceable.width\n                } else {\n                    0\n                }\n            val detailOffsetY =\n                if (gapOrientation == Orientation.Vertical) {\n                    0\n                } else {\n                    constraints.maxHeight - secondPlaceable.height\n                }\n            secondPlaceable.placeRelative(detailOffsetX, detailOffsetY)\n        }\n    }\n}\n\n/**\n * The configuration for which type of folds for a [TwoPane] to automatically avoid.\n */\n@JvmInline\npublic value class FoldAwareConfiguration private constructor(private val value: Int) {\n\n    public companion object {\n        /**\n         * The [TwoPane] will only be aware of horizontal folds only, splitting the content\n         * vertically.\n         */\n        public val HorizontalFoldsOnly: FoldAwareConfiguration = FoldAwareConfiguration(0)\n\n        /**\n         * The [TwoPane] will only be aware of vertical folds only, splitting the content\n         * horizontally.\n         */\n        public val VerticalFoldsOnly: FoldAwareConfiguration = FoldAwareConfiguration(1)\n\n        /**\n         * The [TwoPane] will be aware of both horizontal and vertical folds, splitting the content\n         * vertically and horizontally respectively.\n         */\n        public val AllFolds: FoldAwareConfiguration = FoldAwareConfiguration(2)\n    }\n}\n\n/**\n * Returns the specification for where to place a split in [TwoPane] as a result of\n * [TwoPaneStrategy.calculateSplitResult]\n */\npublic class SplitResult(\n\n    /**\n     * Whether the gap is vertical or horizontal\n     */\n    public val gapOrientation: Orientation,\n\n    /**\n     * The bounds that are nether a `start` pane or an `end` pane, but a separation between those\n     * two. In case width or height is 0 - it means that the gap itself is a 0 width/height, but the\n     * place within the layout is still defined.\n     *\n     * The [gapBounds] should be defined in local bounds to the [TwoPane].\n     */\n    public val gapBounds: Rect,\n)\n\n/**\n * A strategy for configuring the [TwoPane] component, that is responsible for the meta-data\n * corresponding to the arrangement of the two panes of the layout.\n */\npublic fun interface TwoPaneStrategy {\n    /**\n     * Calculates the split result in local bounds of the [TwoPane].\n     *\n     * @param density the [Density] for measuring and laying out\n     * @param layoutDirection the [LayoutDirection] for measuring and laying out\n     * @param layoutCoordinates the [LayoutCoordinates] of the [TwoPane]\n     */\n    public fun calculateSplitResult(\n        density: Density,\n        layoutDirection: LayoutDirection,\n        layoutCoordinates: LayoutCoordinates\n    ): SplitResult\n}\n\n/**\n * A strategy for configuring the [TwoPane] component, that is responsible for the meta-data\n * corresponding to the arrangement of the two panes of the layout.\n *\n * This strategy can be conditional: If `null` is returned from [calculateSplitResult], then this\n * strategy did not produce a split result to use, and a different strategy should be used.\n */\nprivate fun interface ConditionalTwoPaneStrategy {\n    /**\n     * Calculates the split result in local bounds of the [TwoPane], or `null` if this strategy\n     * does not apply.\n     *\n     * @param density the [Density] for measuring and laying out\n     * @param layoutDirection the [LayoutDirection] for measuring and laying out\n     * @param layoutCoordinates the [LayoutCoordinates] of the [TwoPane]\n     */\n    fun calculateSplitResult(\n        density: Density,\n        layoutDirection: LayoutDirection,\n        layoutCoordinates: LayoutCoordinates\n    ): SplitResult?\n}\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The gap will be placed at the given [splitFraction] from start, with the given\n * [gapWidth].\n */\npublic fun HorizontalTwoPaneStrategy(\n    splitFraction: Float,\n    gapWidth: Dp = 0.dp,\n): TwoPaneStrategy = FractionHorizontalTwoPaneStrategy(\n    splitFraction = splitFraction,\n    gapWidth = gapWidth\n)\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The gap will be placed at [splitOffset] either from the start or end based on\n * [offsetFromStart], with the given [gapWidth].\n */\npublic fun HorizontalTwoPaneStrategy(\n    splitOffset: Dp,\n    offsetFromStart: Boolean = true,\n    gapWidth: Dp = 0.dp,\n): TwoPaneStrategy = FixedOffsetHorizontalTwoPaneStrategy(\n    splitOffset = splitOffset,\n    offsetFromStart = offsetFromStart,\n    gapWidth = gapWidth\n)\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The gap will be placed at the given [splitFraction] from top, with the given\n * [gapHeight].\n */\npublic fun VerticalTwoPaneStrategy(\n    splitFraction: Float,\n    gapHeight: Dp = 0.dp,\n): TwoPaneStrategy = FractionVerticalTwoPaneStrategy(\n    splitFraction = splitFraction,\n    gapHeight = gapHeight\n)\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The gap will be placed at [splitOffset] either from the top or bottom based on\n * [offsetFromTop], with the given [gapHeight].\n */\npublic fun VerticalTwoPaneStrategy(\n    splitOffset: Dp,\n    offsetFromTop: Boolean = true,\n    gapHeight: Dp = 0.dp,\n): TwoPaneStrategy = FixedOffsetVerticalTwoPaneStrategy(\n    splitOffset = splitOffset,\n    offsetFromTop = offsetFromTop,\n    gapHeight = gapHeight\n)\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots vertically or horizontally if there is a\n * horizontal or vertical fold respectively.\n *\n * If there is no fold, then the [defaultStrategy] will be used instead.\n */\nprivate fun TwoPaneStrategy(\n    displayFeatures: List<DisplayFeature>,\n    defaultStrategy: TwoPaneStrategy,\n): TwoPaneStrategy = TwoPaneStrategy(\n    FoldAwareHorizontalTwoPaneStrategy(displayFeatures),\n    FoldAwareVerticalTwoPaneStrategy(displayFeatures),\n    defaultStrategy = defaultStrategy\n)\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally if there is a vertical fold.\n *\n * If there is no fold, then the [defaultStrategy] will be used instead.\n */\nprivate fun HorizontalTwoPaneStrategy(\n    displayFeatures: List<DisplayFeature>,\n    defaultStrategy: TwoPaneStrategy,\n): TwoPaneStrategy = TwoPaneStrategy(\n    FoldAwareHorizontalTwoPaneStrategy(displayFeatures),\n    defaultStrategy = defaultStrategy\n)\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots vertically if there is a horizontal fold.\n *\n * If there is no fold, then the [defaultStrategy] will be used instead.\n */\nprivate fun VerticalTwoPaneStrategy(\n    displayFeatures: List<DisplayFeature>,\n    defaultStrategy: TwoPaneStrategy,\n): TwoPaneStrategy = TwoPaneStrategy(\n    FoldAwareVerticalTwoPaneStrategy(displayFeatures),\n    defaultStrategy = defaultStrategy\n)\n\n/**\n * Returns a composite [TwoPaneStrategy].\n *\n * The conditional strategies (if any) will be attempted in order, and their split result used\n * if they return one. If none return a split result, then the [defaultStrategy] will be used,\n * which guarantees returning a [SplitResult].\n */\nprivate fun TwoPaneStrategy(\n    vararg conditionalStrategies: ConditionalTwoPaneStrategy,\n    defaultStrategy: TwoPaneStrategy\n): TwoPaneStrategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->\n    conditionalStrategies.firstNotNullOfOrNull { conditionalTwoPaneStrategy ->\n        conditionalTwoPaneStrategy.calculateSplitResult(\n            density = density,\n            layoutDirection = layoutDirection,\n            layoutCoordinates = layoutCoordinates\n        )\n    } ?: defaultStrategy.calculateSplitResult(\n        density = density,\n        layoutDirection = layoutDirection,\n        layoutCoordinates = layoutCoordinates\n    )\n}\n\n/**\n * Returns a [ConditionalTwoPaneStrategy] that will place the slots horizontally if there is a\n * vertical fold, or `null` if there is no fold.\n */\nprivate fun FoldAwareHorizontalTwoPaneStrategy(\n    displayFeatures: List<DisplayFeature>,\n): ConditionalTwoPaneStrategy = ConditionalTwoPaneStrategy { _, _, layoutCoordinates ->\n    val verticalFold = displayFeatures.find {\n        it is FoldingFeature && it.orientation == FoldingFeature.Orientation.VERTICAL\n    } as FoldingFeature?\n\n    if (verticalFold != null &&\n        (\n            verticalFold.isSeparating ||\n                verticalFold.occlusionType == FoldingFeature.OcclusionType.FULL\n            ) &&\n        verticalFold.bounds.toComposeRect().overlaps(layoutCoordinates.boundsInWindow())\n    ) {\n        val foldBounds = verticalFold.bounds.toComposeRect()\n        SplitResult(\n            gapOrientation = Orientation.Vertical,\n            gapBounds = Rect(\n                layoutCoordinates.windowToLocal(foldBounds.topLeft),\n                layoutCoordinates.windowToLocal(foldBounds.bottomRight)\n            )\n        )\n    } else {\n        null\n    }\n}\n\n/**\n * Returns a [ConditionalTwoPaneStrategy] that will place the slots vertically if there is a\n * horizontal fold, or `null` if there is no fold.\n */\nprivate fun FoldAwareVerticalTwoPaneStrategy(\n    displayFeatures: List<DisplayFeature>,\n): ConditionalTwoPaneStrategy = ConditionalTwoPaneStrategy { _, _, layoutCoordinates ->\n    val horizontalFold = displayFeatures.find {\n        it is FoldingFeature && it.orientation == FoldingFeature.Orientation.HORIZONTAL\n    } as FoldingFeature?\n\n    if (horizontalFold != null &&\n        (\n            horizontalFold.isSeparating ||\n                horizontalFold.occlusionType == FoldingFeature.OcclusionType.FULL\n            ) &&\n        horizontalFold.bounds.toComposeRect().overlaps(layoutCoordinates.boundsInWindow())\n    ) {\n        val foldBounds = horizontalFold.bounds.toComposeRect()\n        SplitResult(\n            gapOrientation = Orientation.Horizontal,\n            gapBounds = Rect(\n                layoutCoordinates.windowToLocal(foldBounds.topLeft),\n                layoutCoordinates.windowToLocal(foldBounds.bottomRight)\n            )\n        )\n    } else {\n        null\n    }\n}\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The gap will be placed at the given [splitFraction] from start, with the given [gapWidth].\n *\n * This strategy is _not_ fold aware.\n */\ninternal fun FractionHorizontalTwoPaneStrategy(\n    splitFraction: Float,\n    gapWidth: Dp = 0.dp,\n): TwoPaneStrategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->\n    val splitX = layoutCoordinates.size.width * when (layoutDirection) {\n        LayoutDirection.Ltr -> splitFraction\n        LayoutDirection.Rtl -> 1 - splitFraction\n    }\n    val splitWidthPixel = with(density) { gapWidth.toPx() }\n\n    SplitResult(\n        gapOrientation = Orientation.Vertical,\n        gapBounds = Rect(\n            left = splitX - splitWidthPixel / 2f,\n            top = 0f,\n            right = splitX + splitWidthPixel / 2f,\n            bottom = layoutCoordinates.size.height.toFloat(),\n        )\n    )\n}\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The gap will be placed at [splitOffset] either from the start or end based on\n * [offsetFromStart], with the given [gapWidth].\n *\n * This strategy is _not_ fold aware.\n */\ninternal fun FixedOffsetHorizontalTwoPaneStrategy(\n    splitOffset: Dp,\n    offsetFromStart: Boolean,\n    gapWidth: Dp = 0.dp,\n): TwoPaneStrategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->\n    val splitOffsetPixel = with(density) { splitOffset.toPx() }\n    val splitX = when (layoutDirection) {\n        LayoutDirection.Ltr ->\n            if (offsetFromStart) {\n                splitOffsetPixel\n            } else {\n                layoutCoordinates.size.width - splitOffsetPixel\n            }\n        LayoutDirection.Rtl ->\n            if (offsetFromStart) {\n                layoutCoordinates.size.width - splitOffsetPixel\n            } else {\n                splitOffsetPixel\n            }\n    }\n    val splitWidthPixel = with(density) { gapWidth.toPx() }\n\n    SplitResult(\n        gapOrientation = Orientation.Vertical,\n        gapBounds = Rect(\n            left = splitX - splitWidthPixel / 2f,\n            top = 0f,\n            right = splitX + splitWidthPixel / 2f,\n            bottom = layoutCoordinates.size.height.toFloat(),\n        )\n    )\n}\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The split will be placed at the given [splitFraction] from start, with the given [gapHeight].\n *\n * This strategy is _not_ fold aware.\n */\ninternal fun FractionVerticalTwoPaneStrategy(\n    splitFraction: Float,\n    gapHeight: Dp = 0.dp,\n): TwoPaneStrategy = TwoPaneStrategy { density, _, layoutCoordinates ->\n    val splitY = layoutCoordinates.size.height * splitFraction\n    val splitHeightPixel = with(density) { gapHeight.toPx() }\n\n    SplitResult(\n        gapOrientation = Orientation.Horizontal,\n        gapBounds = Rect(\n            left = 0f,\n            top = splitY - splitHeightPixel / 2f,\n            right = layoutCoordinates.size.width.toFloat(),\n            bottom = splitY + splitHeightPixel / 2f,\n        )\n    )\n}\n\n/**\n * Returns a [TwoPaneStrategy] that will place the slots horizontally.\n *\n * The split will be placed at [splitOffset] either from the top or bottom based on\n * [offsetFromTop], with the given [gapHeight].\n *\n * This strategy is _not_ fold aware.\n */\ninternal fun FixedOffsetVerticalTwoPaneStrategy(\n    splitOffset: Dp,\n    offsetFromTop: Boolean,\n    gapHeight: Dp = 0.dp,\n): TwoPaneStrategy = TwoPaneStrategy { density, _, layoutCoordinates ->\n    val splitOffsetPixel = with(density) { splitOffset.toPx() }\n    val splitY =\n        if (offsetFromTop) {\n            splitOffsetPixel\n        } else {\n            layoutCoordinates.size.height - splitOffsetPixel\n        }\n    val splitHeightPixel = with(density) { gapHeight.toPx() }\n\n    SplitResult(\n        gapOrientation = Orientation.Horizontal,\n        gapBounds = Rect(\n            left = 0f,\n            top = splitY - splitHeightPixel / 2f,\n            right = layoutCoordinates.size.width.toFloat(),\n            bottom = splitY + splitHeightPixel / 2f,\n        )\n    )\n}\n"
  },
  {
    "path": "adaptive/src/sharedTest/kotlin/com/google/accompanist/adaptive/DisplayFeaturesTest.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport androidx.activity.ComponentActivity\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.window.layout.DisplayFeature\nimport androidx.window.layout.FoldingFeature\nimport androidx.window.layout.WindowLayoutInfo\nimport androidx.window.testing.layout.FoldingFeature\nimport androidx.window.testing.layout.WindowLayoutInfoPublisherRule\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@RunWith(AndroidJUnit4::class)\nclass DisplayFeaturesTest {\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    @get:Rule\n    val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()\n\n    @Test\n    fun empty_folding_features_is_correct() {\n        lateinit var displayFeatures: List<DisplayFeature>\n\n        composeTestRule.setContent {\n            displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity)\n        }\n\n        windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(WindowLayoutInfo(emptyList()))\n\n        composeTestRule.waitForIdle()\n\n        assertThat(displayFeatures).isEmpty()\n    }\n\n    @Test\n    fun single_folding_features_is_correct() {\n        lateinit var displayFeatures: List<DisplayFeature>\n\n        composeTestRule.setContent {\n            displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity)\n        }\n\n        val fakeFoldingFeature = FoldingFeature(\n            activity = composeTestRule.activity,\n            center = 200,\n            size = 40,\n            state = FoldingFeature.State.HALF_OPENED,\n            orientation = FoldingFeature.Orientation.VERTICAL,\n        )\n\n        windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(\n            WindowLayoutInfo(\n                listOf(\n                    fakeFoldingFeature\n                )\n            )\n        )\n\n        composeTestRule.waitForIdle()\n\n        assertThat(displayFeatures).hasSize(1)\n        assertThat(displayFeatures[0]).isEqualTo(fakeFoldingFeature)\n    }\n\n    @Test\n    fun updating_folding_features_is_correct() {\n        lateinit var displayFeatures: List<DisplayFeature>\n\n        composeTestRule.setContent {\n            displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity)\n        }\n\n        windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(WindowLayoutInfo(emptyList()))\n\n        val fakeFoldingFeature = FoldingFeature(\n            activity = composeTestRule.activity,\n            center = 200,\n            size = 40,\n            state = FoldingFeature.State.HALF_OPENED,\n            orientation = FoldingFeature.Orientation.VERTICAL,\n        )\n\n        windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(\n            WindowLayoutInfo(\n                listOf(\n                    fakeFoldingFeature\n                )\n            )\n        )\n\n        composeTestRule.waitForIdle()\n\n        assertThat(displayFeatures).hasSize(1)\n        assertThat(displayFeatures[0]).isEqualTo(fakeFoldingFeature)\n    }\n}\n"
  },
  {
    "path": "adaptive/src/sharedTest/kotlin/com/google/accompanist/adaptive/FoldAwareColumnTest.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport android.annotation.SuppressLint\nimport androidx.activity.ComponentActivity\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.toComposeRect\nimport androidx.compose.ui.layout.onGloballyPositioned\nimport androidx.compose.ui.layout.positionInWindow\nimport androidx.compose.ui.platform.LocalConfiguration\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.platform.testTag\nimport androidx.compose.ui.test.assertTopPositionInRootIsEqualTo\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.compose.ui.test.onNodeWithTag\nimport androidx.compose.ui.test.onRoot\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.dp\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.window.layout.FoldingFeature\nimport androidx.window.layout.WindowLayoutInfo\nimport androidx.window.layout.WindowMetricsCalculator\nimport androidx.window.testing.layout.FoldingFeature\nimport androidx.window.testing.layout.WindowLayoutInfoPublisherRule\nimport com.google.accompanist.adaptive.FoldAwareColumnScopeInstance.ignoreFold\nimport org.junit.After\nimport org.junit.Assert.assertEquals\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n@RunWith(AndroidJUnit4::class)\nclass FoldAwareColumnTest {\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    @get:Rule\n    val publisherRule = WindowLayoutInfoPublisherRule()\n\n    private val testTag = \"FoldAwareColumnTestTag\"\n    private var firstSpacerHeightDp = 0.dp\n    private var secondSpacerTopPx = 0f\n    private var secondSpacerBottomPx = 0f\n\n    @After\n    fun cleanUp() {\n        firstSpacerHeightDp = 0.dp\n        secondSpacerTopPx = 0f\n        secondSpacerBottomPx = 0f\n    }\n\n    @Test\n    fun second_spacer_placed_below_fold_with_hinge() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        val foldBoundsPx = simulateFoldingFeature()\n\n        assertEquals(foldBoundsPx.bottom, secondSpacerTopPx)\n    }\n\n    @Test\n    fun second_spacer_placed_below_fold_with_separating_fold() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        val foldBoundsPx = simulateFoldingFeature(foldSizePx = 0)\n\n        assertEquals(foldBoundsPx.bottom, secondSpacerTopPx)\n    }\n\n    @Test\n    fun second_spacer_placed_below_first_spacer_without_fold() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        composeTestRule.onNodeWithTag(testTag).assertTopPositionInRootIsEqualTo(firstSpacerHeightDp)\n    }\n\n    @Test\n    fun second_spacer_placed_below_first_spacer_with_non_separating_fold() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        simulateFoldingFeature(foldSizePx = 0, foldState = FoldingFeature.State.FLAT)\n\n        composeTestRule.onNodeWithTag(testTag).assertTopPositionInRootIsEqualTo(firstSpacerHeightDp)\n    }\n\n    @Test\n    fun second_spacer_placed_below_first_spacer_with_vertical_hinge() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        simulateFoldingFeature(foldOrientation = FoldingFeature.Orientation.VERTICAL)\n\n        composeTestRule.onNodeWithTag(testTag).assertTopPositionInRootIsEqualTo(firstSpacerHeightDp)\n    }\n\n    @Test\n    fun second_spacer_placed_below_first_spacer_with_ignore_fold_modifier() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers(secondSpacerModifier = Modifier.ignoreFold())\n        }\n\n        simulateFoldingFeature()\n\n        composeTestRule.onNodeWithTag(testTag).assertTopPositionInRootIsEqualTo(firstSpacerHeightDp)\n    }\n\n    @Test\n    fun even_fold_padding_modifier_applies_around_hinge() {\n        val foldPaddingDp = 20.dp\n        lateinit var density: Density\n\n        composeTestRule.setContent {\n            density = LocalDensity.current\n\n            FoldAwareColumnWithSpacers(\n                foldPadding = PaddingValues(vertical = foldPaddingDp)\n            )\n        }\n\n        val foldBoundsPx = simulateFoldingFeature()\n\n        with(density) {\n            assertEquals(foldBoundsPx.bottom + foldPaddingDp.roundToPx(), secondSpacerTopPx)\n        }\n    }\n\n    @Test\n    fun uneven_fold_padding_modifier_applies_around_hinge() {\n        val foldPaddingBottom = 40.dp\n        lateinit var density: Density\n\n        composeTestRule.setContent {\n            density = LocalDensity.current\n\n            FoldAwareColumnWithSpacers(\n                foldPadding = PaddingValues(top = 15.dp, bottom = foldPaddingBottom)\n            )\n        }\n\n        val foldBoundsPx = simulateFoldingFeature()\n\n        with(density) {\n            assertEquals(foldBoundsPx.bottom + foldPaddingBottom.roundToPx(), secondSpacerTopPx)\n        }\n    }\n\n    @Test\n    fun layout_bounds_align_with_child_bounds_without_separating_fold() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        val layoutBottomPx = composeTestRule.onRoot()\n            .fetchSemanticsNode().layoutInfo.coordinates.trueBoundsInWindow().bottom\n\n        assertEquals(layoutBottomPx, secondSpacerBottomPx)\n    }\n\n    @Test\n    fun layout_bounds_contain_child_bounds_when_placed_above_hinge() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers(\n                firstSpacerHeightPct = 0.1f,\n                secondSpacerHeightPct = 0.1f\n            )\n        }\n\n        simulateFoldingFeature()\n\n        val layoutBottomPx = composeTestRule.onRoot()\n            .fetchSemanticsNode().layoutInfo.coordinates.trueBoundsInWindow().bottom\n\n        assert(secondSpacerBottomPx <= layoutBottomPx)\n    }\n\n    @Test\n    fun layout_bounds_contain_child_bounds_when_placed_below_hinge() {\n        composeTestRule.setContent {\n            FoldAwareColumnWithSpacers()\n        }\n\n        simulateFoldingFeature()\n\n        val layoutBottomPx = composeTestRule.onRoot()\n            .fetchSemanticsNode().layoutInfo.coordinates.trueBoundsInWindow().bottom\n\n        assert(secondSpacerBottomPx <= layoutBottomPx)\n    }\n\n    /**\n     * Test layout for FoldAwareColumn that includes two spacers with the provided heights\n     */\n    @Composable\n    @SuppressLint(\"ModifierParameter\")\n    private fun FoldAwareColumnWithSpacers(\n        foldPadding: PaddingValues = PaddingValues(),\n        firstSpacerHeightPct: Float = 0.25f,\n        secondSpacerHeightPct: Float = 0.25f,\n        secondSpacerModifier: Modifier = Modifier,\n    ) {\n        var secondSpacerHeightDp: Dp\n        val metrics = remember(LocalConfiguration.current) {\n            WindowMetricsCalculator.getOrCreate()\n                .computeCurrentWindowMetrics(composeTestRule.activity)\n        }\n\n        with(LocalDensity.current) {\n            val windowHeight = metrics.bounds.height().toDp().value\n            firstSpacerHeightDp = (firstSpacerHeightPct * windowHeight).dp\n            secondSpacerHeightDp = (secondSpacerHeightPct * windowHeight).dp\n        }\n\n        FoldAwareColumn(\n            displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity),\n            foldPadding = foldPadding,\n        ) {\n            Spacer(\n                modifier = Modifier.height(firstSpacerHeightDp)\n            )\n            Spacer(\n                modifier = secondSpacerModifier\n                    .height(secondSpacerHeightDp)\n                    .testTag(testTag)\n                    .onGloballyPositioned {\n                        secondSpacerTopPx = it.positionInWindow().y\n                        secondSpacerBottomPx = secondSpacerTopPx + it.size.height\n                    }\n            )\n        }\n    }\n\n    /**\n     * Simulates a Jetpack Window Manager folding feature with the provided properties and returns\n     * the bounding box of the fold\n     */\n    private fun simulateFoldingFeature(\n        foldSizePx: Int = 25,\n        foldState: FoldingFeature.State = FoldingFeature.State.HALF_OPENED,\n        foldOrientation: FoldingFeature.Orientation = FoldingFeature.Orientation.HORIZONTAL\n    ): Rect {\n        val fakeFoldingFeature = FoldingFeature(\n            activity = composeTestRule.activity,\n            size = foldSizePx,\n            state = foldState,\n            orientation = foldOrientation,\n        )\n\n        publisherRule.overrideWindowLayoutInfo(WindowLayoutInfo(listOf(fakeFoldingFeature)))\n\n        composeTestRule.waitForIdle()\n\n        return fakeFoldingFeature.bounds.toComposeRect()\n    }\n}\n"
  },
  {
    "path": "adaptive/src/sharedTest/kotlin/com/google/accompanist/adaptive/TwoPaneTest.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.adaptive\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.geometry.Rect\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.layout.LayoutCoordinates\nimport androidx.compose.ui.layout.onPlaced\nimport androidx.compose.ui.platform.LocalDensity\nimport androidx.compose.ui.test.DeviceConfigurationOverride\nimport androidx.compose.ui.test.ForcedSize\nimport androidx.compose.ui.test.LayoutDirection\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.then\nimport androidx.compose.ui.unit.Density\nimport androidx.compose.ui.unit.Dp\nimport androidx.compose.ui.unit.DpOffset\nimport androidx.compose.ui.unit.DpRect\nimport androidx.compose.ui.unit.DpSize\nimport androidx.compose.ui.unit.IntRect\nimport androidx.compose.ui.unit.LayoutDirection\nimport androidx.compose.ui.unit.dp\nimport androidx.compose.ui.unit.toIntRect\nimport androidx.compose.ui.unit.toOffset\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport androidx.window.core.ExperimentalWindowApi\nimport androidx.window.layout.DisplayFeature\nimport androidx.window.layout.FoldingFeature\nimport androidx.window.testing.layout.FoldingFeature\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport kotlin.math.roundToInt\n\n@RunWith(AndroidJUnit4::class)\nclass TwoPaneTest {\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun fraction_horizontal_renders_correctly_ltr() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Ltr)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FractionHorizontalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(300.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(300.dp, 0.dp),\n                    DpSize(600.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fraction_horizontal_renders_correctly_rtl() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Rtl)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FractionHorizontalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(600.dp, 0.dp),\n                    DpSize(300.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(600.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fraction_horizontal_renders_correctly_with_split_width_ltr() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Ltr)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FractionHorizontalTwoPaneStrategy(\n                        splitFraction = 1f / 3f,\n                        gapWidth = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(268.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(332.dp, 0.dp),\n                    DpSize(568.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fraction_horizontal_renders_correctly_with_split_width_rtl() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Rtl)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FractionHorizontalTwoPaneStrategy(\n                        splitFraction = 1f / 3f,\n                        gapWidth = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(632.dp, 0.dp),\n                    DpSize(268.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(568.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fraction_vertical_renders_correctly() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FractionVerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 400.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 400.dp),\n                    DpSize(900.dp, 800.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fraction_vertical_renders_correctly_with_split_height() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FractionVerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f,\n                        gapHeight = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 368.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 432.dp),\n                    DpSize(900.dp, 768.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_horizontal_from_start_horizontal_renders_correctly_ltr() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Ltr)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetHorizontalTwoPaneStrategy(\n                        splitOffset = 200.dp,\n                        offsetFromStart = true,\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(200.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(200.dp, 0.dp),\n                    DpSize(700.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_horizontal_from_start_horizontal_renders_correctly_rtl() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Rtl)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetHorizontalTwoPaneStrategy(\n                        splitOffset = 200.dp,\n                        offsetFromStart = true,\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(700.dp, 0.dp),\n                    DpSize(200.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(700.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_horizontal_from_start_renders_correctly_with_split_width_ltr() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Ltr)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetHorizontalTwoPaneStrategy(\n                        splitOffset = 200.dp,\n                        offsetFromStart = true,\n                        gapWidth = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(168.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(232.dp, 0.dp),\n                    DpSize(668.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_horizontal_from_start_renders_correctly_with_split_width_rtl() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp)) then\n                    DeviceConfigurationOverride.LayoutDirection(LayoutDirection.Rtl)\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetHorizontalTwoPaneStrategy(\n                        splitOffset = 200.dp,\n                        offsetFromStart = true,\n                        gapWidth = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(732.dp, 0.dp),\n                    DpSize(168.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(668.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_vertical_from_top_renders_correctly() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetVerticalTwoPaneStrategy(\n                        splitOffset = 300.dp,\n                        offsetFromTop = true\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 300.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 300.dp),\n                    DpSize(900.dp, 900.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_vertical_from_top_renders_correctly_with_split_height() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetVerticalTwoPaneStrategy(\n                        splitOffset = 300.dp,\n                        offsetFromTop = true,\n                        gapHeight = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 268.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 332.dp),\n                    DpSize(900.dp, 868.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_vertical_from_bottom_renders_correctly() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetVerticalTwoPaneStrategy(\n                        splitOffset = 300.dp,\n                        offsetFromTop = false\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 900.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 900.dp),\n                    DpSize(900.dp, 300.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun fixed_offset_vertical_from_bottom_renders_correctly_with_split_height() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = FixedOffsetVerticalTwoPaneStrategy(\n                        splitOffset = 300.dp,\n                        offsetFromTop = false,\n                        gapHeight = 64.dp\n                    ),\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 868.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 932.dp),\n                    DpSize(900.dp, 268.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_fallback_when_no_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = emptyList()\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 400.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 400.dp),\n                    DpSize(900.dp, 800.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_vertical_placing_when_occluding_horizontal_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = listOf(\n                    LocalFoldingFeature(\n                        center = 600.dp,\n                        size = 0.dp,\n                        state = FoldingFeature.State.HALF_OPENED,\n                        orientation = FoldingFeature.Orientation.HORIZONTAL\n                    )\n                )\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 600.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 600.dp),\n                    DpSize(900.dp, 600.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_vertical_placing_when_separating_horizontal_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = listOf(\n                    LocalFoldingFeature(\n                        center = 600.dp,\n                        size = 60.dp,\n                        state = FoldingFeature.State.FLAT,\n                        orientation = FoldingFeature.Orientation.HORIZONTAL\n                    )\n                )\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 570.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 630.dp),\n                    DpSize(900.dp, 570.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_fallback_when_non_occluding_horizontal_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = listOf(\n                    LocalFoldingFeature(\n                        center = 600.dp,\n                        size = 0.dp,\n                        state = FoldingFeature.State.FLAT,\n                        orientation = FoldingFeature.Orientation.HORIZONTAL\n                    )\n                )\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 400.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 400.dp),\n                    DpSize(900.dp, 800.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_horizontal_placing_when_occluding_vertical_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = listOf(\n                    LocalFoldingFeature(\n                        center = 450.dp,\n                        size = 0.dp,\n                        state = FoldingFeature.State.HALF_OPENED,\n                        orientation = FoldingFeature.Orientation.VERTICAL\n                    )\n                )\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(450.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(450.dp, 0.dp),\n                    DpSize(450.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_horizontal_placing_when_separating_vertical_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = listOf(\n                    LocalFoldingFeature(\n                        center = 450.dp,\n                        size = 64.dp,\n                        state = FoldingFeature.State.FLAT,\n                        orientation = FoldingFeature.Orientation.VERTICAL\n                    )\n                )\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(418.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(482.dp, 0.dp),\n                    DpSize(418.dp, 1200.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n\n    @Test\n    fun two_pane_strategy_uses_fallback_when_non_occluding_vertical_fold_present() {\n        lateinit var density: Density\n        lateinit var twoPaneCoordinates: LayoutCoordinates\n        lateinit var firstCoordinates: LayoutCoordinates\n        lateinit var secondCoordinates: LayoutCoordinates\n        val displayFeatures = DelegateList {\n            fakeDisplayFeatures(\n                density = density,\n                twoPaneCoordinates = twoPaneCoordinates,\n                localFoldingFeatures = listOf(\n                    LocalFoldingFeature(\n                        center = 450.dp,\n                        size = 0.dp,\n                        state = FoldingFeature.State.FLAT,\n                        orientation = FoldingFeature.Orientation.VERTICAL\n                    )\n                )\n            )\n        }\n\n        composeTestRule.setContent {\n            DeviceConfigurationOverride(\n                DeviceConfigurationOverride.ForcedSize(DpSize(900.dp, 1200.dp))\n            ) {\n                density = LocalDensity.current\n\n                TwoPane(\n                    first = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Red)\n                                .fillMaxSize()\n                                .onPlaced { firstCoordinates = it }\n                        )\n                    },\n                    second = {\n                        Spacer(\n                            Modifier\n                                .background(Color.Blue)\n                                .fillMaxSize()\n                                .onPlaced { secondCoordinates = it }\n                        )\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitFraction = 1f / 3f\n                    ),\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.onPlaced { twoPaneCoordinates = it }\n                )\n            }\n        }\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 0.dp),\n                    DpSize(900.dp, 400.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(firstCoordinates),\n            1f\n        )\n\n        compareRectWithTolerance(\n            with(density) {\n                DpRect(\n                    DpOffset(0.dp, 400.dp),\n                    DpSize(900.dp, 800.dp)\n                ).toRect().round().toRect()\n            },\n            twoPaneCoordinates.localBoundingBoxOf(secondCoordinates),\n            1f\n        )\n    }\n}\n\nprivate fun compareRectWithTolerance(\n    expected: Rect,\n    actual: Rect,\n    tolerance: Float,\n) {\n    assertThat(actual.left).isWithin(tolerance).of(expected.left)\n    assertThat(actual.right).isWithin(tolerance).of(expected.right)\n    assertThat(actual.top).isWithin(tolerance).of(expected.top)\n    assertThat(actual.bottom).isWithin(tolerance).of(expected.bottom)\n}\n\n/**\n * A descriptor of a [FoldingFeature] but with the [center] and [size] specified relative to the\n * to the coordinates of the [TwoPane] layout.\n */\nprivate data class LocalFoldingFeature(\n    val center: Dp,\n    val size: Dp,\n    val state: FoldingFeature.State,\n    val orientation: FoldingFeature.Orientation\n)\n\n/**\n * A [List] that lazily constructs the backing delegate list by calling the provided lambda.\n */\nprivate class DelegateList<T>(\n    listFactory: () -> List<T>\n) : List<T> {\n    val delegate by lazy(listFactory)\n    override val size: Int get() = delegate.size\n    override fun get(index: Int): T = delegate[index]\n    override fun isEmpty(): Boolean = delegate.isEmpty()\n    override fun iterator(): Iterator<T> = delegate.iterator()\n    override fun listIterator(): ListIterator<T> = delegate.listIterator()\n    override fun listIterator(index: Int): ListIterator<T> = delegate.listIterator(index)\n    override fun subList(fromIndex: Int, toIndex: Int): List<T> =\n        delegate.subList(fromIndex, toIndex)\n    override fun lastIndexOf(element: T): Int = delegate.lastIndexOf(element)\n    override fun indexOf(element: T): Int = delegate.indexOf(element)\n    override fun containsAll(elements: Collection<T>): Boolean = delegate.containsAll(elements)\n    override fun contains(element: T): Boolean = delegate.contains(element)\n}\n\n/**\n * Folding features are always expressed in window coordinates.\n *\n * For the sake of testing, however, we want to specify them relative to the [TwoPane] under test.\n *\n * Therefore, this method takes in a list of [LocalFoldingFeature]s and the [TwoPane] layout info\n * in order to map the [LocalFoldingFeature]s into real [FoldingFeature] with the proper window\n * coordinates.\n *\n * In other words, this allows specifying [LocalFoldingFeature]s as if the [TwoPane] layout matches\n * the window bounds.\n */\n@OptIn(ExperimentalWindowApi::class)\nprivate fun fakeDisplayFeatures(\n    density: Density,\n    twoPaneCoordinates: LayoutCoordinates,\n    localFoldingFeatures: List<LocalFoldingFeature>\n): List<DisplayFeature> {\n    val boundsTopLeftOffset = twoPaneCoordinates.localToWindow(\n        twoPaneCoordinates.size.toIntRect().topLeft.toOffset()\n    )\n    val boundsBottomRightOffset = twoPaneCoordinates.localToWindow(\n        twoPaneCoordinates.size.toIntRect().bottomRight.toOffset()\n    )\n    val bounds = Rect(\n        boundsTopLeftOffset,\n        boundsBottomRightOffset\n    )\n\n    return localFoldingFeatures.map { localFoldingFeature ->\n        val foldLeft: Float\n        val foldTop: Float\n        val foldRight: Float\n        val foldBottom: Float\n\n        with(density) {\n            if (localFoldingFeature.orientation == FoldingFeature.Orientation.HORIZONTAL) {\n                foldLeft = 0f\n                foldTop =\n                    (localFoldingFeature.center - localFoldingFeature.size / 2).toPx()\n                foldRight = twoPaneCoordinates.size.width.toFloat()\n                foldBottom =\n                    (localFoldingFeature.center + localFoldingFeature.size / 2).toPx()\n            } else {\n                foldLeft =\n                    (localFoldingFeature.center - localFoldingFeature.size / 2).toPx()\n                foldTop = 0f\n                foldRight =\n                    (localFoldingFeature.center + localFoldingFeature.size / 2).toPx()\n                foldBottom = twoPaneCoordinates.size.height.toFloat()\n            }\n        }\n\n        val foldTopLeftOffset = twoPaneCoordinates.localToWindow(\n            Offset(foldLeft, foldTop)\n        )\n        val foldBottomRightOffset = twoPaneCoordinates.localToWindow(\n            Offset(foldRight, foldBottom)\n        )\n        val foldBounds = Rect(\n            foldTopLeftOffset,\n            foldBottomRightOffset,\n        )\n\n        val center: Int\n        val size: Int\n\n        if (localFoldingFeature.orientation == FoldingFeature.Orientation.HORIZONTAL) {\n            center = foldBounds.center.y.roundToInt()\n            size = foldBounds.height.roundToInt()\n        } else {\n            center = foldBounds.center.x.roundToInt()\n            size = foldBounds.width.roundToInt()\n        }\n\n        FoldingFeature(\n            windowBounds = android.graphics.Rect(\n                bounds.left.toInt(),\n                bounds.top.toInt(),\n                bounds.right.toInt(),\n                bounds.bottom.toInt()\n            ),\n            center = center,\n            size = size,\n            state = localFoldingFeature.state,\n            orientation = localFoldingFeature.orientation,\n        )\n    }\n}\n\nprivate fun Rect.round(): IntRect =\n    IntRect(\n        left = left.roundToInt(),\n        top = top.roundToInt(),\n        right = right.roundToInt(),\n        bottom = bottom.roundToInt()\n    )\n\nprivate fun IntRect.toRect(): Rect =\n    Rect(\n        left = left.toFloat(),\n        top = top.toFloat(),\n        right = right.toFloat(),\n        bottom = bottom.toFloat()\n    )\n"
  },
  {
    "path": "adaptive/src/test/resources/robolectric.properties",
    "content": "# Pin SDK to 30 since Robolectric does not currently support API 31:\n# https://github.com/robolectric/robolectric/issues/6635\nsdk=30\n"
  },
  {
    "path": "build-logic/convention/build.gradle.kts",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n    `kotlin-dsl`\n}\n\ngroup = \"com.google.accompanist.buildlogic\"\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17\n    targetCompatibility = JavaVersion.VERSION_17\n}\n\nkotlin {\n    compilerOptions {\n        jvmTarget = JvmTarget.JVM_17\n    }\n}\n\ndependencies {\n    // used by BundleInsideHelper.kt\n    implementation(libs.apacheAnt)\n    implementation(libs.shadow)\n\n    compileOnly(libs.android.gradlePlugin)\n    compileOnly(libs.android.tools.common)\n    compileOnly(libs.compose.gradlePlugin)\n    compileOnly(libs.kotlin.gradlePlugin)\n    compileOnly(libs.metalavaGradle)\n    compileOnly(libs.gradleMavenPublishPlugin)\n    implementation(libs.truth)\n}\n\ntasks {\n    validatePlugins {\n        enableStricterValidation = true\n        failOnWarning = true\n    }\n}\n\ngradlePlugin {\n    plugins {\n        register(\"androidLibrary\") {\n            id = \"accompanist.android.library\"\n            implementationClass = \"AndroidLibraryConventionPlugin\"\n        }\n        register(\"androidLibraryCompose\") {\n            id = \"accompanist.android.library.compose\"\n            implementationClass = \"AndroidLibraryComposeConventionPlugin\"\n        }\n        register(\"androidLint\") {\n            id = \"accompanist.android.lint\"\n            implementationClass = \"AndroidLintConventionPlugin\"\n        }\n        register(\"androidLibraryPublished\") {\n            id = \"accompanist.android.library.published\"\n            implementationClass = \"AndroidLibraryPublishedConventionPlugin\"\n        }\n    }\n}\n\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport com.android.build.gradle.LibraryExtension\nimport com.google.accompanist.configureAndroidCompose\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.apply\nimport org.gradle.kotlin.dsl.getByType\n\nclass AndroidLibraryComposeConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            apply(plugin = \"com.android.library\")\n            apply(plugin = \"org.jetbrains.kotlin.plugin.compose\")\n\n            val extension = extensions.getByType<LibraryExtension>()\n            configureAndroidCompose(extension)\n        }\n    }\n\n}"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n *   Licensed under the Apache License, Version 2.0 (the \"License\");\n *   you may not use this file except in compliance with the License.\n *   You may obtain a copy of the License at\n *\n *       https://www.apache.org/licenses/LICENSE-2.0\n *\n *   Unless required by applicable law or agreed to in writing, software\n *   distributed under the License is distributed on an \"AS IS\" BASIS,\n *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *   See the License for the specific language governing permissions and\n *   limitations under the License.\n */\n\nimport com.android.build.gradle.LibraryExtension\nimport com.google.accompanist.configureKotlinAndroid\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.kotlin\n\nclass AndroidLibraryConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(\"com.android.library\")\n                apply(\"org.jetbrains.kotlin.android\")\n            }\n\n            extensions.configure<LibraryExtension> {\n                configureKotlinAndroid(this)\n                defaultConfig.targetSdk = 35\n                defaultConfig.testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n\n                buildFeatures.buildConfig = false\n\n                testOptions.animationsDisabled = true\n                // The resource prefix is derived from the module name,\n                // so resources inside \":core:module1\" must be prefixed with \"core_module1_\"\n                resourcePrefix = path.split(\"\"\"\\W\"\"\".toRegex()).drop(1).distinct().joinToString(separator = \"_\").lowercase() + \"_\"\n            }\n\n            dependencies {\n                add(\"androidTestImplementation\", kotlin(\"test\"))\n                add(\"testImplementation\", kotlin(\"test\"))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidLibraryPublishedConventionPlugin.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport me.tylerbwong.gradle.metalava.extension.MetalavaExtension\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.apply\nimport org.gradle.kotlin.dsl.configure\n\nclass AndroidLibraryPublishedConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            with(pluginManager) {\n                apply(AndroidLintConventionPlugin::class)\n\n                apply(\"me.tylerbwong.gradle.metalava\")\n                apply(\"org.jetbrains.dokka\")\n                apply(\"com.vanniktech.maven.publish\")\n            }\n\n            extensions.configure<MetalavaExtension> {\n                sourcePaths.setFrom(\"src/main\")\n                filename.set(\"api/current.api\")\n                reportLintsAsErrors.set(true)\n            }\n        }\n    }\n}"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport com.android.build.api.dsl.ApplicationExtension\nimport com.android.build.api.dsl.LibraryExtension\nimport com.android.build.api.dsl.Lint\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\nimport org.gradle.kotlin.dsl.configure\nimport java.io.File\n\nclass AndroidLintConventionPlugin : Plugin<Project> {\n    override fun apply(target: Project) {\n        with(target) {\n            when {\n                pluginManager.hasPlugin(\"com.android.application\") ->\n                    configure<ApplicationExtension> { lint(Lint::configure) }\n\n                pluginManager.hasPlugin(\"com.android.library\") ->\n                    configure<LibraryExtension> { lint(Lint::configure) }\n\n                else -> {\n                    pluginManager.apply(\"com.android.lint\")\n                    configure<Lint>(Lint::configure)\n                }\n            }\n        }\n    }\n}\n\nprivate fun Lint.configure() {\n    textReport = true\n    textOutput = File(\"stdout\")\n    // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks\n    checkReleaseBuilds = false\n    disable += setOf(\"GradleOverrides\")\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/google/accompanist/AndroidCompose.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist\n\nimport com.android.build.api.dsl.CommonExtension\nimport org.gradle.api.Project\nimport org.gradle.api.provider.Provider\nimport org.gradle.kotlin.dsl.configure\nimport org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension\n\n/**\n * Configure Compose-specific options\n */\ninternal fun Project.configureAndroidCompose(\n    commonExtension: CommonExtension<*, *, *, *, *, *>,\n) {\n    commonExtension.apply {\n        buildFeatures {\n            compose = true\n        }\n\n        testOptions {\n            unitTests {\n                // For Robolectric\n                isIncludeAndroidResources = true\n            }\n        }\n    }\n\n    extensions.configure<ComposeCompilerGradlePluginExtension> {\n        fun Provider<String>.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }\n        fun Provider<*>.relativeToRootProject(dir: String) = flatMap {\n            rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir))\n        }.map { it.dir(dir) }\n\n        project.providers.gradleProperty(\"enableComposeCompilerMetrics\").onlyIfTrue()\n            .relativeToRootProject(\"compose-metrics\")\n            .let(metricsDestination::set)\n\n        project.providers.gradleProperty(\"enableComposeCompilerReports\").onlyIfTrue()\n            .relativeToRootProject(\"compose-reports\")\n            .let(reportsDestination::set)\n\n        // We include source information to match how the main Compose libraries are shipped.\n        // This allows accompanist to be displayed properly in the layout inspector and systrace\n        includeSourceInformation.set(true)\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/google/accompanist/BundleInsideHelper.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist\n\nimport com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar\nimport com.github.jengelman.gradle.plugins.shadow.transformers.Transformer\nimport com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext\nimport org.apache.tools.zip.ZipOutputStream\nimport org.gradle.api.Action\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.Configuration\nimport org.gradle.api.attributes.Usage\nimport org.gradle.api.file.FileTreeElement\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.Optional\nimport org.gradle.api.tasks.SourceSetContainer\nimport org.gradle.api.tasks.TaskProvider\nimport org.gradle.kotlin.dsl.named\nimport org.gradle.kotlin.dsl.register\n\n/**\n * Originally from https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:buildSrc/public/src/main/kotlin/androidx/build/BundleInsideHelper.kt\n * Small modifications based on gradle version\n */\n\n/** Allow java and Android libraries to bundle other projects inside the project jar/aar. */\nobject BundleInsideHelper {\n    val CONFIGURATION_NAME = \"bundleInside\"\n    val REPACKAGE_TASK_NAME = \"repackageBundledJars\"\n\n    /**\n     * Creates a configuration for the users to use that will be used to bundle these dependency\n     * jars inside of libs/ directory inside of the aar.\n     *\n     * ```\n     * dependencies {\n     *   bundleInside(project(\":foo\"))\n     * }\n     * ```\n     *\n     * Used project are expected\n     *\n     * @param relocations a list of package relocations to apply\n     * @param dropResourcesWithSuffix used to drop Java resources if they match this suffix, null\n     *   means no filtering\n     * @receiver the project that should bundle jars specified by this configuration\n     * @see forInsideAar(String, String)\n     */\n    @JvmStatic\n    fun Project.forInsideAar(relocations: List<Relocation>?, dropResourcesWithSuffix: String?) {\n        val bundle = createBundleConfiguration()\n        val repackage = configureRepackageTaskForType(relocations, bundle, dropResourcesWithSuffix)\n        // Add to AGP's configuration so this jar get packaged inside of the aar.\n        dependencies.add(\"implementation\", files(repackage.flatMap { it.archiveFile }))\n    }\n\n    /**\n     * Creates 3 configurations for the users to use that will be used bundle these dependency jars\n     * inside of libs/ directory inside of the aar.\n     *\n     * ```\n     * dependencies {\n     *   bundleInside(project(\":foo\"))\n     * }\n     * ```\n     *\n     * Used project are expected\n     *\n     * @param from specifies from which package the rename should happen\n     * @param to specifies to which package to put the renamed classes\n     * @param dropResourcesWithSuffix used to drop Java resources if they match this suffix, null\n     *   means no filtering\n     * @receiver the project that should bundle jars specified by these configurations\n     */\n    @JvmStatic\n    fun Project.forInsideAar(from: String, to: String, dropResourcesWithSuffix: String?) {\n        forInsideAar(listOf(Relocation(from, to)), dropResourcesWithSuffix)\n    }\n\n    /**\n     * Creates a configuration for users to use that will bundle the dependency jars inside of this\n     * lint check's jar. This is required because lintPublish does not currently support\n     * dependencies, so instead we need to bundle any dependencies with the lint jar manually.\n     * (b/182319899)\n     *\n     * ```\n     * dependencies {\n     *     if (rootProject.hasProperty(\"android.injected.invoked.from.ide\")) {\n     *         compileOnly(LINT_API_LATEST)\n     *     } else {\n     *         compileOnly(LINT_API_MIN)\n     *     }\n     *     compileOnly(KOTLIN_STDLIB)\n     *     // Include this library inside the resulting lint jar\n     *     bundleInside(project(\":foo-lint-utils\"))\n     * }\n     * ```\n     *\n     * @receiver the project that should bundle jars specified by these configurations\n     */\n    @JvmStatic\n    fun Project.forInsideLintJar(): Configuration {\n        val bundle = createBundleConfiguration()\n        val compileOnly = configurations.getByName(\"compileOnly\")\n        val testImplementation = configurations.getByName(\"testImplementation\")\n\n        compileOnly.extendsFrom(bundle)\n        testImplementation.extendsFrom(bundle)\n\n        // Relocation needed to avoid classpath conflicts with Android Studio (b/337980250)\n        // Can be removed if we migrate from using kotlin-metadata-jvm inside of lint checks\n        val relocations = listOf(Relocation(\"kotlin.metadata\", \"androidx.lint.kotlin.metadata\"))\n        val repackage = configureRepackageTaskForType(relocations, bundle, null)\n        val sourceSets = extensions.getByType(SourceSetContainer::class.java)\n        repackage.configure {\n            this.from(sourceSets.findByName(\"main\")?.output)\n            // kotlin-metadata-jvm has a service descriptor that needs transformation\n            this.mergeServiceFiles()\n            // Exclude Kotlin metadata files from kotlin-metadata-jvm\n            this.exclude(\n                \"META-INF/kotlin-metadata-jvm.kotlin_module\",\n                \"META-INF/kotlin-metadata.kotlin_module\",\n                \"META-INF/metadata.jvm.kotlin_module\",\n                \"META-INF/metadata.kotlin_module\"\n            )\n        }\n\n        listOf(\"apiElements\", \"runtimeElements\").forEach { config ->\n            configurations.getByName(config).apply {\n                outgoing.artifacts.clear()\n                outgoing.artifact(repackage)\n            }\n        }\n\n        return bundle\n    }\n\n    data class Relocation(val from: String, val to: String)\n\n    private fun Project.configureRepackageTaskForType(\n        relocations: List<Relocation>?,\n        configuration: Configuration,\n        dropResourcesWithSuffix: String?\n    ): TaskProvider<ShadowJar> {\n        val action = Action<ShadowJar> {\n            configurations = listOf(configuration)\n            if (relocations != null) {\n                for (relocation in relocations) {\n                    println(\"Relocating ${relocation.from} to ${relocation.to}\")\n                    relocate(relocation.from, relocation.to)\n                }\n            }\n            val dontIncludeResourceTransformer = DontIncludeResourceTransformer()\n            dontIncludeResourceTransformer.dropResourcesWithSuffix = dropResourcesWithSuffix\n            transformers.add(dontIncludeResourceTransformer)\n            archiveBaseName.set(\"repackaged\")\n            archiveVersion.set(\"\")\n            destinationDirectory.set(layout.buildDirectory.dir(\"repackaged\"))\n        }\n        return tasks.register(REPACKAGE_TASK_NAME, ShadowJar::class.java, action)\n    }\n\n    private fun Project.createBundleConfiguration(): Configuration {\n        val bundle =\n            configurations.create(CONFIGURATION_NAME) {\n                attributes {\n                    attribute(Usage.USAGE_ATTRIBUTE, objects.named<Usage>(Usage.JAVA_RUNTIME))\n                }\n                isCanBeConsumed = false\n            }\n        return bundle\n    }\n\n    class DontIncludeResourceTransformer : Transformer {\n        @Optional @Input var dropResourcesWithSuffix: String? = null\n\n        override fun getName(): String {\n            return \"DontIncludeResourceTransformer\"\n        }\n\n        override fun canTransformResource(element: FileTreeElement?): Boolean {\n            val path = element?.relativePath?.pathString\n            return dropResourcesWithSuffix != null &&\n                (path?.endsWith(dropResourcesWithSuffix!!) == true)\n        }\n\n        override fun transform(context: TransformerContext?) {\n            // no op\n        }\n\n        override fun hasTransformedResource(): Boolean {\n            return true\n        }\n\n        override fun modifyOutputStream(zipOutputStream: ZipOutputStream?, b: Boolean) {\n            // no op\n        }\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/google/accompanist/KotlinAndroid.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist\n\nimport com.android.build.api.dsl.CommonExtension\nimport org.gradle.api.JavaVersion\nimport org.gradle.api.Project\nimport org.gradle.api.plugins.JavaPluginExtension\nimport org.gradle.kotlin.dsl.assign\nimport org.gradle.kotlin.dsl.configure\nimport org.gradle.kotlin.dsl.dependencies\nimport org.gradle.kotlin.dsl.provideDelegate\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension\nimport org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension\nimport org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension\n\n/**\n * Configure base Kotlin with Android options\n */\ninternal fun Project.configureKotlinAndroid(\n    commonExtension: CommonExtension<*, *, *, *, *, *>,\n) {\n    commonExtension.apply {\n        compileSdk = 35\n\n        defaultConfig {\n            minSdk = 21\n        }\n\n        compileOptions {\n            sourceCompatibility = JavaVersion.VERSION_1_8\n            targetCompatibility = JavaVersion.VERSION_1_8\n        }\n    }\n\n    configureKotlin<KotlinAndroidProjectExtension>()\n}\n\n/**\n * Configure base Kotlin options\n */\nprivate inline fun <reified T : KotlinTopLevelExtension> Project.configureKotlin() = configure<T> {\n    // Treat all Kotlin warnings as errors (disabled by default)\n    // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties\n    val warningsAsErrors: String? by project\n    when (this) {\n        is KotlinAndroidProjectExtension -> compilerOptions\n        is KotlinJvmProjectExtension -> compilerOptions\n        else -> TODO(\"Unsupported project extension $this ${T::class}\")\n    }.apply {\n        jvmTarget = JvmTarget.JVM_1_8\n        allWarningsAsErrors = warningsAsErrors.toBoolean()\n        explicitApi()\n    }\n}\n"
  },
  {
    "path": "build-logic/convention/src/main/kotlin/com/google/accompanist/ProjectExtensions.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist\n\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.VersionCatalog\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.kotlin.dsl.getByType\n\nval Project.libs\n    get(): VersionCatalog = extensions.getByType<VersionCatalogsExtension>().named(\"libs\")"
  },
  {
    "path": "build-logic/gradle.properties",
    "content": "# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534\norg.gradle.parallel=true\norg.gradle.caching=true\norg.gradle.configureondemand=true\n\n"
  },
  {
    "path": "build-logic/settings.gradle.kts",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\ndependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n    versionCatalogs {\n        create(\"libs\") {\n            from(files(\"../gradle/libs.versions.toml\"))\n        }\n    }\n}\n\nrootProject.name = \"build-logic\"\ninclude(\":convention\")\n"
  },
  {
    "path": "build.gradle",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport org.jetbrains.dokka.gradle.DokkaMultiModuleTask\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath libs.affectedmoduledetector\n    }\n}\n\nplugins {\n    id \"com.diffplug.spotless\" version \"6.5.2\"\n    alias(libs.plugins.android.application) apply false\n    alias(libs.plugins.android.library) apply false\n    alias(libs.plugins.android.kotlin) apply false\n    alias(libs.plugins.jetbrains.dokka)\n    alias(libs.plugins.compose.plugin) apply false\n    alias(libs.plugins.accompanist.android.library) apply false\n    alias(libs.plugins.accompanist.android.library.compose) apply false\n    alias(libs.plugins.accompanist.android.library.published) apply false\n    alias(libs.plugins.accompanist.android.lint) apply false\n    alias(libs.plugins.gradle.metalava) apply false\n    alias(libs.plugins.vanniktech.maven.publish) apply false\n}\n\napply plugin: 'com.dropbox.affectedmoduledetector'\napply plugin: 'com.diffplug.spotless'\n\ntasks.withType(DokkaMultiModuleTask).configureEach {\n    outputDirectory = rootProject.file('docs/api')\n    failOnWarning = true\n}\n\naffectedModuleDetector {\n    baseDir = \"${project.rootDir}\"\n    pathsAffectingAllModules = [\n            \"gradle/libs.versions.toml\",\n    ]\n    excludedModules = [\n            \"sample\"\n    ]\n\n    logFilename = \"output.log\"\n    logFolder = \"${rootProject.buildDir}/affectedModuleDetector\"\n\n    String baseRef = findProperty(\"affected_base_ref\")\n    // If we have a base ref to diff against, extract the branch name and use it\n    if (baseRef != null && !baseRef.isEmpty()) {\n        // Remove the prefix from the head.\n        // TODO: need to support other types of git refs\n        specifiedBranch = baseRef.replace('refs/heads/', '')\n        compareFrom = \"SpecifiedBranchCommit\"\n    } else {\n        // Otherwise we use the previous commit. This is mostly used for commits to main.\n        compareFrom = \"PreviousCommit\"\n    }\n}\n\nsubprojects {\n    apply plugin: 'com.diffplug.spotless'\n\n    spotless {\n        kotlin {\n            target \"**/*.kt\"\n            ktlint(libs.versions.ktlint.get())\n            licenseHeaderFile rootProject.file('spotless/copyright.txt')\n        }\n\n        groovyGradle {\n            target '**/*.gradle'\n            greclipse().configFile(rootProject.file('spotless/greclipse.properties'))\n            licenseHeaderFile rootProject.file('spotless/copyright.txt'),\n                    '(buildscript|apply|import|plugins)'\n        }\n    }\n\n    // Remove all test apps after running UI tests.\n    // This is specially important in CI so that test emulators don't run out of space.\n    tasks.whenTaskAdded { task ->\n        if (task.name == 'connectedDebugAndroidTest') {\n            task.finalizedBy 'uninstallDebugAndroidTest'\n        }\n    }\n\n    configurations.configureEach {\n        resolutionStrategy.eachDependency { DependencyResolveDetails details ->\n            // Make sure that we're using the Android version of Guava\n            if (details.requested.group == 'com.google.guava'\n                    && details.requested.module.name == 'guava'\n                    && details.requested.version.contains('jre')) {\n                details.useVersion details.requested.version.replace('jre', 'android')\n            }\n        }\n    }\n\n    // Read in the signing.properties file if it is exists\n    def signingPropsFile = rootProject.file('release/signing.properties')\n    if (signingPropsFile.exists()) {\n        def localProperties = new Properties()\n        signingPropsFile.withInputStream { is -> localProperties.load(is) }\n        localProperties.each { prop ->\n            if (prop.key == \"signing.secretKeyRingFile\") {\n                // If this is the key ring, treat it as a relative path\n                project.ext.set(prop.key, rootProject.file(prop.value).absolutePath)\n            } else {\n                project.ext.set(prop.key, prop.value)\n            }\n        }\n    }\n\n    // Must be afterEvaluate or else com.vanniktech.maven.publish will overwrite our\n    // dokka and version configuration.\n    afterEvaluate {\n        if (tasks.findByName('dokkaHtmlPartial') == null) {\n            // If dokka isn't enabled on this module, skip\n            return\n        }\n\n        tasks.named('dokkaHtmlPartial') {\n            dokkaSourceSets.configureEach {\n                reportUndocumented.set(true)\n                skipEmptyPackages.set(true)\n                skipDeprecated.set(true)\n                jdkVersion.set(8)\n\n                // Add Android SDK packages\n                noAndroidSdkLink.set(false)\n\n                // Add samples from :sample module\n                samples.from(rootProject.file(\"sample/src/main/java/\"))\n\n                // AndroidX + Compose docs\n                externalDocumentationLink {\n                    url.set(new URL(\"https://developer.android.com/reference/\"))\n                    packageListUrl.set(new URL(\"https://developer.android.com/reference/androidx/package-list\"))\n                }\n                externalDocumentationLink {\n                    url.set(new URL(\"https://developer.android.com/reference/kotlin/\"))\n                    packageListUrl.set(new URL(\"https://developer.android.com/reference/kotlin/androidx/package-list\"))\n                }\n\n                sourceLink {\n                    localDirectory.set(project.file(\"src/main/java\"))\n                    // URL showing where the source code can be accessed through the web browser\n                    remoteUrl.set(new URL(\"https://github.com/google/accompanist/blob/main/${project.name}/src/main/java\"))\n                    // Suffix which is used to append the line number to the URL. Use #L for GitHub\n                    remoteLineSuffix.set(\"#L\")\n                }\n            }\n        }\n    }\n\n    afterEvaluate {\n        def composeSnapshot = libs.versions.composesnapshot.get()\n        if (composeSnapshot.length() > 1) {\n            // We're depending on a Jetpack Compose snapshot, update the library version name\n            // to indicate it's from a Compose snapshot\n            def versionName = project.properties.get('VERSION_NAME')\n            if (versionName.contains(\"SNAPSHOT\")) {\n                version = versionName.replace('-SNAPSHOT', \".compose-${composeSnapshot}-SNAPSHOT\")\n            }\n        }\n\n        if (!version.endsWith('SNAPSHOT')) {\n            // If we're not a SNAPSHOT library version, we fail the build if we're relying on\n            // any snapshot dependencies\n            configurations.configureEach { configuration ->\n                configuration.dependencies.configureEach { dependency ->\n                    if (dependency instanceof ProjectDependency) {\n                        // We don't care about internal project dependencies\n                        return\n                    }\n\n                    def depVersion = dependency.version\n                    if (depVersion != null && depVersion.endsWith('SNAPSHOT')) {\n                        throw new IllegalArgumentException(\n                                \"Using SNAPSHOT dependency with non-SNAPSHOT library version: $dependency\"\n                        )\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "checksum.sh",
    "content": "#!/bin/bash\n\n# Copyright 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nRESULT_FILE=$1\n\nif [ -f $RESULT_FILE ]; then\n  rm $RESULT_FILE\nfi\ntouch $RESULT_FILE\n\nchecksum_file() {\n  echo $(openssl md5 $1 | awk '{print $2}')\n}\n\nFILES=()\nwhile read -r -d ''; do\n\tFILES+=(\"$REPLY\")\ndone < <(find . -type f \\( -name \"build.gradle*\" -o -name \"*.versions.toml\" -o -name \"gradle-wrapper.properties\" \\) -print0)\n\n# Loop through files and append MD5 to result file\nfor FILE in ${FILES[@]}; do\n\techo $(checksum_file $FILE) >> $RESULT_FILE\ndone\n# Now sort the file so that it is idempotent\nsort $RESULT_FILE -o $RESULT_FILE\n"
  },
  {
    "path": "docs/adaptive.md",
    "content": "# Adaptive utilities for Jetpack Compose\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-adaptive)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nA library providing a collection of utilities for adaptive layouts.\n\n## calculateDisplayFeatures\n\n[`calculateDisplayFeatures(activity)`](../api/adaptive/com.google.accompanist.adaptive/calculate-display-features.html) returns the current list of `DisplayFeature`s,\nas reported by the [Jetpack WindowManager library](https://developer.android.com/jetpack/androidx/releases/window).\n\nThese contain the list of folds (if any), and can be used to drive components like [`TwoPane`](#TwoPane).\n\n## TwoPane\n\n[`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) is a UI component that positions exactly two slots on the screen.\n\nThe default positioning of these two slots is driven by a [`TwoPaneStrategy`](../api/adaptive/com.google.accompanist.adaptive/-two-pane-strategy.html),\nwhich can decide to orient the two slots side-by-side horizontally or vertically, and also configure the gap between them.\n\nThe built-in [`HorizontalTwoPaneStrategy`](../api/adaptive/com.google.accompanist.adaptive/-horizontal-two-pane-strategy.html) and\n[`VerticalTwoPaneStrategy`](../api/adaptive/com.google.accompanist.adaptive/-vertical-two-pane-strategy.html) allow positioning the\nslots based on a fixed offset, or as some fraction of the space.\n\n[`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) also requires a list of display features (to be retrieved with [`calculateDisplayFeatures`](#calculateDisplayFeatures)),\nand optionally a [`FoldAwareConfiguration`](../api/adaptive/com.google.accompanist.adaptive/-fold-aware-configuration.html) to determine which folds to handle automatically.\n\nWhen there is a fold that intersects with the [`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) component that is obscuring or separating,\nthe [`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) will automatically place the slots to avoid the fold.\n\nWhen there is no fold, the default supplied strategy will be used instead.\n\n## FoldAwareColumn\n\n[`FoldAwareColumn`](../api/adaptive/com.google.accompanist.adaptive/-fold-aware-column.html) is a simplified version of [Column](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#Column(androidx.compose.ui.Modifier,androidx.compose.foundation.layout.Arrangement.Vertical,androidx.compose.ui.Alignment.Horizontal,kotlin.Function1)) that places children in a fold-aware manner.\n\n[`FoldAwareColumn`](../api/adaptive/com.google.accompanist.adaptive/-fold-aware-column.html) requires a list of display features (to be retrieved with [`calculateDisplayFeatures`](#calculatedisplayfeatures)) to determine which folds to handle automatically.\n\nThe built-in `foldPadding` parameter is zero, and the values of the vertical padding are used in the layout determine how much space should be left around a fold when placing children.\n\nWhen there is a horizontal fold that is obscuring or separating, the layout will begin placing children from the top of the available space. If a child is projected to overlap the fold, then its y-coordinate is increased so it will be placed fully below the fold, as will any other remaining children.\n\nWhen there is no fold, the children will be placed consecutively with no y-coordinate adjustments.\n\nOptionally, children can be modified with the `ignoreFold()` attribute, which means that they will be placed as if no fold is present even if they overlap a fold.\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-adaptive)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-adaptive:<version>\"\n}\n```"
  },
  {
    "path": "docs/appcompat-theme.md",
    "content": "# AppCompat Compose Theme Adapter\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-appcompat-theme)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n!!! warning\n\t**This library is deprecated in favor of the new [`themeadapter-appcompat`][themeadapterappcompatlib] artifact.** The migration guide and original documentation is below.\n\n## Migration\n\nAccompanist AppCompat Theme Adapter has moved from the [`appcompat-theme`][appcompatthemelib] artifact to the [`themeadapter-appcompat`][themeadapterappcompatlib] artifact.\nThe implementation is identical but the dependency and import package have changed.\n\n### Migration steps\n\n1. Change the dependency from `com.google.accompanist:accompanist-appcompat-theme:<version>` to `com.google.accompanist:accompanist-themeadapter-appcompat:<version>`\n2. Change any `com.google.accompanist.appcompattheme.*` imports to `com.google.accompanist.themeadapter.appcompat.*`\n\n## Original Docs\n\nA library that enables reuse of [AppCompat][appcompat] XML themes for theming in [Jetpack Compose][compose].\n\nThe basis of theming in [Jetpack Compose][compose] is the [`MaterialTheme`][materialtheme] composable, where you provide [`Colors`][colors], [`Shapes`][shapes] and [`Typography`][typography] instances containing your styling parameters:\n\n``` kotlin\nMaterialTheme(\n    typography = type,\n    colors = colors,\n    shapes = shapes\n) {\n    // Surface, Scaffold, etc\n}\n```\n\n[AppCompat][appcompat] XML themes allow for similar but coarser theming via XML theme attributes, like so:\n\n``` xml\n<style name=\"Theme.MyApp\" parent=\"Theme.AppCompat.DayNight\">\n    <item name=\"colorPrimary\">@color/purple_500</item>\n    <item name=\"colorAccent\">@color/green_200</item>\n</style>\n```\n\nThis library attempts to bridge the gap between [AppCompat][appcompat] XML themes, and themes in [Jetpack Compose][compose], allowing your composable [`MaterialTheme`][materialtheme] to be based on the `Activity`'s XML theme:\n\n``` kotlin\nAppCompatTheme {\n    // MaterialTheme.colors, MaterialTheme.shapes, MaterialTheme.typography\n    // will now contain copies of the context's theme\n}\n```\n\nThis is especially handy when you're migrating an existing app, a fragment (or other UI container) at a time.\n\n!!! caution\n    If you are using [Material Design Components](https://material.io/develop/android/) in your app, you should use the\n    [MDC Compose Theme Adapter](https://github.com/material-components/material-components-android-compose-theme-adapter)\n    instead, as it allows much finer-grained reading of your theme.\n\n\n### Customizing the theme\n\nThe [`AppCompatTheme()`][appcompattheme] function will automatically read the host context's AppCompat theme and pass them to [`MaterialTheme`][materialtheme] on your behalf, but if you want to customize the generated values, you can do so via the [`createAppCompatTheme()`][createappcompattheme] function:\n\n``` kotlin\nval context = LocalContext.current\nvar (colors, type) = context.createAppCompatTheme()\n\n// Modify colors or type as required. Then pass them\n// through to MaterialTheme...\n\nMaterialTheme(\n    colors = colors,\n    typography = type\n) {\n    // rest of layout\n}\n```\n\n</details>\n\n## Generated theme\n\nSynthesizing a material theme from a `Theme.AppCompat` theme is not perfect, since `Theme.AppCompat`\ndoes not expose the same level of customization as is available in material theming.\nGoing through the pillars of material theming:\n\n### Colors\n\nAppCompat has a limited set of top-level color attributes, which means that [`AppCompatTheme()`][appcompattheme]\nhas to generate/select alternative colors in certain situations. The mapping is currently:\n\n| MaterialTheme color | AppCompat attribute                                            |\n|---------------------|-------------------------------------------------------|\n| primary             | `colorPrimary`                                          |\n| primaryVariant      | `colorPrimaryDark`                                      |\n| onPrimary           | Calculated black/white                                |\n| secondary           | `colorAccent`                                           |\n| secondaryVariant    | `colorAccent`                                           |\n| onSecondary         | Calculated black/white                                |\n| surface             | Default                                               |\n| onSurface           | `android:textColorPrimary`, else calculated black/white |\n| background          | `android:colorBackground`                               |\n| onBackground        | `android:textColorPrimary`, else calculated black/white |\n| error               | `colorError`                                            |\n| onError             | Calculated black/white                                |\n\nWhere the table says \"calculated black/white\", this means either black/white, depending on\nwhich provides the greatest contrast against the corresponding background color.\n\n### Typography\n\nAppCompat does not provide any semantic text appearances (such as headline6, body1, etc), and\ninstead relies on text appearances for specific widgets or use cases. As such, the only thing\nwe read from an AppCompat theme is the default `app:fontFamily` or `android:fontFamily`.\nFor example:\n\n``` xml\n<style name=\"Theme.MyApp\" parent=\"Theme.AppCompat\">\n    <item name=\"fontFamily\">@font/my_font</item>\n</style>\n```\n\nCompose does not currently support downloadable fonts, so any font referenced from the theme\nshould from your resources. See [here](https://developer.android.com/guide/topics/resources/font-resource)\nfor more information.\n\n### Shape\n\nAppCompat has no concept of shape theming, therefore we use the default value from\n[`MaterialTheme.shapes`][shapes]. If you wish to provide custom values, use the `shapes` parameter on `AppCompatTheme`.\n\n## Limitations\n\nThere are some known limitations with the implementation at the moment:\n\n* This relies on your `Activity`/`Context` theme extending one of the `Theme.AppCompat` themes.\n* Variable fonts are not supported in Compose yet, meaning that the value of `android:fontVariationSettings` are currently ignored.\n* You can modify the resulting `MaterialTheme` in Compose as required, but this _only_ works in Compose. Any changes you make will not be reflected in the Activity theme.\n\n---\n\n## Usage\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-appcompat-theme)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n``` groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-appcompat-theme:<version>\"\n}\n```\n\n### Library Snapshots\n\nSnapshots of the current development version of this library are available, which track the latest commit. See [here](../using-snapshot-version) for more information on how to use them.\n\n---\n\n## Contributions\n\nPlease contribute! We will gladly review any pull requests.\nMake sure to read the [Contributing](../contributing) page first though.\n\n## License\n\n```\nCopyright 2020 The Android Open Source Project\n \nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n [appcompatthemelib]: ../appcompat-theme\n [themeadapterappcompatlib]: ../themeadapter-appcompat\n [compose]: https://developer.android.com/jetpack/compose\n [appcompat]: https://developer.android.com/jetpack/androidx/releases/appcompat\n [appcompattheme]: ../api/appcompat-theme/appcompat-theme/com.google.accompanist.appcompattheme/-app-compat-theme.html\n [createappcompattheme]: ../api/appcompat-theme/appcompat-theme/com.google.accompanist.appcompattheme/create-app-compat-theme.html\n [materialtheme]: https://developer.android.com/reference/kotlin/androidx/compose/material/MaterialTheme\n [shapes]: https://developer.android.com/reference/kotlin/androidx/compose/material/Shapes\n [colors]: https://developer.android.com/reference/kotlin/androidx/compose/material/Colors\n [typography]: https://developer.android.com/reference/kotlin/androidx/compose/material/Typography"
  },
  {
    "path": "docs/drawablepainter.md",
    "content": "# Drawable Painter\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-drawablepainter)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nA library which provides a way to use Android [drawables](https://developer.android.com/guide/topics/resources/drawable-resource) as Jetpack Compose [Painters](https://developer.android.com/reference/kotlin/androidx/compose/ui/graphics/painter/Painter).\n\nThis library attempts to support most Drawable configuration, as well as [Animatable](https://developer.android.com/reference/android/graphics/drawable/Animatable) drawables, such as [AnimatedVectorDrawable](https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable).\n\n## Usage\n\n``` kotlin\n@Composable\nfun DrawDrawable() {\n    val drawable = AppCompatResources.getDrawable(LocalContext.current, R.drawable.rectangle)\n\n    Image(\n        painter = rememberDrawablePainter(drawable = drawable),\n        contentDescription = \"content description\",\n    )\n}\n```\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-drawablepainter)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-drawablepainter:<version>\"\n}\n```\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n[compose]: https://developer.android.com/jetpack/compose\n[snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-drawablepainter/\n"
  },
  {
    "path": "docs/migration.md",
    "content": "# Migration from dev.chrisbanes.accompanist\n\nIn March 2021, the Accompanist project moved from [github.com/chrisbanes/accompanist](https://github.com/chrisbanes/accompanist) to [github.com/google/accompanist](https://github.com/google/accompanist). At the same time we migrated the libraries over to a new package name and Maven group ID.\n\nAs a summary:\n\n- All code was refactored from the `dev.chrisbanes.accompanist` root package to `com.google.accompanist` package.\n- The Maven group ID was changed from `dev.chrisbanes.accompanist` to `com.google.accompanist`.\n\n## Semi-automatic migration...\n\nThe following methods below are available for your information only, but may help if you need to migrate from the old package name. \n\n!!! warning\n    Use these at your own risk, but they have worked on multiple projects from my testing. It's a good idea to make sure that you've made a backup or committed any changes before running these.\n\n### Android Studio / IntelliJ\n\nYou can use the [Replace in Path](https://www.jetbrains.com/help/idea/finding-and-replacing-text-in-project.html#replace_search_string_in_project) pane (⇧⌘R on Mac) in Android Studio to do a project-wide search and replace.\n\n![Android Studio Replace in Path pane](studio.png)\n\n- Find query: `dev.chrisbanes.accompanist`\n- Replace string: `com.google.accompanist`\n- _Optional:_ Set the file mask to `*.kt` so that only Kotlin files are searched. Repeat for `*.gradle`.\n\nSimilar can be achieved in [Visual Studio Code](https://code.visualstudio.com/docs/editor/codebasics#_search-across-files). Other IDEs / text editors are available.\n\n### YOLO commands\n\nThese commands while automatically replace any imports and Gradle dependencies for the project in the current directory.\n\n#### MacOS\n\n``` bash\nfind . -type f \\( -name '*.kt' -or -name '*.gradle*' \\) \\\n    -exec sed -i '' 's/dev\\.chrisbanes\\.accompanist/com\\.google\\.accompanist/' {} \\;\n```\n\n#### Linux\n\n``` bash\nfind . -type f \\( -name '*.kt' -or -name '*.gradle*' \\) \\\n    -exec sed -i 's/dev\\.chrisbanes\\.accompanist/com\\.google\\.accompanist/' {} \\;\n```\n"
  },
  {
    "path": "docs/navigation-animation.md",
    "content": "# Jetpack Navigation Compose Animation\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-navigation-animation)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nA library which provides [Compose Animation](https://developer.android.com/jetpack/compose/animation) support for [Jetpack Navigation Compose](https://developer.android.com/jetpack/compose/navigation).\n\n!!! warning\n    **This library is deprecated, with official navigation-compose support in [androidx.navigation.compose](https://developer.android.com/jetpack/compose/navigation).** The original documentation is below the migration guide.\n\n## Migration\n\nThe official `androidx.navigation.compose` version 2.7.0-alpha01+ offers all of the same functionality as Accompanist Navigation Animation.\n\n1. Make sure you are using Compose 1.5.0-beta01+ before migrating to `androidx.navigation.compose`.\n2. Replace dependency `com.google.accompanist:accompanist-navigation-animation:<version>` with `androidx.navigation:navigation-compose:<version>`\n3. Replace `rememberAnimatedNavController` with `rememberNavController` and change import to `androidx.navigation.compose.rememberNavController`\n4. Replace `AnimatedNavHost` with `NavHost` and change import to `androidx.navigation.compose.NavHost`\n5. Replace `AnimatedComposeNavigator` with `ComposeNavigator` and change import to `androidx.navigation.compose.ComposeNavigator`\n6. Replace `AnimatedComposeNavigator()` constructor with `ComposeNavigator()` constructor\n7. Replace `AnimatedComposeNavigator.Destination` with `ComposeNavigator.Destination`\n8. Change import for composable from `com.google.accompanist.navigation.animation.composable` to `androidx.navigation.compose.composable`\n9. Change import for navigation from `com.google.accompanist.navigation.animation.navigation` to `androidx.navigation.compose.navigation`\n\n## Migration Table\n\nThe following is a mapping of Navigation classes and functions from accompanist to androidx.compose:\n\n| accompanist navigation-animation       | androidx.navigation.compose    |\n|----------------------------------------|--------------------------------|\n| `AnimatedNavHost`                      | `NavHost`                      |\n| `AnimatedComposeNavigator`             | `ComposeNavigator`             |\n| `AnimatedComposeNavigator.Destination` | `ComposeNavigator.Destination` |\n| `rememberAnimatedNavController()`      | `rememberNavController()`      |\n| `NavGraphBuilder.composable()`         | `NavGraphBuilder.composable()` |\n| `NavGraphBuilder.navigation()`         | `NavGraphBuilder.navigation()` |\n\nOf note, ComposeNavigation.Destination allows use of `AnimatedContentScope` instead of just `AnimatedVisibilityScope`.\n\n# Deprecated Guidance for Accompanist Navigation\n\nThe following is the deprecated guide for using Navigation in Accompanist. Please see above migration section for how to use the `androidx.navigation.compose` Navigation.\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-navigation-animation)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-navigation-animation:<version>\"\n}\n```\n\nFollow the steps below to either [add](#usage) Jetpack Navigation Compose to your app, or to [migrate](#migration) an existing Jetpack Navigation Compose implementation.\n\n## Usage\n\nThe `AnimatedNavHost` composable offers a way to add custom transitions to composables in\nNavigation Compose via parameters that can be attached to either an individual `composable`\ndestination, a `navigation` element, or to the `AnimatedNavHost` itself.\n\nEach lambda has an [`AnimatedContentScope<NavBackStackEntry>`](https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope) receiver scope that allows you to use special transitions (such as [`slideIntoContainer`](https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope#slideIntoContainer(androidx.compose.animation.AnimatedContentScope.SlideDirection,androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function1)) and [`slideOutOfContainer`](https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope#slideOutOfContainer(androidx.compose.animation.AnimatedContentScope.SlideDirection,androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function1))) and gives you access to the [`initialState`](https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope#initialState()) and [`targetState`](https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope#targetState()) properties that let you customize what transitions are run based on what screen you are transitioning from (the `initialState`) and transitioning to (the `targetState`). \n\n- `enterTransition` controls what [`EnterTransition`](https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition.html) is run when the `targetState` `NavBackStackEntry` is appearing on the screen.\n- `exitTransition` controls what [`ExitTransition`](https://developer.android.com/reference/kotlin/androidx/compose/animation/ExitTransition) is run when the `initialState` `NavBackStackEntry` is disappearing from the screen.\n- `popEnterTransition` defaults to `enterTransition`, but can be overridden to provide a separate [`EnterTransition`](https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition.html) when the `targetState` `NavBackStackEntry` is appearing on the screen due to a pop operation (i.e., `popBackStack()`).\n- `popExitTransition` defaults to `exitTransition`, but can be overridden to provide a separate [`ExitTransition`](https://developer.android.com/reference/kotlin/androidx/compose/animation/ExitTransition) when the `initialState` `NavBackStackEntry` is disappearing from the screen due to a pop operation (i.e., `popBackStack()`).\n\nFor each transition, if a `composable` destination returns `null`, the parent `navigation` element's transition will be used, thus allowing you to set a global set of transitions at the navigation graph level that will apply to every `composable` in that graph. This continues up the hierarchy until you reach the root `AnimatedNavHost`, which controls the global transitions for all destinations and nested graphs that do not specify one.\n\nNote: this means that if a destination wants to instantly jump cut between destinations, it should return [`EnterTransition.None`](https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition#None()) or [`ExitTransition.None`](https://developer.android.com/reference/kotlin/androidx/compose/animation/ExitTransition#None()) to signify that no transition should be run, rather than return `null`.\n\n```kotlin\n@Composable\nprivate fun ExperimentalAnimationNav() {\n    val navController = rememberAnimatedNavController()\n    AnimatedNavHost(navController, startDestination = \"Blue\") {\n        composable(\n            \"Blue\",\n            enterTransition = {\n                when (initialState.destination.route) {\n                    \"Red\" ->\n                        slideIntoContainer(AnimatedContentScope.SlideDirection.Left, animationSpec = tween(700))\n                    else -> null\n                }\n            },\n            exitTransition = {\n                when (targetState.destination.route) {\n                    \"Red\" ->\n                        slideOutOfContainer(AnimatedContentScope.SlideDirection.Left, animationSpec = tween(700))\n                     else -> null\n                }\n            },\n            popEnterTransition = {\n                            when (initialState.destination.route) {\n                                \"Red\" ->\n                                    slideIntoContainer(AnimatedContentScope.SlideDirection.Right, animationSpec = tween(700))\n                                else -> null\n                            }\n                        },\n            popExitTransition = {\n                when (targetState.destination.route) {\n                    \"Red\" ->\n                        slideOutOfContainer(AnimatedContentScope.SlideDirection.Right, animationSpec = tween(700))\n                    else -> null\n                }\n            }\n        ) { BlueScreen(navController) }\n        composable(\n            \"Red\",\n            enterTransition = {\n                when (initialState.destination.route) {\n                    \"Blue\" ->\n                        slideIntoContainer(AnimatedContentScope.SlideDirection.Left, animationSpec = tween(700))\n                    else -> null\n                }\n            },\n            exitTransition = {\n                when (targetState.destination.route) {\n                    \"Blue\" ->\n                        slideOutOfContainer(AnimatedContentScope.SlideDirection.Left, animationSpec = tween(700))\n                    else -> null\n                }\n            },\n            popEnterTransition = {\n                when (initialState.destination.route) {\n                    \"Blue\" ->\n                        slideIntoContainer(AnimatedContentScope.SlideDirection.Right, animationSpec = tween(700))\n                    else -> null\n                }\n            },\n            popExitTransition = {\n                when (targetState.destination.route) {\n                    \"Blue\" ->\n                        slideOutOfContainer(AnimatedContentScope.SlideDirection.Right, animationSpec = tween(700))\n                    else -> null\n                }\n            }\n        ) { RedScreen(navController) }\n    }\n}\n```\n\nFor more examples, refer to the [samples](https://github.com/google/accompanist/tree/main/sample/src/main/java/com/google/accompanist/sample/navigation/animation).\n\n## Migration\n\nTo migrate from using the Navigation Compose APIs do the following:\n\n* Replace `rememberNavController()` with `rememberAnimatedNavController()`\n* Replace `NavHost` with `AnimatedNavHost`\n* Replace `import androidx.navigation.compose.navigation` with `import com.google.accompanist.navigation.animation.navigation`\n* Replace `import androidx.navigation.compose.composable` with `import com.google.accompanist.navigation.animation.composable`\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n[compose]: https://developer.android.com/jetpack/compose\n[snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-navigation-animation/\n\nFor more details see [Animations in Navigation Compose](https://medium.com/androiddevelopers/animations-in-navigation-compose-36d48870776b)\n"
  },
  {
    "path": "docs/navigation-material.md",
    "content": "# Jetpack Navigation Compose Material\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-navigation-material)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nA library which provides [Compose Material](https://developer.android.com/jetpack/androidx/releases/compose-material) support for [Jetpack Navigation Compose](https://developer.android.com/jetpack/compose/navigation).\nThis features composable bottom sheet destinations.\n\n!!! warning\n    **This library is deprecated, with official material-navigation support in [androidx.compose.material.navigation](https://developer.android.com/jetpack/androidx/releases/compose-material#1.7.0-alpha04).** The original documentation is below the migration guide.\n\n## Migration\n\nThe official `androidx.compose.material.navigation` version 1.7.0-alpha04+ offers all of the same functionality as Accompanist Navigation Material.\n\nAll class names are the same, the only needed changes are import related.\n\n1. Replace dependency `com.google.accompanist:accompanist-navigation-material:<version>` with `androidx.compose.material:material-navigation:<version>`\n2. Change import for ModalBottomSheetLayout from `com.google.accompanist.navigation.material.ModalBottomSheetLayout` to `androidx.compose.material.navigation.ModalBottomSheetLayout`\n3. Change import for bottomSheet from `com.google.accompanist.navigation.material.bottomSheet` to `androidx.compose.material.navigation.bottomSheet`\n4. Change import for rememberBottomSheetNavigator from `com.google.accompanist.navigation.material.rememberBottomSheetNavigator` to `androidx.compose.material.navigation.rememberBottomSheetNavigator`\n5. Change import for BottomSheetNavigator from `com.google.accompanist.navigation.material.BottomSheetNavigator` to `androidx.compose.material.navigation.BottomSheetNavigator`\n6. Change import for BottomSheetNavigatorSheetState from `com.google.accompanist.navigation.material.BottomSheetNavigatorSheetState` to `androidx.compose.material.navigation.BottomSheetNavigatorSheetState`\n\n# Deprecated Guidance for Accompanist Navigation Material\n\nThe following is the deprecated guide for using Navigation Material in Accompanist. Please see above migration section for how to use the `androidx.compose.material.navigation` Material Navigation.\n\n## Usage\n\n### Bottom Sheet Destinations\n\n1. Create a `BottomSheetNavigator` and add it to the `NavController`:\n\n    ```kotlin\n    @Composable\n    fun MyApp() {\n        val bottomSheetNavigator = rememberBottomSheetNavigator()\n        val navController = rememberNavController(bottomSheetNavigator)\n    }\n    ```\n\n2. Wrap your `NavHost` in the `ModalBottomSheetLayout` composable that accepts a `BottomSheetNavigator`.\n\n    ```kotlin\n    @Composable\n    fun MyApp() {\n        val bottomSheetNavigator = rememberBottomSheetNavigator()\n        val navController = rememberNavController(bottomSheetNavigator)\n        ModalBottomSheetLayout(bottomSheetNavigator) {\n            NavHost(navController, \"home\") {\n               // We'll define our graph here in a bit!\n            }\n        }\n    }\n    ```\n\n3. Register a bottom sheet destination\n\n    ```kotlin\n    @Composable\n    fun MyApp() {\n        val bottomSheetNavigator = rememberBottomSheetNavigator()\n        val navController = rememberNavController(bottomSheetNavigator)\n        ModalBottomSheetLayout(bottomSheetNavigator) {\n            NavHost(navController, \"home\") {\n               composable(route = \"home\") {\n                   ...\n               }\n               bottomSheet(route = \"sheet\") {\n                   Text(\"This is a cool bottom sheet!\")\n               }\n            }\n        }\n    }\n    ```\n\nFor more examples, refer to the [samples](https://github.com/google/accompanist/tree/main/sample/src/main/java/com/google/accompanist/sample/navigation/material).\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-navigation-material)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-navigation-material:<version>\"\n}\n```\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n[compose]: https://developer.android.com/jetpack/compose\n[snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-navigation-material/\n"
  },
  {
    "path": "docs/permissions.md",
    "content": "# Jetpack Compose Permissions\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nA library which provides [Android runtime permissions](https://developer.android.com/guide/topics/permissions/overview) support for Jetpack Compose.\n\n!!! warning\n    The permission APIs are currently experimental and they could change at any time.\n    All of the APIs are marked with the `@ExperimentalPermissionsApi` annotation.\n\n## Usage\n\n### `rememberPermissionState` and `rememberMultiplePermissionsState` APIs\n\nThe `rememberPermissionState(permission: String)` API allows you to request a certain permission\nto the user and check for the status of the permission.\n`rememberMultiplePermissionsState(permissions: List<String>)` offers the same but for multiple\npermissions at the same time.\n\nBoth APIs expose properties for you to follow the workflow as described in the\n[permissions documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n\n!!! caution\n    The call to the method that requests the permission to the user (e.g. `PermissionState.launchPermissionRequest()`)\n    needs to be invoked from a non-composable scope. For example, from a side-effect or from a\n    non-composable callback such as a `Button`'s `onClick` lambda.\n\nThe following code exercises the [permission request workflow](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n\n```kotlin\n@OptIn(ExperimentalPermissionsApi::class)\n@Composable\nprivate fun FeatureThatRequiresCameraPermission() {\n\n    // Camera permission state\n    val cameraPermissionState = rememberPermissionState(\n        android.Manifest.permission.CAMERA\n    )\n\n    if (cameraPermissionState.status.isGranted) {\n        Text(\"Camera permission Granted\")\n    } else {\n        Column {\n            val textToShow = if (cameraPermissionState.status.shouldShowRationale) {\n                // If the user has denied the permission but the rationale can be shown,\n                // then gently explain why the app requires this permission\n                \"The camera is important for this app. Please grant the permission.\"\n            } else {\n                // If it's the first time the user lands on this feature, or the user\n                // doesn't want to be asked again for this permission, explain that the\n                // permission is required\n                \"Camera permission required for this feature to be available. \" +\n                    \"Please grant the permission\"\n            }\n            Text(textToShow)\n            Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {\n                Text(\"Request permission\")\n            }\n        }\n    }\n}\n```\n\nFor more examples, refer to the [samples](https://github.com/google/accompanist/tree/main/sample/src/main/java/com/google/accompanist/sample/permissions).\n\n## Limitations\n\nThis permissions wrapper is built on top of the available Android platform APIs. We cannot extend\nthe platform's capabilities. For example, it's not possible to differentiate between the\n_it's the first time requesting the permission_ vs _the user doesn't want to be asked again_\nuse cases.\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-permissions:<version>\"\n}\n```\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n[compose]: https://developer.android.com/jetpack/compose\n[snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-permissions/\n"
  },
  {
    "path": "docs/systemuicontroller.md",
    "content": "# System UI Controller for Jetpack Compose\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-systemuicontroller)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n!!! warning\n    **This library is deprecated, and the API is no longer maintained. We recommend forking the implementation and customising it to your needs.** The original documentation is below.\n\n## Migration\nRecommendation: If you were using SystemUIController to go edge-to-edge in your activity and change the system bar colors and system bar icon colors, use the new [Activity.enableEdgeToEdge](https://developer.android.com/reference/androidx/activity/ComponentActivity#(androidx.activity.ComponentActivity).enableEdgeToEdge(androidx.activity.SystemBarStyle,androidx.activity.SystemBarStyle)) method available in androidx.activity 1.8.0-alpha03 and later. This method backports the scrims used on some versions of Android. [This](https://github.com/android/nowinandroid/pull/817) is a sample PR of the migration to the new method and removing the dependency on SystemUIController in Now in Android.\n\nFor other usages, migrate to using WindowInsetsControllerCompat or window APIs directly.\n\n## Original Documentation\nSystem UI Controller provides easy-to-use utilities for updating the System UI bar colors within Jetpack Compose.\n\n## Usage\nTo control the system UI in your composables, you need to get a [`SystemUiController`](../api/systemuicontroller/systemuicontroller/com.google.accompanist.systemuicontroller/-system-ui-controller/) instance. The library provides the [`rememberSystemUiController()`](../api/systemuicontroller/systemuicontroller/com.google.accompanist.systemuicontroller/remember-system-ui-controller.html) function which returns an instance for the current system (currently only Android).\n\nIn your layouts you can update the system bar colors like so:\n\n``` kotlin\n// Remember a SystemUiController\nval systemUiController = rememberSystemUiController()\nval useDarkIcons = !isSystemInDarkTheme()\n\nDisposableEffect(systemUiController, useDarkIcons) {\n    // Update all of the system bar colors to be transparent, and use\n    // dark icons if we're in light theme\n    systemUiController.setSystemBarsColor(\n        color = Color.Transparent,\n        darkIcons = useDarkIcons\n    )\n\n    // setStatusBarColor() and setNavigationBarColor() also exist\n\n    onDispose {}\n}\n```\n\n## System bar icon colors\nThe library automatically handles API level differences when running on Android devices. If we look at the example\nof status bar icons, Android only natively supports dark icons on API 23+. This library handles this by automatically\naltering the requested color with a scrim, to maintain contrast:\n\n![](api-scrim.png)\n\nSimilar happens on navigation bar color, which is only available on API 26+.\n\n### Modifying scrim logic\n\nThe scrim logic can be modified if needed:\n\n``` kotlin\nsystemUiController.setStatusBarColor(\n    color = Color.Transparent,\n    darkIcons = true\n) { requestedColor ->\n    // TODO: return a darkened color to be used when the system doesn't\n    // natively support dark icons\n}\n```\n\n## Samples\n\nFor complete samples, check out the [Insets samples](https://github.com/google/accompanist/tree/main/sample/src/main/java/com/google/accompanist/sample/insets) which all use `SystemUiController` to set transparent system bars.\n\n## Download\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-systemuicontroller)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-systemuicontroller:<version>\"\n}\n```\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n[compose]: https://developer.android.com/jetpack/compose\n[snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-systemuicontroller/\n"
  },
  {
    "path": "docs/updating.md",
    "content": "# Updating & releasing Accompanist\n\nThis doc is mostly for maintainers.\n\n## New features & bugfixes\nAll new features should be uploaded as PRs against the `main` branch. \n\nOnce merged into `main`, they will be automatically merged into the `snapshot` branch.\n\n## Jetpack Compose Snapshots\n\nWe publish snapshot versions of Accompanist, which depend on a `SNAPSHOT` versions of Jetpack Compose. These are built from the `snapshot` branch.\n\n### Updating to a newer Compose snapshot\n\nAs mentioned above, updating to a new Compose snapshot is done by submitting a new PR against the `snapshot` branch:\n\n``` sh\ngit checkout snapshot && git pull\n# Create branch for PR\ngit checkout -b update_snapshot\n```\n\nNow edit the project to depend on the new Compose SNAPSHOT version:\n\nEdit [`/gradle/libs.versions.toml`](https://github.com/google/accompanist/blob/main/gradle/libs.versions.toml):\n\nUnder `[versions]`:\n\n1. Update the `composesnapshot` property to be the snapshot number\n2. Ensure that the `compose` property is correct\n\nMake sure the project builds and test pass:\n```\n./gradlew check\n```\n\nNow `git commit` the changes and push to GitHub.\n\nFinally create a PR (with the base branch as `snapshot`) and send for review.\n\n## Releasing\n\nOnce the next Jetpack Compose version is out, we're ready to push a new release:\n\n### #1: Merge `snapshot` into `main`\n\nFirst we merge the `snapshot` branch into `main`:\n\n``` sh\ngit checkout snapshot && git pull\ngit checkout main && git pull\n\n# Create branch for PR\ngit checkout -b main_snapshot_merge\n\n# Merge in the snapshot branch\ngit merge snapshot\n```\n\n### #2: Update dependencies\n\nEdit [`/gradle/libs.versions.toml`](https://github.com/google/accompanist/blob/main/gradle/libs.versions.toml):\n\nUnder `[versions]`:\n\n1. Update the `composesnapshot` property to a single character (usually `-`). This disables the snapshot repository.\n2. Update the `compose` property to match the new release (i.e. `1.0.0-beta06`)\n\nMake sure the project builds and test pass:\n```\n./gradlew check\n```\n\nCommit the changes.\n\n### #3: Bump the version number\n\nEdit [gradle.properties](https://github.com/google/accompanist/blob/main/gradle.properties):\n\n * Update the `VERSION_NAME` property and remove the `-SNAPSHOT` suffix.\n\nCommit the changes, using the commit message containing the new version name.\n\n### #4: Push to GitHub\n\nPush the branch to GitHub and create a PR against the `main` branch, and send for review. Once approved and merged, it will be automatically deployed to Maven Central.\n\n### #5: Create release\n\nOnce the above PR has been approved and merged, we need to create the GitHub release:\n\n * Open up the [Releases](https://github.com/google/accompanist/releases) page.\n * At the top you should see a 'Draft' release, auto populated with any PRs since the last release. Click 'Edit'.\n * Make sure that the version number matches what we released (the tool guesses but is not always correct).\n * Double check everything, then press 'Publish release'.\n\nAt this point the release is published. This will trigger the docs action to run, which will auto-deploy a new version of the [website](https://google.github.io/accompanist/).\n\n### #6: Prepare the next development version\n\nThe current release is now finished, but we need to update the version for the next development version:\n\nEdit [gradle.properties](https://github.com/google/accompanist/blob/main/gradle.properties):\n\n * Update the `VERSION_NAME` property, by increasing the version number, and adding the `-SNAPSHOT` suffix.\n * Example: released version: `0.3.0`. Update to `0.3.1-SNAPSHOT`\n\n `git commit` and push to `main`.\n\nFinally, merge all of these changes back to `snapshot`:\n\n```\ngit checkout snapshot && git pull\ngit merge main\ngit push\n```"
  },
  {
    "path": "docs/using-snapshot-version.md",
    "content": "# Using a Snapshot Version of the Library\n\nIf you would like to depend on the cutting edge version of the Accompanist\nlibrary, you can use the [snapshot versions][snap] that are published to\n[Sonatype OSSRH](https://central.sonatype.org/)'s snapshot repository. These are updated on every commit to `main`.\n\nTo do so:\n\n```groovy\nrepositories {\n    // ...\n    maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }\n}\n\ndependencies {\n    // Check the latest SNAPSHOT version from the link above\n    classpath 'com.google.accompanist:accompanist-coil:XXX-SNAPSHOT'\n}\n```\n\nYou might see a number of different versioned snapshots. If we use an example:\n\n* `0.3.0-SNAPSHOT` is a build from the `main` branch, and depends on the latest tagged Jetpack Compose release (i.e. [alpha03](https://developer.android.com/jetpack/androidx/releases/compose#1.0.0-alpha03)).\n* `0.3.0.compose-6574163-SNAPSHOT` is a build from the `snapshot` branch. This depends on the [SNAPSHOT build](https://androidx.dev) of Jetpack Compose from build `6574163`. You should only use these if you are using Jetpack Compose snapshot versions (see below).\n\n### Using Jetpack Compose Snapshots\n\nIf you're using [`SNAPSHOT`](https://androidx.dev) versions of the `androidx.compose` libraries, you might run into issues with the current stable Accompanist release forcing an older version of those libraries.\n\nWe publish snapshot versions of Accompanist which depend on recent Jetpack Compose SNAPSHOT repositories. To find a recent build, look through the [snapshot repository][snap] for any versions in the scheme `x.x.x.compose-YYYY-SNAPSHOT` (for example: `0.3.0.compose-6574163-SNAPSHOT`). The `YYYY` in the scheme is the snapshot build being used from [AndroidX](https://androidx.dev) (from the example: build [`6574163`](https://androidx.dev/snapshots/builds/6574163/artifacts)). You can then use it like so:\n\n\n``` groovy\nrepositories {\n    // ...\n    maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }\n}\n\ndependencies {\n    // Check the latest SNAPSHOT version from the link above\n    classpath 'com.google.accompanist:accompanist-coil:XXXX.compose-YYYYY-SNAPSHOT'\n}\n```\n\nThese builds are updated regularly, but there's no guarantee that we will create one for a given snapshot number.\n\n*Note:* you might also see versions in the scheme `x.x.x.ui-YYYY-SNAPSHOT`. These are the same, just using an older suffix.\n\n\n [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/"
  },
  {
    "path": "docs/web.md",
    "content": "# WebView wrapper for Jetpack Compose\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-webview)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nA library which provides a Jetpack Compose wrapper around Android's WebView.\n\n!!! warning\n    **This library is deprecated, and the API is no longer maintained. We recommend forking the implementation and customising it to your needs.** The original documentation is below.\n\n## Usage\n\nTo implement this wrapper there are two key APIs which are needed: [`WebView`](../api/web/com.google.accompanist.web/-web-view.html), which is provides the layout, and [`rememberWebViewState(url)`](../api/web/com.google.accompanist.web/remember-web-view-state.html) which provides some remembered state including the URL to display.\n\nThe basic usage is as follows:\n\n```kotlin\nval state = rememberWebViewState(\"https://example.com\")\n\nWebView(\n    state\n)\n```\n\nThis will display a WebView in your Compose layout that shows the URL provided.\n\nThere is a larger sample in the sample app which can be found [here](https://github.com/google/accompanist/blob/main/sample/src/main/java/com/google/accompanist/sample/webview/BasicWebViewSample.kt). This sample also shows how to show a loading state.\n\n### WebView settings including JavaScript\n\nBy default, JavaScript is disabled in the WebView. To enable it or any other settings you can use the `onCreated` callback.\n\n```kotlin\nWebView(\n    state = webViewState,\n    onCreated = { it.settings.javaScriptEnabled = true }\n)\n```\n\n### Capturing back presses\n\nBy default the WebView will capture back presses/swipes when relevant and navigate the WebView back. This can be disabled via the parameter on \nthe Composable.\n\n```kotlin\nWebView(\n    ...\n    captureBackPresses = false\n)\n```\n\n### Using a subclass of WebView\n\nIf you want to use a subclass of `WebView`, or simply require more control over its instantiation, you can provide a factory.\n\n```kotlin\nWebView(\n    ...\n    factory = { context -> CustomWebView(context) }\n)\n```\n\n## Download\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-webview)](https://search.maven.org/search?q=g:com.google.accompanist)\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-webview:<version>\"\n}\n```\n"
  },
  {
    "path": "drawablepainter/README.md",
    "content": "# Accompanist Drawable Painter\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.google.accompanist/accompanist-imageloading-core/badge.svg)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nFor more information, visit the documentation: https://google.github.io/accompanist/drawablepainter\n\n## Download\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-drawablepainter:<version>\"\n}\n```\n"
  },
  {
    "path": "drawablepainter/api/current.api",
    "content": "// Signature format: 4.0\npackage com.google.accompanist.drawablepainter {\n\n  public final class DrawablePainter extends androidx.compose.ui.graphics.painter.Painter implements androidx.compose.runtime.RememberObserver {\n    ctor public DrawablePainter(android.graphics.drawable.Drawable drawable);\n    method public android.graphics.drawable.Drawable getDrawable();\n    method public long getIntrinsicSize();\n    method public void onAbandoned();\n    method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope);\n    method public void onForgotten();\n    method public void onRemembered();\n    property public final android.graphics.drawable.Drawable drawable;\n    property public long intrinsicSize;\n  }\n\n  public final class DrawablePainterKt {\n    method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.painter.Painter rememberDrawablePainter(android.graphics.drawable.Drawable? drawable);\n  }\n\n}\n\n"
  },
  {
    "path": "drawablepainter/build.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n    alias(libs.plugins.accompanist.android.library)\n    alias(libs.plugins.accompanist.android.library.compose)\n    alias(libs.plugins.accompanist.android.library.published)\n}\n\nandroid {\n    namespace = \"com.google.accompanist.drawablepainter\"\n}\n\ndependencies {\n    implementation(libs.compose.ui.ui)\n    implementation(libs.kotlin.coroutines.android)\n}\n"
  },
  {
    "path": "drawablepainter/gradle.properties",
    "content": "POM_ARTIFACT_ID=accompanist-drawablepainter\nPOM_NAME=Accompanist Drawable Painter library\nPOM_PACKAGING=aar"
  },
  {
    "path": "drawablepainter/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n  <uses-sdk android:minSdkVersion=\"21\"  />\n</manifest>\n"
  },
  {
    "path": "drawablepainter/src/main/java/com/google/accompanist/drawablepainter/DrawablePainter.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.drawablepainter\n\nimport android.graphics.drawable.Animatable\nimport android.graphics.drawable.AnimatedImageDrawable\nimport android.graphics.drawable.ColorDrawable\nimport android.graphics.drawable.Drawable\nimport android.os.Build\nimport android.os.Handler\nimport android.os.Looper\nimport android.view.View\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.RememberObserver\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.geometry.Size\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.ColorFilter\nimport androidx.compose.ui.graphics.asAndroidColorFilter\nimport androidx.compose.ui.graphics.drawscope.DrawScope\nimport androidx.compose.ui.graphics.drawscope.drawIntoCanvas\nimport androidx.compose.ui.graphics.nativeCanvas\nimport androidx.compose.ui.graphics.painter.ColorPainter\nimport androidx.compose.ui.graphics.painter.Painter\nimport androidx.compose.ui.graphics.withSave\nimport androidx.compose.ui.unit.LayoutDirection\nimport kotlin.math.roundToInt\n\nprivate val MAIN_HANDLER by lazy(LazyThreadSafetyMode.NONE) {\n    Handler(Looper.getMainLooper())\n}\n\n/**\n * A [Painter] which draws an Android [Drawable] and supports [Animatable] drawables. Instances\n * should be remembered to be able to start and stop [Animatable] animations.\n *\n * Instances are usually retrieved from [rememberDrawablePainter].\n */\npublic class DrawablePainter(\n    public val drawable: Drawable\n) : Painter(), RememberObserver {\n    private var drawInvalidateTick by mutableStateOf(0)\n    private var drawableIntrinsicSize by mutableStateOf(drawable.intrinsicSize)\n\n    private val callback: Drawable.Callback by lazy {\n        object : Drawable.Callback {\n            override fun invalidateDrawable(d: Drawable) {\n                // Update the tick so that we get re-drawn\n                drawInvalidateTick++\n                // Update our intrinsic size too\n                drawableIntrinsicSize = drawable.intrinsicSize\n            }\n\n            override fun scheduleDrawable(d: Drawable, what: Runnable, time: Long) {\n                MAIN_HANDLER.postAtTime(what, time)\n            }\n\n            override fun unscheduleDrawable(d: Drawable, what: Runnable) {\n                MAIN_HANDLER.removeCallbacks(what)\n            }\n        }\n    }\n\n    init {\n        if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) {\n            // Update the drawable's bounds to match the intrinsic size\n            drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)\n        }\n    }\n\n    override fun onRemembered() {\n        drawable.callback = callback\n        drawable.setVisible(true, true)\n        if (drawable is Animatable) drawable.start()\n    }\n\n    override fun onAbandoned(): Unit = onForgotten()\n\n    override fun onForgotten() {\n        if (drawable is Animatable) drawable.stop()\n        drawable.setVisible(false, false)\n        drawable.callback = null\n    }\n\n    override fun applyAlpha(alpha: Float): Boolean {\n        drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255)\n        return true\n    }\n\n    override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {\n        drawable.colorFilter = colorFilter?.asAndroidColorFilter()\n        return true\n    }\n\n    override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean {\n        if (Build.VERSION.SDK_INT >= 23) {\n            return drawable.setLayoutDirection(\n                when (layoutDirection) {\n                    LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR\n                    LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL\n                }\n            )\n        }\n        return false\n    }\n\n    override val intrinsicSize: Size get() = drawableIntrinsicSize\n\n    override fun DrawScope.onDraw() {\n        drawIntoCanvas { canvas ->\n            // Reading this ensures that we invalidate when invalidateDrawable() is called\n            drawInvalidateTick\n\n            canvas.withSave {\n                // AnimatedImageDrawable is not respecting the bounds below Android 12, so this is\n                // a workaround to make the render size correct in this specific case\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&\n                    Build.VERSION.SDK_INT < Build.VERSION_CODES.S &&\n                    drawable is AnimatedImageDrawable\n                ) {\n                    canvas.scale(\n                        size.width / intrinsicSize.width,\n                        size.height / intrinsicSize.height\n                    )\n                } else {\n                    // Update the Drawable's bounds\n                    drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt())\n                }\n\n                drawable.draw(canvas.nativeCanvas)\n            }\n        }\n    }\n}\n\n/**\n * Remembers [Drawable] wrapped up as a [Painter]. This function attempts to un-wrap the\n * drawable contents and use Compose primitives where possible.\n *\n * If the provided [drawable] is `null`, an empty no-op painter is returned.\n *\n * This function tries to dispatch lifecycle events to [drawable] as much as possible from\n * within Compose.\n *\n * @sample com.google.accompanist.sample.drawablepainter.BasicSample\n */\n@Composable\npublic fun rememberDrawablePainter(drawable: Drawable?): Painter = remember(drawable) {\n    when (drawable) {\n        null -> EmptyPainter\n        is ColorDrawable -> ColorPainter(Color(drawable.color))\n        // Since the DrawablePainter will be remembered and it implements RememberObserver, it\n        // will receive the necessary events\n        else -> DrawablePainter(drawable.mutate())\n    }\n}\n\nprivate val Drawable.intrinsicSize: Size\n    get() = when {\n        // Only return a finite size if the drawable has an intrinsic size\n        intrinsicWidth >= 0 && intrinsicHeight >= 0 -> {\n            Size(width = intrinsicWidth.toFloat(), height = intrinsicHeight.toFloat())\n        }\n        else -> Size.Unspecified\n    }\n\ninternal object EmptyPainter : Painter() {\n    override val intrinsicSize: Size get() = Size.Unspecified\n    override fun DrawScope.onDraw() {}\n}\n"
  },
  {
    "path": "generate_docs.sh",
    "content": "#!/bin/bash\n\n# Copyright 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Fail on any error\nset -ex\n\nDOCS_ROOT=docs-gen\n\n[ -d $DOCS_ROOT ] && rm -r $DOCS_ROOT\nmkdir $DOCS_ROOT\n\n# Clear out the old API docs\n[ -d docs/api ] && rm -r docs/api\n# Build the docs with dokka\n./gradlew dokkaHtmlMultiModule --stacktrace\n\n# Create a copy of our docs at our $DOCS_ROOT\ncp -a docs/* $DOCS_ROOT\n\ncp README.md $DOCS_ROOT/index.md\ncp CONTRIBUTING.md $DOCS_ROOT/contributing.md\n\nsed -i.bak 's/CONTRIBUTING.md/contributing/' $DOCS_ROOT/index.md\nsed -i.bak 's/README.md//' $DOCS_ROOT/index.md\nsed -i.bak 's/docs\\/header.png/header.png/' $DOCS_ROOT/index.md\n\n# Convert docs/xxx.md links to just xxx/\nsed -i.bak 's/docs\\/\\([a-zA-Z-]*\\).md/\\1/' $DOCS_ROOT/index.md\n\n# Finally delete all of the backup files\nfind . -name '*.bak' -delete\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\n\ncompose = \"1.7.0\"\ncomposeMaterial3 = \"1.0.1\"\ncomposesnapshot = \"-\" # a single character = no snapshot\n\ndesugar_jdk_libs = \"2.1.3\"\ndokka = \"1.8.10\"\n\n# gradlePlugin and lint need to be updated together\nandroidTools = \"31.7.2\"\ngradlePlugin = \"8.7.3\"\nlintMinCompose = \"30.0.0\"\n\nktlint = \"0.45.2\"\nkotlin = \"2.0.20\"\ncoroutines = \"1.6.4\"\ncoil = \"1.3.2\"\n\nandroidlint = \"25.3.0\"\nandroidxtest = \"1.6.1\"\nandroidxnavigation = \"2.7.7\"\nandroidxWindow = \"1.0.0\"\n\nmetalava = \"0.3.5\"\nvanniktechPublish = \"0.30.0\"\n\n[libraries]\ncompose-ui-ui = { module = \"androidx.compose.ui:ui\", version.ref = \"compose\" }\ncompose-ui-util = { module = \"androidx.compose.ui:ui-util\", version.ref = \"compose\" }\ncompose-ui-tooling = { module = \"androidx.compose.ui:ui-tooling\", version.ref = \"compose\" }\ncompose-ui-tooling-preview = { module = \"androidx.compose.ui:ui-tooling-preview\", version.ref = \"compose\" }\ncompose-ui-test = { module = \"androidx.compose.ui:ui-test\", version.ref = \"compose\" }\ncompose-ui-test-junit4 = { module = \"androidx.compose.ui:ui-test-junit4\", version.ref = \"compose\" }\ncompose-ui-test-manifest = { module = \"androidx.compose.ui:ui-test-manifest\", version.ref = \"compose\" }\ncompose-foundation-foundation = { module = \"androidx.compose.foundation:foundation\", version.ref = \"compose\" }\ncompose-foundation-layout = { module = \"androidx.compose.foundation:foundation-layout\", version.ref = \"compose\" }\ncompose-material-material = { module = \"androidx.compose.material:material\", version.ref = \"compose\" }\ncompose-material-iconsext = { module = \"androidx.compose.material:material-icons-extended\", version.ref = \"compose\" }\ncompose-material3-material3 = { module = \"androidx.compose.material3:material3\", version.ref = \"composeMaterial3\" }\ncompose-animation-animation = { module = \"androidx.compose.animation:animation\", version.ref = \"compose\" }\ncompose-gradlePlugin = { module = \"org.jetbrains.kotlin:compose-compiler-gradle-plugin\", version.ref = \"kotlin\" }\n\napacheAnt = { module = \"org.apache.ant:ant\", version = \"1.10.11\" }\nandroid-gradlePlugin = { module = \"com.android.tools.build:gradle\", version.ref = \"gradlePlugin\" }\ndesugar_jdk_libs = { module = \"com.android.tools:desugar_jdk_libs\", version.ref = \"desugar_jdk_libs\" }\ngradleMavenPublishPlugin = { module = \"com.vanniktech:gradle-maven-publish-plugin\", version.ref = \"vanniktechPublish\" }\nmetalavaGradle = { module = \"me.tylerbwong.gradle.metalava:plugin\", version.ref = \"metalava\" }\nshadow = { module = \"com.gradleup.shadow:shadow-gradle-plugin\", version = \"8.3.3\" }\n\nkotlin-stdlib = { module = \"org.jetbrains.kotlin:kotlin-stdlib\", version.ref = \"kotlin\" }\nkotlin-stdlibJdk8 = { module = \"org.jetbrains.kotlin:kotlin-stdlib-jdk8\", version.ref = \"kotlin\" }\nkotlin-gradlePlugin = { module = \"org.jetbrains.kotlin:kotlin-gradle-plugin\", version.ref = \"kotlin\" }\nkotlin-reflect = { module = \"org.jetbrains.kotlin:kotlin-reflect\", version.ref = \"kotlin\" }\nkotlin-metadataJvm = { module = \"org.jetbrains.kotlin:kotlin-metadata-jvm\", version.ref = \"kotlin\" }\n\nkotlin-coroutines-android = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-android\", version.ref = \"coroutines\" }\n\ncoil-gif = { module = \"io.coil-kt:coil-gif\", version.ref = \"coil\" }\ncoil-compose = { module = \"io.coil-kt:coil-compose\", version.ref = \"coil\" }\n\nandroidx-appcompat = \"androidx.appcompat:appcompat:1.4.2\"\nandroidx-core = \"androidx.core:core-ktx:1.8.0\"\nandroidx-activity-compose = \"androidx.activity:activity-compose:1.9.0\"\nandroidx-fragment = \"androidx.fragment:fragment-ktx:1.8.1\"\nandroidx-lifecycle-runtime = \"androidx.lifecycle:lifecycle-runtime-ktx:2.6.1\"\nandroidx-lifecycle-viewmodel-compose = \"androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1\"\nandroidx-window = { module = \"androidx.window:window\", version.ref = \"androidxWindow\" }\nandroidx-window-testing = { module = \"androidx.window:window-testing\", version.ref = \"androidxWindow\" }\n\nandroidx-navigation-compose = { module = \"androidx.navigation:navigation-compose\", version.ref = \"androidxnavigation\" }\nandroidx-navigation-testing = { module = \"androidx.navigation:navigation-testing\", version.ref = \"androidxnavigation\" }\n\nmdc = \"com.google.android.material:material:1.8.0\"\n\nandroidx-test-core = \"androidx.test:core-ktx:1.6.1\"\nandroidx-test-runner = \"androidx.test:runner:1.6.1\"\nandroidx-test-rules = { module = \"androidx.test:rules\", version.ref = \"androidxtest\" }\nandroidx-test-orchestrator = \"androidx.test:orchestrator:1.5.0\"\nandroidx-test-uiAutomator = \"androidx.test.uiautomator:uiautomator:2.3.0\"\n\njunit = \"junit:junit:4.13.2\"\ntruth = \"com.google.truth:truth:1.1.3\"\nrobolectric = \"org.robolectric:robolectric:4.12.1\"\n\naffectedmoduledetector = \"com.dropbox.affectedmoduledetector:affectedmoduledetector:0.1.2\"\n\nandroid-tools-common = { module = \"com.android.tools:common\", version.ref = \"androidTools\" }\nandroid-tools-lint-lint = { module = \"com.android.tools.lint:lint\", version.ref = \"lintMinCompose\" }\nandroid-tools-lint-api = { module = \"com.android.tools.lint:lint-api\", version.ref = \"lintMinCompose\" }\nandroid-tools-lint-tests = { module = \"com.android.tools.lint:lint-tests\", version.ref = \"lintMinCompose\" }\n\n[plugins]\nandroid-application = { id = \"com.android.application\", version.ref = \"gradlePlugin\" }\nandroid-kotlin = { id = \"org.jetbrains.kotlin.android\", version.ref = \"kotlin\" }\nandroid-library = { id = \"com.android.library\", version.ref = \"gradlePlugin\" }\nandroid-lint = { id = \"com.android.lint\", version.ref = \"androidlint\"}\njetbrains-dokka = { id = \"org.jetbrains.dokka\", version.ref = \"dokka\" }\ngradle-metalava = { id = \"me.tylerbwong.gradle.metalava\", version.ref = \"metalava\" }\nvanniktech-maven-publish = { id = \"com.vanniktech.maven.publish\", version.ref = \"vanniktechPublish\" }\ncompose-plugin = { id = \"org.jetbrains.kotlin.plugin.compose\", version.ref = \"kotlin\" }\n\n#build-logic plugins\naccompanist-android-library = { id = \"accompanist.android.library\" }\naccompanist-android-library-compose = { id = \"accompanist.android.library.compose\" }\naccompanist-android-library-published = { id = \"accompanist.android.library.published\" }\naccompanist-android-lint = { id = \"accompanist.android.lint\" }\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Jul 10 11:49:25 AEST 2024\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.10-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "#\n# Copyright 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Turn on parallel compilation, caching and on-demand configuration\norg.gradle.configureondemand=true\norg.gradle.caching=true\norg.gradle.parallel=true\n\n# Declare we support AndroidX\nandroid.useAndroidX=true\n\n# Increase memory\norg.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError\n\n# Required to publish to Nexus (see https://github.com/gradle/gradle/issues/11308)\nsystemProp.org.gradle.internal.publish.checksums.insecure=true\n\n# Increase timeout when pushing to Sonatype (otherwise we get timeouts)\nsystemProp.org.gradle.internal.http.socketTimeout=120000\n\nGROUP=com.google.accompanist\n# !! No longer need to update this manually when using a Compose SNAPSHOT\nVERSION_NAME=0.37.4-SNAPSHOT\n\nPOM_DESCRIPTION=Utilities for Jetpack Compose\n\nPOM_URL=https://github.com/google/accompanist/\nPOM_SCM_URL=https://github.com/google/accompanist/\nPOM_SCM_CONNECTION=scm:git:git://github.com/google/accompanist.git\nPOM_SCM_DEV_CONNECTION=scm:git:git://github.com/google/accompanist.git\n\nPOM_LICENCE_NAME=The Apache Software License, Version 2.0\nPOM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt\nPOM_LICENCE_DIST=repo\n\nPOM_DEVELOPER_ID=google\nPOM_DEVELOPER_NAME=Google\n\nSONATYPE_HOST=DEFAULT\nRELEASE_SIGNING_ENABLED=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@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 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "internal-testutils/build.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n    alias(libs.plugins.accompanist.android.library)\n    alias(libs.plugins.accompanist.android.library.compose)\n}\n\nandroid {\n    namespace = \"com.google.accompanist.internal.test\"\n}\n\ndependencies {\n    implementation(libs.kotlin.stdlib)\n    implementation(libs.kotlin.coroutines.android)\n\n    implementation(libs.compose.foundation.foundation)\n    api(libs.compose.ui.test.junit4)\n\n    api(libs.androidx.test.core)\n    implementation(libs.truth)\n}\n"
  },
  {
    "path": "internal-testutils/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ Copyright 2021 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application android:theme=\"@style/InternalTestutilsTheme.Test\" />\n\n</manifest>\n"
  },
  {
    "path": "internal-testutils/src/main/java/com/google/accompanist/internal/test/ActivityScenario.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.internal.test\n\nimport android.app.Activity\nimport androidx.test.core.app.ActivityScenario\n\npublic fun <A : Activity, T : Any> ActivityScenario<A>.withActivity(\n    action: (A) -> T\n): T {\n    lateinit var result: T\n    onActivity { result = action(it) }\n    return result\n}\n"
  },
  {
    "path": "internal-testutils/src/main/java/com/google/accompanist/internal/test/Assertions.kt",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.internal.test\n\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.graphics.ImageBitmap\nimport androidx.compose.ui.graphics.toPixelMap\nimport androidx.compose.ui.test.SemanticsNodeInteraction\nimport androidx.compose.ui.test.assertHeightIsAtLeast\nimport androidx.compose.ui.test.assertWidthIsAtLeast\nimport androidx.compose.ui.unit.Dp\nimport com.google.common.truth.Truth.assertThat\n\n/**\n * Assert that all of the pixels in this image as of the [expected] color.\n */\npublic fun ImageBitmap.assertPixels(expected: Color, tolerance: Float = 0.001f) {\n    toPixelMap().buffer.forEach { pixel ->\n        val color = Color(pixel)\n        assertThat(color.red).isWithin(tolerance).of(expected.red)\n        assertThat(color.green).isWithin(tolerance).of(expected.green)\n        assertThat(color.blue).isWithin(tolerance).of(expected.blue)\n        assertThat(color.alpha).isWithin(tolerance).of(expected.alpha)\n    }\n}\n\n/**\n * Run the [SemanticsNodeInteraction] provided by [block] repeatedly until either\n * the assertion succeeds, or the execution runs past [timeoutMillis].\n */\npublic fun SemanticsNodeInteraction.assertWithTimeout(\n    timeoutMillis: Long,\n    block: SemanticsNodeInteraction.() -> SemanticsNodeInteraction,\n): SemanticsNodeInteraction {\n    val startTime = System.nanoTime()\n    while (System.nanoTime() - startTime <= timeoutMillis * 1_000_000) {\n        try {\n            return block()\n        } catch (error: AssertionError) {\n            // If the assertion failed, sleep for 10ms before the next loop iteration\n            Thread.sleep(10)\n        }\n    }\n    // If we reach here, each assertion has failed and we've reached the time out.\n    // Run block one last time...\n    return block()\n}\n\npublic val SemanticsNodeInteraction.exists: Boolean\n    get() = try {\n        assertExists()\n        true\n    } catch (t: Throwable) {\n        false\n    }\n\npublic val SemanticsNodeInteraction.isLaidOut: Boolean\n    get() = try {\n        assertWidthIsAtLeast(Dp.Hairline).assertHeightIsAtLeast(Dp.Hairline)\n        true\n    } catch (t: Throwable) {\n        false\n    }\n"
  },
  {
    "path": "internal-testutils/src/main/java/com/google/accompanist/internal/test/IgnoreOnRobolectric.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.internal.test\n\n/**\n * Marker interface to use as a category for filtering out tests when running on Robolectric.\n */\npublic interface IgnoreOnRobolectric\n"
  },
  {
    "path": "internal-testutils/src/main/java/com/google/accompanist/internal/test/TestUtils.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\"NOTHING_TO_INLINE\")\n\npackage com.google.accompanist.internal.test\n\npublic inline fun parameterizedParams(): List<Array<Any>> = emptyList()\n\npublic inline fun <reified T> List<Array<T>>.combineWithParameters(\n    vararg values: T\n): List<Array<T>> {\n    if (isEmpty()) {\n        return values.map { arrayOf(it) }\n    }\n\n    return fold(emptyList()) { acc, args ->\n        val result = acc.toMutableList()\n        values.forEach { v ->\n            result += ArrayList<T>().apply {\n                addAll(args)\n                add(v)\n            }.toTypedArray()\n        }\n        result.toList()\n    }\n}\n"
  },
  {
    "path": "internal-testutils/src/main/java/com/google/accompanist/internal/test/WaitUntil.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.internal.test\n\nimport android.os.Looper\nimport com.google.common.truth.Truth.assertThat\nimport java.util.concurrent.TimeoutException\n\npublic fun waitUntil(timeoutMillis: Long = 2_000, condition: () -> Boolean) {\n    if (Looper.getMainLooper() == Looper.myLooper()) {\n        assertThat(condition()).isTrue()\n    }\n\n    val startTime = System.nanoTime()\n    while (!condition()) {\n        // Let Android run measure, draw and in general any other async operations.\n        Thread.sleep(10)\n        if (System.nanoTime() - startTime > timeoutMillis * 1_000_000) {\n            throw TimeoutException(\"Condition still not satisfied after $timeoutMillis ms\")\n        }\n    }\n}\n"
  },
  {
    "path": "internal-testutils/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2021 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<resources>\n\n    <style name=\"InternalTestutilsTheme.Test\" parent=\"@android:style/Theme.Material.Light.NoActionBar\">\n        <item name=\"android:windowAnimationStyle\">@null</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "mkdocs.yml",
    "content": "# Project information\nsite_name: 'Accompanist'\nsite_description: 'A group of libraries to help write Jetpack Compose apps.'\nsite_author: 'Google'\nsite_url: 'https://google.github.io/accompanist/'\nedit_uri: 'tree/main/docs/'\nremote_branch: gh-pages\n\ndocs_dir: docs-gen\n\n# Repository\nrepo_name: 'Accompanist'\nrepo_url: 'https://github.com/google/accompanist'\n\n# Navigation\nnav:\n  - 'Overview': index.md\n  - 'System UI Controller':\n    - 'Guide': systemuicontroller.md\n    - 'API': api/systemuicontroller/\n  - 'Drawable Painter':\n    - 'Guide': drawablepainter.md\n    - 'API': api/drawablepainter/ \n  - 'Permissions':\n    - 'Guide': permissions.md\n    - 'API': api/permissions/\n  - 'Navigation Animation':\n      - 'Guide': navigation-animation.md\n      - 'API': api/navigation-animation/\n  - 'Navigation Material':\n      - 'Guide': navigation-material.md\n      - 'API': api/navigation-material/\n  - 'Adaptive':\n      - 'Guide': adaptive.md\n      - 'API': api/adaptive\n  - 'Snapshots': using-snapshot-version.md\n  - 'Contributing': contributing.md\n  - 'Maintainers':\n    - 'Update guide': updating.md\n\n# Configuration\ntheme:\n  name: 'material'\n  language: 'en'\n  icon:\n    logo: material/music-clef-treble\n  palette:\n    primary: 'black'\n    accent: 'deep orange'\n  font:\n    text: 'Roboto'\n    code: 'JetBrains Mono'\n\n# Extensions\nmarkdown_extensions:\n  - admonition\n  - attr_list\n  - codehilite:\n      guess_lang: false\n  - footnotes\n  - toc:\n      permalink: true\n  - pymdownx.betterem\n  - pymdownx.superfences\n  - pymdownx.tabbed:\n      alternate_style: true\n  - pymdownx.details\n"
  },
  {
    "path": "permissions/README.md",
    "content": "# Permissions for Jetpack Compose\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions)](https://search.maven.org/search?q=g:com.google.accompanist)\n\nFor more information, visit the documentation: https://google.github.io/accompanist/permissions\n\n## Download\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-permissions:<version>\"\n}\n```\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit.\n\n  [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-permissions/"
  },
  {
    "path": "permissions/api/current.api",
    "content": "// Signature format: 4.0\npackage com.google.accompanist.permissions {\n\n  @kotlin.RequiresOptIn(message=\"Accompanist Permissions is experimental. The API may be changed in the future.\") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalPermissionsApi {\n  }\n\n  @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public interface MultiplePermissionsState {\n    method public boolean getAllPermissionsGranted();\n    method public java.util.List<com.google.accompanist.permissions.PermissionState> getPermissions();\n    method public java.util.List<com.google.accompanist.permissions.PermissionState> getRevokedPermissions();\n    method public boolean getShouldShowRationale();\n    method public void launchMultiplePermissionRequest();\n    property public abstract boolean allPermissionsGranted;\n    property public abstract java.util.List<com.google.accompanist.permissions.PermissionState> permissions;\n    property public abstract java.util.List<com.google.accompanist.permissions.PermissionState> revokedPermissions;\n    property public abstract boolean shouldShowRationale;\n  }\n\n  public final class MultiplePermissionsStateKt {\n    method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.MultiplePermissionsState rememberMultiplePermissionsState(java.util.List<java.lang.String> permissions, optional kotlin.jvm.functions.Function1<? super java.util.Map<java.lang.String,java.lang.Boolean>,kotlin.Unit> onPermissionsResult);\n    method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.MultiplePermissionsState rememberMultiplePermissionsState(java.util.List<java.lang.String> permissions, optional kotlin.jvm.functions.Function1<? super java.util.Map<java.lang.String,java.lang.Boolean>,kotlin.Unit> onPermissionsResult, optional java.util.Map<java.lang.String,? extends com.google.accompanist.permissions.PermissionStatus> previewPermissionStatuses);\n  }\n\n  @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public interface PermissionState {\n    method public String getPermission();\n    method public com.google.accompanist.permissions.PermissionStatus getStatus();\n    method public void launchPermissionRequest();\n    property public abstract String permission;\n    property public abstract com.google.accompanist.permissions.PermissionStatus status;\n  }\n\n  public final class PermissionStateKt {\n    method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.PermissionState rememberPermissionState(String permission, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onPermissionResult);\n    method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.PermissionState rememberPermissionState(String permission, optional kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onPermissionResult, optional com.google.accompanist.permissions.PermissionStatus previewPermissionStatus);\n  }\n\n  @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public sealed interface PermissionStatus {\n  }\n\n  public static final class PermissionStatus.Denied implements com.google.accompanist.permissions.PermissionStatus {\n    ctor public PermissionStatus.Denied(boolean shouldShowRationale);\n    method public boolean component1();\n    method public com.google.accompanist.permissions.PermissionStatus.Denied copy(boolean shouldShowRationale);\n    method public boolean getShouldShowRationale();\n    property public final boolean shouldShowRationale;\n  }\n\n  public static final class PermissionStatus.Granted implements com.google.accompanist.permissions.PermissionStatus {\n    field public static final com.google.accompanist.permissions.PermissionStatus.Granted INSTANCE;\n  }\n\n  public final class PermissionsUtilKt {\n    method public static boolean getShouldShowRationale(com.google.accompanist.permissions.PermissionStatus);\n    method public static boolean isGranted(com.google.accompanist.permissions.PermissionStatus);\n  }\n\n}\n\n"
  },
  {
    "path": "permissions/build.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n    alias(libs.plugins.accompanist.android.library)\n    alias(libs.plugins.accompanist.android.library.compose)\n    alias(libs.plugins.accompanist.android.library.published)\n}\n\nandroid {\n    namespace = \"com.google.accompanist.permissions\"\n\n    defaultConfig {\n        // The following argument makes the Android Test Orchestrator run its\n        // \"pm clear\" command after each test invocation. This command ensures\n        // that the app's state is completely cleared between tests.\n        testInstrumentationRunnerArguments[\"clearPackageData\"] = \"true\"\n    }\n\n    testOptions {\n        execution = \"ANDROIDX_TEST_ORCHESTRATOR\"\n    }\n}\n\ndependencies {\n    implementation(libs.androidx.activity.compose)\n    implementation(libs.compose.foundation.foundation)\n    implementation(libs.kotlin.coroutines.android)\n\n    lintChecks(project(\":permissions-lint\"))\n    lintPublish(project(\":permissions-lint\"))\n\n    // ======================\n    // Test dependencies\n    // ======================\n\n    androidTestUtil(libs.androidx.test.orchestrator)\n\n    androidTestImplementation(project(\":internal-testutils\"))\n    androidTestImplementation(libs.androidx.activity.compose)\n    androidTestImplementation(libs.compose.material.material)\n\n    androidTestImplementation(libs.junit)\n    androidTestImplementation(libs.truth)\n\n    androidTestImplementation(libs.compose.ui.test.junit4)\n    androidTestImplementation(libs.compose.ui.test.manifest)\n    androidTestImplementation(libs.compose.foundation.foundation)\n    androidTestImplementation(libs.androidx.test.runner)\n    androidTestImplementation(libs.androidx.test.rules)\n    androidTestImplementation(libs.androidx.test.uiAutomator)\n}\n"
  },
  {
    "path": "permissions/gradle.properties",
    "content": "POM_ARTIFACT_ID=accompanist-permissions\nPOM_NAME=Accompanist Permissions\nPOM_PACKAGING=aar"
  },
  {
    "path": "permissions/src/androidTest/AndroidManifest.xml",
    "content": "<!--\n  ~ Copyright 2021 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n\n    <application>\n        <activity\n            android:name=\"com.google.accompanist.permissions.test.PermissionsTestActivity\"\n            android:exported=\"true\"\n            android:theme=\"@android:style/Theme.Material.NoActionBar\" />\n        <activity\n            android:name=\"com.google.accompanist.permissions.test.EmptyPermissionsTestActivity\"\n            android:exported=\"true\"\n            android:theme=\"@android:style/Theme.Material.NoActionBar\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/FakeTests.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.test.filters.SdkSuppress\nimport org.junit.Test\n\n/**\n * Fake tests to avoid the \"No tests found error\" when running in Build.VERSION.SDK_INT < 23\n */\nclass FakeTests {\n\n    @SdkSuppress(maxSdkVersion = 22)\n    @Test\n    fun fakeTestToAvoidNoTestsFoundErrorInAPI22AndBelow() = Unit\n\n    // More Fake tests to help with sharding: https://github.com/android/android-test/issues/973\n    @Test\n    fun fake1() = Unit\n\n    @Test\n    fun fake2() = Unit\n\n    @Test\n    fun fake3() = Unit\n\n    @Test\n    fun fake4() = Unit\n\n    @Test\n    fun fake5() = Unit\n\n    @Test\n    fun fake6() = Unit\n\n    @Test\n    fun fake7() = Unit\n\n    @Test\n    fun fake8() = Unit\n\n    @Test\n    fun fake9() = Unit\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/MultipleAndSinglePermissionsTest.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport android.content.Intent\nimport android.os.Build\nimport androidx.activity.ComponentActivity\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.material.Button\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.compose.ui.unit.dp\nimport androidx.test.filters.FlakyTest\nimport androidx.test.filters.SdkSuppress\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.uiautomator.UiDevice\nimport com.google.accompanist.permissions.test.PermissionsTestActivity\nimport org.junit.Rule\nimport org.junit.Test\n\n@OptIn(ExperimentalPermissionsApi::class)\n@FlakyTest(detail = \"https://github.com/google/accompanist/issues/490\")\n@SdkSuppress(minSdkVersion = 23)\nclass MultipleAndSinglePermissionsTest {\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    private val instrumentation by lazy {\n        InstrumentationRegistry.getInstrumentation()\n    }\n\n    private val uiDevice by lazy {\n        UiDevice.getInstance(instrumentation)\n    }\n\n    @Test\n    fun singlePermission_granted() {\n        composeTestRule.setContent {\n            ComposableUnderTest(listOf(android.Manifest.permission.CAMERA))\n        }\n\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun singlePermission_deniedAndGrantedInSecondActivity() {\n        composeTestRule.setContent {\n            ComposableUnderTest(listOf(android.Manifest.permission.CAMERA))\n        }\n\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n        uiDevice.pressBack()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun singlePermission_deniedAndGrantedInFirstActivity() {\n        composeTestRule.setContent {\n            ComposableUnderTest(listOf(android.Manifest.permission.CAMERA))\n        }\n\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        uiDevice.pressBack()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun singlePermission_deniedAndGrantedInFirstActivity_singlePermission() {\n        composeTestRule.setContent {\n            ComposableUnderTest(\n                listOf(android.Manifest.permission.CAMERA),\n                requestSinglePermission = true\n            )\n        }\n\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        uiDevice.pressBack()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun multiplePermissions_granted() {\n        composeTestRule.setContent {\n            ComposableUnderTest(\n                listOf(\n                    android.Manifest.permission.ACCESS_FINE_LOCATION,\n                    android.Manifest.permission.CAMERA\n                )\n            )\n        }\n\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant first permission\n        grantPermissionInDialog() // Grant second permission\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun multiplePermissions_denied() {\n        composeTestRule.setContent {\n            ComposableUnderTest(\n                listOf(\n                    android.Manifest.permission.ACCESS_FINE_LOCATION,\n                    android.Manifest.permission.CAMERA\n                )\n            )\n        }\n\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog() // Deny first permission\n        denyPermissionInDialog() // Deny second permission\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Navigate\").performClick()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"PermissionsTestActivity\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant the permission\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n        uiDevice.pressBack()\n        instrumentation.waitForIdleSync()\n        composeTestRule.onNodeWithText(\"MultipleAndSinglePermissionsTest\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // only one permission to grant now\n        if (Build.VERSION.SDK_INT == 23) { // API 23 shows all permissions again\n            grantPermissionInDialog()\n        }\n\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Composable\n    private fun ComposableUnderTest(\n        permissions: List<String>,\n        requestSinglePermission: Boolean = false\n    ) {\n        val state = rememberMultiplePermissionsState(permissions)\n        Column {\n            Text(\"MultipleAndSinglePermissionsTest\")\n            Spacer(Modifier.height(16.dp))\n            if (state.allPermissionsGranted) {\n                Text(\"Granted\")\n            } else {\n                Column {\n                    val textToShow = if (state.shouldShowRationale) {\n                        \"ShowRationale\"\n                    } else {\n                        \"No permission\"\n                    }\n\n                    Text(textToShow)\n                    Button(\n                        onClick = {\n                            if (requestSinglePermission && state.revokedPermissions.size == 1) {\n                                state.revokedPermissions[0].launchPermissionRequest()\n                            } else {\n                                state.launchMultiplePermissionRequest()\n                            }\n                        }\n                    ) {\n                        Text(\"Request\")\n                    }\n                }\n            }\n            Spacer(Modifier.height(16.dp))\n            Button(\n                onClick = {\n                    composeTestRule.activity.startActivity(\n                        Intent(composeTestRule.activity, PermissionsTestActivity::class.java)\n                    )\n                }\n            ) {\n                Text(\"Navigate\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/MultiplePermissionsStateTest.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.test.filters.SdkSuppress\nimport androidx.test.rule.GrantPermissionRule\nimport com.google.accompanist.permissions.test.EmptyPermissionsTestActivity\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Rule\nimport org.junit.Test\n\n/**\n * Simple tests that prove the data comes from the right place\n */\n@OptIn(ExperimentalPermissionsApi::class)\n@SdkSuppress(minSdkVersion = 23)\nclass MultiplePermissionsStateTest {\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<EmptyPermissionsTestActivity>()\n\n    @get:Rule\n    val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(\n        \"android.permission.CAMERA\",\n        \"android.permission.ACCESS_FINE_LOCATION\"\n    )\n\n    @Test\n    fun permissionState_hasPermission() {\n        composeTestRule.setContent {\n            val state = rememberMultiplePermissionsState(\n                listOf(\n                    android.Manifest.permission.ACCESS_FINE_LOCATION,\n                    android.Manifest.permission.CAMERA\n                )\n            )\n\n            assertThat(state.allPermissionsGranted).isTrue()\n            assertThat(state.shouldShowRationale).isFalse()\n        }\n    }\n\n    @Test\n    fun permissionTest_shouldShowRationale() {\n        composeTestRule.activity.shouldShowRequestPermissionRationale = mapOf(\n            android.Manifest.permission.WRITE_EXTERNAL_STORAGE to true\n        )\n\n        composeTestRule.setContent {\n            val state = rememberMultiplePermissionsState(\n                listOf(\n                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE,\n                    android.Manifest.permission.ACCESS_FINE_LOCATION,\n                    android.Manifest.permission.CAMERA\n                )\n            )\n\n            assertThat(state.allPermissionsGranted).isFalse()\n            assertThat(state.shouldShowRationale).isTrue()\n            assertThat(state.permissions).hasSize(3)\n            assertThat(state.revokedPermissions).hasSize(1)\n            assertThat(state.revokedPermissions[0].permission)\n                .isEqualTo(\"android.permission.WRITE_EXTERNAL_STORAGE\")\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/PermissionStateTest.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.test.filters.SdkSuppress\nimport androidx.test.rule.GrantPermissionRule\nimport com.google.accompanist.permissions.test.EmptyPermissionsTestActivity\nimport com.google.common.truth.Truth.assertThat\nimport org.junit.Rule\nimport org.junit.Test\n\n/**\n * Simple tests that prove the data comes from the right place\n */\n@OptIn(ExperimentalPermissionsApi::class)\n@SdkSuppress(minSdkVersion = 23)\nclass PermissionStateTest {\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<EmptyPermissionsTestActivity>()\n\n    @get:Rule\n    val permissionRule: GrantPermissionRule =\n        GrantPermissionRule.grant(\"android.permission.CAMERA\")\n\n    @Test\n    fun permissionState_hasPermission() {\n        composeTestRule.setContent {\n            val state = rememberPermissionState(android.Manifest.permission.CAMERA)\n            assertThat(state.status.isGranted).isTrue()\n            assertThat(state.status.shouldShowRationale).isFalse()\n        }\n    }\n\n    @Test\n    fun permissionTest_shouldShowRationale() {\n        val permission = android.Manifest.permission.ACCESS_FINE_LOCATION\n        composeTestRule.activity.shouldShowRequestPermissionRationale = mapOf(\n            permission to true\n        )\n\n        composeTestRule.setContent {\n            val state = rememberPermissionState(permission)\n\n            assertThat(state.status.isGranted).isFalse()\n            assertThat(state.status.shouldShowRationale).isTrue()\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/RequestMultiplePermissionsTest.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport android.os.Build\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.material.Button\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.filters.SdkSuppress\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\n\n@OptIn(ExperimentalPermissionsApi::class)\n@SdkSuppress(minSdkVersion = 27) // Flaky on 26\nclass RequestMultiplePermissionsTest {\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    @Before\n    fun setup() {\n        composeTestRule.setContent { ComposableUnderTest() }\n    }\n\n    @Test\n    fun permissionTest_grantPermissions() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant first permission\n        grantPermissionInDialog() // Grant second permission\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun permissionTest_denyOnePermission() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant first permission\n        denyPermissionInDialog() // Deny second permission\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant second permission\n        if (Build.VERSION.SDK_INT == 23) { // API 23 shows all permissions again\n            grantPermissionInDialog()\n        }\n\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun permissionTest_doNotAskAgainPermission() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant first permission\n        denyPermissionInDialog() // Deny second permission\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n\n        if (Build.VERSION.SDK_INT == 23) { // API 23 shows all permissions again\n            grantPermissionInDialog()\n        }\n        doNotAskAgainPermissionInDialog() // Do not ask again second permission\n\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n    }\n\n    @Test\n    fun permissionTest_grantInTheBackground() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog() // Grant first permission\n        denyPermissionInDialog() // Deny second permission\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n\n        if (Build.VERSION.SDK_INT == 23) { // API 23 shows all permissions again\n            grantPermissionInDialog()\n        }\n        doNotAskAgainPermissionInDialog() // Do not ask again second permission\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n\n        // This simulates the user going to the Settings screen and granting both permissions.\n        // This is cheating, I know, but the order in which the system request the permissions\n        // is unpredictable. Therefore, we need to grant both to make this test deterministic.\n        grantPermissionProgrammatically(\"android.permission.CAMERA\")\n        grantPermissionProgrammatically(\"android.permission.ACCESS_FINE_LOCATION\")\n        simulateAppComingFromTheBackground(composeTestRule)\n        composeTestRule.activityRule.scenario.onActivity {\n            it.setContent { ComposableUnderTest() }\n        }\n        composeTestRule.waitForIdle()\n\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Composable\n    private fun ComposableUnderTest() {\n        val state = rememberMultiplePermissionsState(\n            listOf(\n                android.Manifest.permission.ACCESS_FINE_LOCATION,\n                android.Manifest.permission.CAMERA\n            )\n        )\n        if (state.allPermissionsGranted) {\n            Text(\"Granted\")\n        } else {\n            Column {\n                val textToShow = if (state.shouldShowRationale) {\n                    \"ShowRationale\"\n                } else {\n                    \"No permission\"\n                }\n\n                Text(textToShow)\n                Button(onClick = { state.launchMultiplePermissionRequest() }) {\n                    Text(\"Request\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/RequestPermissionTest.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.material.Button\nimport androidx.compose.material.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.test.assertIsDisplayed\nimport androidx.compose.ui.test.junit4.createAndroidComposeRule\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\nimport androidx.test.filters.FlakyTest\nimport androidx.test.filters.SdkSuppress\nimport org.junit.Before\nimport org.junit.Rule\nimport org.junit.Test\n\n@OptIn(ExperimentalPermissionsApi::class)\n@SdkSuppress(minSdkVersion = 23)\nclass RequestPermissionTest {\n\n    @get:Rule\n    val composeTestRule = createAndroidComposeRule<ComponentActivity>()\n\n    @Before\n    fun setup() {\n        composeTestRule.setContent { ComposableUnderTest() }\n    }\n\n    @Test\n    fun permissionTest_grantPermission() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun permissionTest_denyPermission() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        grantPermissionInDialog()\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Test\n    fun permissionTest_doNotAskAgainPermission() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        doNotAskAgainPermissionInDialog()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n    }\n\n    @SdkSuppress(minSdkVersion = 29) // Flaky below\n    @FlakyTest\n    @Test\n    fun permissionTest_grantInTheBackground() {\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        denyPermissionInDialog()\n        composeTestRule.onNodeWithText(\"ShowRationale\").assertIsDisplayed()\n        composeTestRule.onNodeWithText(\"Request\").performClick()\n        doNotAskAgainPermissionInDialog()\n        composeTestRule.onNodeWithText(\"No permission\").assertIsDisplayed()\n\n        // This simulates the user going to the Settings screen and granting the permission\n        grantPermissionProgrammatically(android.Manifest.permission.CAMERA)\n        simulateAppComingFromTheBackground(composeTestRule)\n        composeTestRule.activityRule.scenario.onActivity {\n            it.setContent { ComposableUnderTest() }\n        }\n\n        composeTestRule.waitForIdle()\n\n        composeTestRule.onNodeWithText(\"Granted\").assertIsDisplayed()\n    }\n\n    @Composable\n    private fun ComposableUnderTest() {\n\n        val state = rememberPermissionState(android.Manifest.permission.CAMERA)\n        when (state.status) {\n            PermissionStatus.Granted -> {\n                Text(\"Granted\")\n            }\n            is PermissionStatus.Denied -> {\n                Column {\n                    val textToShow = if (state.status.shouldShowRationale) {\n                        \"ShowRationale\"\n                    } else {\n                        \"No permission\"\n                    }\n\n                    Text(textToShow)\n                    Button(onClick = { state.launchPermissionRequest() }) {\n                        Text(\"Request\")\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/TestUtils.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport android.app.Instrumentation\nimport android.os.Build\nimport androidx.activity.ComponentActivity\nimport androidx.compose.ui.test.junit4.AndroidComposeTestRule\nimport androidx.lifecycle.Lifecycle\nimport androidx.test.ext.junit.rules.ActivityScenarioRule\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.uiautomator.By\nimport androidx.test.uiautomator.UiDevice\nimport androidx.test.uiautomator.UiObject2\nimport androidx.test.uiautomator.Until\nimport java.io.ByteArrayOutputStream\n\ninternal fun <T : ComponentActivity> simulateAppComingFromTheBackground(\n    composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<T>, T>\n) {\n    // Make Activity go through ON_START, and ON_RESUME\n    composeTestRule.activityRule.scenario.moveToState(Lifecycle.State.STARTED)\n    composeTestRule.activityRule.scenario.moveToState(Lifecycle.State.RESUMED)\n}\n\ninternal fun grantPermissionProgrammatically(\n    permission: String,\n    instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()\n) {\n    if (Build.VERSION.SDK_INT < 28) {\n        val fileDescriptor = instrumentation.uiAutomation.executeShellCommand(\n            \"pm grant ${instrumentation.targetContext.packageName} $permission\"\n        )\n        fileDescriptor.checkError()\n        fileDescriptor.close()\n    } else {\n        instrumentation.uiAutomation.grantRuntimePermission(\n            instrumentation.targetContext.packageName, permission\n        )\n    }\n}\n\ninternal fun grantPermissionInDialog(\n    instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()\n) {\n    val uiDevice = UiDevice.getInstance(instrumentation)\n    val sdkVersion = Build.VERSION.SDK_INT\n    val button = uiDevice.findPermissionButton(\n        when (sdkVersion) {\n            in 24..28 -> \"ALLOW\"\n            29 -> \"Allow\"\n            else -> \"While using the app\"\n        }\n    )\n\n    button.clickForPermission(instrumentation)\n}\n\ninternal fun denyPermissionInDialog(\n    instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()\n) {\n    val text = when (Build.VERSION.SDK_INT) {\n        in 24..28 -> \"DENY\"\n        in 29..30 -> \"Deny\"\n        else -> \"t allow\" // Different sdks and devices seem to have either ' or ’\n    }\n    val permissionButton = UiDevice.getInstance(instrumentation).findPermissionButton(text)\n    permissionButton.clickForPermission(instrumentation)\n}\n\ninternal fun doNotAskAgainPermissionInDialog(\n    instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()\n) {\n    val uiDevice = UiDevice.getInstance(instrumentation)\n    when {\n        Build.VERSION.SDK_INT >= 30 -> {\n            denyPermissionInDialog(instrumentation)\n        }\n\n        Build.VERSION.SDK_INT > 28 -> {\n            uiDevice\n                .findPermissionButton(\"Deny & don’t ask again\")\n                .clickForPermission(instrumentation)\n        }\n\n        Build.VERSION.SDK_INT == 23 -> {\n            uiDevice.findPermissionButton(\"Never ask again\")\n                .clickForPermission(instrumentation)\n            denyPermissionInDialog(instrumentation)\n        }\n\n        else -> {\n            uiDevice.findPermissionButton(\n                \"Don't ask again\"\n            ).clickForPermission(instrumentation)\n            denyPermissionInDialog(instrumentation)\n        }\n    }\n}\n\nprivate fun UiDevice.findPermissionButton(\n    text: String\n): UiObject2 {\n    val selector = By\n        .textContains(text)\n        .clickable(true)\n\n    val found = wait(Until.hasObject(selector), 3000)\n\n    if (!found) {\n        val output = ByteArrayOutputStream()\n        dumpWindowHierarchy(output)\n        println(output.toByteArray().decodeToString())\n\n        error(\"Could not find button with text $text\")\n    }\n\n    return findObject(selector)\n}\n\nprivate fun UiObject2.clickForPermission(\n    instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()\n): Boolean {\n    click()\n    // Make sure that the tests waits for this click to be processed\n    instrumentation.waitForIdleSync()\n    return true\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/test/EmptyPermissionsTestActivity.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions.test\n\nimport androidx.activity.ComponentActivity\nimport com.google.accompanist.permissions.ExperimentalPermissionsApi\n\n@OptIn(ExperimentalPermissionsApi::class)\nclass EmptyPermissionsTestActivity : ComponentActivity() {\n\n    var shouldShowRequestPermissionRationale: Map<String, Boolean> = emptyMap()\n\n    override fun shouldShowRequestPermissionRationale(permission: String): Boolean {\n        if (permission in shouldShowRequestPermissionRationale.keys) {\n            return shouldShowRequestPermissionRationale[permission]!!\n        }\n        return super.shouldShowRequestPermissionRationale(permission)\n    }\n}\n"
  },
  {
    "path": "permissions/src/androidTest/java/com/google/accompanist/permissions/test/PermissionsTestActivity.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions.test\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.material.Button\nimport androidx.compose.material.Text\nimport com.google.accompanist.permissions.ExperimentalPermissionsApi\nimport com.google.accompanist.permissions.PermissionStatus\nimport com.google.accompanist.permissions.rememberPermissionState\nimport com.google.accompanist.permissions.shouldShowRationale\n\n@OptIn(ExperimentalPermissionsApi::class)\nclass PermissionsTestActivity : ComponentActivity() {\n\n    var shouldShowRequestPermissionRationale: Map<String, Boolean> = emptyMap()\n\n    override fun shouldShowRequestPermissionRationale(permission: String): Boolean {\n        if (permission in shouldShowRequestPermissionRationale.keys) {\n            return shouldShowRequestPermissionRationale[permission]!!\n        }\n        return super.shouldShowRequestPermissionRationale(permission)\n    }\n\n    /**\n     * Code used in `MultipleAndSinglePermissionsTest`\n     */\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            Column {\n                Text(\"PermissionsTestActivity\")\n\n                val state = rememberPermissionState(android.Manifest.permission.CAMERA,)\n                when (state.status) {\n                    PermissionStatus.Granted -> {\n                        Text(\"Granted\")\n                    }\n                    is PermissionStatus.Denied -> {\n                        val textToShow = if (state.status.shouldShowRationale) {\n                            \"ShowRationale\"\n                        } else {\n                            \"No permission\"\n                        }\n\n                        Text(textToShow)\n                        Button(onClick = { state.launchPermissionRequest() }) {\n                            Text(\"Request\")\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ Copyright 2021 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n  <uses-sdk android:minSdkVersion=\"21\"  />\n</manifest>"
  },
  {
    "path": "permissions/src/main/java/com/google/accompanist/permissions/MultiplePermissionsState.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.Stable\nimport androidx.compose.ui.platform.LocalInspectionMode\nimport androidx.compose.ui.util.fastMap\n\n/**\n * Creates a [MultiplePermissionsState] that is remembered across compositions.\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n *\n * @param permissions the permissions to control and observe.\n * @param onPermissionsResult will be called with whether or not the user granted the permissions\n *  after [MultiplePermissionsState.launchMultiplePermissionRequest] is called.\n */\n@ExperimentalPermissionsApi\n@Composable\npublic fun rememberMultiplePermissionsState(\n    permissions: List<String>,\n    onPermissionsResult: (Map<String, Boolean>) -> Unit = {}\n): MultiplePermissionsState {\n    return rememberMultiplePermissionsState(permissions, onPermissionsResult, emptyMap())\n}\n\n/**\n * Creates a [MultiplePermissionsState] that is remembered across compositions.\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n *\n * @param permissions the permissions to control and observe.\n * @param onPermissionsResult will be called with whether or not the user granted the permissions\n *  after [MultiplePermissionsState.launchMultiplePermissionRequest] is called.\n * @param previewPermissionStatuses provides a [PermissionStatus] for a given permission when running\n *  in a preview.\n */\n@ExperimentalPermissionsApi\n@Composable\npublic fun rememberMultiplePermissionsState(\n    permissions: List<String>,\n    onPermissionsResult: (Map<String, Boolean>) -> Unit = {},\n    previewPermissionStatuses: Map<String, PermissionStatus> = emptyMap()\n): MultiplePermissionsState {\n    return when {\n        LocalInspectionMode.current ->\n            PreviewMultiplePermissionsState(permissions, previewPermissionStatuses)\n        else -> rememberMutableMultiplePermissionsState(permissions, onPermissionsResult)\n    }\n}\n\n/**\n * A state object that can be hoisted to control and observe multiple [permissions] status changes.\n *\n * In most cases, this will be created via [rememberMultiplePermissionsState].\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n */\n@ExperimentalPermissionsApi\n@Stable\npublic interface MultiplePermissionsState {\n\n    /**\n     * List of all permissions to request.\n     */\n    public val permissions: List<PermissionState>\n\n    /**\n     * List of permissions revoked by the user.\n     */\n    public val revokedPermissions: List<PermissionState>\n\n    /**\n     * When `true`, the user has granted all [permissions].\n     */\n    public val allPermissionsGranted: Boolean\n\n    /**\n     * When `true`, the user should be presented with a rationale.\n     */\n    public val shouldShowRationale: Boolean\n\n    /**\n     * Request the [permissions] to the user.\n     *\n     * This should always be triggered from non-composable scope, for example, from a side-effect\n     * or a non-composable callback. Otherwise, this will result in an IllegalStateException.\n     *\n     * This triggers a system dialog that asks the user to grant or revoke the permission.\n     * Note that this dialog might not appear on the screen if the user doesn't want to be asked\n     * again or has denied the permission multiple times.\n     * This behavior varies depending on the Android level API.\n     */\n    public fun launchMultiplePermissionRequest(): Unit\n}\n\n@OptIn(ExperimentalPermissionsApi::class)\n@Immutable\nprivate class PreviewMultiplePermissionsState(\n    permissions: List<String>,\n    permissionStatuses: Map<String, PermissionStatus>\n) : MultiplePermissionsState {\n    override val permissions: List<PermissionState> = permissions.fastMap { permission ->\n        PreviewPermissionState(\n            permission = permission,\n            status = permissionStatuses[permission] ?: PermissionStatus.Granted,\n        )\n    }\n\n    override val revokedPermissions: List<PermissionState> = emptyList()\n    override val allPermissionsGranted: Boolean = false\n    override val shouldShowRationale: Boolean = false\n\n    override fun launchMultiplePermissionRequest() {}\n}\n"
  },
  {
    "path": "permissions/src/main/java/com/google/accompanist/permissions/MutableMultiplePermissionsState.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.activity.compose.rememberLauncherForActivityResult\nimport androidx.activity.result.ActivityResultLauncher\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.DisposableEffect\nimport androidx.compose.runtime.Stable\nimport androidx.compose.runtime.derivedStateOf\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.key\nimport androidx.compose.runtime.remember\nimport androidx.compose.ui.platform.LocalContext\n\n/**\n * Creates a [MultiplePermissionsState] that is remembered across compositions.\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n *\n * @param permissions the permissions to control and observe.\n * @param onPermissionsResult will be called with whether or not the user granted the permissions\n *  after [MultiplePermissionsState.launchMultiplePermissionRequest] is called.\n */\n@ExperimentalPermissionsApi\n@Composable\ninternal fun rememberMutableMultiplePermissionsState(\n    permissions: List<String>,\n    onPermissionsResult: (Map<String, Boolean>) -> Unit = {}\n): MultiplePermissionsState {\n    // Create mutable permissions that can be requested individually\n    val mutablePermissions = rememberMutablePermissionsState(permissions)\n    // Refresh permissions when the lifecycle is resumed.\n    PermissionsLifecycleCheckerEffect(mutablePermissions)\n\n    val multiplePermissionsState = remember(permissions) {\n        MutableMultiplePermissionsState(mutablePermissions)\n    }\n\n    // Remember RequestMultiplePermissions launcher and assign it to multiplePermissionsState\n    val launcher = rememberLauncherForActivityResult(\n        ActivityResultContracts.RequestMultiplePermissions()\n    ) { permissionsResult ->\n        multiplePermissionsState.updatePermissionsStatus(permissionsResult)\n        onPermissionsResult(permissionsResult)\n    }\n    DisposableEffect(multiplePermissionsState, launcher) {\n        multiplePermissionsState.launcher = launcher\n        onDispose {\n            multiplePermissionsState.launcher = null\n        }\n    }\n\n    return multiplePermissionsState\n}\n\n@ExperimentalPermissionsApi\n@Composable\nprivate fun rememberMutablePermissionsState(\n    permissions: List<String>\n): List<MutablePermissionState> {\n    // Create list of MutablePermissionState for each permission\n    val context = LocalContext.current\n    val activity = context.findActivity()\n    val mutablePermissions: List<MutablePermissionState> = remember(permissions) {\n        return@remember permissions.map { MutablePermissionState(it, context, activity) }\n    }\n    // Update each permission with its own launcher\n    for (permissionState in mutablePermissions) {\n        key(permissionState.permission) {\n            // Remember launcher and assign it to the permissionState\n            val launcher = rememberLauncherForActivityResult(\n                ActivityResultContracts.RequestPermission()\n            ) {\n                permissionState.refreshPermissionStatus()\n            }\n            DisposableEffect(launcher) {\n                permissionState.launcher = launcher\n                onDispose {\n                    permissionState.launcher = null\n                }\n            }\n        }\n    }\n\n    return mutablePermissions\n}\n\n/**\n * A state object that can be hoisted to control and observe multiple permission status changes.\n *\n * In most cases, this will be created via [rememberMutableMultiplePermissionsState].\n *\n * @param mutablePermissions list of mutable permissions to control and observe.\n */\n@ExperimentalPermissionsApi\n@Stable\ninternal class MutableMultiplePermissionsState(\n    private val mutablePermissions: List<MutablePermissionState>\n) : MultiplePermissionsState {\n\n    override val permissions: List<PermissionState> = mutablePermissions\n\n    override val revokedPermissions: List<PermissionState> by derivedStateOf {\n        permissions.filter { it.status != PermissionStatus.Granted }\n    }\n\n    override val allPermissionsGranted: Boolean by derivedStateOf {\n        permissions.all { it.status.isGranted } || // Up to date when the lifecycle is resumed\n            revokedPermissions.isEmpty() // Up to date when the user launches the action\n    }\n\n    override val shouldShowRationale: Boolean by derivedStateOf {\n        permissions.any { it.status.shouldShowRationale } &&\n            permissions.none { !it.status.isGranted && !it.status.shouldShowRationale }\n    }\n\n    override fun launchMultiplePermissionRequest() {\n        launcher?.launch(\n            permissions.map { it.permission }.toTypedArray()\n        ) ?: throw IllegalStateException(\"ActivityResultLauncher cannot be null\")\n    }\n\n    internal var launcher: ActivityResultLauncher<Array<String>>? = null\n\n    internal fun updatePermissionsStatus(permissionsStatus: Map<String, Boolean>) {\n        // Update all permissions with the result\n        for (permission in permissionsStatus.keys) {\n            mutablePermissions.firstOrNull { it.permission == permission }?.apply {\n                permissionsStatus[permission]?.let {\n                    this.refreshPermissionStatus()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/main/java/com/google/accompanist/permissions/MutablePermissionState.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport android.app.Activity\nimport android.content.Context\nimport androidx.activity.compose.rememberLauncherForActivityResult\nimport androidx.activity.result.ActivityResultLauncher\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.DisposableEffect\nimport androidx.compose.runtime.Stable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.platform.LocalContext\n\n/**\n * Creates a [MutablePermissionState] that is remembered across compositions.\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n *\n * @param permission the permission to control and observe.\n * @param onPermissionResult will be called with whether or not the user granted the permission\n *  after [PermissionState.launchPermissionRequest] is called.\n */\n@ExperimentalPermissionsApi\n@Composable\ninternal fun rememberMutablePermissionState(\n    permission: String,\n    onPermissionResult: (Boolean) -> Unit = {}\n): MutablePermissionState {\n    val context = LocalContext.current\n    val permissionState = remember(permission) {\n        MutablePermissionState(permission, context, context.findActivity())\n    }\n\n    // Refresh the permission status when the lifecycle is resumed\n    PermissionLifecycleCheckerEffect(permissionState)\n\n    // Remember RequestPermission launcher and assign it to permissionState\n    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) {\n        permissionState.refreshPermissionStatus()\n        onPermissionResult(it)\n    }\n    DisposableEffect(permissionState, launcher) {\n        permissionState.launcher = launcher\n        onDispose {\n            permissionState.launcher = null\n        }\n    }\n\n    return permissionState\n}\n\n/**\n * A mutable state object that can be used to control and observe permission status changes.\n *\n * In most cases, this will be created via [rememberMutablePermissionState].\n *\n * @param permission the permission to control and observe.\n * @param context to check the status of the [permission].\n * @param activity to check if the user should be presented with a rationale for [permission].\n */\n@ExperimentalPermissionsApi\n@Stable\ninternal class MutablePermissionState(\n    override val permission: String,\n    private val context: Context,\n    private val activity: Activity\n) : PermissionState {\n\n    override var status: PermissionStatus by mutableStateOf(getPermissionStatus())\n\n    override fun launchPermissionRequest() {\n        launcher?.launch(\n            permission\n        ) ?: throw IllegalStateException(\"ActivityResultLauncher cannot be null\")\n    }\n\n    internal var launcher: ActivityResultLauncher<String>? = null\n\n    internal fun refreshPermissionStatus() {\n        status = getPermissionStatus()\n    }\n\n    private fun getPermissionStatus(): PermissionStatus {\n        val hasPermission = context.checkPermission(permission)\n        return if (hasPermission) {\n            PermissionStatus.Granted\n        } else {\n            PermissionStatus.Denied(activity.shouldShowRationale(permission))\n        }\n    }\n}\n"
  },
  {
    "path": "permissions/src/main/java/com/google/accompanist/permissions/PermissionState.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.Immutable\nimport androidx.compose.runtime.Stable\nimport androidx.compose.ui.platform.LocalInspectionMode\n\n/**\n * Creates a [PermissionState] that is remembered across compositions.\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n *\n * @param permission the permission to control and observe.\n * @param onPermissionResult will be called with whether or not the user granted the permission\n *  after [PermissionState.launchPermissionRequest] is called.\n */\n@ExperimentalPermissionsApi\n@Composable\npublic fun rememberPermissionState(\n    permission: String,\n    onPermissionResult: (Boolean) -> Unit = {}\n): PermissionState {\n    return rememberPermissionState(permission, onPermissionResult, PermissionStatus.Granted)\n}\n\n/**\n * Creates a [PermissionState] that is remembered across compositions.\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n *\n * @param permission the permission to control and observe.\n * @param onPermissionResult will be called with whether or not the user granted the permission\n *  after [PermissionState.launchPermissionRequest] is called.\n * @param previewPermissionStatus provides a [PermissionStatus] when running in a preview.\n */\n@ExperimentalPermissionsApi\n@Composable\npublic fun rememberPermissionState(\n    permission: String,\n    onPermissionResult: (Boolean) -> Unit = {},\n    previewPermissionStatus: PermissionStatus = PermissionStatus.Granted\n): PermissionState {\n    return when {\n        LocalInspectionMode.current -> PreviewPermissionState(permission, previewPermissionStatus)\n        else -> rememberMutablePermissionState(permission, onPermissionResult)\n    }\n}\n\n/**\n * A state object that can be hoisted to control and observe [permission] status changes.\n *\n * In most cases, this will be created via [rememberPermissionState].\n *\n * It's recommended that apps exercise the permissions workflow as described in the\n * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions).\n */\n@ExperimentalPermissionsApi\n@Stable\npublic interface PermissionState {\n\n    /**\n     * The permission to control and observe.\n     */\n    public val permission: String\n\n    /**\n     * [permission]'s status\n     */\n    public val status: PermissionStatus\n\n    /**\n     * Request the [permission] to the user.\n     *\n     * This should always be triggered from non-composable scope, for example, from a side-effect\n     * or a non-composable callback. Otherwise, this will result in an IllegalStateException.\n     *\n     * This triggers a system dialog that asks the user to grant or revoke the permission.\n     * Note that this dialog might not appear on the screen if the user doesn't want to be asked\n     * again or has denied the permission multiple times.\n     * This behavior varies depending on the Android level API.\n     */\n    public fun launchPermissionRequest(): Unit\n}\n\n@OptIn(ExperimentalPermissionsApi::class)\n@Immutable\ninternal class PreviewPermissionState(\n    override val permission: String,\n    override val status: PermissionStatus\n) : PermissionState {\n    override fun launchPermissionRequest() {}\n}\n"
  },
  {
    "path": "permissions/src/main/java/com/google/accompanist/permissions/PermissionsUtil.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.content.pm.PackageManager\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.DisposableEffect\nimport androidx.compose.runtime.Stable\nimport androidx.compose.runtime.remember\nimport androidx.core.app.ActivityCompat\nimport androidx.core.content.ContextCompat\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleEventObserver\n\n@RequiresOptIn(message = \"Accompanist Permissions is experimental. The API may be changed in the future.\")\n@Retention(AnnotationRetention.BINARY)\npublic annotation class ExperimentalPermissionsApi\n\n/**\n * Model of the status of a permission. It can be granted or denied.\n * If denied, the user might need to be presented with a rationale.\n */\n@ExperimentalPermissionsApi\n@Stable\npublic sealed interface PermissionStatus {\n    public object Granted : PermissionStatus\n    public data class Denied(\n        val shouldShowRationale: Boolean\n    ) : PermissionStatus\n}\n\n/**\n * `true` if the permission is granted.\n */\n@ExperimentalPermissionsApi\npublic val PermissionStatus.isGranted: Boolean\n    get() = this == PermissionStatus.Granted\n\n/**\n * `true` if a rationale should be presented to the user.\n */\n@ExperimentalPermissionsApi\npublic val PermissionStatus.shouldShowRationale: Boolean\n    get() = when (this) {\n        PermissionStatus.Granted -> false\n        is PermissionStatus.Denied -> shouldShowRationale\n    }\n\n/**\n * Effect that updates the `hasPermission` state of a revoked [MutablePermissionState] permission\n * when the lifecycle gets called with [lifecycleEvent].\n */\n@ExperimentalPermissionsApi\n@Composable\ninternal fun PermissionLifecycleCheckerEffect(\n    permissionState: MutablePermissionState,\n    lifecycleEvent: Lifecycle.Event = Lifecycle.Event.ON_RESUME\n) {\n    // Check if the permission was granted when the lifecycle is resumed.\n    // The user might've gone to the Settings screen and granted the permission.\n    val permissionCheckerObserver = remember(permissionState) {\n        LifecycleEventObserver { _, event ->\n            if (event == lifecycleEvent) {\n                // If the permission is revoked, check again.\n                // We don't check if the permission was denied as that triggers a process restart.\n                if (permissionState.status != PermissionStatus.Granted) {\n                    permissionState.refreshPermissionStatus()\n                }\n            }\n        }\n    }\n    val lifecycle = androidx.lifecycle.compose.LocalLifecycleOwner.current.lifecycle\n    DisposableEffect(lifecycle, permissionCheckerObserver) {\n        lifecycle.addObserver(permissionCheckerObserver)\n        onDispose { lifecycle.removeObserver(permissionCheckerObserver) }\n    }\n}\n\n/**\n * Effect that updates the `hasPermission` state of a list of permissions\n * when the lifecycle gets called with [lifecycleEvent] and the permission is revoked.\n */\n@ExperimentalPermissionsApi\n@Composable\ninternal fun PermissionsLifecycleCheckerEffect(\n    permissions: List<MutablePermissionState>,\n    lifecycleEvent: Lifecycle.Event = Lifecycle.Event.ON_RESUME\n) {\n    // Check if the permission was granted when the lifecycle is resumed.\n    // The user might've gone to the Settings screen and granted the permission.\n    val permissionsCheckerObserver = remember(permissions) {\n        LifecycleEventObserver { _, event ->\n            if (event == lifecycleEvent) {\n                for (permission in permissions) {\n                    // If the permission is revoked, check again. We don't check if the permission\n                    // was denied as that triggers a process restart.\n                    if (permission.status != PermissionStatus.Granted) {\n                        permission.refreshPermissionStatus()\n                    }\n                }\n            }\n        }\n    }\n    val lifecycle = androidx.lifecycle.compose.LocalLifecycleOwner.current.lifecycle\n    DisposableEffect(lifecycle, permissionsCheckerObserver) {\n        lifecycle.addObserver(permissionsCheckerObserver)\n        onDispose { lifecycle.removeObserver(permissionsCheckerObserver) }\n    }\n}\n\n/**\n * Find the closest Activity in a given Context.\n */\ninternal fun Context.findActivity(): Activity {\n    var context = this\n    while (context is ContextWrapper) {\n        if (context is Activity) return context\n        context = context.baseContext\n    }\n    throw IllegalStateException(\"Permissions should be called in the context of an Activity\")\n}\n\ninternal fun Context.checkPermission(permission: String): Boolean {\n    return ContextCompat.checkSelfPermission(this, permission) ==\n        PackageManager.PERMISSION_GRANTED\n}\n\ninternal fun Activity.shouldShowRationale(permission: String): Boolean {\n    return ActivityCompat.shouldShowRequestPermissionRationale(this, permission)\n}\n"
  },
  {
    "path": "permissions-lint/README.md",
    "content": "# Lint checks for Permissions for Jetpack Compose\n\nLint checks for preventing calling `PermissionState.launchPermissionRequest` and\n`MultiplePermissionsState.launchMultiplePermissionRequest()` within the Composition as that throws\na runtime exception.\n\nThese functions should be called inside a regular lambda or a side-effect but never in the\nComposition.\n\nThese lint checks will be automatically applied to your project when using\n📫 [Permissions](https://google.github.io/accompanist/permissions/).\n\n## Download Permissions for Jetpack Compose\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation \"com.google.accompanist:accompanist-permissions:<version>\"\n}\n```\n"
  },
  {
    "path": "permissions-lint/build.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport com.google.accompanist.BundleInsideHelper\nimport com.google.accompanist.BundleInsideHelper.forInsideLintJar\n\nplugins {\n    `java-library`\n    id(\"kotlin\")\n    id(libs.plugins.jetbrains.dokka.get().pluginId)\n    id(libs.plugins.android.lint.get().pluginId)\n}\n\nlint {\n    htmlReport = true\n    htmlOutput = file(\"lint-report.html\")\n    textReport = true\n    absolutePaths = false\n    ignoreTestSources = true\n}\n\naffectedTestConfiguration {\n    jvmTestTask = \"test\"\n}\n\n/**\n * Creates a configuration for users to use that will be used bundle these dependency\n * jars inside of this lint check's jar. This is required because lintPublish does\n * not currently support dependencies, so instead we need to bundle any dependencies with the\n * lint jar manually. (b/182319899)\n */\nval bundleInside = forInsideLintJar()\n\ndependencies {\n    // Bundle metadataJvm inside the Jar\n    bundleInside(libs.kotlin.metadataJvm)\n\n    compileOnly(libs.android.tools.lint.api)\n    compileOnly(libs.kotlin.reflect)\n    compileOnly(libs.kotlin.stdlib)\n    compileOnly(libs.kotlin.stdlibJdk8) // Override version from transitive dependencies\n\n    testImplementation(libs.junit)\n    testImplementation(libs.kotlin.reflect)\n    testImplementation(libs.kotlin.stdlib)\n    testImplementation(libs.kotlin.stdlibJdk8) // Override version from transitive dependencies\n    testImplementation(libs.android.tools.lint.lint)\n    testImplementation(libs.android.tools.lint.tests)\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17\n    targetCompatibility = JavaVersion.VERSION_17\n}"
  },
  {
    "path": "permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsIssueRegistry.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions.lint\n\nimport com.android.tools.lint.client.api.IssueRegistry\nimport com.android.tools.lint.client.api.Vendor\nimport com.android.tools.lint.detector.api.CURRENT_API\nimport com.android.tools.lint.detector.api.Issue\n\n@Suppress(\"UnstableApiUsage\")\npublic class PermissionsIssueRegistry : IssueRegistry() {\n\n    override val issues: List<Issue> = listOf(\n        PermissionsLaunchDetector.PermissionLaunchedDuringComposition\n    )\n\n    override val api: Int = 10\n    override val minApi: Int = CURRENT_API\n\n    override val vendor: Vendor = Vendor(\n        vendorName = \"Accompanist Permissions\",\n        identifier = \"com.google.accompanist.permissions\",\n        feedbackUrl = \"https://github.com/google/accompanist/issues/new/choose\"\n    )\n}\n"
  },
  {
    "path": "permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetector.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\"UnstableApiUsage\")\n\npackage com.google.accompanist.permissions.lint\n\nimport com.android.tools.lint.detector.api.Category\nimport com.android.tools.lint.detector.api.Detector\nimport com.android.tools.lint.detector.api.Implementation\nimport com.android.tools.lint.detector.api.Issue\nimport com.android.tools.lint.detector.api.JavaContext\nimport com.android.tools.lint.detector.api.Scope\nimport com.android.tools.lint.detector.api.Severity\nimport com.android.tools.lint.detector.api.SourceCodeScanner\nimport com.google.accompanist.permissions.lint.util.Name\nimport com.google.accompanist.permissions.lint.util.Package\nimport com.google.accompanist.permissions.lint.util.PackageName\nimport com.google.accompanist.permissions.lint.util.isInvokedWithinComposable\nimport com.intellij.psi.PsiJavaFile\nimport com.intellij.psi.PsiMethod\nimport org.jetbrains.uast.UCallExpression\nimport java.util.EnumSet\n\n/**\n * [Detector] that checks `PermissionState.launchPermissionRequest` and\n * `MultiplePermissionsState.launchMultiplePermissionRequest` calls to make sure they don't happen\n * inside the body of a composable function / lambda.\n */\npublic class PermissionsLaunchDetector : Detector(), SourceCodeScanner {\n\n    override fun getApplicableMethodNames(): List<String> = listOf(\n        LaunchPermissionRequest.shortName, LaunchMultiplePermissionsRequest.shortName\n    )\n\n    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {\n        if (!method.isInPackageName(PermissionsPackageName)) return\n\n        if (node.isInvokedWithinComposable()) {\n            context.report(\n                PermissionLaunchedDuringComposition,\n                node,\n                context.getNameLocation(node),\n                \"Calls to ${method.name} should happen inside a regular lambda or \" +\n                    \" a side-effect, but never in the Composition.\"\n            )\n        }\n    }\n\n    public companion object {\n        public val PermissionLaunchedDuringComposition: Issue = Issue.create(\n            \"PermissionLaunchedDuringComposition\",\n            \"Calls to `launchPermissionRequest` or `launchMultiplePermissionRequest` \" +\n                \"should happen inside a regular lambda or a side-effect but never in the \" +\n                \"Composition.\",\n            \"Calls to `launchPermissionRequest` or `launchMultiplePermissionRequest` \" +\n                \"in the Composition throw a runtime exception. Please call them inside a regular \" +\n                \"lambda or in a side-effect.\",\n            Category.CORRECTNESS, 3, Severity.ERROR,\n            Implementation(\n                PermissionsLaunchDetector::class.java,\n                EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)\n            )\n        )\n    }\n}\n\n/**\n * Returns whether [this] has [packageName] as its package name.\n */\nprivate fun PsiMethod.isInPackageName(packageName: PackageName): Boolean =\n    packageName.javaPackageName == (containingFile as? PsiJavaFile)?.packageName\n\nprivate val PermissionsPackageName = Package(\"com.google.accompanist.permissions\")\nprivate val LaunchPermissionRequest =\n    Name(PermissionsPackageName, \"launchPermissionRequest\")\nprivate val LaunchMultiplePermissionsRequest =\n    Name(PermissionsPackageName, \"launchMultiplePermissionRequest\")\n"
  },
  {
    "path": "permissions-lint/src/main/java/com/google/accompanist/permissions/lint/util/ComposableUtils.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\"FunctionName\")\n\npackage com.google.accompanist.permissions.lint.util\n\n// FILE COPIED FROM:\n// https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/lint/common/src/main/java/androidx/compose/lint/ComposableUtils.kt\n\nimport com.intellij.lang.java.JavaLanguage\nimport com.intellij.psi.PsiMethod\nimport com.intellij.psi.PsiParameter\nimport com.intellij.psi.impl.compiled.ClsParameterImpl\nimport com.intellij.psi.impl.light.LightParameter\nimport org.jetbrains.kotlin.psi.KtAnnotated\nimport org.jetbrains.kotlin.psi.KtFunction\nimport org.jetbrains.kotlin.psi.KtProperty\nimport org.jetbrains.kotlin.psi.KtTypeReference\nimport org.jetbrains.kotlin.psi.psiUtil.getParentOfType\nimport org.jetbrains.uast.UAnnotation\nimport org.jetbrains.uast.UAnonymousClass\nimport org.jetbrains.uast.UCallExpression\nimport org.jetbrains.uast.UDeclaration\nimport org.jetbrains.uast.UElement\nimport org.jetbrains.uast.UExpression\nimport org.jetbrains.uast.ULambdaExpression\nimport org.jetbrains.uast.UMethod\nimport org.jetbrains.uast.UParameter\nimport org.jetbrains.uast.UTypeReferenceExpression\nimport org.jetbrains.uast.UVariable\nimport org.jetbrains.uast.getContainingDeclaration\nimport org.jetbrains.uast.getContainingUClass\nimport org.jetbrains.uast.getParameterForArgument\nimport org.jetbrains.uast.toUElement\nimport org.jetbrains.uast.withContainingElements\nimport kotlin.metadata.jvm.annotations\n\n/**\n * Returns whether this [UCallExpression] is directly invoked within the body of a Composable\n * function or lambda without being `remember`ed.\n */\npublic fun UCallExpression.isNotRemembered(): Boolean = isNotRememberedWithKeys()\n\n/**\n * Returns whether this [UCallExpression] is directly invoked within the body of a Composable\n * function or lambda without being `remember`ed, or whether it is invoked inside a `remember call\n * without the provided [keys][keyClassNames].\n * - Returns true if this [UCallExpression] is directly invoked inside a Composable function or\n *   lambda without being `remember`ed\n * - Returns true if this [UCallExpression] is invoked inside a call to `remember`, but without all\n *   of the provided [keys][keyClassNames] being used as key parameters to `remember`\n * - Returns false if this [UCallExpression] is correctly `remember`ed with the provided\n *   [keys][keyClassNames], or is not called inside a `remember` block, and is not called inside a\n *   Composable function or lambda\n *\n * @param keyClassNames [Name]s representing the expected classes that should be used as a key\n *   parameter to the `remember` call\n */\npublic fun UCallExpression.isNotRememberedWithKeys(vararg keyClassNames: Name): Boolean {\n    val visitor = ComposableBodyVisitor(this)\n    // The nearest method or lambda expression that contains this call expression\n    val boundaryElement = visitor.parentUElements().last()\n    // Check if the nearest lambda expression is actually a call to remember\n    val rememberCall: UCallExpression? =\n        (boundaryElement.uastParent as? UCallExpression)?.takeIf {\n            it.methodName == Names.Runtime.Remember.shortName &&\n                it.resolve()?.isInPackageName(Names.Runtime.PackageName) == true\n        }\n    return if (rememberCall == null) {\n        visitor.isComposable()\n    } else {\n        val parameterTypes =\n            rememberCall.valueArguments.mapNotNull { it.getExpressionType()?.canonicalText }\n        !keyClassNames.all { parameterTypes.contains(it.javaFqn) }\n    }\n}\n\n/**\n * Returns whether this [UExpression] is invoked within the body of a Composable function or lambda.\n *\n * This searches parent declarations until we find a lambda expression or a function, and looks to\n * see if these are Composable.\n */\nfun UExpression.isInvokedWithinComposable(): Boolean {\n    return ComposableBodyVisitor(this).isComposable()\n}\n\n// TODO: https://youtrack.jetbrains.com/issue/KT-45406\n// KotlinUMethodWithFakeLightDelegate.hasAnnotation() (for reified functions for example)\n// doesn't find annotations, so just look at the annotations directly.\n/** Returns whether this method is @Composable or not */\nval PsiMethod.isComposable\n    get() = annotations.any { it.qualifiedName == Names.Runtime.Composable.javaFqn }\n\n/** Returns whether this variable's type is @Composable or not */\nval UVariable.isComposable: Boolean\n    get() {\n        // Annotation on the lambda\n        val annotationOnLambda =\n            when (val initializer = uastInitializer) {\n                is ULambdaExpression -> {\n                    val source = initializer.sourcePsi\n                    if (source is KtFunction) {\n                        // Anonymous function, val foo = @Composable fun() {}\n                        source.hasComposableAnnotation\n                    } else {\n                        // Lambda, val foo = @Composable {}\n                        initializer.findAnnotation(Names.Runtime.Composable.javaFqn) != null\n                    }\n                }\n                else -> false\n            }\n        // Annotation on the type, foo: @Composable () -> Unit = { }\n        val annotationOnType = typeReference?.isComposable == true\n        return annotationOnLambda || annotationOnType\n    }\n\n/** Returns whether this parameter's type is @Composable or not */\nprivate val PsiParameter.isComposable: Boolean\n    get() =\n        when {\n            // The parameter is in a class file. Currently type annotations aren't currently added\n            // to\n            // the underlying type (https://youtrack.jetbrains.com/issue/KT-45307), so instead we\n            // use\n            // the metadata annotation.\n            this is ClsParameterImpl ||\n                // In some cases when a method is defined in bytecode and the call fails to resolve\n                // to the ClsMethodImpl, we will instead get a LightParameter. Note that some Kotlin\n                // declarations too will also appear as a LightParameter, so we can check to see if\n                // the source language is Java, which means that this is a LightParameter for\n                // bytecode, as opposed to for a Kotlin declaration.\n                // https://youtrack.jetbrains.com/issue/KT-46883\n                (this is LightParameter && this.language is JavaLanguage) -> {\n                // Find the containing method, so we can get metadata from the containing class\n                val containingMethod = getParentOfType<PsiMethod>(true)\n                val kmFunction = containingMethod!!.toKmFunction()\n\n                val kmValueParameter = kmFunction?.valueParameters?.find { it.name == name }\n\n                kmValueParameter?.type?.annotations?.find {\n                    it.className == Names.Runtime.Composable.kmClassName\n                } != null\n            }\n            // The parameter is in a source declaration\n            else -> (toUElement() as? UParameter)?.typeReference?.isComposable == true\n        }\n\n/** Returns whether this lambda expression is @Composable or not */\nval ULambdaExpression.isComposable: Boolean\n    get() =\n        when (val lambdaParent = uastParent) {\n            // Function call with a lambda parameter\n            is UCallExpression -> {\n                val parameter = lambdaParent.getParameterForArgument(this)\n                parameter?.isComposable == true\n            }\n            // A local / non-local lambda variable\n            is UVariable -> {\n                lambdaParent.isComposable\n            }\n            // Either a new UAST type we haven't handled, or non-Kotlin declarations\n            else -> false\n        }\n\n/**\n * Helper class that visits parent declarations above the provided [expression], until it finds a\n * lambda or method. This 'boundary' is used as the indicator for whether this [expression] can be\n * considered to be inside a Composable body or not.\n *\n * @see isComposable\n * @see parentUElements\n */\nprivate class ComposableBodyVisitor(private val expression: UExpression) {\n    /** @return whether the body can be considered Composable or not */\n    fun isComposable(): Boolean =\n        when (val element = parentUElements.last()) {\n            is UMethod -> element.isComposable\n            is ULambdaExpression -> element.isComposable\n            else -> false\n        }\n\n    /** Returns all parent [UElement]s until and including the boundary lambda / method. */\n    fun parentUElements() = parentUElements\n\n    /**\n     * The outermost UElement that corresponds to the surrounding UDeclaration that contains\n     * [expression], with the following special cases:\n     * - if the containing UDeclaration is a local property, we ignore it and search above as it\n     *   still could be created in the context of a Composable body\n     * - if the containing UDeclaration is an anonymous class (object { }), we ignore it and search\n     *   above as it still could be created in the context of a Composable body\n     */\n    private val boundaryUElement by lazy {\n        // The nearest property / function / etc declaration that contains this call expression\n        var containingDeclaration = expression.getContainingDeclaration()\n\n        fun UDeclaration.isLocalProperty() = (sourcePsi as? KtProperty)?.isLocal == true\n        fun UDeclaration.isAnonymousClass() = this is UAnonymousClass\n        fun UDeclaration.isPropertyInsideAnonymousClass() =\n            getContainingUClass()?.isAnonymousClass() == true\n\n        while (\n            containingDeclaration != null &&\n            (\n                containingDeclaration.isLocalProperty() ||\n                    containingDeclaration.isAnonymousClass() ||\n                    containingDeclaration.isPropertyInsideAnonymousClass()\n                )\n        ) {\n            containingDeclaration = containingDeclaration.getContainingDeclaration()\n        }\n\n        containingDeclaration\n    }\n\n    private val parentUElements by lazy {\n        val elements = mutableListOf<UElement>()\n\n        // Look through containing elements until we find a lambda or a method\n        for (element in expression.withContainingElements) {\n            elements += element\n            when (element) {\n                // TODO: consider handling the case of a lambda inside an inline function call,\n                //  such as `apply` or `forEach`. These calls don't really change the\n                //  'composability' here, but there may be other inline function calls that\n                //  capture the lambda and invoke it elsewhere, so we might need to look for\n                //  a callsInPlace contract in the metadata for the function, or the body of the\n                //  source definition.\n                is ULambdaExpression -> break\n                is UMethod -> break\n                // Stop when we reach the parent declaration to avoid escaping the scope.\n                boundaryUElement -> break\n            }\n        }\n        elements\n    }\n}\n\n/** Returns whether this type reference is @Composable or not */\nval UTypeReferenceExpression.isComposable: Boolean\n    get() {\n        if (type.hasAnnotation(Names.Runtime.Composable.javaFqn)) return true\n\n        // Annotations on the types of local properties (val foo: @Composable () -> Unit = {})\n        // are currently not present on the PsiType, we so need to manually check the underlying\n        // type reference. (https://youtrack.jetbrains.com/issue/KTIJ-18821)\n        return (sourcePsi as? KtTypeReference)?.hasComposableAnnotation == true\n    }\n\n/** Returns whether this annotated declaration has a Composable annotation */\nprivate val KtAnnotated.hasComposableAnnotation: Boolean\n    get() =\n        annotationEntries.any {\n            (it.toUElement() as UAnnotation).qualifiedName == Names.Runtime.Composable.javaFqn\n        }\n"
  },
  {
    "path": "permissions-lint/src/main/java/com/google/accompanist/permissions/lint/util/KotlinMetadataUtils.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions.lint.util\n\n// FILE COPIED FROM:\n// https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/lint/common/src/main/java/androidx/compose/lint/KotlinMetadataUtils.kt\n\nimport com.intellij.lang.jvm.annotation.JvmAnnotationArrayValue\nimport com.intellij.lang.jvm.annotation.JvmAnnotationAttributeValue\nimport com.intellij.lang.jvm.annotation.JvmAnnotationConstantValue\nimport com.intellij.psi.PsiAnnotation\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiMethod\nimport com.intellij.psi.impl.compiled.ClsMethodImpl\nimport com.intellij.psi.util.ClassUtil\nimport kotlin.metadata.KmDeclarationContainer\nimport kotlin.metadata.KmFunction\nimport kotlin.metadata.jvm.KotlinClassMetadata\nimport kotlin.metadata.jvm.Metadata\nimport kotlin.metadata.jvm.signature\n\n/**\n * @return the corresponding [KmFunction] for this [PsiMethod], or `null` if there is no\n *   corresponding [KmFunction]. This method is only meaningful if this [PsiMethod] represents a\n *   method defined in bytecode (most often a [ClsMethodImpl]).\n */\npublic fun PsiMethod.toKmFunction(): KmFunction? =\n    containingClass!!.getKmDeclarationContainer()?.findKmFunctionForPsiMethod(this)\n\n// TODO: https://youtrack.jetbrains.com/issue/KT-45310\n// Currently there is no built in support for parsing kotlin metadata from kotlin class files, so\n// we need to manually inspect the annotations and work with Cls* (compiled PSI).\n/**\n * Returns the [KmDeclarationContainer] using the kotlin.Metadata annotation present on this\n * [PsiClass]. Returns null if there is no annotation (not parsing a Kotlin class file), the\n * annotation data is for an unsupported version of Kotlin, or if the metadata represents a\n * synthetic class.\n */\nprivate fun PsiClass.getKmDeclarationContainer(): KmDeclarationContainer? {\n    val classKotlinMetadataPsiAnnotation =\n        annotations.find {\n            // hasQualifiedName() not available on the min version of Lint we compile against\n            it.qualifiedName == KotlinMetadataFqn\n        } ?: return null\n\n    val metadata =\n        try {\n            KotlinClassMetadata.readStrict(classKotlinMetadataPsiAnnotation.toMetadataAnnotation())\n        } catch (e: Exception) {\n            // Don't crash if we are trying to parse metadata from a newer version of Kotlin, than\n            // is\n            // supported by the bundled version of kotlin-metadata-jvm\n            return null\n        }\n\n    return when (metadata) {\n        is KotlinClassMetadata.Class -> metadata.kmClass\n        is KotlinClassMetadata.FileFacade -> metadata.kmPackage\n        is KotlinClassMetadata.SyntheticClass -> null\n        is KotlinClassMetadata.MultiFileClassFacade -> null\n        is KotlinClassMetadata.MultiFileClassPart -> metadata.kmPackage\n        is KotlinClassMetadata.Unknown -> null\n    }\n}\n\n/** Returns a [Metadata] by parsing the attributes of this @kotlin.Metadata PSI annotation. */\nprivate fun PsiAnnotation.toMetadataAnnotation(): Metadata {\n    val attributes = attributes.associate { it.attributeName to it.attributeValue }\n\n    fun JvmAnnotationAttributeValue.parseString(): String =\n        (this as JvmAnnotationConstantValue).constantValue as String\n\n    fun JvmAnnotationAttributeValue.parseInt(): Int =\n        (this as JvmAnnotationConstantValue).constantValue as Int\n\n    fun JvmAnnotationAttributeValue.parseStringArray(): Array<String> =\n        (this as JvmAnnotationArrayValue).values.map { it.parseString() }.toTypedArray()\n\n    fun JvmAnnotationAttributeValue.parseIntArray(): IntArray =\n        (this as JvmAnnotationArrayValue).values.map { it.parseInt() }.toTypedArray().toIntArray()\n\n    val kind = attributes[\"k\"]?.parseInt()\n    val metadataVersion = attributes[\"mv\"]?.parseIntArray()\n    val data1 = attributes[\"d1\"]?.parseStringArray()\n    val data2 = attributes[\"d2\"]?.parseStringArray()\n    val extraString = attributes[\"xs\"]?.parseString()\n    val packageName = attributes[\"pn\"]?.parseString()\n    val extraInt = attributes[\"xi\"]?.parseInt()\n\n    return Metadata(kind, metadataVersion, data1, data2, extraString, packageName, extraInt)\n}\n\n/**\n * @return the corresponding [KmFunction] in [this] for the given [method], matching by name and\n *   signature.\n */\nprivate fun KmDeclarationContainer.findKmFunctionForPsiMethod(method: PsiMethod): KmFunction? {\n    // Strip any mangled part of the name in case of value / inline classes\n    val expectedName = method.name.substringBefore(\"-\")\n    val expectedSignature = ClassUtil.getAsmMethodSignature(method)\n    // Since Kotlin 1.6 PSI updates, in some cases what used to be `void` return types are converted\n    // to `kotlin.Unit`, even though in the actual metadata they are still void. Try to match those\n    // cases as well\n    val unitReturnTypeSuffix = \"Lkotlin/Unit;\"\n    val expectedSignatureConvertedFromUnitToVoid =\n        if (expectedSignature.endsWith(unitReturnTypeSuffix)) {\n            expectedSignature.substringBeforeLast(unitReturnTypeSuffix) + \"V\"\n        } else {\n            expectedSignature\n        }\n\n    return functions.find {\n        it.name == expectedName &&\n            (\n                it.signature?.descriptor == expectedSignature ||\n                    it.signature?.descriptor == expectedSignatureConvertedFromUnitToVoid\n                )\n    }\n}\n\nprivate const val KotlinMetadataFqn = \"kotlin.Metadata\"\n"
  },
  {
    "path": "permissions-lint/src/main/java/com/google/accompanist/permissions/lint/util/Names.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions.lint.util\n\n/**\n * File copied from\n * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/lint/common/src/main/java/androidx/compose/lint/Names.kt\n */\n\nimport kotlin.metadata.ClassName\n\n/** Contains common names used for lint checks. */\nobject Names {\n    object Animation {\n        val PackageName = Package(\"androidx.compose.animation\")\n\n        object Core {\n            val PackageName = Package(\"androidx.compose.animation.core\")\n            val Animatable = Name(PackageName, \"Animatable\")\n        }\n    }\n\n    object AnimationCore {\n        val PackageName = Package(\"androidx.compose.animation.core\")\n    }\n\n    object Runtime {\n        val PackageName = Package(\"androidx.compose.runtime\")\n\n        val Composable = Name(PackageName, \"Composable\")\n        val CompositionLocal = Name(PackageName, \"CompositionLocal\")\n        val DerivedStateOf = Name(PackageName, \"derivedStateOf\")\n        val State = Name(PackageName, \"State\")\n        val MutableState = Name(PackageName, \"MutableState\")\n        val MutableStateOf = Name(PackageName, \"mutableStateOf\")\n        val MutableIntStateOf = Name(PackageName, \"mutableIntStateOf\")\n        val MutableLongStateOf = Name(PackageName, \"mutableLongStateOf\")\n        val MutableFloatStateOf = Name(PackageName, \"mutableFloatStateOf\")\n        val MutableDoubleStateOf = Name(PackageName, \"mutableDoubleStateOf\")\n        val MutableStateListOf = Name(PackageName, \"mutableStateListOf\")\n        val MutableStateMapOf = Name(PackageName, \"mutableStateMapOf\")\n        val ProduceState = Name(PackageName, \"produceState\")\n        val Remember = Name(PackageName, \"remember\")\n        val DisposableEffect = Name(PackageName, \"DisposableEffect\")\n        val RememberSaveable = Name(PackageName, \"rememberSaveable\")\n        val LaunchedEffect = Name(PackageName, \"LaunchedEffect\")\n        val ReusableContent = Name(PackageName, \"ReusableContent\")\n        val Key = Name(PackageName, \"key\")\n        val StructuralEqualityPolicy = Name(PackageName, \"structuralEqualityPolicy\")\n    }\n\n    object Ui {\n        val PackageName = Package(\"androidx.compose.ui\")\n        val Composed = Name(PackageName, \"composed\")\n        val Modifier = Name(PackageName, \"Modifier\")\n\n        object Layout {\n            val PackageName = Package(\"androidx.compose.ui.layout\")\n            val ParentDataModifier = Name(PackageName, \"ParentDataModifier\")\n        }\n\n        object Pointer {\n            val PackageName = Package(Ui.PackageName, \"input.pointer\")\n            val PointerInputScope = Name(PackageName, \"PointerInputScope\")\n            val PointerInputScopeModifier = Name(PackageName, \"pointerInput\")\n            val AwaitPointerEventScope = Name(PackageName, \"awaitPointerEventScope\")\n        }\n\n        object Unit {\n            val PackageName = Package(\"androidx.compose.ui.unit\")\n            val Dp = Name(PackageName, \"Dp\")\n        }\n\n        object Node {\n            val PackageName = Package(Ui.PackageName, \"node\")\n            val CurrentValueOf = Name(PackageName, \"currentValueOf\")\n        }\n    }\n\n    object UiGraphics {\n        val PackageName = Package(\"androidx.compose.ui.graphics\")\n        val Color = Name(PackageName, \"Color\")\n    }\n}\n\n/**\n * Represents a qualified package\n *\n * @property segments the segments representing the package\n */\nclass PackageName internal constructor(internal val segments: List<String>) {\n    /** The Java-style package name for this [Name], separated with `.` */\n    val javaPackageName: String\n        get() = segments.joinToString(\".\")\n}\n\n/**\n * Represents the qualified name for an element\n *\n * @property pkg the package for this element\n * @property nameSegments the segments representing the element - there can be multiple in the case\n *   of nested classes.\n */\nclass Name\ninternal constructor(private val pkg: PackageName, private val nameSegments: List<String>) {\n    /** The short name for this [Name] */\n    val shortName: String\n        get() = nameSegments.last()\n\n    /** The Java-style fully qualified name for this [Name], separated with `.` */\n    val javaFqn: String\n        get() = pkg.segments.joinToString(\".\", postfix = \".\") + nameSegments.joinToString(\".\")\n\n    /**\n     * The [ClassName] for use with kotlin.metadata. Note that in kotlin.metadata the actual type\n     * might be different from the underlying JVM type, for example: kotlin/Int -> java/lang/Integer\n     */\n    val kmClassName: ClassName\n        get() = pkg.segments.joinToString(\"/\", postfix = \"/\") + nameSegments.joinToString(\".\")\n\n    /** The [PackageName] of this element. */\n    val packageName: PackageName\n        get() = pkg\n}\n\n/** @return a [PackageName] with a Java-style (separated with `.`) [packageName]. */\nfun Package(packageName: String): PackageName = PackageName(packageName.split(\".\"))\n\n/** @return a [PackageName] with a Java-style (separated with `.`) [packageName]. */\nfun Package(packageName: PackageName, shortName: String): PackageName =\n    PackageName(packageName.segments + shortName.split(\".\"))\n\n/** @return a [Name] with the provided [pkg] and Java-style (separated with `.`) [shortName]. */\nfun Name(pkg: PackageName, shortName: String): Name = Name(pkg, shortName.split(\".\"))\n"
  },
  {
    "path": "permissions-lint/src/main/java/com/google/accompanist/permissions/lint/util/PsiUtils.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.permissions.lint.util\n\nimport com.intellij.psi.PsiClass\nimport com.intellij.psi.PsiClassOwner\nimport com.intellij.psi.PsiMethod\nimport com.intellij.psi.PsiType\nimport com.intellij.psi.util.InheritanceUtil\n\n/**\n * File copied from\n * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/lint/common/src/main/java/androidx/compose/lint/PsiUtils.kt\n */\n\n/** Returns whether [this] has [packageName] as its package name. */\nfun PsiMethod.isInPackageName(packageName: PackageName): Boolean {\n    val actual = (containingFile as? PsiClassOwner)?.packageName\n    return packageName.javaPackageName == actual\n}\n\n/** Whether this [PsiMethod] returns Unit */\nval PsiMethod.returnsUnit\n    get() = returnType.isVoidOrUnit\n\n/**\n * Whether this [PsiType] is `void` or [Unit]\n *\n * In Kotlin 1.6 some expressions now explicitly return [Unit] instead of just being [PsiType.VOID],\n * so this returns whether this type is either.\n */\nval PsiType?.isVoidOrUnit\n    get() = this == PsiType.VOID || this?.canonicalText == \"kotlin.Unit\"\n\n/** @return whether [this] inherits from [name]. Returns `true` if [this] _is_ directly [name]. */\nfun PsiType.inheritsFrom(name: Name) = InheritanceUtil.isInheritor(this, name.javaFqn)\n\n/** @return whether [this] inherits from [name]. Returns `true` if [this] _is_ directly [name]. */\nfun PsiClass.inheritsFrom(name: Name) = InheritanceUtil.isInheritor(this, name.javaFqn)\n"
  },
  {
    "path": "permissions-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry",
    "content": "#\n# Copyright 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\ncom.google.accompanist.permissions.lint.PermissionsIssueRegistry\n"
  },
  {
    "path": "permissions-lint/src/test/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetectorTest.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n@file:Suppress(\"UnstableApiUsage\")\n\npackage com.google.accompanist.permissions.lint\n\nimport com.android.tools.lint.checks.infrastructure.TestFiles\nimport com.android.tools.lint.checks.infrastructure.TestLintResult\nimport com.android.tools.lint.checks.infrastructure.TestLintTask\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.junit.runners.JUnit4\n\n/* ktlint-disable max-line-length */\n\n/**\n * Test for [PermissionsLaunchDetector].\n */\n@RunWith(JUnit4::class)\ninternal class PermissionsLaunchDetectorTest {\n\n    private fun check(fileToAdd: String): TestLintResult {\n        return TestLintTask.lint()\n            .files(\n                LaunchPermissionsStub,\n                ComposableStub,\n                TestFiles.kt(fileToAdd)\n            )\n            .allowMissingSdk()\n            .issues(PermissionsLaunchDetector.PermissionLaunchedDuringComposition)\n            .run()\n    }\n\n    @Test\n    fun errors() {\n        check(\n            \"\"\"\n            import androidx.compose.runtime.Composable\n            import com.google.accompanist.permissions.*\n\n            @Composable\n            fun Test() {\n                PermissionState().launchPermissionRequest()\n                MultiplePermissionsState().launchMultiplePermissionRequest()\n            }\n\n            val lambda = @Composable {\n                PermissionState().launchPermissionRequest()\n                MultiplePermissionsState().launchMultiplePermissionRequest()\n            }\n\n            val lambda2: @Composable () -> Unit = {\n                PermissionState().launchPermissionRequest()\n                MultiplePermissionsState().launchMultiplePermissionRequest()\n            }\n\n            @Composable\n            fun LambdaParameter(content: @Composable () -> Unit) {}\n\n            @Composable\n            fun Test2() {\n                LambdaParameter(content = {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                })\n                LambdaParameter {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                }\n            }\n\n            fun test3() {\n                val localLambda1 = @Composable {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                }\n\n                val localLambda2: @Composable () -> Unit = {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                }\n            }\n        \"\"\"\n        )\n            .expect(\n                \"\"\"\n                    src/test.kt:7: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                    PermissionState().launchPermissionRequest()\n                                                      ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:8: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:12: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                    PermissionState().launchPermissionRequest()\n                                                      ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:13: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:17: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                    PermissionState().launchPermissionRequest()\n                                                      ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:18: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:27: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        PermissionState().launchPermissionRequest()\n                                                          ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:28: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:31: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        PermissionState().launchPermissionRequest()\n                                                          ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:32: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:38: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        PermissionState().launchPermissionRequest()\n                                                          ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:39: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:43: Error: Calls to launchPermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        PermissionState().launchPermissionRequest()\n                                                          ~~~~~~~~~~~~~~~~~~~~~~~\n                    src/test.kt:44: Error: Calls to launchMultiplePermissionRequest should happen inside a regular lambda or  a side-effect, but never in the Composition. [PermissionLaunchedDuringComposition]\n                                        MultiplePermissionsState().launchMultiplePermissionRequest()\n                                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n                    14 errors, 0 warnings\n                \"\"\".trimIndent()\n            )\n    }\n\n    @Test\n    fun noErrors() {\n        check(\n            \"\"\"\n            import com.google.accompanist.permissions.*\n            \n            fun test() {\n                PermissionState().launchPermissionRequest()\n                MultiplePermissionsState().launchMultiplePermissionRequest()\n            }\n\n            val lambda = {\n                PermissionState().launchPermissionRequest()\n                MultiplePermissionsState().launchMultiplePermissionRequest()\n            }\n\n            val lambda2: () -> Unit = {\n                PermissionState().launchPermissionRequest()\n                MultiplePermissionsState().launchMultiplePermissionRequest()\n            }\n\n            fun lambdaParameter(action: () -> Unit) {}\n\n            fun test2() {\n                lambdaParameter(action = {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                })\n                lambdaParameter {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                }\n            }\n\n            fun test3() {\n                val localLambda1 = {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                }\n\n                val localLambda2: () -> Unit = {\n                    PermissionState().launchPermissionRequest()\n                    MultiplePermissionsState().launchMultiplePermissionRequest()\n                }\n            }\n        \"\"\"\n        )\n            .expectClean()\n    }\n}\n\nprivate val LaunchPermissionsStub = TestFiles.kt(\n    \"com/google/accompanist/permissions/LaunchPermissions.kt\",\n    \"\"\"\n        package com.google.accompanist.permissions\n\n        class PermissionState {\n            fun launchPermissionRequest()\n        }\n\n        class MultiplePermissionsState {\n            fun launchMultiplePermissionRequest()\n        }\n    \"\"\"\n).indented().within(\"src\")\n\nprivate val ComposableStub = TestFiles.kt(\n    \"androidx/compose/runtime/Composable.kt\",\n    \"\"\"\n        package androidx.compose.runtime\n\n        annotation class Composable\n    \"\"\"\n).indented().within(\"src\")\n/* ktlint-enable max-line-length */\n"
  },
  {
    "path": "release/signing-cleanup.sh",
    "content": "#!/bin/sh\n\n# Copyright 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nrm -f release/*.gpg\nrm -f release/*.properties\n"
  },
  {
    "path": "release/signing-setup.sh",
    "content": "#!/bin/bash\n\n# Copyright 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nENCRYPT_KEY=$1\n\nif [[ ! -z \"$ENCRYPT_KEY\" ]]; then\n  # Decrypt GnuPG keyring\n  openssl aes-256-cbc -md sha256 -d -in release/secring.gpg.aes -out release/secring.gpg -k ${ENCRYPT_KEY}\n\n  # Decrypt Play Store key\n  openssl aes-256-cbc -md sha256 -d -in release/signing.properties.aes -out release/signing.properties -k ${ENCRYPT_KEY}\n\nelse\n  echo \"ENCRYPT_KEY is empty\"\nfi\n"
  },
  {
    "path": "sample/build.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n    alias(libs.plugins.android.application)\n    alias(libs.plugins.android.kotlin)\n    alias(libs.plugins.compose.plugin)\n}\n\nandroid {\n    compileSdk = 35\n\n    defaultConfig {\n        applicationId = \"com.google.accompanist.sample\"\n        minSdk = 21\n        targetSdk = 35\n\n        versionCode = 1\n        versionName = \"1.0\"\n\n        testInstrumentationRunner = \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n\n        isCoreLibraryDesugaringEnabled = true\n    }\n\n    buildFeatures {\n        compose = true\n    }\n\n    buildTypes {\n        getByName(\"release\") {\n            signingConfig = signingConfigs.getByName(\"debug\")\n        }\n    }\n\n    kotlinOptions {\n        jvmTarget = \"17\"\n    }\n\n    namespace = \"com.google.accompanist.sample\"\n}\n\ndependencies {\n    coreLibraryDesugaring(libs.desugar.jdk.libs)\n\n    implementation(project(\":adaptive\"))\n    implementation(project(\":drawablepainter\"))\n    implementation(project(\":permissions\"))\n\n    implementation(libs.compose.material.iconsext)\n    implementation(libs.compose.material3.material3)\n    implementation(libs.compose.foundation.layout)\n    debugImplementation(libs.compose.ui.tooling)\n    implementation(libs.compose.ui.tooling.preview)\n    implementation(libs.compose.ui.util)\n\n    implementation(libs.androidx.core)\n    implementation(libs.androidx.activity.compose)\n    implementation(libs.androidx.lifecycle.runtime)\n\n    implementation(libs.kotlin.stdlib)\n\n    lintChecks(project(\":permissions-lint\"))\n}\n"
  },
  {
    "path": "sample/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2022 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <!-- Used for loading images from the network -->\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <!-- Used for the permissions sample -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-feature android:name=\"android.hardware.camera\" android:required=\"false\"/>\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n\n    <application\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@android:style/Theme.Material.NoActionBar\">\n\n        <profileable android:shell=\"true\"\n            tools:targetApi=\"q\" />\n\n        <activity\n            android:name=\"com.google.accompanist.sample.MainActivity\"\n            android:label=\"@string/app_name\"\n            android:theme=\"@android:style/Theme.Material.Light.NoActionBar\"\n            android:windowSoftInputMode=\"adjustResize\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".permissions.RequestPermissionSample\"\n            android:label=\"@string/permissions_title_one\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".permissions.RequestMultiplePermissionsSample\"\n            android:label=\"@string/permissions_title_multiple\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".permissions.RequestLocationPermissionsSample\"\n            android:label=\"@string/permissions_title_location\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".adaptive.BasicTwoPaneSample\"\n            android:label=\"@string/adaptive_two_pane_basic\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".adaptive.HorizontalTwoPaneSample\"\n            android:label=\"@string/adaptive_two_pane_horizontal\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".adaptive.VerticalTwoPaneSample\"\n            android:label=\"@string/adaptive_two_pane_vertical\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".adaptive.NavRailFoldAwareColumnSample\"\n            android:label=\"@string/adaptive_fold_aware_column_nav_rail\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".adaptive.NavDrawerFoldAwareColumnSample\"\n            android:label=\"@string/adaptive_fold_aware_column_nav_drawer\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".adaptive.DraggableFoldAwareColumnSample\"\n            android:label=\"@string/adaptive_fold_aware_column_draggable\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"com.google.accompanist.sample.SAMPLE_CODE\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/ImageLoadingSampleUtils.kt",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.remember\n\nprivate val rangeForRandom = (0..100000)\n\nfun randomSampleImageUrl(\n    seed: Int = rangeForRandom.random(),\n    width: Int = 300,\n    height: Int = width,\n): String {\n    return \"https://picsum.photos/seed/$seed/$width/$height\"\n}\n\n/**\n * Remember a URL generate by [randomSampleImageUrl].\n */\n@Composable\nfun rememberRandomSampleImageUrl(\n    seed: Int = rangeForRandom.random(),\n    width: Int = 300,\n    height: Int = width,\n): String = remember { randomSampleImageUrl(seed, width, height) }\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/MainActivity.kt",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.activity.enableEdgeToEdge\n\n/**\n * A list which automatically populates the list of sample activities in this app\n * with the category `com.google.accompanist.sample.SAMPLE_CODE`.\n */\nclass MainActivity : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        enableEdgeToEdge()\n        val data = getData(intent.getStringExtra(EXTRA_PATH))\n\n        setContent {\n            AccompanistSampleTheme {\n                MainScreen(\n                    listData = data,\n                    onItemClick = { startActivity(it) }\n                )\n            }\n        }\n    }\n\n    private fun getData(prefix: String?): List<AccompanistSample> {\n        val myData = mutableListOf<AccompanistSample>()\n\n        val mainIntent = Intent(Intent.ACTION_MAIN, null)\n        mainIntent.addCategory(\"com.google.accompanist.sample.SAMPLE_CODE\")\n\n        @SuppressLint(\"QueryPermissionsNeeded\") // Only querying our own Activities\n        val list = packageManager.queryIntentActivities(mainIntent, 0)\n\n        val prefixPath: Array<String>?\n        var prefixWithSlash = prefix\n\n        if (prefix.isNullOrEmpty()) {\n            prefixPath = null\n        } else {\n            prefixPath = prefix.split(\"/\".toRegex()).toTypedArray()\n            prefixWithSlash = \"$prefix/\"\n        }\n\n        val entries = mutableMapOf<String, Boolean>()\n\n        list.forEach { info ->\n            val labelSeq = info.loadLabel(packageManager)\n            val label = labelSeq?.toString() ?: info.activityInfo.name\n\n            if (prefixWithSlash.isNullOrEmpty() || label.startsWith(prefixWithSlash)) {\n                val labelPath = label.split(\"/\".toRegex()).toTypedArray()\n                val nextLabel = if (prefixPath == null) labelPath[0] else labelPath[prefixPath.size]\n                if ((prefixPath?.size ?: 0) == labelPath.size - 1) {\n                    myData.add(\n                        AccompanistSample(\n                            title = nextLabel,\n                            intent = activityIntent(\n                                info.activityInfo.applicationInfo.packageName,\n                                info.activityInfo.name\n                            )\n                        )\n                    )\n                } else {\n                    if (entries[nextLabel] == null) {\n                        myData.add(\n                            AccompanistSample(\n                                title = nextLabel,\n                                intent = browseIntent(\n                                    if (prefix == \"\") nextLabel else \"$prefix/$nextLabel\"\n                                )\n                            )\n                        )\n                        entries[nextLabel] = true\n                    }\n                }\n            }\n        }\n\n        myData.sortBy { it.title }\n\n        return myData\n    }\n\n    private fun activityIntent(pkg: String, componentName: String): Intent {\n        val result = Intent()\n        result.setClassName(pkg, componentName)\n        return result\n    }\n\n    private fun browseIntent(path: String): Intent {\n        val result = Intent()\n        result.setClass(this, MainActivity::class.java)\n        result.putExtra(EXTRA_PATH, path)\n        return result\n    }\n}\n\nprivate const val EXTRA_PATH = \"com.example.android.apis.Path\"\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/MainScreen.kt",
    "content": "/*\n * Copyright 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample\n\nimport android.content.Intent\nimport androidx.compose.foundation.clickable\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.WindowInsets\nimport androidx.compose.foundation.layout.WindowInsetsSides\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.only\nimport androidx.compose.foundation.layout.safeDrawing\nimport androidx.compose.foundation.layout.systemBars\nimport androidx.compose.foundation.layout.windowInsetsBottomHeight\nimport androidx.compose.foundation.layout.windowInsetsPadding\nimport androidx.compose.foundation.lazy.LazyColumn\nimport androidx.compose.foundation.lazy.items\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.ListItem\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.material3.TopAppBar\nimport androidx.compose.material3.TopAppBarDefaults\nimport androidx.compose.material3.rememberTopAppBarState\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.input.nestedscroll.nestedScroll\nimport androidx.compose.ui.res.stringResource\n\ndata class AccompanistSample(\n    val title: String,\n    val intent: Intent\n)\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\nfun MainScreen(\n    listData: List<AccompanistSample>,\n    onItemClick: (Intent) -> Unit,\n    modifier: Modifier = Modifier\n) {\n    Surface(modifier) {\n        Column {\n            val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())\n            TopAppBar(\n                title = { Text(stringResource(R.string.app_name)) },\n                scrollBehavior = scrollBehavior\n            )\n\n            ContentList(\n                listData,\n                onItemClick,\n                modifier = Modifier\n                    .fillMaxSize()\n                    .windowInsetsPadding(\n                        WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)\n                    )\n                    .nestedScroll(scrollBehavior.nestedScrollConnection)\n            )\n        }\n    }\n}\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\nprivate fun ContentList(\n    listData: List<AccompanistSample>,\n    onItemClick: (Intent) -> Unit,\n    modifier: Modifier = Modifier\n) {\n    LazyColumn(\n        modifier = modifier\n    ) {\n        items(listData) {\n            ListItem(\n                headlineText = { Text(it.title) },\n                modifier = Modifier.clickable { onItemClick(it.intent) }\n            )\n        }\n\n        item {\n            Spacer(\n                Modifier.windowInsetsBottomHeight(\n                    WindowInsets.systemBars\n                )\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/Theme.kt",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample\n\nimport androidx.compose.foundation.isSystemInDarkTheme\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.safeDrawingPadding\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.darkColorScheme\nimport androidx.compose.material3.lightColorScheme\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\n\n@Composable\nfun AccompanistSample(\n    contentPadding: PaddingValues = PaddingValues(16.dp),\n    content: @Composable () -> Unit\n) {\n    AccompanistSampleTheme {\n        Surface(\n            modifier = Modifier.fillMaxSize()\n        ) {\n            Box(\n                modifier = Modifier.padding(contentPadding).safeDrawingPadding(),\n                propagateMinConstraints = true\n            ) {\n                content()\n            }\n        }\n    }\n}\n\n@Composable\nfun AccompanistSampleTheme(\n    darkTheme: Boolean = isSystemInDarkTheme(),\n    content: @Composable () -> Unit\n) {\n    MaterialTheme(\n        colorScheme = if (darkTheme) darkColorScheme() else lightColorScheme(),\n        content = content\n    )\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/adaptive/BasicTwoPaneSample.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.adaptive\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.Text\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.adaptive.HorizontalTwoPaneStrategy\nimport com.google.accompanist.adaptive.TwoPane\nimport com.google.accompanist.adaptive.TwoPaneStrategy\nimport com.google.accompanist.adaptive.VerticalTwoPaneStrategy\nimport com.google.accompanist.adaptive.calculateDisplayFeatures\nimport com.google.accompanist.sample.AccompanistSample\n\nclass BasicTwoPaneSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccompanistSample {\n                val displayFeatures = calculateDisplayFeatures(this)\n\n                TwoPane(\n                    first = {\n                        Card(\n                            modifier = Modifier.padding(8.dp)\n                        ) {\n                            Box(\n                                contentAlignment = Alignment.Center,\n                                modifier = Modifier.fillMaxSize()\n                            ) {\n                                Text(\"First\")\n                            }\n                        }\n                    },\n                    second = {\n                        Card(\n                            modifier = Modifier.padding(8.dp)\n                        ) {\n                            Box(\n                                contentAlignment = Alignment.Center,\n                                modifier = Modifier.fillMaxSize()\n                            ) {\n                                Text(\"Second\")\n                            }\n                        }\n                    },\n                    strategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->\n                        // Split vertically if the height is larger than the width\n                        if (layoutCoordinates.size.height >= layoutCoordinates.size.width) {\n                            VerticalTwoPaneStrategy(\n                                splitFraction = 0.75f\n                            )\n                        } else {\n                            HorizontalTwoPaneStrategy(\n                                splitFraction = 0.75f\n                            )\n                        }.calculateSplitResult(density, layoutDirection, layoutCoordinates)\n                    },\n                    displayFeatures = displayFeatures,\n                    modifier = Modifier.padding(8.dp)\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/adaptive/DraggableFoldAwareColumnSample.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.adaptive\n\nimport android.app.Activity\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.Image\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.gestures.detectDragGestures\nimport androidx.compose.foundation.layout.offset\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.layout.width\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.filled.FavoriteBorder\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.geometry.Offset\nimport androidx.compose.ui.input.pointer.pointerInput\nimport androidx.compose.ui.res.painterResource\nimport androidx.compose.ui.unit.IntOffset\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.adaptive.FoldAwareColumn\nimport com.google.accompanist.adaptive.calculateDisplayFeatures\nimport com.google.accompanist.sample.AccompanistSample\nimport com.google.accompanist.sample.R\nimport kotlin.math.roundToInt\n\nclass DraggableFoldAwareColumnSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccompanistSample {\n                DraggableExample(this@DraggableFoldAwareColumnSample)\n            }\n        }\n    }\n}\n\n@Composable\nfun DraggableExample(activity: Activity) {\n    var offset by remember { mutableStateOf(Offset(0f, 0f)) }\n    FoldAwareColumn(\n        modifier = Modifier\n            .offset { IntOffset(offset.x.roundToInt(), offset.y.roundToInt()) }\n            .pointerInput(Unit) {\n                detectDragGestures { change, dragAmount ->\n                    change.consume()\n                    offset = Offset(offset.x + dragAmount.x, offset.y + dragAmount.y)\n                }\n            }\n            .width(400.dp)\n            .border(5.dp, MaterialTheme.colorScheme.secondary),\n        displayFeatures = calculateDisplayFeatures(activity),\n        horizontalAlignment = Alignment.CenterHorizontally,\n    ) {\n        Icon(\n            modifier = Modifier\n                .border(2.dp, MaterialTheme.colorScheme.primary)\n                .padding(20.dp)\n                .align(Alignment.Start),\n            imageVector = Icons.Default.FavoriteBorder,\n            contentDescription = null\n        )\n        Text(\n            modifier = Modifier\n                .align(Alignment.CenterHorizontally)\n                .border(2.dp, MaterialTheme.colorScheme.primary),\n            text = \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\"\n        )\n        Image(\n            modifier = Modifier\n                .ignoreFold()\n                .align(Alignment.End)\n                .border(2.dp, MaterialTheme.colorScheme.primary),\n            painter = painterResource(id = R.drawable.placeholder),\n            contentDescription = null\n        )\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/adaptive/HorizontalTwoPaneSample.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.adaptive\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.Text\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.adaptive.FoldAwareConfiguration\nimport com.google.accompanist.adaptive.HorizontalTwoPaneStrategy\nimport com.google.accompanist.adaptive.TwoPane\nimport com.google.accompanist.adaptive.calculateDisplayFeatures\nimport com.google.accompanist.sample.AccompanistSample\n\nclass HorizontalTwoPaneSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccompanistSample {\n                val displayFeatures = calculateDisplayFeatures(this)\n\n                TwoPane(\n                    first = {\n                        Card(\n                            modifier = Modifier.padding(8.dp)\n                        ) {\n                            Box(\n                                contentAlignment = Alignment.Center,\n                                modifier = Modifier.fillMaxSize()\n                            ) {\n                                Text(\"First\")\n                            }\n                        }\n                    },\n                    second = {\n                        Card(\n                            modifier = Modifier.padding(8.dp)\n                        ) {\n                            Box(\n                                contentAlignment = Alignment.Center,\n                                modifier = Modifier.fillMaxSize()\n                            ) {\n                                Text(\"Second\")\n                            }\n                        }\n                    },\n                    strategy = HorizontalTwoPaneStrategy(\n                        splitFraction = 1f / 3f,\n                    ),\n                    displayFeatures = displayFeatures,\n                    foldAwareConfiguration = FoldAwareConfiguration.VerticalFoldsOnly,\n                    modifier = Modifier.padding(8.dp)\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/adaptive/NavDrawerFoldAwareColumnSample.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.adaptive\n\nimport android.app.Activity\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.layout.PaddingValues\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.shape.CircleShape\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.filled.Done\nimport androidx.compose.material.icons.filled.Face\nimport androidx.compose.material.icons.filled.Lock\nimport androidx.compose.material.icons.filled.Search\nimport androidx.compose.material.icons.filled.Star\nimport androidx.compose.material.icons.filled.ThumbUp\nimport androidx.compose.material.icons.filled.Warning\nimport androidx.compose.material3.ExperimentalMaterial3Api\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.ModalDrawerSheet\nimport androidx.compose.material3.ModalNavigationDrawer\nimport androidx.compose.material3.NavigationDrawerItem\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.adaptive.FoldAwareColumn\nimport com.google.accompanist.adaptive.calculateDisplayFeatures\nimport com.google.accompanist.sample.AccompanistSample\n\nclass NavDrawerFoldAwareColumnSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccompanistSample(contentPadding = PaddingValues(all = 0.dp)) {\n                NavDrawerExample(this)\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalMaterial3Api::class)\n@Composable\nfun NavDrawerExample(activity: Activity) {\n    val icons = listOf(\n        Icons.Default.Done,\n        Icons.Default.Face,\n        Icons.Default.Lock,\n        Icons.Default.Search,\n        Icons.Default.ThumbUp,\n        Icons.Default.Warning,\n        Icons.Default.Star\n    )\n\n    var selectedIcon by remember { mutableStateOf(icons[0]) }\n\n    ModalNavigationDrawer(\n        drawerContent = {\n            ModalDrawerSheet {\n                FoldAwareColumn(\n                    displayFeatures = calculateDisplayFeatures(activity),\n                    foldPadding = PaddingValues(vertical = 10.dp)\n                ) {\n                    icons.forEach {\n                        NavigationDrawerItem(\n                            modifier = Modifier\n                                .padding(5.dp)\n                                .border(2.dp, MaterialTheme.colorScheme.primary, CircleShape),\n                            icon = { Icon(imageVector = it, contentDescription = it.name) },\n                            label = { Text(it.name.substringAfter('.')) },\n                            selected = it == selectedIcon,\n                            onClick = { selectedIcon = it }\n                        )\n                    }\n                }\n            }\n        },\n        content = { Surface(modifier = Modifier.fillMaxSize()) {} }\n    )\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/adaptive/NavRailFoldAwareColumnSample.kt",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.adaptive\n\nimport android.app.Activity\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.border\nimport androidx.compose.foundation.layout.Row\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material.icons.Icons\nimport androidx.compose.material.icons.filled.Done\nimport androidx.compose.material.icons.filled.Face\nimport androidx.compose.material.icons.filled.Lock\nimport androidx.compose.material.icons.filled.Search\nimport androidx.compose.material.icons.filled.Star\nimport androidx.compose.material.icons.filled.ThumbUp\nimport androidx.compose.material.icons.filled.Warning\nimport androidx.compose.material3.Icon\nimport androidx.compose.material3.MaterialTheme\nimport androidx.compose.material3.NavigationRail\nimport androidx.compose.material3.NavigationRailItem\nimport androidx.compose.material3.Surface\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.adaptive.FoldAwareColumn\nimport com.google.accompanist.adaptive.calculateDisplayFeatures\nimport com.google.accompanist.sample.AccompanistSample\n\nclass NavRailFoldAwareColumnSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccompanistSample {\n                Row {\n                    NavRail(this@NavRailFoldAwareColumnSample)\n                    Surface(modifier = Modifier.fillMaxSize()) {}\n                }\n            }\n        }\n    }\n}\n\n@Composable\nfun NavRail(activity: Activity) {\n    val icons = listOf(\n        Icons.Default.Done,\n        Icons.Default.Face,\n        Icons.Default.Lock,\n        Icons.Default.Search,\n        Icons.Default.ThumbUp,\n        Icons.Default.Warning,\n        Icons.Default.Star\n    )\n\n    var selectedIcon by remember { mutableStateOf(icons[0]) }\n\n    NavigationRail {\n        FoldAwareColumn(displayFeatures = calculateDisplayFeatures(activity)) {\n            icons.forEach {\n                NavigationRailItem(\n                    modifier = Modifier\n                        .padding(5.dp)\n                        .border(2.dp, MaterialTheme.colorScheme.primary),\n                    selected = it == selectedIcon,\n                    onClick = { selectedIcon = it },\n                    icon = { Icon(imageVector = it, contentDescription = it.name) },\n                    label = { Text(it.name.substringAfter('.')) }\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/adaptive/VerticalTwoPaneSample.kt",
    "content": "/*\n * Copyright 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.adaptive\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.Card\nimport androidx.compose.material3.Text\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.adaptive.FoldAwareConfiguration\nimport com.google.accompanist.adaptive.TwoPane\nimport com.google.accompanist.adaptive.VerticalTwoPaneStrategy\nimport com.google.accompanist.adaptive.calculateDisplayFeatures\nimport com.google.accompanist.sample.AccompanistSample\n\nclass VerticalTwoPaneSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContent {\n            AccompanistSample {\n                val displayFeatures = calculateDisplayFeatures(this)\n\n                TwoPane(\n                    first = {\n                        Card(\n                            modifier = Modifier.padding(8.dp)\n                        ) {\n                            Box(\n                                contentAlignment = Alignment.Center,\n                                modifier = Modifier.fillMaxSize()\n                            ) {\n                                Text(\"First\")\n                            }\n                        }\n                    },\n                    second = {\n                        Card(\n                            modifier = Modifier.padding(8.dp)\n                        ) {\n                            Box(\n                                contentAlignment = Alignment.Center,\n                                modifier = Modifier.fillMaxSize()\n                            ) {\n                                Text(\"Second\")\n                            }\n                        }\n                    },\n                    strategy = VerticalTwoPaneStrategy(\n                        splitOffset = 200.dp,\n                    ),\n                    displayFeatures = displayFeatures,\n                    foldAwareConfiguration = FoldAwareConfiguration.HorizontalFoldsOnly,\n                    modifier = Modifier.padding(8.dp)\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/drawablepainter/DocSamples.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.drawablepainter\n\nimport androidx.compose.foundation.Image\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.platform.LocalContext\nimport androidx.core.content.ContextCompat\nimport com.google.accompanist.drawablepainter.rememberDrawablePainter\nimport com.google.accompanist.sample.R\n\n@Composable\nfun BasicSample() {\n    val drawable = ContextCompat.getDrawable(LocalContext.current, R.drawable.rectangle)\n\n    Image(\n        painter = rememberDrawablePainter(drawable = drawable),\n        contentDescription = \"content description\",\n    )\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/permissions/RequestLocationPermissionsSample.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.permissions\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.permissions.ExperimentalPermissionsApi\nimport com.google.accompanist.permissions.rememberMultiplePermissionsState\nimport com.google.accompanist.sample.AccompanistSample\n\nclass RequestLocationPermissionsSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        setContent {\n            AccompanistSample {\n                Sample()\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalPermissionsApi::class)\n@Composable\nprivate fun Sample() {\n    val locationPermissionsState = rememberMultiplePermissionsState(\n        listOf(\n            android.Manifest.permission.ACCESS_COARSE_LOCATION,\n            android.Manifest.permission.ACCESS_FINE_LOCATION,\n        )\n    )\n\n    if (locationPermissionsState.allPermissionsGranted) {\n        Text(\"Thanks! I can access your exact location :D\")\n    } else {\n        Column {\n            val allPermissionsRevoked =\n                locationPermissionsState.permissions.size ==\n                    locationPermissionsState.revokedPermissions.size\n\n            val textToShow = if (!allPermissionsRevoked) {\n                // If not all the permissions are revoked, it's because the user accepted the COARSE\n                // location permission, but not the FINE one.\n                \"Yay! Thanks for letting me access your approximate location. \" +\n                    \"But you know what would be great? If you allow me to know where you \" +\n                    \"exactly are. Thank you!\"\n            } else if (locationPermissionsState.shouldShowRationale) {\n                // Both location permissions have been denied\n                \"Getting your exact location is important for this app. \" +\n                    \"Please grant us fine location. Thank you :D\"\n            } else {\n                // First time the user sees this feature or the user doesn't want to be asked again\n                \"This feature requires location permission\"\n            }\n\n            val buttonText = if (!allPermissionsRevoked) {\n                \"Allow precise location\"\n            } else {\n                \"Request permissions\"\n            }\n\n            Text(text = textToShow)\n            Spacer(modifier = Modifier.height(8.dp))\n            Button(onClick = { locationPermissionsState.launchMultiplePermissionRequest() }) {\n                Text(buttonText)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/permissions/RequestMultiplePermissionsSample.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.permissions\n\nimport android.Manifest\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.permissions.ExperimentalPermissionsApi\nimport com.google.accompanist.permissions.MultiplePermissionsState\nimport com.google.accompanist.permissions.PermissionState\nimport com.google.accompanist.permissions.rememberMultiplePermissionsState\nimport com.google.accompanist.sample.AccompanistSample\n\n@OptIn(ExperimentalPermissionsApi::class)\nclass RequestMultiplePermissionsSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        setContent {\n            AccompanistSample {\n                val multiplePermissionsState = rememberMultiplePermissionsState(\n                    listOf(\n                        Manifest.permission.RECORD_AUDIO,\n                        Manifest.permission.CAMERA,\n                    )\n                )\n                Sample(multiplePermissionsState)\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalPermissionsApi::class)\n@Composable\nprivate fun Sample(multiplePermissionsState: MultiplePermissionsState) {\n    if (multiplePermissionsState.allPermissionsGranted) {\n        // If all permissions are granted, then show screen with the feature enabled\n        Text(\"Camera and Read storage permissions Granted! Thank you!\")\n    } else {\n        Column {\n            Text(\n                getTextToShowGivenPermissions(\n                    multiplePermissionsState.revokedPermissions,\n                    multiplePermissionsState.shouldShowRationale\n                )\n            )\n            Spacer(modifier = Modifier.height(8.dp))\n            Button(onClick = { multiplePermissionsState.launchMultiplePermissionRequest() }) {\n                Text(\"Request permissions\")\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalPermissionsApi::class)\nprivate fun getTextToShowGivenPermissions(\n    permissions: List<PermissionState>,\n    shouldShowRationale: Boolean\n): String {\n    val revokedPermissionsSize = permissions.size\n    if (revokedPermissionsSize == 0) return \"\"\n\n    val textToShow = StringBuilder().apply {\n        append(\"The \")\n    }\n\n    for (i in permissions.indices) {\n        textToShow.append(permissions[i].permission)\n        when {\n            revokedPermissionsSize > 1 && i == revokedPermissionsSize - 2 -> {\n                textToShow.append(\", and \")\n            }\n            i == revokedPermissionsSize - 1 -> {\n                textToShow.append(\" \")\n            }\n            else -> {\n                textToShow.append(\", \")\n            }\n        }\n    }\n    textToShow.append(if (revokedPermissionsSize == 1) \"permission is\" else \"permissions are\")\n    textToShow.append(\n        if (shouldShowRationale) {\n            \" important. Please grant all of them for the app to function properly.\"\n        } else {\n            \" denied. The app cannot function without them.\"\n        }\n    )\n    return textToShow.toString()\n}\n"
  },
  {
    "path": "sample/src/main/java/com/google/accompanist/sample/permissions/RequestPermissionSample.kt",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.google.accompanist.sample.permissions\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.Spacer\nimport androidx.compose.foundation.layout.height\nimport androidx.compose.material3.Button\nimport androidx.compose.material3.Text\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.google.accompanist.permissions.ExperimentalPermissionsApi\nimport com.google.accompanist.permissions.isGranted\nimport com.google.accompanist.permissions.rememberPermissionState\nimport com.google.accompanist.permissions.shouldShowRationale\nimport com.google.accompanist.sample.AccompanistSample\n\nclass RequestPermissionSample : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        setContent {\n            AccompanistSample {\n                Sample()\n            }\n        }\n    }\n}\n\n@OptIn(ExperimentalPermissionsApi::class)\n@Composable\nprivate fun Sample() {\n    val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)\n    if (cameraPermissionState.status.isGranted) {\n        Text(\"Camera permission Granted\")\n    } else {\n        Column {\n            val textToShow = if (cameraPermissionState.status.shouldShowRationale) {\n                \"The camera is important for this app. Please grant the permission.\"\n            } else {\n                \"Camera not available\"\n            }\n\n            Text(textToShow)\n            Spacer(modifier = Modifier.height(8.dp))\n            Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {\n                Text(\"Request permission\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#008577\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeWidth=\"0.8\"\n        android:strokeColor=\"#33FFFFFF\" />\n</vector>\n"
  },
  {
    "path": "sample/src/main/res/drawable/rectangle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"#00FF00\" />\n</shape>"
  },
  {
    "path": "sample/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeWidth=\"1\"\n        android:strokeColor=\"#00000000\" />\n</vector>\n"
  },
  {
    "path": "sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright 2020 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "sample/src/main/res/values/strings.xml",
    "content": "<!--\n  ~ Copyright 2022 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"MissingTranslation\">\n    <string name=\"app_name\">Accompanist Sample</string>\n\n    <string name=\"insets_title_basic\">Insets: Basic</string>\n    <string name=\"insets_title_fragment\">Insets: Fragment</string>\n    <string name=\"insets_title_list\">Insets: Edge-to-edge list</string>\n    <string name=\"insets_title_list_bottomnav\">Insets: Edge-to-edge list with Bottom Navigation</string>\n    <string name=\"insets_title_imeanim\">Insets: IME Animations</string>\n\n    <string name=\"horiz_pager_title_basics\">Horizontal Pager: Basic</string>\n    <string name=\"horiz_pager_with_indicator_title\">Horizontal Pager: Indicator</string>\n    <string name=\"horiz_pager_with_transition_title\">Horizontal Pager: Transition</string>\n    <string name=\"horiz_pager_title_looping\">Horizontal Pager: Looping</string>\n    <string name=\"horiz_pager_title_looping_indicator\">Horizontal Pager: Looping with Indicators</string>\n    <string name=\"horiz_pager_title_looping_tabs\">Horizontal Pager: Looping with Tabs</string>\n    <string name=\"horiz_pager_title_tabs\">Horizontal Pager: Tabs</string>\n    <string name=\"horiz_pager_title_scroll_content\">Horizontal Pager: Scrolling content</string>\n    <string name=\"horiz_pager_title_different_paddings\">Horizontal Pager: Different paddings</string>\n\n    <string name=\"vertical_pager_title_basics\">Vertical Pager: Basic</string>\n    <string name=\"vertical_pager_with_indicator_title\">Vertical Pager: Indicator</string>\n\n    <string name=\"pagers_title_nested\">Pagers: Nested</string>\n\n    <string name=\"navigation_title_animated\">Navigation: Transitions</string>\n    <string name=\"navigation_title_bottom_sheet\">Navigation: Bottom Sheets</string>\n\n    <string name=\"permissions_title_one\">Permissions: Request permission</string>\n    <string name=\"permissions_title_multiple\">Permissions: Request multiple permissions</string>\n    <string name=\"permissions_title_location\">Permissions: Request location permissions</string>\n\n    <string name=\"flowlayout_title_column\">Flow Layout: Column</string>\n    <string name=\"flowlayout_title_row\">Flow Layout: Row</string>\n\n    <string name=\"swiperefresh_title_basics\">Swipe Refresh: Basic</string>\n    <string name=\"swiperefresh_title_content_padding\">Swipe Refresh: Edge-to-edge</string>\n    <string name=\"swiperefresh_title_tweaked\">Swipe Refresh: Tweaked indicator</string>\n    <string name=\"swiperefresh_title_custom\">Swipe Refresh: Custom indicator</string>\n    <string name=\"swiperefresh_title_verticalpager\">Swipe Refresh: VerticalPager</string>\n\n    <string name=\"system_ui_controller_title_color\">System Ui Controller: Color</string>\n    <string name=\"system_ui_controller_title_color_dialog\">System Ui Controller: Color in Dialog</string>\n    <string name=\"system_ui_controller_title_visibility\">System Ui Controller: Visibility</string>\n\n    <string name=\"placeholder_title_basics\">Placeholder: Basic</string>\n    <string name=\"placeholder_title_fade\">Placeholder: Fade</string>\n    <string name=\"placeholder_title_shimmer\">Placeholder: Shimmer</string>\n\n    <string name=\"webview_title_basic\">WebView: Basic</string>\n    <string name=\"webview_title_wrapped\">WebView: Wrapped Content</string>\n    <string name=\"webview_savestate\">WebView: Save State on Navigation</string>\n\n    <string name=\"adaptive_two_pane_basic\">Adaptive: TwoPane Basic</string>\n    <string name=\"adaptive_two_pane_horizontal\">Adaptive: TwoPane Horizontal</string>\n    <string name=\"adaptive_two_pane_vertical\">Adaptive: TwoPane Vertical</string>\n    <string name=\"adaptive_fold_aware_column_nav_rail\">Adaptive: Fold Aware Column with Nav Rail</string>\n    <string name=\"adaptive_fold_aware_column_nav_drawer\">Adaptive: Fold Aware Column with Nav Drawer</string>\n    <string name=\"adaptive_fold_aware_column_draggable\">Adaptive: Draggable Fold Aware Column</string>\n\n    <string name=\"testharness\">Test Harness</string>\n    <string name=\"this_is_content\">This is content\\n%s</string>\n\n    <string name=\"themeadapter_title_material\">Theme Adapter: Material</string>\n    <string name=\"themeadapter_title_material3\">Theme Adapter: Material 3</string>\n\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright 2022 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      https://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<resources>\n    <string name=\"this_is_content\">هذا مضمون \\n%s</string>\n</resources>\n"
  },
  {
    "path": "scripts/run-tests.sh",
    "content": "#!/bin/bash\n\n# Copyright 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Fail on error and print out commands\nset -ex\n\n# By default we don't shard\nSHARD_COUNT=0\nSHARD_INDEX=0\n# By default we don't log\nLOG_FILE=\"\"\n# By default we run tests on device\nDEVICE=true\n\n# Parse parameters\nfor i in \"$@\"; do\n  case $i in\n  --shard-count=*)\n    SHARD_COUNT=\"${i#*=}\"\n    shift\n    ;;\n  --unit-tests)\n    DEVICE=false\n    shift\n    ;;\n  --shard-index=*)\n    SHARD_INDEX=\"${i#*=}\"\n    shift\n    ;;\n  --log-file=*)\n    LOG_FILE=\"${i#*=}\"\n    shift\n    ;;\n  --run-affected)\n    RUN_AFFECTED=true\n    shift\n    ;;\n  --run-flaky-tests)\n    RUN_FLAKY=true\n    shift\n    ;;\n  --affected-base-ref=*)\n    BASE_REF=\"${i#*=}\"\n    shift\n    ;;\n  *)\n    echo \"Unknown option\"\n    exit 1\n    ;;\n  esac\ndone\n\n# Start logcat if we have a file to log to\nif [[ ! -z \"$LOG_FILE\" ]]; then\n  adb logcat >$LOG_FILE &\nfi\n\nFILTER_OPTS=\"\"\n# Filter out flaky tests if we're not set to run them\nif [[ -z \"$RUN_FLAKY\" ]]; then\n  FILTER_OPTS=\"$FILTER_OPTS -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.FlakyTest\"\nfi\n\n# If we're set to only run affected test, update the Gradle task\nif [[ ! -z \"$RUN_AFFECTED\" ]]; then\n  if [ \"$DEVICE\" = true ]; then\n    TASK=\"runAffectedAndroidTests\"\n  else\n    TASK=\"runAffectedUnitTests\"\n  fi\n  TASK=\"$TASK -Paffected_module_detector.enable\"\n\n  # If we have a base branch set, add the Gradle property\n  if [[ ! -z \"$BASE_REF\" ]]; then\n    TASK=\"$TASK -Paffected_base_ref=$BASE_REF\"\n  fi\nfi\n\n# If we don't have a task yet, use the defaults\nif [[ -z \"$TASK\" ]]; then\n  if [ \"$DEVICE\" = true ]; then\n    TASK=\"connectedCheck\"\n  else\n    TASK=\"testDebug\"\n  fi\nfi\n\nSHARD_OPTS=\"\"\nif [ \"$SHARD_COUNT\" -gt \"0\" ]; then\n  # If we have a shard count value, create the necessary Gradle property args.\n  # We assume that SHARD_INDEX has been set too\n  SHARD_OPTS=\"$SHARD_OPTS -Pandroid.testInstrumentationRunnerArguments.numShards=$SHARD_COUNT\"\n  SHARD_OPTS=\"$SHARD_OPTS -Pandroid.testInstrumentationRunnerArguments.shardIndex=$SHARD_INDEX\"\nfi\n\n./gradlew --scan --continue --no-configuration-cache --stacktrace $TASK $FILTER_OPTS $SHARD_OPTS\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "/*\n * Copyright 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npluginManagement {\n    includeBuild(\"build-logic\")\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\ndependencyResolutionManagement {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\ninclude(\":adaptive\")\ninclude(\":internal-testutils\")\ninclude(\":drawablepainter\")\ninclude(\":permissions\")\ninclude(\":permissions-lint\")\ninclude(\":sample\")\n\nrootProject.name = \"accompanist\"\n\ncheck(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {\n    \"\"\"\n    Accompanist requires JDK 17+ but it is currently using JDK ${JavaVersion.current()}.\n    Java Home: [${System.getProperty(\"java.home\")}]\n    https://developer.android.com/build/jdks#jdk-config-in-studio\n    \"\"\".trimIndent()\n}"
  },
  {
    "path": "spotless/copyright.txt",
    "content": "/*\n * Copyright $YEAR The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n"
  },
  {
    "path": "spotless/greclipse.properties",
    "content": "#\n# Copyright 2020 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#Whether to use 'space', 'tab' or 'mixed' (both) characters for indentation.\n#The default value is 'tab'.\norg.eclipse.jdt.core.formatter.tabulation.char=space\n\n#Number of spaces used for indentation in case 'space' characters\n#have been selected. The default value is 4.\norg.eclipse.jdt.core.formatter.tabulation.size=4\n\n#Number of spaces used for indentation in case 'mixed' characters\n#have been selected. The default value is 4.\norg.eclipse.jdt.core.formatter.indentation.size=4\n\n#Whether or not indentation characters are inserted into empty lines.\n#The default value is 'true'.\norg.eclipse.jdt.core.formatter.indent_empty_lines=false\n\n#Number of spaces used for multiline indentation.\n#The default value is 2.\ngroovy.formatter.multiline.indentation=2\n\n#Length after which list are considered too long. These will be wrapped.\n#The default value is 30.\ngroovy.formatter.longListLength=30\n\n#Whether opening braces position shall be the next line.\n#The default value is 'same'.\ngroovy.formatter.braces.start=same\n\n#Whether closing braces position shall be the next line.\n#The default value is 'next'.\ngroovy.formatter.braces.end=next\n\n#Remove unnecessary semicolons. The default value is 'false'.\ngroovy.formatter.remove.unnecessary.semicolons=false"
  }
]