├── .allstar └── binary_artifacts.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── adaptive-bug-report.md │ ├── bug_report.md │ ├── config.yml │ ├── general-bug-report.md │ ├── general-other-bug-report.md │ ├── navigation-material-bug-report.md │ ├── permissions-bug-report.md │ └── testharness-bug-report.md ├── auto-merge.yml ├── ci-gradle.properties ├── pull_request_template.md ├── release-drafter.yml └── workflows │ ├── automerger.yml │ ├── build-snapshot.yml │ ├── build.yml │ ├── issues-stale.yml │ ├── publish-docs.yml │ └── update-release.yml ├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── copyright │ ├── AOSP.xml │ └── profiles_settings.xml ├── deploymentTargetSelector.xml ├── inspectionProfiles │ ├── ktlint.xml │ └── profiles_settings.xml ├── kotlinScripting.xml ├── kotlinc.xml ├── runConfigurations.xml └── vcs.xml ├── ASSETS_LICENSE.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── adaptive ├── README.md ├── api │ └── current.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── google │ │ └── accompanist │ │ └── adaptive │ │ ├── DisplayFeatures.kt │ │ ├── FoldAwareColumn.kt │ │ ├── FoldAwareColumnScope.kt │ │ ├── RowColumnImpl.kt │ │ ├── RowColumnMeasurementHelper.kt │ │ └── TwoPane.kt │ ├── sharedTest │ └── kotlin │ │ └── com │ │ └── google │ │ └── accompanist │ │ └── adaptive │ │ ├── DisplayFeaturesTest.kt │ │ ├── FoldAwareColumnTest.kt │ │ └── TwoPaneTest.kt │ └── test │ └── resources │ └── robolectric.properties ├── build-logic ├── convention │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── AndroidLibraryComposeConventionPlugin.kt │ │ ├── AndroidLibraryConventionPlugin.kt │ │ ├── AndroidLibraryPublishedConventionPlugin.kt │ │ ├── AndroidLintConventionPlugin.kt │ │ └── com │ │ └── google │ │ └── accompanist │ │ ├── AndroidCompose.kt │ │ ├── BundleInsideHelper.kt │ │ ├── KotlinAndroid.kt │ │ └── ProjectExtensions.kt ├── gradle.properties └── settings.gradle.kts ├── build.gradle ├── checksum.sh ├── docs ├── adaptive.md ├── appcompat-theme.md ├── drawablepainter.md ├── header.png ├── insets │ └── images │ │ ├── edge-to-edge-list.jpg │ │ ├── ime-insets.gif │ │ └── ime-scroll.gif ├── migration.md ├── migration │ └── studio.png ├── navigation-animation.md ├── navigation-material.md ├── permissions.md ├── swiperefresh │ ├── demo.mp4 │ └── tweaked.mp4 ├── systemuicontroller.md ├── systemuicontroller │ └── api-scrim.png ├── themeadapter │ ├── material-header.png │ └── material3-header.png ├── updating.md ├── using-snapshot-version.md └── web.md ├── drawablepainter ├── README.md ├── api │ └── current.api ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── google │ └── accompanist │ └── drawablepainter │ └── DrawablePainter.kt ├── generate_docs.sh ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images └── Social.sketch ├── internal-testutils ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── google │ │ └── accompanist │ │ └── internal │ │ └── test │ │ ├── ActivityScenario.kt │ │ ├── Assertions.kt │ │ ├── IgnoreOnRobolectric.kt │ │ ├── TestUtils.kt │ │ └── WaitUntil.kt │ └── res │ └── values │ └── themes.xml ├── mkdocs.yml ├── permissions-lint ├── README.md ├── build.gradle.kts └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── google │ │ │ └── accompanist │ │ │ └── permissions │ │ │ └── lint │ │ │ ├── PermissionsIssueRegistry.kt │ │ │ ├── PermissionsLaunchDetector.kt │ │ │ └── util │ │ │ ├── ComposableUtils.kt │ │ │ ├── KotlinMetadataUtils.kt │ │ │ ├── Names.kt │ │ │ └── PsiUtils.kt │ └── resources │ │ └── META-INF │ │ └── services │ │ └── com.android.tools.lint.client.api.IssueRegistry │ └── test │ └── java │ └── com │ └── google │ └── accompanist │ └── permissions │ └── lint │ └── PermissionsLaunchDetectorTest.kt ├── permissions ├── README.md ├── api │ └── current.api ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidTest │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── google │ │ └── accompanist │ │ └── permissions │ │ ├── FakeTests.kt │ │ ├── MultipleAndSinglePermissionsTest.kt │ │ ├── MultiplePermissionsStateTest.kt │ │ ├── PermissionStateTest.kt │ │ ├── RequestMultiplePermissionsTest.kt │ │ ├── RequestPermissionTest.kt │ │ ├── TestUtils.kt │ │ └── test │ │ ├── EmptyPermissionsTestActivity.kt │ │ └── PermissionsTestActivity.kt │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── google │ └── accompanist │ └── permissions │ ├── MultiplePermissionsState.kt │ ├── MutableMultiplePermissionsState.kt │ ├── MutablePermissionState.kt │ ├── PermissionState.kt │ └── PermissionsUtil.kt ├── release ├── secring.gpg.aes ├── signing-cleanup.sh ├── signing-setup.sh └── signing.properties.aes ├── sample ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── google │ │ └── accompanist │ │ └── sample │ │ ├── ImageLoadingSampleUtils.kt │ │ ├── MainActivity.kt │ │ ├── MainScreen.kt │ │ ├── Theme.kt │ │ ├── adaptive │ │ ├── BasicTwoPaneSample.kt │ │ ├── DraggableFoldAwareColumnSample.kt │ │ ├── HorizontalTwoPaneSample.kt │ │ ├── NavDrawerFoldAwareColumnSample.kt │ │ ├── NavRailFoldAwareColumnSample.kt │ │ └── VerticalTwoPaneSample.kt │ │ ├── drawablepainter │ │ └── DocSamples.kt │ │ └── permissions │ │ ├── RequestLocationPermissionsSample.kt │ │ ├── RequestMultiplePermissionsSample.kt │ │ └── RequestPermissionSample.kt │ └── res │ ├── drawable-nodpi │ └── placeholder.jpg │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── alpha.png │ ├── ic_launcher_background.xml │ └── rectangle.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── values-ar │ └── strings.xml │ └── values │ └── strings.xml ├── scripts └── run-tests.sh ├── settings.gradle.kts └── spotless ├── copyright.txt └── greclipse.properties /.allstar/binary_artifacts.yaml: -------------------------------------------------------------------------------- 1 | # Exemption reason: This repo uses binary artifacts to ship gradle.jar for users. It does not allow any others. 2 | # Exemption timeframe: permanent 3 | optConfig: 4 | optOut: true 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/adaptive-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Adaptive bug report 3 | about: Create a report about adaptive 4 | title: "[Adaptive]" 5 | labels: adaptive 6 | assignees: alexvanyo 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | **Steps to reproduce** 13 | 14 | **Expected behavior** 15 | 16 | **Additional context** 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ## To Reproduce 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | ## Expected behavior 21 | A clear and concise description of what you expected to happen. 22 | 23 | ## Screenshots? 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | ## Environment: 27 | - Android OS version: [e.g. Android 5.0] 28 | - Device: [e.g. Emulator, Google Pixel 4] 29 | - Accompanist version: [e.g. v0.X] 30 | 31 | ## Additional context 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ## To Reproduce (if applicable) 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | ## Expected behavior (if applicable) 21 | A clear and concise description of what you expected to happen. 22 | 23 | ## Screenshots? (if applicable) 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | ## Environment: (if applicable) 27 | - Android OS version: [e.g. Android 5.0] 28 | - Device: [e.g. Emulator, Google Pixel 4] 29 | - Accompanist version: [e.g. v0.X] 30 | 31 | ## Additional context 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-other-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General/Other bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ## To Reproduce (if applicable) 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | ## Expected behavior (if applicable) 21 | A clear and concise description of what you expected to happen. 22 | 23 | ## Screenshots? (if applicable) 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | ## Environment: (if applicable) 27 | - Android OS version: [e.g. Android 5.0] 28 | - Device: [e.g. Emulator, Google Pixel 4] 29 | - Accompanist version: [e.g. v0.X] 30 | 31 | ## Additional context 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/navigation-material-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Navigation Material bug report 3 | about: Create a report to help us improve 4 | title: "[Navigation Material] " 5 | labels: '' 6 | assignees: jossiwolf 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | **Steps to reproduce** 13 | 14 | **Expected behavior** 15 | 16 | **Additional context** 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/permissions-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Permissions bug report 3 | about: Create a report to help us improve 4 | title: "[Permissions] " 5 | labels: '' 6 | assignees: bentrengrove 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | **Steps to reproduce** 13 | 14 | **Expected behavior** 15 | 16 | **Additional context** 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/testharness-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test harness bug report 3 | about: Create a report about test harness 4 | title: "[Test Harness]" 5 | labels: testharness 6 | assignees: alexvanyo 7 | 8 | --- 9 | 10 | **Description** 11 | 12 | **Steps to reproduce** 13 | 14 | **Expected behavior** 15 | 16 | **Additional context** 17 | -------------------------------------------------------------------------------- /.github/auto-merge.yml: -------------------------------------------------------------------------------- 1 | # Config for github.com/bobvanderlinden/probot-auto-merge 2 | minApprovals: 3 | COLLABORATOR: 1 4 | requiredLabels: 5 | - automerge 6 | mergeMethod: merge 7 | reportStatus: true -------------------------------------------------------------------------------- /.github/ci-gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2019 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | # Turn Gradle daemon off due to https://github.com/Kotlin/dokka/issues/1405 18 | org.gradle.daemon=false 19 | 20 | org.gradle.parallel=true 21 | org.gradle.jvmargs=-Xmx4608m -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError 22 | org.gradle.workers.max=2 23 | 24 | kotlin.compiler.execution.strategy=in-process 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Please add the library name to the PR title. Example: "[Insets] Fixes typo" ### 2 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$NEXT_PATCH_VERSION 🌈' 2 | tag-template: 'v$NEXT_PATCH_VERSION' 3 | template: | 4 | ## What’s Changed 5 | 6 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/automerger.yml: -------------------------------------------------------------------------------- 1 | name: main to snapshot auto merger 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | automerge: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: '0' # 0 == fetch all history history 16 | ref: 'snapshot' 17 | token: ${{ secrets.AUTOMERGE_PAT }} 18 | 19 | - run: | 20 | git config user.name github-actions 21 | git config user.email github-actions@github.com 22 | git fetch origin 23 | git merge origin/main --no-edit 24 | git push 25 | -------------------------------------------------------------------------------- /.github/workflows/issues-stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '15 3 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v3 11 | with: 12 | 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.' 13 | days-before-stale: 45 14 | days-before-close: 5 15 | exempt-all-pr-milestones: true 16 | exempt-issue-labels: 'waiting for info,waiting on dependency' 17 | -------------------------------------------------------------------------------- /.github/workflows/publish-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish docs 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | workflow_dispatch: 8 | 9 | jobs: 10 | deploy_docs: 11 | runs-on: ubuntu-latest 12 | env: 13 | TERM: dumb 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Copy CI gradle.properties 19 | run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties 20 | 21 | - name: Setup java 22 | uses: actions/setup-java@v3 23 | with: 24 | distribution: temurin 25 | java-version: 17 26 | 27 | - name: Setup Gradle 28 | uses: gradle/gradle-build-action@v2 29 | 30 | - name: Setup Python 31 | uses: actions/setup-python@v4 32 | with: 33 | python-version: '3.x' 34 | 35 | - name: Install dependencies 36 | run: | 37 | python3 -m pip install --upgrade pip 38 | python3 -m pip install mkdocs-material=="9.*" 39 | 40 | - name: Generate docs 41 | run: ./generate_docs.sh 42 | 43 | - name: Build site 44 | run: mkdocs build 45 | 46 | - name: Deploy 47 | uses: peaceiris/actions-gh-pages@v3 48 | with: 49 | github_token: ${{ secrets.GITHUB_TOKEN }} 50 | publish_dir: ./site 51 | -------------------------------------------------------------------------------- /.github/workflows/update-release.yml: -------------------------------------------------------------------------------- 1 | name: Update release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_draft_release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | # pin directly to 6.0.0 because we don't want to update without knowledge 13 | - uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | .gradle 3 | build/ 4 | 5 | captures 6 | 7 | /local.properties 8 | 9 | # IntelliJ .idea folder 10 | .idea/workspace.xml 11 | .idea/libraries 12 | .idea/caches 13 | .idea/navEditor.xml 14 | .idea/tasks.xml 15 | .idea/modules.xml 16 | .idea/compiler.xml 17 | .idea/jarRepositories.xml 18 | .idea/deploymentTargetDropDown.xml 19 | .idea/misc.xml 20 | .idea/androidTestResultsUserPreferences.xml 21 | .idea/deploymentTargetSelector.xml 22 | gradle.xml 23 | *.iml 24 | 25 | # General 26 | .DS_Store 27 | .externalNativeBuild 28 | 29 | # Do not commit plain-text signing info 30 | release/*.properties 31 | release/*.gpg 32 | 33 | # VS Code config 34 | org.eclipse.buildship.core.prefs 35 | .classpath 36 | .project 37 | 38 | # Temporary API docs 39 | docs/api 40 | package-list-coil-base 41 | 42 | # Mkdocs temporary serving folder 43 | docs-gen 44 | site 45 | *.bak 46 | 47 | # Lint reports 48 | lint-report.* 49 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/AOSP.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 11 | 12 | 14 | 15 | 17 | 18 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/ktlint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/kotlinScripting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ASSETS_LICENSE.txt: -------------------------------------------------------------------------------- 1 | All font files are licensed under the SIL OPEN FONT LICENSE license. All other files are licensed under the Apache 2 license. 2 | 3 | 4 | SIL OPEN FONT LICENSE 5 | Version 1.1 - 26 February 2007 6 | 7 | PREAMBLE 8 | The goals of the Open Font License (OFL) are to stimulate worldwide 9 | development of collaborative font projects, to support the font creation 10 | efforts of academic and linguistic communities, and to provide a free and 11 | open framework in which fonts may be shared and improved in partnership 12 | with others. 13 | 14 | The OFL allows the licensed fonts to be used, studied, modified and 15 | redistributed freely as long as they are not sold by themselves. The 16 | fonts, including any derivative works, can be bundled, embedded, 17 | redistributed and/or sold with any software provided that any reserved 18 | names are not used by derivative works. The fonts and derivatives, 19 | however, cannot be released under any other type of license. The 20 | requirement for fonts to remain under this license does not apply 21 | to any document created using the fonts or their derivatives. 22 | 23 | DEFINITIONS 24 | "Font Software" refers to the set of files released by the Copyright 25 | Holder(s) under this license and clearly marked as such. This may 26 | include source files, build scripts and documentation. 27 | 28 | "Reserved Font Name" refers to any names specified as such after the 29 | copyright statement(s). 30 | 31 | "Original Version" refers to the collection of Font Software components as 32 | distributed by the Copyright Holder(s). 33 | 34 | "Modified Version" refers to any derivative made by adding to, deleting, 35 | or substituting — in part or in whole — any of the components of the 36 | Original Version, by changing formats or by porting the Font Software to a 37 | new environment. 38 | 39 | "Author" refers to any designer, engineer, programmer, technical 40 | writer or other person who contributed to the Font Software. 41 | 42 | PERMISSION & CONDITIONS 43 | Permission is hereby granted, free of charge, to any person obtaining 44 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 45 | redistribute, and sell modified and unmodified copies of the Font 46 | Software, subject to the following conditions: 47 | 48 | 1) Neither the Font Software nor any of its individual components, 49 | in Original or Modified Versions, may be sold by itself. 50 | 51 | 2) Original or Modified Versions of the Font Software may be bundled, 52 | redistributed and/or sold with any software, provided that each copy 53 | contains the above copyright notice and this license. These can be 54 | included either as stand-alone text files, human-readable headers or 55 | in the appropriate machine-readable metadata fields within text or 56 | binary files as long as those fields can be easily viewed by the user. 57 | 58 | 3) No Modified Version of the Font Software may use the Reserved Font 59 | Name(s) unless explicit written permission is granted by the corresponding 60 | Copyright Holder. This restriction only applies to the primary font name as 61 | presented to the users. 62 | 63 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 64 | Software shall not be used to promote, endorse or advertise any 65 | Modified Version, except to acknowledge the contribution(s) of the 66 | Copyright Holder(s) and the Author(s) or with their explicit written 67 | permission. 68 | 69 | 5) The Font Software, modified or unmodified, in part or in whole, 70 | must be distributed entirely under this license, and must not be 71 | distributed under any other license. The requirement for fonts to 72 | remain under this license does not apply to any document created 73 | using the Font Software. 74 | 75 | TERMINATION 76 | This license becomes null and void if any of the above conditions are 77 | not met. 78 | 79 | DISCLAIMER 80 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 81 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 82 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 83 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 84 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 85 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 86 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 87 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 88 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## New Features/Libraries 7 | 8 | Before contributing large new features and/or libraries please start a discussion 9 | with us first via GitHub Issues and check that we can support it. 10 | We are unable to support all new features, even though we wish we could! If we 11 | are unable to support adding your feature, we always encourage you to open source it 12 | in your own repository to help the Compose community grow. 13 | 14 | ## Contributor License Agreement 15 | 16 | Contributions to this project must be accompanied by a Contributor License 17 | Agreement. You (or your employer) retain the copyright to your contribution, 18 | this simply gives us permission to use and redistribute your contributions as 19 | part of the project. Head over to to see 20 | your current agreements on file or to sign a new one. 21 | 22 | You generally only need to submit a CLA once, so if you've already submitted one 23 | (even if it was for a different project), you probably don't need to do it 24 | again. 25 | 26 | ## Code Reviews 27 | 28 | All submissions, including submissions by project members, require review. We 29 | use GitHub pull requests for this purpose. Consult 30 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 31 | information on using pull requests. 32 | 33 | ## API Changes 34 | 35 | If you are changing any public APIs, you need to run `./gradlew metalavaGenerateSignatureRelease` which will 36 | update the API signatures. 37 | 38 | ## Formatting 39 | 40 | To apply formatting, we use spotless. Run `./gradlew :pager:spotlessApply` to format the code according 41 | to the spec. 42 | -------------------------------------------------------------------------------- /adaptive/README.md: -------------------------------------------------------------------------------- 1 | # Adaptive utilities for Jetpack Compose 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-adaptive)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | For more information, visit the documentation: https://google.github.io/accompanist/adaptive 6 | 7 | ## Download 8 | 9 | ```groovy 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation "com.google.accompanist:accompanist-adaptive:" 16 | } 17 | ``` 18 | 19 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit. 20 | 21 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-adaptive/ -------------------------------------------------------------------------------- /adaptive/api/current.api: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | package com.google.accompanist.adaptive { 3 | 4 | public final class DisplayFeaturesKt { 5 | method @androidx.compose.runtime.Composable public static java.util.List calculateDisplayFeatures(android.app.Activity activity); 6 | } 7 | 8 | public final class FoldAwareColumnKt { 9 | method @androidx.compose.runtime.Composable public static void FoldAwareColumn(java.util.List 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 content); 10 | } 11 | 12 | @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Immutable public interface FoldAwareColumnScope { 13 | method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier align(androidx.compose.ui.Modifier, androidx.compose.ui.Alignment.Horizontal alignment); 14 | method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, androidx.compose.ui.layout.VerticalAlignmentLine alignmentLine); 15 | method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier alignBy(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function1 alignmentLineBlock); 16 | method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier ignoreFold(androidx.compose.ui.Modifier); 17 | } 18 | 19 | @kotlin.jvm.JvmInline public final value class FoldAwareConfiguration { 20 | field public static final com.google.accompanist.adaptive.FoldAwareConfiguration.Companion Companion; 21 | } 22 | 23 | public static final class FoldAwareConfiguration.Companion { 24 | method public int getAllFolds(); 25 | method public int getHorizontalFoldsOnly(); 26 | method public int getVerticalFoldsOnly(); 27 | property public final int AllFolds; 28 | property public final int HorizontalFoldsOnly; 29 | property public final int VerticalFoldsOnly; 30 | } 31 | 32 | public final class SplitResult { 33 | ctor public SplitResult(androidx.compose.foundation.gestures.Orientation gapOrientation, androidx.compose.ui.geometry.Rect gapBounds); 34 | method public androidx.compose.ui.geometry.Rect getGapBounds(); 35 | method public androidx.compose.foundation.gestures.Orientation getGapOrientation(); 36 | property public final androidx.compose.ui.geometry.Rect gapBounds; 37 | property public final androidx.compose.foundation.gestures.Orientation gapOrientation; 38 | } 39 | 40 | public final class TwoPaneKt { 41 | method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(float splitOffset, optional boolean offsetFromStart, optional float gapWidth); 42 | method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(float splitFraction, optional float gapWidth); 43 | method @androidx.compose.runtime.Composable public static void TwoPane(kotlin.jvm.functions.Function0 first, kotlin.jvm.functions.Function0 second, com.google.accompanist.adaptive.TwoPaneStrategy strategy, java.util.List displayFeatures, optional androidx.compose.ui.Modifier modifier, optional int foldAwareConfiguration); 44 | method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(float splitOffset, optional boolean offsetFromTop, optional float gapHeight); 45 | method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(float splitFraction, optional float gapHeight); 46 | } 47 | 48 | public fun interface TwoPaneStrategy { 49 | 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); 50 | } 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /adaptive/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("UnstableApiUsage") 17 | 18 | plugins { 19 | alias(libs.plugins.accompanist.android.library) 20 | alias(libs.plugins.accompanist.android.library.compose) 21 | alias(libs.plugins.accompanist.android.library.published) 22 | } 23 | 24 | android { 25 | namespace = "com.google.accompanist.adaptive" 26 | 27 | sourceSets { 28 | named("test") { 29 | java.srcDirs("src/sharedTest/kotlin") 30 | res.srcDirs("src/sharedTest/res") 31 | } 32 | named("androidTest") { 33 | java.srcDirs("src/sharedTest/kotlin") 34 | res.srcDirs("src/sharedTest/res") 35 | } 36 | } 37 | } 38 | 39 | dependencies { 40 | api(libs.compose.foundation.foundation) 41 | api(libs.compose.ui.ui) 42 | api(libs.androidx.window) 43 | 44 | implementation(libs.kotlin.coroutines.android) 45 | implementation(libs.compose.ui.util) 46 | 47 | // ====================== 48 | // Test dependencies 49 | // ====================== 50 | 51 | androidTestImplementation(project(":internal-testutils")) 52 | testImplementation(project(":internal-testutils")) 53 | 54 | androidTestImplementation(libs.junit) 55 | testImplementation(libs.junit) 56 | 57 | androidTestImplementation(libs.truth) 58 | testImplementation(libs.truth) 59 | 60 | androidTestImplementation(libs.compose.ui.test.junit4) 61 | testImplementation(libs.compose.ui.test.junit4) 62 | 63 | androidTestImplementation(libs.compose.ui.test.manifest) 64 | testImplementation(libs.compose.ui.test.manifest) 65 | 66 | androidTestImplementation(libs.androidx.test.runner) 67 | testImplementation(libs.androidx.test.runner) 68 | 69 | androidTestImplementation(libs.androidx.window.testing) 70 | testImplementation(libs.androidx.window.testing) 71 | 72 | testImplementation(libs.robolectric) 73 | } 74 | -------------------------------------------------------------------------------- /adaptive/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=accompanist-adaptive 2 | POM_NAME=Accompanist Adaptive library 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /adaptive/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /adaptive/src/main/java/com/google/accompanist/adaptive/DisplayFeatures.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.adaptive 18 | 19 | import android.app.Activity 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.runtime.getValue 22 | import androidx.compose.runtime.produceState 23 | import androidx.compose.runtime.remember 24 | import androidx.window.layout.DisplayFeature 25 | import androidx.window.layout.WindowInfoTracker 26 | 27 | /** 28 | * Calculates the list of [DisplayFeature]s from the given [activity]. 29 | */ 30 | @Composable 31 | public fun calculateDisplayFeatures(activity: Activity): List { 32 | val windowLayoutInfo = remember(activity) { 33 | WindowInfoTracker.getOrCreate(activity).windowLayoutInfo(activity) 34 | } 35 | val displayFeatures by produceState( 36 | initialValue = emptyList(), 37 | key1 = windowLayoutInfo 38 | ) { 39 | windowLayoutInfo.collect { info -> 40 | value = info.displayFeatures 41 | } 42 | } 43 | 44 | return displayFeatures 45 | } 46 | -------------------------------------------------------------------------------- /adaptive/src/sharedTest/kotlin/com/google/accompanist/adaptive/DisplayFeaturesTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.adaptive 18 | 19 | import androidx.activity.ComponentActivity 20 | import androidx.compose.ui.test.junit4.createAndroidComposeRule 21 | import androidx.test.ext.junit.runners.AndroidJUnit4 22 | import androidx.window.layout.DisplayFeature 23 | import androidx.window.layout.FoldingFeature 24 | import androidx.window.layout.WindowLayoutInfo 25 | import androidx.window.testing.layout.FoldingFeature 26 | import androidx.window.testing.layout.WindowLayoutInfoPublisherRule 27 | import com.google.common.truth.Truth.assertThat 28 | import org.junit.Rule 29 | import org.junit.Test 30 | import org.junit.runner.RunWith 31 | 32 | @RunWith(AndroidJUnit4::class) 33 | class DisplayFeaturesTest { 34 | 35 | @get:Rule 36 | val composeTestRule = createAndroidComposeRule() 37 | 38 | @get:Rule 39 | val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule() 40 | 41 | @Test 42 | fun empty_folding_features_is_correct() { 43 | lateinit var displayFeatures: List 44 | 45 | composeTestRule.setContent { 46 | displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity) 47 | } 48 | 49 | windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(WindowLayoutInfo(emptyList())) 50 | 51 | composeTestRule.waitForIdle() 52 | 53 | assertThat(displayFeatures).isEmpty() 54 | } 55 | 56 | @Test 57 | fun single_folding_features_is_correct() { 58 | lateinit var displayFeatures: List 59 | 60 | composeTestRule.setContent { 61 | displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity) 62 | } 63 | 64 | val fakeFoldingFeature = FoldingFeature( 65 | activity = composeTestRule.activity, 66 | center = 200, 67 | size = 40, 68 | state = FoldingFeature.State.HALF_OPENED, 69 | orientation = FoldingFeature.Orientation.VERTICAL, 70 | ) 71 | 72 | windowLayoutInfoPublisherRule.overrideWindowLayoutInfo( 73 | WindowLayoutInfo( 74 | listOf( 75 | fakeFoldingFeature 76 | ) 77 | ) 78 | ) 79 | 80 | composeTestRule.waitForIdle() 81 | 82 | assertThat(displayFeatures).hasSize(1) 83 | assertThat(displayFeatures[0]).isEqualTo(fakeFoldingFeature) 84 | } 85 | 86 | @Test 87 | fun updating_folding_features_is_correct() { 88 | lateinit var displayFeatures: List 89 | 90 | composeTestRule.setContent { 91 | displayFeatures = calculateDisplayFeatures(activity = composeTestRule.activity) 92 | } 93 | 94 | windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(WindowLayoutInfo(emptyList())) 95 | 96 | val fakeFoldingFeature = FoldingFeature( 97 | activity = composeTestRule.activity, 98 | center = 200, 99 | size = 40, 100 | state = FoldingFeature.State.HALF_OPENED, 101 | orientation = FoldingFeature.Orientation.VERTICAL, 102 | ) 103 | 104 | windowLayoutInfoPublisherRule.overrideWindowLayoutInfo( 105 | WindowLayoutInfo( 106 | listOf( 107 | fakeFoldingFeature 108 | ) 109 | ) 110 | ) 111 | 112 | composeTestRule.waitForIdle() 113 | 114 | assertThat(displayFeatures).hasSize(1) 115 | assertThat(displayFeatures[0]).isEqualTo(fakeFoldingFeature) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /adaptive/src/test/resources/robolectric.properties: -------------------------------------------------------------------------------- 1 | # Pin SDK to 30 since Robolectric does not currently support API 31: 2 | # https://github.com/robolectric/robolectric/issues/6635 3 | sdk=30 4 | -------------------------------------------------------------------------------- /build-logic/convention/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 18 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 19 | 20 | plugins { 21 | `kotlin-dsl` 22 | } 23 | 24 | group = "com.google.accompanist.buildlogic" 25 | 26 | java { 27 | sourceCompatibility = JavaVersion.VERSION_17 28 | targetCompatibility = JavaVersion.VERSION_17 29 | } 30 | 31 | kotlin { 32 | compilerOptions { 33 | jvmTarget = JvmTarget.JVM_17 34 | } 35 | } 36 | 37 | dependencies { 38 | // used by BundleInsideHelper.kt 39 | implementation(libs.apacheAnt) 40 | implementation(libs.shadow) 41 | 42 | compileOnly(libs.android.gradlePlugin) 43 | compileOnly(libs.android.tools.common) 44 | compileOnly(libs.compose.gradlePlugin) 45 | compileOnly(libs.kotlin.gradlePlugin) 46 | compileOnly(libs.metalavaGradle) 47 | compileOnly(libs.gradleMavenPublishPlugin) 48 | implementation(libs.truth) 49 | } 50 | 51 | tasks { 52 | validatePlugins { 53 | enableStricterValidation = true 54 | failOnWarning = true 55 | } 56 | } 57 | 58 | gradlePlugin { 59 | plugins { 60 | register("androidLibrary") { 61 | id = "accompanist.android.library" 62 | implementationClass = "AndroidLibraryConventionPlugin" 63 | } 64 | register("androidLibraryCompose") { 65 | id = "accompanist.android.library.compose" 66 | implementationClass = "AndroidLibraryComposeConventionPlugin" 67 | } 68 | register("androidLint") { 69 | id = "accompanist.android.lint" 70 | implementationClass = "AndroidLintConventionPlugin" 71 | } 72 | register("androidLibraryPublished") { 73 | id = "accompanist.android.library.published" 74 | implementationClass = "AndroidLibraryPublishedConventionPlugin" 75 | } 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import com.android.build.gradle.LibraryExtension 18 | import com.google.accompanist.configureAndroidCompose 19 | import org.gradle.api.Plugin 20 | import org.gradle.api.Project 21 | import org.gradle.kotlin.dsl.apply 22 | import org.gradle.kotlin.dsl.getByType 23 | 24 | class AndroidLibraryComposeConventionPlugin : Plugin { 25 | override fun apply(target: Project) { 26 | with(target) { 27 | apply(plugin = "com.android.library") 28 | apply(plugin = "org.jetbrains.kotlin.plugin.compose") 29 | 30 | val extension = extensions.getByType() 31 | configureAndroidCompose(extension) 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import com.android.build.gradle.LibraryExtension 18 | import com.google.accompanist.configureKotlinAndroid 19 | import org.gradle.api.Plugin 20 | import org.gradle.api.Project 21 | import org.gradle.kotlin.dsl.configure 22 | import org.gradle.kotlin.dsl.dependencies 23 | import org.gradle.kotlin.dsl.kotlin 24 | 25 | class AndroidLibraryConventionPlugin : Plugin { 26 | override fun apply(target: Project) { 27 | with(target) { 28 | with(pluginManager) { 29 | apply("com.android.library") 30 | apply("org.jetbrains.kotlin.android") 31 | } 32 | 33 | extensions.configure { 34 | configureKotlinAndroid(this) 35 | defaultConfig.targetSdk = 35 36 | defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 37 | 38 | buildFeatures.buildConfig = false 39 | 40 | testOptions.animationsDisabled = true 41 | // The resource prefix is derived from the module name, 42 | // so resources inside ":core:module1" must be prefixed with "core_module1_" 43 | resourcePrefix = path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_").lowercase() + "_" 44 | } 45 | 46 | dependencies { 47 | add("androidTestImplementation", kotlin("test")) 48 | add("testImplementation", kotlin("test")) 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidLibraryPublishedConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import me.tylerbwong.gradle.metalava.extension.MetalavaExtension 18 | import org.gradle.api.Plugin 19 | import org.gradle.api.Project 20 | import org.gradle.kotlin.dsl.apply 21 | import org.gradle.kotlin.dsl.configure 22 | 23 | class AndroidLibraryPublishedConventionPlugin : Plugin { 24 | override fun apply(target: Project) { 25 | with(target) { 26 | with(pluginManager) { 27 | apply(AndroidLintConventionPlugin::class) 28 | 29 | apply("me.tylerbwong.gradle.metalava") 30 | apply("org.jetbrains.dokka") 31 | apply("com.vanniktech.maven.publish") 32 | } 33 | 34 | extensions.configure { 35 | sourcePaths.setFrom("src/main") 36 | filename.set("api/current.api") 37 | reportLintsAsErrors.set(true) 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import com.android.build.api.dsl.ApplicationExtension 18 | import com.android.build.api.dsl.LibraryExtension 19 | import com.android.build.api.dsl.Lint 20 | import org.gradle.api.Plugin 21 | import org.gradle.api.Project 22 | import org.gradle.kotlin.dsl.configure 23 | import java.io.File 24 | 25 | class AndroidLintConventionPlugin : Plugin { 26 | override fun apply(target: Project) { 27 | with(target) { 28 | when { 29 | pluginManager.hasPlugin("com.android.application") -> 30 | configure { lint(Lint::configure) } 31 | 32 | pluginManager.hasPlugin("com.android.library") -> 33 | configure { lint(Lint::configure) } 34 | 35 | else -> { 36 | pluginManager.apply("com.android.lint") 37 | configure(Lint::configure) 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | private fun Lint.configure() { 45 | textReport = true 46 | textOutput = File("stdout") 47 | // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks 48 | checkReleaseBuilds = false 49 | disable += setOf("GradleOverrides") 50 | } 51 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/com/google/accompanist/AndroidCompose.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist 18 | 19 | import com.android.build.api.dsl.CommonExtension 20 | import org.gradle.api.Project 21 | import org.gradle.api.provider.Provider 22 | import org.gradle.kotlin.dsl.configure 23 | import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension 24 | 25 | /** 26 | * Configure Compose-specific options 27 | */ 28 | internal fun Project.configureAndroidCompose( 29 | commonExtension: CommonExtension<*, *, *, *, *, *>, 30 | ) { 31 | commonExtension.apply { 32 | buildFeatures { 33 | compose = true 34 | } 35 | 36 | testOptions { 37 | unitTests { 38 | // For Robolectric 39 | isIncludeAndroidResources = true 40 | } 41 | } 42 | } 43 | 44 | extensions.configure { 45 | fun Provider.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } } 46 | fun Provider<*>.relativeToRootProject(dir: String) = flatMap { 47 | rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir)) 48 | }.map { it.dir(dir) } 49 | 50 | project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue() 51 | .relativeToRootProject("compose-metrics") 52 | .let(metricsDestination::set) 53 | 54 | project.providers.gradleProperty("enableComposeCompilerReports").onlyIfTrue() 55 | .relativeToRootProject("compose-reports") 56 | .let(reportsDestination::set) 57 | 58 | // We include source information to match how the main Compose libraries are shipped. 59 | // This allows accompanist to be displayed properly in the layout inspector and systrace 60 | includeSourceInformation.set(true) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/com/google/accompanist/KotlinAndroid.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist 18 | 19 | import com.android.build.api.dsl.CommonExtension 20 | import org.gradle.api.JavaVersion 21 | import org.gradle.api.Project 22 | import org.gradle.api.plugins.JavaPluginExtension 23 | import org.gradle.kotlin.dsl.assign 24 | import org.gradle.kotlin.dsl.configure 25 | import org.gradle.kotlin.dsl.dependencies 26 | import org.gradle.kotlin.dsl.provideDelegate 27 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 28 | import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension 29 | import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension 30 | import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension 31 | 32 | /** 33 | * Configure base Kotlin with Android options 34 | */ 35 | internal fun Project.configureKotlinAndroid( 36 | commonExtension: CommonExtension<*, *, *, *, *, *>, 37 | ) { 38 | commonExtension.apply { 39 | compileSdk = 35 40 | 41 | defaultConfig { 42 | minSdk = 21 43 | } 44 | 45 | compileOptions { 46 | sourceCompatibility = JavaVersion.VERSION_1_8 47 | targetCompatibility = JavaVersion.VERSION_1_8 48 | } 49 | } 50 | 51 | configureKotlin() 52 | } 53 | 54 | /** 55 | * Configure base Kotlin options 56 | */ 57 | private inline fun Project.configureKotlin() = configure { 58 | // Treat all Kotlin warnings as errors (disabled by default) 59 | // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties 60 | val warningsAsErrors: String? by project 61 | when (this) { 62 | is KotlinAndroidProjectExtension -> compilerOptions 63 | is KotlinJvmProjectExtension -> compilerOptions 64 | else -> TODO("Unsupported project extension $this ${T::class}") 65 | }.apply { 66 | jvmTarget = JvmTarget.JVM_1_8 67 | allWarningsAsErrors = warningsAsErrors.toBoolean() 68 | explicitApi() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /build-logic/convention/src/main/kotlin/com/google/accompanist/ProjectExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist 18 | 19 | import org.gradle.api.Project 20 | import org.gradle.api.artifacts.VersionCatalog 21 | import org.gradle.api.artifacts.VersionCatalogsExtension 22 | import org.gradle.kotlin.dsl.getByType 23 | 24 | val Project.libs 25 | get(): VersionCatalog = extensions.getByType().named("libs") -------------------------------------------------------------------------------- /build-logic/gradle.properties: -------------------------------------------------------------------------------- 1 | # Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534 2 | org.gradle.parallel=true 3 | org.gradle.caching=true 4 | org.gradle.configureondemand=true 5 | 6 | -------------------------------------------------------------------------------- /build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | dependencyResolutionManagement { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | gradlePluginPortal() 22 | } 23 | versionCatalogs { 24 | create("libs") { 25 | from(files("../gradle/libs.versions.toml")) 26 | } 27 | } 28 | } 29 | 30 | rootProject.name = "build-logic" 31 | include(":convention") 32 | -------------------------------------------------------------------------------- /checksum.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2021 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | RESULT_FILE=$1 18 | 19 | if [ -f $RESULT_FILE ]; then 20 | rm $RESULT_FILE 21 | fi 22 | touch $RESULT_FILE 23 | 24 | checksum_file() { 25 | echo $(openssl md5 $1 | awk '{print $2}') 26 | } 27 | 28 | FILES=() 29 | while read -r -d ''; do 30 | FILES+=("$REPLY") 31 | done < <(find . -type f \( -name "build.gradle*" -o -name "*.versions.toml" -o -name "gradle-wrapper.properties" \) -print0) 32 | 33 | # Loop through files and append MD5 to result file 34 | for FILE in ${FILES[@]}; do 35 | echo $(checksum_file $FILE) >> $RESULT_FILE 36 | done 37 | # Now sort the file so that it is idempotent 38 | sort $RESULT_FILE -o $RESULT_FILE 39 | -------------------------------------------------------------------------------- /docs/adaptive.md: -------------------------------------------------------------------------------- 1 | # Adaptive utilities for Jetpack Compose 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-adaptive)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | A library providing a collection of utilities for adaptive layouts. 6 | 7 | ## calculateDisplayFeatures 8 | 9 | [`calculateDisplayFeatures(activity)`](../api/adaptive/com.google.accompanist.adaptive/calculate-display-features.html) returns the current list of `DisplayFeature`s, 10 | as reported by the [Jetpack WindowManager library](https://developer.android.com/jetpack/androidx/releases/window). 11 | 12 | These contain the list of folds (if any), and can be used to drive components like [`TwoPane`](#TwoPane). 13 | 14 | ## TwoPane 15 | 16 | [`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) is a UI component that positions exactly two slots on the screen. 17 | 18 | The default positioning of these two slots is driven by a [`TwoPaneStrategy`](../api/adaptive/com.google.accompanist.adaptive/-two-pane-strategy.html), 19 | which can decide to orient the two slots side-by-side horizontally or vertically, and also configure the gap between them. 20 | 21 | The built-in [`HorizontalTwoPaneStrategy`](../api/adaptive/com.google.accompanist.adaptive/-horizontal-two-pane-strategy.html) and 22 | [`VerticalTwoPaneStrategy`](../api/adaptive/com.google.accompanist.adaptive/-vertical-two-pane-strategy.html) allow positioning the 23 | slots based on a fixed offset, or as some fraction of the space. 24 | 25 | [`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) also requires a list of display features (to be retrieved with [`calculateDisplayFeatures`](#calculateDisplayFeatures)), 26 | and optionally a [`FoldAwareConfiguration`](../api/adaptive/com.google.accompanist.adaptive/-fold-aware-configuration.html) to determine which folds to handle automatically. 27 | 28 | When there is a fold that intersects with the [`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) component that is obscuring or separating, 29 | the [`TwoPane`](../api/adaptive/com.google.accompanist.adaptive/-two-pane.html) will automatically place the slots to avoid the fold. 30 | 31 | When there is no fold, the default supplied strategy will be used instead. 32 | 33 | ## FoldAwareColumn 34 | 35 | [`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. 36 | 37 | [`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. 38 | 39 | The 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. 40 | 41 | When 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. 42 | 43 | When there is no fold, the children will be placed consecutively with no y-coordinate adjustments. 44 | 45 | Optionally, 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. 46 | 47 | ## Download 48 | 49 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-adaptive)](https://search.maven.org/search?q=g:com.google.accompanist) 50 | 51 | ```groovy 52 | repositories { 53 | mavenCentral() 54 | } 55 | 56 | dependencies { 57 | implementation "com.google.accompanist:accompanist-adaptive:" 58 | } 59 | ``` -------------------------------------------------------------------------------- /docs/drawablepainter.md: -------------------------------------------------------------------------------- 1 | # Drawable Painter 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-drawablepainter)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | A 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). 6 | 7 | This 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). 8 | 9 | ## Usage 10 | 11 | ``` kotlin 12 | @Composable 13 | fun DrawDrawable() { 14 | val drawable = AppCompatResources.getDrawable(LocalContext.current, R.drawable.rectangle) 15 | 16 | Image( 17 | painter = rememberDrawablePainter(drawable = drawable), 18 | contentDescription = "content description", 19 | ) 20 | } 21 | ``` 22 | 23 | ## Download 24 | 25 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-drawablepainter)](https://search.maven.org/search?q=g:com.google.accompanist) 26 | 27 | ```groovy 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | implementation "com.google.accompanist:accompanist-drawablepainter:" 34 | } 35 | ``` 36 | 37 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit. 38 | 39 | [compose]: https://developer.android.com/jetpack/compose 40 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-drawablepainter/ 41 | -------------------------------------------------------------------------------- /docs/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/header.png -------------------------------------------------------------------------------- /docs/insets/images/edge-to-edge-list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/insets/images/edge-to-edge-list.jpg -------------------------------------------------------------------------------- /docs/insets/images/ime-insets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/insets/images/ime-insets.gif -------------------------------------------------------------------------------- /docs/insets/images/ime-scroll.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/insets/images/ime-scroll.gif -------------------------------------------------------------------------------- /docs/migration.md: -------------------------------------------------------------------------------- 1 | # Migration from dev.chrisbanes.accompanist 2 | 3 | In 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. 4 | 5 | As a summary: 6 | 7 | - All code was refactored from the `dev.chrisbanes.accompanist` root package to `com.google.accompanist` package. 8 | - The Maven group ID was changed from `dev.chrisbanes.accompanist` to `com.google.accompanist`. 9 | 10 | ## Semi-automatic migration... 11 | 12 | The following methods below are available for your information only, but may help if you need to migrate from the old package name. 13 | 14 | !!! warning 15 | 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. 16 | 17 | ### Android Studio / IntelliJ 18 | 19 | You 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. 20 | 21 | ![Android Studio Replace in Path pane](studio.png) 22 | 23 | - Find query: `dev.chrisbanes.accompanist` 24 | - Replace string: `com.google.accompanist` 25 | - _Optional:_ Set the file mask to `*.kt` so that only Kotlin files are searched. Repeat for `*.gradle`. 26 | 27 | Similar can be achieved in [Visual Studio Code](https://code.visualstudio.com/docs/editor/codebasics#_search-across-files). Other IDEs / text editors are available. 28 | 29 | ### YOLO commands 30 | 31 | These commands while automatically replace any imports and Gradle dependencies for the project in the current directory. 32 | 33 | #### MacOS 34 | 35 | ``` bash 36 | find . -type f \( -name '*.kt' -or -name '*.gradle*' \) \ 37 | -exec sed -i '' 's/dev\.chrisbanes\.accompanist/com\.google\.accompanist/' {} \; 38 | ``` 39 | 40 | #### Linux 41 | 42 | ``` bash 43 | find . -type f \( -name '*.kt' -or -name '*.gradle*' \) \ 44 | -exec sed -i 's/dev\.chrisbanes\.accompanist/com\.google\.accompanist/' {} \; 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/migration/studio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/migration/studio.png -------------------------------------------------------------------------------- /docs/permissions.md: -------------------------------------------------------------------------------- 1 | # Jetpack Compose Permissions 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | A library which provides [Android runtime permissions](https://developer.android.com/guide/topics/permissions/overview) support for Jetpack Compose. 6 | 7 | !!! warning 8 | The permission APIs are currently experimental and they could change at any time. 9 | All of the APIs are marked with the `@ExperimentalPermissionsApi` annotation. 10 | 11 | ## Usage 12 | 13 | ### `rememberPermissionState` and `rememberMultiplePermissionsState` APIs 14 | 15 | The `rememberPermissionState(permission: String)` API allows you to request a certain permission 16 | to the user and check for the status of the permission. 17 | `rememberMultiplePermissionsState(permissions: List)` offers the same but for multiple 18 | permissions at the same time. 19 | 20 | Both APIs expose properties for you to follow the workflow as described in the 21 | [permissions documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions). 22 | 23 | !!! caution 24 | The call to the method that requests the permission to the user (e.g. `PermissionState.launchPermissionRequest()`) 25 | needs to be invoked from a non-composable scope. For example, from a side-effect or from a 26 | non-composable callback such as a `Button`'s `onClick` lambda. 27 | 28 | The following code exercises the [permission request workflow](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions). 29 | 30 | ```kotlin 31 | @OptIn(ExperimentalPermissionsApi::class) 32 | @Composable 33 | private fun FeatureThatRequiresCameraPermission() { 34 | 35 | // Camera permission state 36 | val cameraPermissionState = rememberPermissionState( 37 | android.Manifest.permission.CAMERA 38 | ) 39 | 40 | if (cameraPermissionState.status.isGranted) { 41 | Text("Camera permission Granted") 42 | } else { 43 | Column { 44 | val textToShow = if (cameraPermissionState.status.shouldShowRationale) { 45 | // If the user has denied the permission but the rationale can be shown, 46 | // then gently explain why the app requires this permission 47 | "The camera is important for this app. Please grant the permission." 48 | } else { 49 | // If it's the first time the user lands on this feature, or the user 50 | // doesn't want to be asked again for this permission, explain that the 51 | // permission is required 52 | "Camera permission required for this feature to be available. " + 53 | "Please grant the permission" 54 | } 55 | Text(textToShow) 56 | Button(onClick = { cameraPermissionState.launchPermissionRequest() }) { 57 | Text("Request permission") 58 | } 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | For more examples, refer to the [samples](https://github.com/google/accompanist/tree/main/sample/src/main/java/com/google/accompanist/sample/permissions). 65 | 66 | ## Limitations 67 | 68 | This permissions wrapper is built on top of the available Android platform APIs. We cannot extend 69 | the platform's capabilities. For example, it's not possible to differentiate between the 70 | _it's the first time requesting the permission_ vs _the user doesn't want to be asked again_ 71 | use cases. 72 | 73 | ## Download 74 | 75 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions)](https://search.maven.org/search?q=g:com.google.accompanist) 76 | 77 | ```groovy 78 | repositories { 79 | mavenCentral() 80 | } 81 | 82 | dependencies { 83 | implementation "com.google.accompanist:accompanist-permissions:" 84 | } 85 | ``` 86 | 87 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit. 88 | 89 | [compose]: https://developer.android.com/jetpack/compose 90 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-permissions/ 91 | -------------------------------------------------------------------------------- /docs/swiperefresh/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/swiperefresh/demo.mp4 -------------------------------------------------------------------------------- /docs/swiperefresh/tweaked.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/swiperefresh/tweaked.mp4 -------------------------------------------------------------------------------- /docs/systemuicontroller.md: -------------------------------------------------------------------------------- 1 | # System UI Controller for Jetpack Compose 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-systemuicontroller)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | !!! warning 6 | **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. 7 | 8 | ## Migration 9 | Recommendation: 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. 10 | 11 | For other usages, migrate to using WindowInsetsControllerCompat or window APIs directly. 12 | 13 | ## Original Documentation 14 | System UI Controller provides easy-to-use utilities for updating the System UI bar colors within Jetpack Compose. 15 | 16 | ## Usage 17 | To 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). 18 | 19 | In your layouts you can update the system bar colors like so: 20 | 21 | ``` kotlin 22 | // Remember a SystemUiController 23 | val systemUiController = rememberSystemUiController() 24 | val useDarkIcons = !isSystemInDarkTheme() 25 | 26 | DisposableEffect(systemUiController, useDarkIcons) { 27 | // Update all of the system bar colors to be transparent, and use 28 | // dark icons if we're in light theme 29 | systemUiController.setSystemBarsColor( 30 | color = Color.Transparent, 31 | darkIcons = useDarkIcons 32 | ) 33 | 34 | // setStatusBarColor() and setNavigationBarColor() also exist 35 | 36 | onDispose {} 37 | } 38 | ``` 39 | 40 | ## System bar icon colors 41 | The library automatically handles API level differences when running on Android devices. If we look at the example 42 | of status bar icons, Android only natively supports dark icons on API 23+. This library handles this by automatically 43 | altering the requested color with a scrim, to maintain contrast: 44 | 45 | ![](api-scrim.png) 46 | 47 | Similar happens on navigation bar color, which is only available on API 26+. 48 | 49 | ### Modifying scrim logic 50 | 51 | The scrim logic can be modified if needed: 52 | 53 | ``` kotlin 54 | systemUiController.setStatusBarColor( 55 | color = Color.Transparent, 56 | darkIcons = true 57 | ) { requestedColor -> 58 | // TODO: return a darkened color to be used when the system doesn't 59 | // natively support dark icons 60 | } 61 | ``` 62 | 63 | ## Samples 64 | 65 | For 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. 66 | 67 | ## Download 68 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-systemuicontroller)](https://search.maven.org/search?q=g:com.google.accompanist) 69 | 70 | ```groovy 71 | repositories { 72 | mavenCentral() 73 | } 74 | 75 | dependencies { 76 | implementation "com.google.accompanist:accompanist-systemuicontroller:" 77 | } 78 | ``` 79 | 80 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit. 81 | 82 | [compose]: https://developer.android.com/jetpack/compose 83 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-systemuicontroller/ 84 | -------------------------------------------------------------------------------- /docs/systemuicontroller/api-scrim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/systemuicontroller/api-scrim.png -------------------------------------------------------------------------------- /docs/themeadapter/material-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/themeadapter/material-header.png -------------------------------------------------------------------------------- /docs/themeadapter/material3-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/docs/themeadapter/material3-header.png -------------------------------------------------------------------------------- /docs/updating.md: -------------------------------------------------------------------------------- 1 | # Updating & releasing Accompanist 2 | 3 | This doc is mostly for maintainers. 4 | 5 | ## New features & bugfixes 6 | All new features should be uploaded as PRs against the `main` branch. 7 | 8 | Once merged into `main`, they will be automatically merged into the `snapshot` branch. 9 | 10 | ## Jetpack Compose Snapshots 11 | 12 | We publish snapshot versions of Accompanist, which depend on a `SNAPSHOT` versions of Jetpack Compose. These are built from the `snapshot` branch. 13 | 14 | ### Updating to a newer Compose snapshot 15 | 16 | As mentioned above, updating to a new Compose snapshot is done by submitting a new PR against the `snapshot` branch: 17 | 18 | ``` sh 19 | git checkout snapshot && git pull 20 | # Create branch for PR 21 | git checkout -b update_snapshot 22 | ``` 23 | 24 | Now edit the project to depend on the new Compose SNAPSHOT version: 25 | 26 | Edit [`/gradle/libs.versions.toml`](https://github.com/google/accompanist/blob/main/gradle/libs.versions.toml): 27 | 28 | Under `[versions]`: 29 | 30 | 1. Update the `composesnapshot` property to be the snapshot number 31 | 2. Ensure that the `compose` property is correct 32 | 33 | Make sure the project builds and test pass: 34 | ``` 35 | ./gradlew check 36 | ``` 37 | 38 | Now `git commit` the changes and push to GitHub. 39 | 40 | Finally create a PR (with the base branch as `snapshot`) and send for review. 41 | 42 | ## Releasing 43 | 44 | Once the next Jetpack Compose version is out, we're ready to push a new release: 45 | 46 | ### #1: Merge `snapshot` into `main` 47 | 48 | First we merge the `snapshot` branch into `main`: 49 | 50 | ``` sh 51 | git checkout snapshot && git pull 52 | git checkout main && git pull 53 | 54 | # Create branch for PR 55 | git checkout -b main_snapshot_merge 56 | 57 | # Merge in the snapshot branch 58 | git merge snapshot 59 | ``` 60 | 61 | ### #2: Update dependencies 62 | 63 | Edit [`/gradle/libs.versions.toml`](https://github.com/google/accompanist/blob/main/gradle/libs.versions.toml): 64 | 65 | Under `[versions]`: 66 | 67 | 1. Update the `composesnapshot` property to a single character (usually `-`). This disables the snapshot repository. 68 | 2. Update the `compose` property to match the new release (i.e. `1.0.0-beta06`) 69 | 70 | Make sure the project builds and test pass: 71 | ``` 72 | ./gradlew check 73 | ``` 74 | 75 | Commit the changes. 76 | 77 | ### #3: Bump the version number 78 | 79 | Edit [gradle.properties](https://github.com/google/accompanist/blob/main/gradle.properties): 80 | 81 | * Update the `VERSION_NAME` property and remove the `-SNAPSHOT` suffix. 82 | 83 | Commit the changes, using the commit message containing the new version name. 84 | 85 | ### #4: Push to GitHub 86 | 87 | Push 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. 88 | 89 | ### #5: Create release 90 | 91 | Once the above PR has been approved and merged, we need to create the GitHub release: 92 | 93 | * Open up the [Releases](https://github.com/google/accompanist/releases) page. 94 | * At the top you should see a 'Draft' release, auto populated with any PRs since the last release. Click 'Edit'. 95 | * Make sure that the version number matches what we released (the tool guesses but is not always correct). 96 | * Double check everything, then press 'Publish release'. 97 | 98 | At 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/). 99 | 100 | ### #6: Prepare the next development version 101 | 102 | The current release is now finished, but we need to update the version for the next development version: 103 | 104 | Edit [gradle.properties](https://github.com/google/accompanist/blob/main/gradle.properties): 105 | 106 | * Update the `VERSION_NAME` property, by increasing the version number, and adding the `-SNAPSHOT` suffix. 107 | * Example: released version: `0.3.0`. Update to `0.3.1-SNAPSHOT` 108 | 109 | `git commit` and push to `main`. 110 | 111 | Finally, merge all of these changes back to `snapshot`: 112 | 113 | ``` 114 | git checkout snapshot && git pull 115 | git merge main 116 | git push 117 | ``` -------------------------------------------------------------------------------- /docs/using-snapshot-version.md: -------------------------------------------------------------------------------- 1 | # Using a Snapshot Version of the Library 2 | 3 | If you would like to depend on the cutting edge version of the Accompanist 4 | library, you can use the [snapshot versions][snap] that are published to 5 | [Sonatype OSSRH](https://central.sonatype.org/)'s snapshot repository. These are updated on every commit to `main`. 6 | 7 | To do so: 8 | 9 | ```groovy 10 | repositories { 11 | // ... 12 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } 13 | } 14 | 15 | dependencies { 16 | // Check the latest SNAPSHOT version from the link above 17 | classpath 'com.google.accompanist:accompanist-coil:XXX-SNAPSHOT' 18 | } 19 | ``` 20 | 21 | You might see a number of different versioned snapshots. If we use an example: 22 | 23 | * `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)). 24 | * `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). 25 | 26 | ### Using Jetpack Compose Snapshots 27 | 28 | If 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. 29 | 30 | We 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: 31 | 32 | 33 | ``` groovy 34 | repositories { 35 | // ... 36 | maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } 37 | } 38 | 39 | dependencies { 40 | // Check the latest SNAPSHOT version from the link above 41 | classpath 'com.google.accompanist:accompanist-coil:XXXX.compose-YYYYY-SNAPSHOT' 42 | } 43 | ``` 44 | 45 | These builds are updated regularly, but there's no guarantee that we will create one for a given snapshot number. 46 | 47 | *Note:* you might also see versions in the scheme `x.x.x.ui-YYYY-SNAPSHOT`. These are the same, just using an older suffix. 48 | 49 | 50 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/ -------------------------------------------------------------------------------- /docs/web.md: -------------------------------------------------------------------------------- 1 | # WebView wrapper for Jetpack Compose 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-webview)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | A library which provides a Jetpack Compose wrapper around Android's WebView. 6 | 7 | !!! warning 8 | **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. 9 | 10 | ## Usage 11 | 12 | To 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. 13 | 14 | The basic usage is as follows: 15 | 16 | ```kotlin 17 | val state = rememberWebViewState("https://example.com") 18 | 19 | WebView( 20 | state 21 | ) 22 | ``` 23 | 24 | This will display a WebView in your Compose layout that shows the URL provided. 25 | 26 | There 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. 27 | 28 | ### WebView settings including JavaScript 29 | 30 | By default, JavaScript is disabled in the WebView. To enable it or any other settings you can use the `onCreated` callback. 31 | 32 | ```kotlin 33 | WebView( 34 | state = webViewState, 35 | onCreated = { it.settings.javaScriptEnabled = true } 36 | ) 37 | ``` 38 | 39 | ### Capturing back presses 40 | 41 | By default the WebView will capture back presses/swipes when relevant and navigate the WebView back. This can be disabled via the parameter on 42 | the Composable. 43 | 44 | ```kotlin 45 | WebView( 46 | ... 47 | captureBackPresses = false 48 | ) 49 | ``` 50 | 51 | ### Using a subclass of WebView 52 | 53 | If you want to use a subclass of `WebView`, or simply require more control over its instantiation, you can provide a factory. 54 | 55 | ```kotlin 56 | WebView( 57 | ... 58 | factory = { context -> CustomWebView(context) } 59 | ) 60 | ``` 61 | 62 | ## Download 63 | 64 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-webview)](https://search.maven.org/search?q=g:com.google.accompanist) 65 | 66 | ```groovy 67 | repositories { 68 | mavenCentral() 69 | } 70 | 71 | dependencies { 72 | implementation "com.google.accompanist:accompanist-webview:" 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /drawablepainter/README.md: -------------------------------------------------------------------------------- 1 | # Accompanist Drawable Painter 2 | 3 | [![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) 4 | 5 | For more information, visit the documentation: https://google.github.io/accompanist/drawablepainter 6 | 7 | ## Download 8 | 9 | ```groovy 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation "com.google.accompanist:accompanist-drawablepainter:" 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /drawablepainter/api/current.api: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | package com.google.accompanist.drawablepainter { 3 | 4 | public final class DrawablePainter extends androidx.compose.ui.graphics.painter.Painter implements androidx.compose.runtime.RememberObserver { 5 | ctor public DrawablePainter(android.graphics.drawable.Drawable drawable); 6 | method public android.graphics.drawable.Drawable getDrawable(); 7 | method public long getIntrinsicSize(); 8 | method public void onAbandoned(); 9 | method protected void onDraw(androidx.compose.ui.graphics.drawscope.DrawScope); 10 | method public void onForgotten(); 11 | method public void onRemembered(); 12 | property public final android.graphics.drawable.Drawable drawable; 13 | property public long intrinsicSize; 14 | } 15 | 16 | public final class DrawablePainterKt { 17 | method @androidx.compose.runtime.Composable public static androidx.compose.ui.graphics.painter.Painter rememberDrawablePainter(android.graphics.drawable.Drawable? drawable); 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /drawablepainter/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("UnstableApiUsage") 17 | 18 | plugins { 19 | alias(libs.plugins.accompanist.android.library) 20 | alias(libs.plugins.accompanist.android.library.compose) 21 | alias(libs.plugins.accompanist.android.library.published) 22 | } 23 | 24 | android { 25 | namespace = "com.google.accompanist.drawablepainter" 26 | } 27 | 28 | dependencies { 29 | implementation(libs.compose.ui.ui) 30 | implementation(libs.kotlin.coroutines.android) 31 | } 32 | -------------------------------------------------------------------------------- /drawablepainter/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=accompanist-drawablepainter 2 | POM_NAME=Accompanist Drawable Painter library 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /drawablepainter/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /generate_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2021 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Fail on any error 18 | set -ex 19 | 20 | DOCS_ROOT=docs-gen 21 | 22 | [ -d $DOCS_ROOT ] && rm -r $DOCS_ROOT 23 | mkdir $DOCS_ROOT 24 | 25 | # Clear out the old API docs 26 | [ -d docs/api ] && rm -r docs/api 27 | # Build the docs with dokka 28 | ./gradlew dokkaHtmlMultiModule --stacktrace 29 | 30 | # Create a copy of our docs at our $DOCS_ROOT 31 | cp -a docs/* $DOCS_ROOT 32 | 33 | cp README.md $DOCS_ROOT/index.md 34 | cp CONTRIBUTING.md $DOCS_ROOT/contributing.md 35 | 36 | sed -i.bak 's/CONTRIBUTING.md/contributing/' $DOCS_ROOT/index.md 37 | sed -i.bak 's/README.md//' $DOCS_ROOT/index.md 38 | sed -i.bak 's/docs\/header.png/header.png/' $DOCS_ROOT/index.md 39 | 40 | # Convert docs/xxx.md links to just xxx/ 41 | sed -i.bak 's/docs\/\([a-zA-Z-]*\).md/\1/' $DOCS_ROOT/index.md 42 | 43 | # Finally delete all of the backup files 44 | find . -name '*.bak' -delete 45 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2020 The Android Open Source Project 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | # Turn on parallel compilation, caching and on-demand configuration 18 | org.gradle.configureondemand=true 19 | org.gradle.caching=true 20 | org.gradle.parallel=true 21 | 22 | # Declare we support AndroidX 23 | android.useAndroidX=true 24 | 25 | # Increase memory 26 | org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError 27 | 28 | # Required to publish to Nexus (see https://github.com/gradle/gradle/issues/11308) 29 | systemProp.org.gradle.internal.publish.checksums.insecure=true 30 | 31 | # Increase timeout when pushing to Sonatype (otherwise we get timeouts) 32 | systemProp.org.gradle.internal.http.socketTimeout=120000 33 | 34 | GROUP=com.google.accompanist 35 | # !! No longer need to update this manually when using a Compose SNAPSHOT 36 | VERSION_NAME=0.37.4-SNAPSHOT 37 | 38 | POM_DESCRIPTION=Utilities for Jetpack Compose 39 | 40 | POM_URL=https://github.com/google/accompanist/ 41 | POM_SCM_URL=https://github.com/google/accompanist/ 42 | POM_SCM_CONNECTION=scm:git:git://github.com/google/accompanist.git 43 | POM_SCM_DEV_CONNECTION=scm:git:git://github.com/google/accompanist.git 44 | 45 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 46 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 47 | POM_LICENCE_DIST=repo 48 | 49 | POM_DEVELOPER_ID=google 50 | POM_DEVELOPER_NAME=Google 51 | 52 | SONATYPE_HOST=DEFAULT 53 | RELEASE_SIGNING_ENABLED=true 54 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 10 11:49:25 AEST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 5 | networkTimeout=10000 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /images/Social.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/images/Social.sketch -------------------------------------------------------------------------------- /internal-testutils/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("UnstableApiUsage") 17 | 18 | plugins { 19 | alias(libs.plugins.accompanist.android.library) 20 | alias(libs.plugins.accompanist.android.library.compose) 21 | } 22 | 23 | android { 24 | namespace = "com.google.accompanist.internal.test" 25 | } 26 | 27 | dependencies { 28 | implementation(libs.kotlin.stdlib) 29 | implementation(libs.kotlin.coroutines.android) 30 | 31 | implementation(libs.compose.foundation.foundation) 32 | api(libs.compose.ui.test.junit4) 33 | 34 | api(libs.androidx.test.core) 35 | implementation(libs.truth) 36 | } 37 | -------------------------------------------------------------------------------- /internal-testutils/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /internal-testutils/src/main/java/com/google/accompanist/internal/test/ActivityScenario.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.internal.test 18 | 19 | import android.app.Activity 20 | import androidx.test.core.app.ActivityScenario 21 | 22 | public fun ActivityScenario.withActivity( 23 | action: (A) -> T 24 | ): T { 25 | lateinit var result: T 26 | onActivity { result = action(it) } 27 | return result 28 | } 29 | -------------------------------------------------------------------------------- /internal-testutils/src/main/java/com/google/accompanist/internal/test/Assertions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.internal.test 18 | 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.graphics.ImageBitmap 21 | import androidx.compose.ui.graphics.toPixelMap 22 | import androidx.compose.ui.test.SemanticsNodeInteraction 23 | import androidx.compose.ui.test.assertHeightIsAtLeast 24 | import androidx.compose.ui.test.assertWidthIsAtLeast 25 | import androidx.compose.ui.unit.Dp 26 | import com.google.common.truth.Truth.assertThat 27 | 28 | /** 29 | * Assert that all of the pixels in this image as of the [expected] color. 30 | */ 31 | public fun ImageBitmap.assertPixels(expected: Color, tolerance: Float = 0.001f) { 32 | toPixelMap().buffer.forEach { pixel -> 33 | val color = Color(pixel) 34 | assertThat(color.red).isWithin(tolerance).of(expected.red) 35 | assertThat(color.green).isWithin(tolerance).of(expected.green) 36 | assertThat(color.blue).isWithin(tolerance).of(expected.blue) 37 | assertThat(color.alpha).isWithin(tolerance).of(expected.alpha) 38 | } 39 | } 40 | 41 | /** 42 | * Run the [SemanticsNodeInteraction] provided by [block] repeatedly until either 43 | * the assertion succeeds, or the execution runs past [timeoutMillis]. 44 | */ 45 | public fun SemanticsNodeInteraction.assertWithTimeout( 46 | timeoutMillis: Long, 47 | block: SemanticsNodeInteraction.() -> SemanticsNodeInteraction, 48 | ): SemanticsNodeInteraction { 49 | val startTime = System.nanoTime() 50 | while (System.nanoTime() - startTime <= timeoutMillis * 1_000_000) { 51 | try { 52 | return block() 53 | } catch (error: AssertionError) { 54 | // If the assertion failed, sleep for 10ms before the next loop iteration 55 | Thread.sleep(10) 56 | } 57 | } 58 | // If we reach here, each assertion has failed and we've reached the time out. 59 | // Run block one last time... 60 | return block() 61 | } 62 | 63 | public val SemanticsNodeInteraction.exists: Boolean 64 | get() = try { 65 | assertExists() 66 | true 67 | } catch (t: Throwable) { 68 | false 69 | } 70 | 71 | public val SemanticsNodeInteraction.isLaidOut: Boolean 72 | get() = try { 73 | assertWidthIsAtLeast(Dp.Hairline).assertHeightIsAtLeast(Dp.Hairline) 74 | true 75 | } catch (t: Throwable) { 76 | false 77 | } 78 | -------------------------------------------------------------------------------- /internal-testutils/src/main/java/com/google/accompanist/internal/test/IgnoreOnRobolectric.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.internal.test 18 | 19 | /** 20 | * Marker interface to use as a category for filtering out tests when running on Robolectric. 21 | */ 22 | public interface IgnoreOnRobolectric 23 | -------------------------------------------------------------------------------- /internal-testutils/src/main/java/com/google/accompanist/internal/test/TestUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @file:Suppress("NOTHING_TO_INLINE") 18 | 19 | package com.google.accompanist.internal.test 20 | 21 | public inline fun parameterizedParams(): List> = emptyList() 22 | 23 | public inline fun List>.combineWithParameters( 24 | vararg values: T 25 | ): List> { 26 | if (isEmpty()) { 27 | return values.map { arrayOf(it) } 28 | } 29 | 30 | return fold(emptyList()) { acc, args -> 31 | val result = acc.toMutableList() 32 | values.forEach { v -> 33 | result += ArrayList().apply { 34 | addAll(args) 35 | add(v) 36 | }.toTypedArray() 37 | } 38 | result.toList() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal-testutils/src/main/java/com/google/accompanist/internal/test/WaitUntil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.internal.test 18 | 19 | import android.os.Looper 20 | import com.google.common.truth.Truth.assertThat 21 | import java.util.concurrent.TimeoutException 22 | 23 | public fun waitUntil(timeoutMillis: Long = 2_000, condition: () -> Boolean) { 24 | if (Looper.getMainLooper() == Looper.myLooper()) { 25 | assertThat(condition()).isTrue() 26 | } 27 | 28 | val startTime = System.nanoTime() 29 | while (!condition()) { 30 | // Let Android run measure, draw and in general any other async operations. 31 | Thread.sleep(10) 32 | if (System.nanoTime() - startTime > timeoutMillis * 1_000_000) { 33 | throw TimeoutException("Condition still not satisfied after $timeoutMillis ms") 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /internal-testutils/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | # Project information 2 | site_name: 'Accompanist' 3 | site_description: 'A group of libraries to help write Jetpack Compose apps.' 4 | site_author: 'Google' 5 | site_url: 'https://google.github.io/accompanist/' 6 | edit_uri: 'tree/main/docs/' 7 | remote_branch: gh-pages 8 | 9 | docs_dir: docs-gen 10 | 11 | # Repository 12 | repo_name: 'Accompanist' 13 | repo_url: 'https://github.com/google/accompanist' 14 | 15 | # Navigation 16 | nav: 17 | - 'Overview': index.md 18 | - 'System UI Controller': 19 | - 'Guide': systemuicontroller.md 20 | - 'API': api/systemuicontroller/ 21 | - 'Drawable Painter': 22 | - 'Guide': drawablepainter.md 23 | - 'API': api/drawablepainter/ 24 | - 'Permissions': 25 | - 'Guide': permissions.md 26 | - 'API': api/permissions/ 27 | - 'Navigation Animation': 28 | - 'Guide': navigation-animation.md 29 | - 'API': api/navigation-animation/ 30 | - 'Navigation Material': 31 | - 'Guide': navigation-material.md 32 | - 'API': api/navigation-material/ 33 | - 'Adaptive': 34 | - 'Guide': adaptive.md 35 | - 'API': api/adaptive 36 | - 'Snapshots': using-snapshot-version.md 37 | - 'Contributing': contributing.md 38 | - 'Maintainers': 39 | - 'Update guide': updating.md 40 | 41 | # Configuration 42 | theme: 43 | name: 'material' 44 | language: 'en' 45 | icon: 46 | logo: material/music-clef-treble 47 | palette: 48 | primary: 'black' 49 | accent: 'deep orange' 50 | font: 51 | text: 'Roboto' 52 | code: 'JetBrains Mono' 53 | 54 | # Extensions 55 | markdown_extensions: 56 | - admonition 57 | - attr_list 58 | - codehilite: 59 | guess_lang: false 60 | - footnotes 61 | - toc: 62 | permalink: true 63 | - pymdownx.betterem 64 | - pymdownx.superfences 65 | - pymdownx.tabbed: 66 | alternate_style: true 67 | - pymdownx.details 68 | -------------------------------------------------------------------------------- /permissions-lint/README.md: -------------------------------------------------------------------------------- 1 | # Lint checks for Permissions for Jetpack Compose 2 | 3 | Lint checks for preventing calling `PermissionState.launchPermissionRequest` and 4 | `MultiplePermissionsState.launchMultiplePermissionRequest()` within the Composition as that throws 5 | a runtime exception. 6 | 7 | These functions should be called inside a regular lambda or a side-effect but never in the 8 | Composition. 9 | 10 | These lint checks will be automatically applied to your project when using 11 | 📫 [Permissions](https://google.github.io/accompanist/permissions/). 12 | 13 | ## Download Permissions for Jetpack Compose 14 | 15 | ```groovy 16 | repositories { 17 | mavenCentral() 18 | } 19 | 20 | dependencies { 21 | implementation "com.google.accompanist:accompanist-permissions:" 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /permissions-lint/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import com.google.accompanist.BundleInsideHelper 17 | import com.google.accompanist.BundleInsideHelper.forInsideLintJar 18 | 19 | plugins { 20 | `java-library` 21 | id("kotlin") 22 | id(libs.plugins.jetbrains.dokka.get().pluginId) 23 | id(libs.plugins.android.lint.get().pluginId) 24 | } 25 | 26 | lint { 27 | htmlReport = true 28 | htmlOutput = file("lint-report.html") 29 | textReport = true 30 | absolutePaths = false 31 | ignoreTestSources = true 32 | } 33 | 34 | affectedTestConfiguration { 35 | jvmTestTask = "test" 36 | } 37 | 38 | /** 39 | * Creates a configuration for users to use that will be used bundle these dependency 40 | * jars inside of this lint check's jar. This is required because lintPublish does 41 | * not currently support dependencies, so instead we need to bundle any dependencies with the 42 | * lint jar manually. (b/182319899) 43 | */ 44 | val bundleInside = forInsideLintJar() 45 | 46 | dependencies { 47 | // Bundle metadataJvm inside the Jar 48 | bundleInside(libs.kotlin.metadataJvm) 49 | 50 | compileOnly(libs.android.tools.lint.api) 51 | compileOnly(libs.kotlin.reflect) 52 | compileOnly(libs.kotlin.stdlib) 53 | compileOnly(libs.kotlin.stdlibJdk8) // Override version from transitive dependencies 54 | 55 | testImplementation(libs.junit) 56 | testImplementation(libs.kotlin.reflect) 57 | testImplementation(libs.kotlin.stdlib) 58 | testImplementation(libs.kotlin.stdlibJdk8) // Override version from transitive dependencies 59 | testImplementation(libs.android.tools.lint.lint) 60 | testImplementation(libs.android.tools.lint.tests) 61 | } 62 | 63 | java { 64 | sourceCompatibility = JavaVersion.VERSION_17 65 | targetCompatibility = JavaVersion.VERSION_17 66 | } -------------------------------------------------------------------------------- /permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsIssueRegistry.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions.lint 18 | 19 | import com.android.tools.lint.client.api.IssueRegistry 20 | import com.android.tools.lint.client.api.Vendor 21 | import com.android.tools.lint.detector.api.CURRENT_API 22 | import com.android.tools.lint.detector.api.Issue 23 | 24 | @Suppress("UnstableApiUsage") 25 | public class PermissionsIssueRegistry : IssueRegistry() { 26 | 27 | override val issues: List = listOf( 28 | PermissionsLaunchDetector.PermissionLaunchedDuringComposition 29 | ) 30 | 31 | override val api: Int = 10 32 | override val minApi: Int = CURRENT_API 33 | 34 | override val vendor: Vendor = Vendor( 35 | vendorName = "Accompanist Permissions", 36 | identifier = "com.google.accompanist.permissions", 37 | feedbackUrl = "https://github.com/google/accompanist/issues/new/choose" 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /permissions-lint/src/main/java/com/google/accompanist/permissions/lint/PermissionsLaunchDetector.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @file:Suppress("UnstableApiUsage") 18 | 19 | package com.google.accompanist.permissions.lint 20 | 21 | import com.android.tools.lint.detector.api.Category 22 | import com.android.tools.lint.detector.api.Detector 23 | import com.android.tools.lint.detector.api.Implementation 24 | import com.android.tools.lint.detector.api.Issue 25 | import com.android.tools.lint.detector.api.JavaContext 26 | import com.android.tools.lint.detector.api.Scope 27 | import com.android.tools.lint.detector.api.Severity 28 | import com.android.tools.lint.detector.api.SourceCodeScanner 29 | import com.google.accompanist.permissions.lint.util.Name 30 | import com.google.accompanist.permissions.lint.util.Package 31 | import com.google.accompanist.permissions.lint.util.PackageName 32 | import com.google.accompanist.permissions.lint.util.isInvokedWithinComposable 33 | import com.intellij.psi.PsiJavaFile 34 | import com.intellij.psi.PsiMethod 35 | import org.jetbrains.uast.UCallExpression 36 | import java.util.EnumSet 37 | 38 | /** 39 | * [Detector] that checks `PermissionState.launchPermissionRequest` and 40 | * `MultiplePermissionsState.launchMultiplePermissionRequest` calls to make sure they don't happen 41 | * inside the body of a composable function / lambda. 42 | */ 43 | public class PermissionsLaunchDetector : Detector(), SourceCodeScanner { 44 | 45 | override fun getApplicableMethodNames(): List = listOf( 46 | LaunchPermissionRequest.shortName, LaunchMultiplePermissionsRequest.shortName 47 | ) 48 | 49 | override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { 50 | if (!method.isInPackageName(PermissionsPackageName)) return 51 | 52 | if (node.isInvokedWithinComposable()) { 53 | context.report( 54 | PermissionLaunchedDuringComposition, 55 | node, 56 | context.getNameLocation(node), 57 | "Calls to ${method.name} should happen inside a regular lambda or " + 58 | " a side-effect, but never in the Composition." 59 | ) 60 | } 61 | } 62 | 63 | public companion object { 64 | public val PermissionLaunchedDuringComposition: Issue = Issue.create( 65 | "PermissionLaunchedDuringComposition", 66 | "Calls to `launchPermissionRequest` or `launchMultiplePermissionRequest` " + 67 | "should happen inside a regular lambda or a side-effect but never in the " + 68 | "Composition.", 69 | "Calls to `launchPermissionRequest` or `launchMultiplePermissionRequest` " + 70 | "in the Composition throw a runtime exception. Please call them inside a regular " + 71 | "lambda or in a side-effect.", 72 | Category.CORRECTNESS, 3, Severity.ERROR, 73 | Implementation( 74 | PermissionsLaunchDetector::class.java, 75 | EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) 76 | ) 77 | ) 78 | } 79 | } 80 | 81 | /** 82 | * Returns whether [this] has [packageName] as its package name. 83 | */ 84 | private fun PsiMethod.isInPackageName(packageName: PackageName): Boolean = 85 | packageName.javaPackageName == (containingFile as? PsiJavaFile)?.packageName 86 | 87 | private val PermissionsPackageName = Package("com.google.accompanist.permissions") 88 | private val LaunchPermissionRequest = 89 | Name(PermissionsPackageName, "launchPermissionRequest") 90 | private val LaunchMultiplePermissionsRequest = 91 | Name(PermissionsPackageName, "launchMultiplePermissionRequest") 92 | -------------------------------------------------------------------------------- /permissions-lint/src/main/java/com/google/accompanist/permissions/lint/util/PsiUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions.lint.util 18 | 19 | import com.intellij.psi.PsiClass 20 | import com.intellij.psi.PsiClassOwner 21 | import com.intellij.psi.PsiMethod 22 | import com.intellij.psi.PsiType 23 | import com.intellij.psi.util.InheritanceUtil 24 | 25 | /** 26 | * File copied from 27 | * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/lint/common/src/main/java/androidx/compose/lint/PsiUtils.kt 28 | */ 29 | 30 | /** Returns whether [this] has [packageName] as its package name. */ 31 | fun PsiMethod.isInPackageName(packageName: PackageName): Boolean { 32 | val actual = (containingFile as? PsiClassOwner)?.packageName 33 | return packageName.javaPackageName == actual 34 | } 35 | 36 | /** Whether this [PsiMethod] returns Unit */ 37 | val PsiMethod.returnsUnit 38 | get() = returnType.isVoidOrUnit 39 | 40 | /** 41 | * Whether this [PsiType] is `void` or [Unit] 42 | * 43 | * In Kotlin 1.6 some expressions now explicitly return [Unit] instead of just being [PsiType.VOID], 44 | * so this returns whether this type is either. 45 | */ 46 | val PsiType?.isVoidOrUnit 47 | get() = this == PsiType.VOID || this?.canonicalText == "kotlin.Unit" 48 | 49 | /** @return whether [this] inherits from [name]. Returns `true` if [this] _is_ directly [name]. */ 50 | fun PsiType.inheritsFrom(name: Name) = InheritanceUtil.isInheritor(this, name.javaFqn) 51 | 52 | /** @return whether [this] inherits from [name]. Returns `true` if [this] _is_ directly [name]. */ 53 | fun PsiClass.inheritsFrom(name: Name) = InheritanceUtil.isInheritor(this, name.javaFqn) 54 | -------------------------------------------------------------------------------- /permissions-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 The Android Open Source Project 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | com.google.accompanist.permissions.lint.PermissionsIssueRegistry 18 | -------------------------------------------------------------------------------- /permissions/README.md: -------------------------------------------------------------------------------- 1 | # Permissions for Jetpack Compose 2 | 3 | [![Maven Central](https://img.shields.io/maven-central/v/com.google.accompanist/accompanist-permissions)](https://search.maven.org/search?q=g:com.google.accompanist) 4 | 5 | For more information, visit the documentation: https://google.github.io/accompanist/permissions 6 | 7 | ## Download 8 | 9 | ```groovy 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | implementation "com.google.accompanist:accompanist-permissions:" 16 | } 17 | ``` 18 | 19 | Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. These are updated on every commit. 20 | 21 | [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/google/accompanist/accompanist-permissions/ -------------------------------------------------------------------------------- /permissions/api/current.api: -------------------------------------------------------------------------------- 1 | // Signature format: 4.0 2 | package com.google.accompanist.permissions { 3 | 4 | @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 { 5 | } 6 | 7 | @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public interface MultiplePermissionsState { 8 | method public boolean getAllPermissionsGranted(); 9 | method public java.util.List getPermissions(); 10 | method public java.util.List getRevokedPermissions(); 11 | method public boolean getShouldShowRationale(); 12 | method public void launchMultiplePermissionRequest(); 13 | property public abstract boolean allPermissionsGranted; 14 | property public abstract java.util.List permissions; 15 | property public abstract java.util.List revokedPermissions; 16 | property public abstract boolean shouldShowRationale; 17 | } 18 | 19 | public final class MultiplePermissionsStateKt { 20 | method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.MultiplePermissionsState rememberMultiplePermissionsState(java.util.List permissions, optional kotlin.jvm.functions.Function1,kotlin.Unit> onPermissionsResult); 21 | method @androidx.compose.runtime.Composable @com.google.accompanist.permissions.ExperimentalPermissionsApi public static com.google.accompanist.permissions.MultiplePermissionsState rememberMultiplePermissionsState(java.util.List permissions, optional kotlin.jvm.functions.Function1,kotlin.Unit> onPermissionsResult, optional java.util.Map previewPermissionStatuses); 22 | } 23 | 24 | @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public interface PermissionState { 25 | method public String getPermission(); 26 | method public com.google.accompanist.permissions.PermissionStatus getStatus(); 27 | method public void launchPermissionRequest(); 28 | property public abstract String permission; 29 | property public abstract com.google.accompanist.permissions.PermissionStatus status; 30 | } 31 | 32 | public final class PermissionStateKt { 33 | 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 onPermissionResult); 34 | 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 onPermissionResult, optional com.google.accompanist.permissions.PermissionStatus previewPermissionStatus); 35 | } 36 | 37 | @androidx.compose.runtime.Stable @com.google.accompanist.permissions.ExperimentalPermissionsApi public sealed interface PermissionStatus { 38 | } 39 | 40 | public static final class PermissionStatus.Denied implements com.google.accompanist.permissions.PermissionStatus { 41 | ctor public PermissionStatus.Denied(boolean shouldShowRationale); 42 | method public boolean component1(); 43 | method public com.google.accompanist.permissions.PermissionStatus.Denied copy(boolean shouldShowRationale); 44 | method public boolean getShouldShowRationale(); 45 | property public final boolean shouldShowRationale; 46 | } 47 | 48 | public static final class PermissionStatus.Granted implements com.google.accompanist.permissions.PermissionStatus { 49 | field public static final com.google.accompanist.permissions.PermissionStatus.Granted INSTANCE; 50 | } 51 | 52 | public final class PermissionsUtilKt { 53 | method public static boolean getShouldShowRationale(com.google.accompanist.permissions.PermissionStatus); 54 | method public static boolean isGranted(com.google.accompanist.permissions.PermissionStatus); 55 | } 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /permissions/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("UnstableApiUsage") 17 | 18 | plugins { 19 | alias(libs.plugins.accompanist.android.library) 20 | alias(libs.plugins.accompanist.android.library.compose) 21 | alias(libs.plugins.accompanist.android.library.published) 22 | } 23 | 24 | android { 25 | namespace = "com.google.accompanist.permissions" 26 | 27 | defaultConfig { 28 | // The following argument makes the Android Test Orchestrator run its 29 | // "pm clear" command after each test invocation. This command ensures 30 | // that the app's state is completely cleared between tests. 31 | testInstrumentationRunnerArguments["clearPackageData"] = "true" 32 | } 33 | 34 | testOptions { 35 | execution = "ANDROIDX_TEST_ORCHESTRATOR" 36 | } 37 | } 38 | 39 | dependencies { 40 | implementation(libs.androidx.activity.compose) 41 | implementation(libs.compose.foundation.foundation) 42 | implementation(libs.kotlin.coroutines.android) 43 | 44 | lintChecks(project(":permissions-lint")) 45 | lintPublish(project(":permissions-lint")) 46 | 47 | // ====================== 48 | // Test dependencies 49 | // ====================== 50 | 51 | androidTestUtil(libs.androidx.test.orchestrator) 52 | 53 | androidTestImplementation(project(":internal-testutils")) 54 | androidTestImplementation(libs.androidx.activity.compose) 55 | androidTestImplementation(libs.compose.material.material) 56 | 57 | androidTestImplementation(libs.junit) 58 | androidTestImplementation(libs.truth) 59 | 60 | androidTestImplementation(libs.compose.ui.test.junit4) 61 | androidTestImplementation(libs.compose.ui.test.manifest) 62 | androidTestImplementation(libs.compose.foundation.foundation) 63 | androidTestImplementation(libs.androidx.test.runner) 64 | androidTestImplementation(libs.androidx.test.rules) 65 | androidTestImplementation(libs.androidx.test.uiAutomator) 66 | } 67 | -------------------------------------------------------------------------------- /permissions/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=accompanist-permissions 2 | POM_NAME=Accompanist Permissions 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /permissions/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /permissions/src/androidTest/java/com/google/accompanist/permissions/FakeTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions 18 | 19 | import androidx.test.filters.SdkSuppress 20 | import org.junit.Test 21 | 22 | /** 23 | * Fake tests to avoid the "No tests found error" when running in Build.VERSION.SDK_INT < 23 24 | */ 25 | class FakeTests { 26 | 27 | @SdkSuppress(maxSdkVersion = 22) 28 | @Test 29 | fun fakeTestToAvoidNoTestsFoundErrorInAPI22AndBelow() = Unit 30 | 31 | // More Fake tests to help with sharding: https://github.com/android/android-test/issues/973 32 | @Test 33 | fun fake1() = Unit 34 | 35 | @Test 36 | fun fake2() = Unit 37 | 38 | @Test 39 | fun fake3() = Unit 40 | 41 | @Test 42 | fun fake4() = Unit 43 | 44 | @Test 45 | fun fake5() = Unit 46 | 47 | @Test 48 | fun fake6() = Unit 49 | 50 | @Test 51 | fun fake7() = Unit 52 | 53 | @Test 54 | fun fake8() = Unit 55 | 56 | @Test 57 | fun fake9() = Unit 58 | } 59 | -------------------------------------------------------------------------------- /permissions/src/androidTest/java/com/google/accompanist/permissions/MultiplePermissionsStateTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions 18 | 19 | import androidx.compose.ui.test.junit4.createAndroidComposeRule 20 | import androidx.test.filters.SdkSuppress 21 | import androidx.test.rule.GrantPermissionRule 22 | import com.google.accompanist.permissions.test.EmptyPermissionsTestActivity 23 | import com.google.common.truth.Truth.assertThat 24 | import org.junit.Rule 25 | import org.junit.Test 26 | 27 | /** 28 | * Simple tests that prove the data comes from the right place 29 | */ 30 | @OptIn(ExperimentalPermissionsApi::class) 31 | @SdkSuppress(minSdkVersion = 23) 32 | class MultiplePermissionsStateTest { 33 | 34 | @get:Rule 35 | val composeTestRule = createAndroidComposeRule() 36 | 37 | @get:Rule 38 | val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( 39 | "android.permission.CAMERA", 40 | "android.permission.ACCESS_FINE_LOCATION" 41 | ) 42 | 43 | @Test 44 | fun permissionState_hasPermission() { 45 | composeTestRule.setContent { 46 | val state = rememberMultiplePermissionsState( 47 | listOf( 48 | android.Manifest.permission.ACCESS_FINE_LOCATION, 49 | android.Manifest.permission.CAMERA 50 | ) 51 | ) 52 | 53 | assertThat(state.allPermissionsGranted).isTrue() 54 | assertThat(state.shouldShowRationale).isFalse() 55 | } 56 | } 57 | 58 | @Test 59 | fun permissionTest_shouldShowRationale() { 60 | composeTestRule.activity.shouldShowRequestPermissionRationale = mapOf( 61 | android.Manifest.permission.WRITE_EXTERNAL_STORAGE to true 62 | ) 63 | 64 | composeTestRule.setContent { 65 | val state = rememberMultiplePermissionsState( 66 | listOf( 67 | android.Manifest.permission.WRITE_EXTERNAL_STORAGE, 68 | android.Manifest.permission.ACCESS_FINE_LOCATION, 69 | android.Manifest.permission.CAMERA 70 | ) 71 | ) 72 | 73 | assertThat(state.allPermissionsGranted).isFalse() 74 | assertThat(state.shouldShowRationale).isTrue() 75 | assertThat(state.permissions).hasSize(3) 76 | assertThat(state.revokedPermissions).hasSize(1) 77 | assertThat(state.revokedPermissions[0].permission) 78 | .isEqualTo("android.permission.WRITE_EXTERNAL_STORAGE") 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /permissions/src/androidTest/java/com/google/accompanist/permissions/PermissionStateTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions 18 | 19 | import androidx.compose.ui.test.junit4.createAndroidComposeRule 20 | import androidx.test.filters.SdkSuppress 21 | import androidx.test.rule.GrantPermissionRule 22 | import com.google.accompanist.permissions.test.EmptyPermissionsTestActivity 23 | import com.google.common.truth.Truth.assertThat 24 | import org.junit.Rule 25 | import org.junit.Test 26 | 27 | /** 28 | * Simple tests that prove the data comes from the right place 29 | */ 30 | @OptIn(ExperimentalPermissionsApi::class) 31 | @SdkSuppress(minSdkVersion = 23) 32 | class PermissionStateTest { 33 | 34 | @get:Rule 35 | val composeTestRule = createAndroidComposeRule() 36 | 37 | @get:Rule 38 | val permissionRule: GrantPermissionRule = 39 | GrantPermissionRule.grant("android.permission.CAMERA") 40 | 41 | @Test 42 | fun permissionState_hasPermission() { 43 | composeTestRule.setContent { 44 | val state = rememberPermissionState(android.Manifest.permission.CAMERA) 45 | assertThat(state.status.isGranted).isTrue() 46 | assertThat(state.status.shouldShowRationale).isFalse() 47 | } 48 | } 49 | 50 | @Test 51 | fun permissionTest_shouldShowRationale() { 52 | val permission = android.Manifest.permission.ACCESS_FINE_LOCATION 53 | composeTestRule.activity.shouldShowRequestPermissionRationale = mapOf( 54 | permission to true 55 | ) 56 | 57 | composeTestRule.setContent { 58 | val state = rememberPermissionState(permission) 59 | 60 | assertThat(state.status.isGranted).isFalse() 61 | assertThat(state.status.shouldShowRationale).isTrue() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /permissions/src/androidTest/java/com/google/accompanist/permissions/test/EmptyPermissionsTestActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions.test 18 | 19 | import androidx.activity.ComponentActivity 20 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 21 | 22 | @OptIn(ExperimentalPermissionsApi::class) 23 | class EmptyPermissionsTestActivity : ComponentActivity() { 24 | 25 | var shouldShowRequestPermissionRationale: Map = emptyMap() 26 | 27 | override fun shouldShowRequestPermissionRationale(permission: String): Boolean { 28 | if (permission in shouldShowRequestPermissionRationale.keys) { 29 | return shouldShowRequestPermissionRationale[permission]!! 30 | } 31 | return super.shouldShowRequestPermissionRationale(permission) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /permissions/src/androidTest/java/com/google/accompanist/permissions/test/PermissionsTestActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions.test 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import androidx.compose.foundation.layout.Column 23 | import androidx.compose.material.Button 24 | import androidx.compose.material.Text 25 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 26 | import com.google.accompanist.permissions.PermissionStatus 27 | import com.google.accompanist.permissions.rememberPermissionState 28 | import com.google.accompanist.permissions.shouldShowRationale 29 | 30 | @OptIn(ExperimentalPermissionsApi::class) 31 | class PermissionsTestActivity : ComponentActivity() { 32 | 33 | var shouldShowRequestPermissionRationale: Map = emptyMap() 34 | 35 | override fun shouldShowRequestPermissionRationale(permission: String): Boolean { 36 | if (permission in shouldShowRequestPermissionRationale.keys) { 37 | return shouldShowRequestPermissionRationale[permission]!! 38 | } 39 | return super.shouldShowRequestPermissionRationale(permission) 40 | } 41 | 42 | /** 43 | * Code used in `MultipleAndSinglePermissionsTest` 44 | */ 45 | override fun onCreate(savedInstanceState: Bundle?) { 46 | super.onCreate(savedInstanceState) 47 | setContent { 48 | Column { 49 | Text("PermissionsTestActivity") 50 | 51 | val state = rememberPermissionState(android.Manifest.permission.CAMERA,) 52 | when (state.status) { 53 | PermissionStatus.Granted -> { 54 | Text("Granted") 55 | } 56 | is PermissionStatus.Denied -> { 57 | val textToShow = if (state.status.shouldShowRationale) { 58 | "ShowRationale" 59 | } else { 60 | "No permission" 61 | } 62 | 63 | Text(textToShow) 64 | Button(onClick = { state.launchPermissionRequest() }) { 65 | Text("Request") 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /permissions/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 20 | -------------------------------------------------------------------------------- /permissions/src/main/java/com/google/accompanist/permissions/MutablePermissionState.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions 18 | 19 | import android.app.Activity 20 | import android.content.Context 21 | import androidx.activity.compose.rememberLauncherForActivityResult 22 | import androidx.activity.result.ActivityResultLauncher 23 | import androidx.activity.result.contract.ActivityResultContracts 24 | import androidx.compose.runtime.Composable 25 | import androidx.compose.runtime.DisposableEffect 26 | import androidx.compose.runtime.Stable 27 | import androidx.compose.runtime.getValue 28 | import androidx.compose.runtime.mutableStateOf 29 | import androidx.compose.runtime.remember 30 | import androidx.compose.runtime.setValue 31 | import androidx.compose.ui.platform.LocalContext 32 | 33 | /** 34 | * Creates a [MutablePermissionState] that is remembered across compositions. 35 | * 36 | * It's recommended that apps exercise the permissions workflow as described in the 37 | * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions). 38 | * 39 | * @param permission the permission to control and observe. 40 | * @param onPermissionResult will be called with whether or not the user granted the permission 41 | * after [PermissionState.launchPermissionRequest] is called. 42 | */ 43 | @ExperimentalPermissionsApi 44 | @Composable 45 | internal fun rememberMutablePermissionState( 46 | permission: String, 47 | onPermissionResult: (Boolean) -> Unit = {} 48 | ): MutablePermissionState { 49 | val context = LocalContext.current 50 | val permissionState = remember(permission) { 51 | MutablePermissionState(permission, context, context.findActivity()) 52 | } 53 | 54 | // Refresh the permission status when the lifecycle is resumed 55 | PermissionLifecycleCheckerEffect(permissionState) 56 | 57 | // Remember RequestPermission launcher and assign it to permissionState 58 | val launcher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { 59 | permissionState.refreshPermissionStatus() 60 | onPermissionResult(it) 61 | } 62 | DisposableEffect(permissionState, launcher) { 63 | permissionState.launcher = launcher 64 | onDispose { 65 | permissionState.launcher = null 66 | } 67 | } 68 | 69 | return permissionState 70 | } 71 | 72 | /** 73 | * A mutable state object that can be used to control and observe permission status changes. 74 | * 75 | * In most cases, this will be created via [rememberMutablePermissionState]. 76 | * 77 | * @param permission the permission to control and observe. 78 | * @param context to check the status of the [permission]. 79 | * @param activity to check if the user should be presented with a rationale for [permission]. 80 | */ 81 | @ExperimentalPermissionsApi 82 | @Stable 83 | internal class MutablePermissionState( 84 | override val permission: String, 85 | private val context: Context, 86 | private val activity: Activity 87 | ) : PermissionState { 88 | 89 | override var status: PermissionStatus by mutableStateOf(getPermissionStatus()) 90 | 91 | override fun launchPermissionRequest() { 92 | launcher?.launch( 93 | permission 94 | ) ?: throw IllegalStateException("ActivityResultLauncher cannot be null") 95 | } 96 | 97 | internal var launcher: ActivityResultLauncher? = null 98 | 99 | internal fun refreshPermissionStatus() { 100 | status = getPermissionStatus() 101 | } 102 | 103 | private fun getPermissionStatus(): PermissionStatus { 104 | val hasPermission = context.checkPermission(permission) 105 | return if (hasPermission) { 106 | PermissionStatus.Granted 107 | } else { 108 | PermissionStatus.Denied(activity.shouldShowRationale(permission)) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /permissions/src/main/java/com/google/accompanist/permissions/PermissionState.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.permissions 18 | 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.Immutable 21 | import androidx.compose.runtime.Stable 22 | import androidx.compose.ui.platform.LocalInspectionMode 23 | 24 | /** 25 | * Creates a [PermissionState] that is remembered across compositions. 26 | * 27 | * It's recommended that apps exercise the permissions workflow as described in the 28 | * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions). 29 | * 30 | * @param permission the permission to control and observe. 31 | * @param onPermissionResult will be called with whether or not the user granted the permission 32 | * after [PermissionState.launchPermissionRequest] is called. 33 | */ 34 | @ExperimentalPermissionsApi 35 | @Composable 36 | public fun rememberPermissionState( 37 | permission: String, 38 | onPermissionResult: (Boolean) -> Unit = {} 39 | ): PermissionState { 40 | return rememberPermissionState(permission, onPermissionResult, PermissionStatus.Granted) 41 | } 42 | 43 | /** 44 | * Creates a [PermissionState] that is remembered across compositions. 45 | * 46 | * It's recommended that apps exercise the permissions workflow as described in the 47 | * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions). 48 | * 49 | * @param permission the permission to control and observe. 50 | * @param onPermissionResult will be called with whether or not the user granted the permission 51 | * after [PermissionState.launchPermissionRequest] is called. 52 | * @param previewPermissionStatus provides a [PermissionStatus] when running in a preview. 53 | */ 54 | @ExperimentalPermissionsApi 55 | @Composable 56 | public fun rememberPermissionState( 57 | permission: String, 58 | onPermissionResult: (Boolean) -> Unit = {}, 59 | previewPermissionStatus: PermissionStatus = PermissionStatus.Granted 60 | ): PermissionState { 61 | return when { 62 | LocalInspectionMode.current -> PreviewPermissionState(permission, previewPermissionStatus) 63 | else -> rememberMutablePermissionState(permission, onPermissionResult) 64 | } 65 | } 66 | 67 | /** 68 | * A state object that can be hoisted to control and observe [permission] status changes. 69 | * 70 | * In most cases, this will be created via [rememberPermissionState]. 71 | * 72 | * It's recommended that apps exercise the permissions workflow as described in the 73 | * [documentation](https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions). 74 | */ 75 | @ExperimentalPermissionsApi 76 | @Stable 77 | public interface PermissionState { 78 | 79 | /** 80 | * The permission to control and observe. 81 | */ 82 | public val permission: String 83 | 84 | /** 85 | * [permission]'s status 86 | */ 87 | public val status: PermissionStatus 88 | 89 | /** 90 | * Request the [permission] to the user. 91 | * 92 | * This should always be triggered from non-composable scope, for example, from a side-effect 93 | * or a non-composable callback. Otherwise, this will result in an IllegalStateException. 94 | * 95 | * This triggers a system dialog that asks the user to grant or revoke the permission. 96 | * Note that this dialog might not appear on the screen if the user doesn't want to be asked 97 | * again or has denied the permission multiple times. 98 | * This behavior varies depending on the Android level API. 99 | */ 100 | public fun launchPermissionRequest(): Unit 101 | } 102 | 103 | @OptIn(ExperimentalPermissionsApi::class) 104 | @Immutable 105 | internal class PreviewPermissionState( 106 | override val permission: String, 107 | override val status: PermissionStatus 108 | ) : PermissionState { 109 | override fun launchPermissionRequest() {} 110 | } 111 | -------------------------------------------------------------------------------- /release/secring.gpg.aes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/release/secring.gpg.aes -------------------------------------------------------------------------------- /release/signing-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2021 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | rm -f release/*.gpg 18 | rm -f release/*.properties 19 | -------------------------------------------------------------------------------- /release/signing-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2021 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ENCRYPT_KEY=$1 18 | 19 | if [[ ! -z "$ENCRYPT_KEY" ]]; then 20 | # Decrypt GnuPG keyring 21 | openssl aes-256-cbc -md sha256 -d -in release/secring.gpg.aes -out release/secring.gpg -k ${ENCRYPT_KEY} 22 | 23 | # Decrypt Play Store key 24 | openssl aes-256-cbc -md sha256 -d -in release/signing.properties.aes -out release/signing.properties -k ${ENCRYPT_KEY} 25 | 26 | else 27 | echo "ENCRYPT_KEY is empty" 28 | fi 29 | -------------------------------------------------------------------------------- /release/signing.properties.aes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/release/signing.properties.aes -------------------------------------------------------------------------------- /sample/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("UnstableApiUsage") 17 | 18 | plugins { 19 | alias(libs.plugins.android.application) 20 | alias(libs.plugins.android.kotlin) 21 | alias(libs.plugins.compose.plugin) 22 | } 23 | 24 | android { 25 | compileSdk = 35 26 | 27 | defaultConfig { 28 | applicationId = "com.google.accompanist.sample" 29 | minSdk = 21 30 | targetSdk = 35 31 | 32 | versionCode = 1 33 | versionName = "1.0" 34 | 35 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 36 | } 37 | 38 | compileOptions { 39 | sourceCompatibility = JavaVersion.VERSION_17 40 | targetCompatibility = JavaVersion.VERSION_17 41 | 42 | isCoreLibraryDesugaringEnabled = true 43 | } 44 | 45 | buildFeatures { 46 | compose = true 47 | } 48 | 49 | buildTypes { 50 | getByName("release") { 51 | signingConfig = signingConfigs.getByName("debug") 52 | } 53 | } 54 | 55 | kotlinOptions { 56 | jvmTarget = "17" 57 | } 58 | 59 | namespace = "com.google.accompanist.sample" 60 | } 61 | 62 | dependencies { 63 | coreLibraryDesugaring(libs.desugar.jdk.libs) 64 | 65 | implementation(project(":adaptive")) 66 | implementation(project(":drawablepainter")) 67 | implementation(project(":permissions")) 68 | 69 | implementation(libs.compose.material.iconsext) 70 | implementation(libs.compose.material3.material3) 71 | implementation(libs.compose.foundation.layout) 72 | debugImplementation(libs.compose.ui.tooling) 73 | implementation(libs.compose.ui.tooling.preview) 74 | implementation(libs.compose.ui.util) 75 | 76 | implementation(libs.androidx.core) 77 | implementation(libs.androidx.activity.compose) 78 | implementation(libs.androidx.lifecycle.runtime) 79 | 80 | implementation(libs.kotlin.stdlib) 81 | 82 | lintChecks(project(":permissions-lint")) 83 | } 84 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/ImageLoadingSampleUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample 18 | 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.runtime.remember 21 | 22 | private val rangeForRandom = (0..100000) 23 | 24 | fun randomSampleImageUrl( 25 | seed: Int = rangeForRandom.random(), 26 | width: Int = 300, 27 | height: Int = width, 28 | ): String { 29 | return "https://picsum.photos/seed/$seed/$width/$height" 30 | } 31 | 32 | /** 33 | * Remember a URL generate by [randomSampleImageUrl]. 34 | */ 35 | @Composable 36 | fun rememberRandomSampleImageUrl( 37 | seed: Int = rangeForRandom.random(), 38 | width: Int = 300, 39 | height: Int = width, 40 | ): String = remember { randomSampleImageUrl(seed, width, height) } 41 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample 18 | 19 | import android.annotation.SuppressLint 20 | import android.content.Intent 21 | import android.os.Bundle 22 | import androidx.activity.ComponentActivity 23 | import androidx.activity.compose.setContent 24 | import androidx.activity.enableEdgeToEdge 25 | 26 | /** 27 | * A list which automatically populates the list of sample activities in this app 28 | * with the category `com.google.accompanist.sample.SAMPLE_CODE`. 29 | */ 30 | class MainActivity : ComponentActivity() { 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | 34 | enableEdgeToEdge() 35 | val data = getData(intent.getStringExtra(EXTRA_PATH)) 36 | 37 | setContent { 38 | AccompanistSampleTheme { 39 | MainScreen( 40 | listData = data, 41 | onItemClick = { startActivity(it) } 42 | ) 43 | } 44 | } 45 | } 46 | 47 | private fun getData(prefix: String?): List { 48 | val myData = mutableListOf() 49 | 50 | val mainIntent = Intent(Intent.ACTION_MAIN, null) 51 | mainIntent.addCategory("com.google.accompanist.sample.SAMPLE_CODE") 52 | 53 | @SuppressLint("QueryPermissionsNeeded") // Only querying our own Activities 54 | val list = packageManager.queryIntentActivities(mainIntent, 0) 55 | 56 | val prefixPath: Array? 57 | var prefixWithSlash = prefix 58 | 59 | if (prefix.isNullOrEmpty()) { 60 | prefixPath = null 61 | } else { 62 | prefixPath = prefix.split("/".toRegex()).toTypedArray() 63 | prefixWithSlash = "$prefix/" 64 | } 65 | 66 | val entries = mutableMapOf() 67 | 68 | list.forEach { info -> 69 | val labelSeq = info.loadLabel(packageManager) 70 | val label = labelSeq?.toString() ?: info.activityInfo.name 71 | 72 | if (prefixWithSlash.isNullOrEmpty() || label.startsWith(prefixWithSlash)) { 73 | val labelPath = label.split("/".toRegex()).toTypedArray() 74 | val nextLabel = if (prefixPath == null) labelPath[0] else labelPath[prefixPath.size] 75 | if ((prefixPath?.size ?: 0) == labelPath.size - 1) { 76 | myData.add( 77 | AccompanistSample( 78 | title = nextLabel, 79 | intent = activityIntent( 80 | info.activityInfo.applicationInfo.packageName, 81 | info.activityInfo.name 82 | ) 83 | ) 84 | ) 85 | } else { 86 | if (entries[nextLabel] == null) { 87 | myData.add( 88 | AccompanistSample( 89 | title = nextLabel, 90 | intent = browseIntent( 91 | if (prefix == "") nextLabel else "$prefix/$nextLabel" 92 | ) 93 | ) 94 | ) 95 | entries[nextLabel] = true 96 | } 97 | } 98 | } 99 | } 100 | 101 | myData.sortBy { it.title } 102 | 103 | return myData 104 | } 105 | 106 | private fun activityIntent(pkg: String, componentName: String): Intent { 107 | val result = Intent() 108 | result.setClassName(pkg, componentName) 109 | return result 110 | } 111 | 112 | private fun browseIntent(path: String): Intent { 113 | val result = Intent() 114 | result.setClass(this, MainActivity::class.java) 115 | result.putExtra(EXTRA_PATH, path) 116 | return result 117 | } 118 | } 119 | 120 | private const val EXTRA_PATH = "com.example.android.apis.Path" 121 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/MainScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample 18 | 19 | import android.content.Intent 20 | import androidx.compose.foundation.clickable 21 | import androidx.compose.foundation.layout.Column 22 | import androidx.compose.foundation.layout.Spacer 23 | import androidx.compose.foundation.layout.WindowInsets 24 | import androidx.compose.foundation.layout.WindowInsetsSides 25 | import androidx.compose.foundation.layout.fillMaxSize 26 | import androidx.compose.foundation.layout.only 27 | import androidx.compose.foundation.layout.safeDrawing 28 | import androidx.compose.foundation.layout.systemBars 29 | import androidx.compose.foundation.layout.windowInsetsBottomHeight 30 | import androidx.compose.foundation.layout.windowInsetsPadding 31 | import androidx.compose.foundation.lazy.LazyColumn 32 | import androidx.compose.foundation.lazy.items 33 | import androidx.compose.material3.ExperimentalMaterial3Api 34 | import androidx.compose.material3.ListItem 35 | import androidx.compose.material3.Surface 36 | import androidx.compose.material3.Text 37 | import androidx.compose.material3.TopAppBar 38 | import androidx.compose.material3.TopAppBarDefaults 39 | import androidx.compose.material3.rememberTopAppBarState 40 | import androidx.compose.runtime.Composable 41 | import androidx.compose.ui.Modifier 42 | import androidx.compose.ui.input.nestedscroll.nestedScroll 43 | import androidx.compose.ui.res.stringResource 44 | 45 | data class AccompanistSample( 46 | val title: String, 47 | val intent: Intent 48 | ) 49 | 50 | @OptIn(ExperimentalMaterial3Api::class) 51 | @Composable 52 | fun MainScreen( 53 | listData: List, 54 | onItemClick: (Intent) -> Unit, 55 | modifier: Modifier = Modifier 56 | ) { 57 | Surface(modifier) { 58 | Column { 59 | val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) 60 | TopAppBar( 61 | title = { Text(stringResource(R.string.app_name)) }, 62 | scrollBehavior = scrollBehavior 63 | ) 64 | 65 | ContentList( 66 | listData, 67 | onItemClick, 68 | modifier = Modifier 69 | .fillMaxSize() 70 | .windowInsetsPadding( 71 | WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal) 72 | ) 73 | .nestedScroll(scrollBehavior.nestedScrollConnection) 74 | ) 75 | } 76 | } 77 | } 78 | 79 | @OptIn(ExperimentalMaterial3Api::class) 80 | @Composable 81 | private fun ContentList( 82 | listData: List, 83 | onItemClick: (Intent) -> Unit, 84 | modifier: Modifier = Modifier 85 | ) { 86 | LazyColumn( 87 | modifier = modifier 88 | ) { 89 | items(listData) { 90 | ListItem( 91 | headlineText = { Text(it.title) }, 92 | modifier = Modifier.clickable { onItemClick(it.intent) } 93 | ) 94 | } 95 | 96 | item { 97 | Spacer( 98 | Modifier.windowInsetsBottomHeight( 99 | WindowInsets.systemBars 100 | ) 101 | ) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/Theme.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample 18 | 19 | import androidx.compose.foundation.isSystemInDarkTheme 20 | import androidx.compose.foundation.layout.Box 21 | import androidx.compose.foundation.layout.PaddingValues 22 | import androidx.compose.foundation.layout.fillMaxSize 23 | import androidx.compose.foundation.layout.padding 24 | import androidx.compose.foundation.layout.safeDrawingPadding 25 | import androidx.compose.material3.MaterialTheme 26 | import androidx.compose.material3.Surface 27 | import androidx.compose.material3.darkColorScheme 28 | import androidx.compose.material3.lightColorScheme 29 | import androidx.compose.runtime.Composable 30 | import androidx.compose.ui.Modifier 31 | import androidx.compose.ui.unit.dp 32 | 33 | @Composable 34 | fun AccompanistSample( 35 | contentPadding: PaddingValues = PaddingValues(16.dp), 36 | content: @Composable () -> Unit 37 | ) { 38 | AccompanistSampleTheme { 39 | Surface( 40 | modifier = Modifier.fillMaxSize() 41 | ) { 42 | Box( 43 | modifier = Modifier.padding(contentPadding).safeDrawingPadding(), 44 | propagateMinConstraints = true 45 | ) { 46 | content() 47 | } 48 | } 49 | } 50 | } 51 | 52 | @Composable 53 | fun AccompanistSampleTheme( 54 | darkTheme: Boolean = isSystemInDarkTheme(), 55 | content: @Composable () -> Unit 56 | ) { 57 | MaterialTheme( 58 | colorScheme = if (darkTheme) darkColorScheme() else lightColorScheme(), 59 | content = content 60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/adaptive/BasicTwoPaneSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.adaptive 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import androidx.compose.foundation.layout.Box 23 | import androidx.compose.foundation.layout.fillMaxSize 24 | import androidx.compose.foundation.layout.padding 25 | import androidx.compose.material3.Card 26 | import androidx.compose.material3.Text 27 | import androidx.compose.ui.Alignment 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.unit.dp 30 | import com.google.accompanist.adaptive.HorizontalTwoPaneStrategy 31 | import com.google.accompanist.adaptive.TwoPane 32 | import com.google.accompanist.adaptive.TwoPaneStrategy 33 | import com.google.accompanist.adaptive.VerticalTwoPaneStrategy 34 | import com.google.accompanist.adaptive.calculateDisplayFeatures 35 | import com.google.accompanist.sample.AccompanistSample 36 | 37 | class BasicTwoPaneSample : ComponentActivity() { 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContent { 41 | AccompanistSample { 42 | val displayFeatures = calculateDisplayFeatures(this) 43 | 44 | TwoPane( 45 | first = { 46 | Card( 47 | modifier = Modifier.padding(8.dp) 48 | ) { 49 | Box( 50 | contentAlignment = Alignment.Center, 51 | modifier = Modifier.fillMaxSize() 52 | ) { 53 | Text("First") 54 | } 55 | } 56 | }, 57 | second = { 58 | Card( 59 | modifier = Modifier.padding(8.dp) 60 | ) { 61 | Box( 62 | contentAlignment = Alignment.Center, 63 | modifier = Modifier.fillMaxSize() 64 | ) { 65 | Text("Second") 66 | } 67 | } 68 | }, 69 | strategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates -> 70 | // Split vertically if the height is larger than the width 71 | if (layoutCoordinates.size.height >= layoutCoordinates.size.width) { 72 | VerticalTwoPaneStrategy( 73 | splitFraction = 0.75f 74 | ) 75 | } else { 76 | HorizontalTwoPaneStrategy( 77 | splitFraction = 0.75f 78 | ) 79 | }.calculateSplitResult(density, layoutDirection, layoutCoordinates) 80 | }, 81 | displayFeatures = displayFeatures, 82 | modifier = Modifier.padding(8.dp) 83 | ) 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/adaptive/HorizontalTwoPaneSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.adaptive 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import androidx.compose.foundation.layout.Box 23 | import androidx.compose.foundation.layout.fillMaxSize 24 | import androidx.compose.foundation.layout.padding 25 | import androidx.compose.material3.Card 26 | import androidx.compose.material3.Text 27 | import androidx.compose.ui.Alignment 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.unit.dp 30 | import com.google.accompanist.adaptive.FoldAwareConfiguration 31 | import com.google.accompanist.adaptive.HorizontalTwoPaneStrategy 32 | import com.google.accompanist.adaptive.TwoPane 33 | import com.google.accompanist.adaptive.calculateDisplayFeatures 34 | import com.google.accompanist.sample.AccompanistSample 35 | 36 | class HorizontalTwoPaneSample : ComponentActivity() { 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | setContent { 40 | AccompanistSample { 41 | val displayFeatures = calculateDisplayFeatures(this) 42 | 43 | TwoPane( 44 | first = { 45 | Card( 46 | modifier = Modifier.padding(8.dp) 47 | ) { 48 | Box( 49 | contentAlignment = Alignment.Center, 50 | modifier = Modifier.fillMaxSize() 51 | ) { 52 | Text("First") 53 | } 54 | } 55 | }, 56 | second = { 57 | Card( 58 | modifier = Modifier.padding(8.dp) 59 | ) { 60 | Box( 61 | contentAlignment = Alignment.Center, 62 | modifier = Modifier.fillMaxSize() 63 | ) { 64 | Text("Second") 65 | } 66 | } 67 | }, 68 | strategy = HorizontalTwoPaneStrategy( 69 | splitFraction = 1f / 3f, 70 | ), 71 | displayFeatures = displayFeatures, 72 | foldAwareConfiguration = FoldAwareConfiguration.VerticalFoldsOnly, 73 | modifier = Modifier.padding(8.dp) 74 | ) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/adaptive/NavDrawerFoldAwareColumnSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.adaptive 18 | 19 | import android.app.Activity 20 | import android.os.Bundle 21 | import androidx.activity.ComponentActivity 22 | import androidx.activity.compose.setContent 23 | import androidx.compose.foundation.border 24 | import androidx.compose.foundation.layout.PaddingValues 25 | import androidx.compose.foundation.layout.fillMaxSize 26 | import androidx.compose.foundation.layout.padding 27 | import androidx.compose.foundation.shape.CircleShape 28 | import androidx.compose.material.icons.Icons 29 | import androidx.compose.material.icons.filled.Done 30 | import androidx.compose.material.icons.filled.Face 31 | import androidx.compose.material.icons.filled.Lock 32 | import androidx.compose.material.icons.filled.Search 33 | import androidx.compose.material.icons.filled.Star 34 | import androidx.compose.material.icons.filled.ThumbUp 35 | import androidx.compose.material.icons.filled.Warning 36 | import androidx.compose.material3.ExperimentalMaterial3Api 37 | import androidx.compose.material3.Icon 38 | import androidx.compose.material3.MaterialTheme 39 | import androidx.compose.material3.ModalDrawerSheet 40 | import androidx.compose.material3.ModalNavigationDrawer 41 | import androidx.compose.material3.NavigationDrawerItem 42 | import androidx.compose.material3.Surface 43 | import androidx.compose.material3.Text 44 | import androidx.compose.runtime.Composable 45 | import androidx.compose.runtime.getValue 46 | import androidx.compose.runtime.mutableStateOf 47 | import androidx.compose.runtime.remember 48 | import androidx.compose.runtime.setValue 49 | import androidx.compose.ui.Modifier 50 | import androidx.compose.ui.unit.dp 51 | import com.google.accompanist.adaptive.FoldAwareColumn 52 | import com.google.accompanist.adaptive.calculateDisplayFeatures 53 | import com.google.accompanist.sample.AccompanistSample 54 | 55 | class NavDrawerFoldAwareColumnSample : ComponentActivity() { 56 | override fun onCreate(savedInstanceState: Bundle?) { 57 | super.onCreate(savedInstanceState) 58 | setContent { 59 | AccompanistSample(contentPadding = PaddingValues(all = 0.dp)) { 60 | NavDrawerExample(this) 61 | } 62 | } 63 | } 64 | } 65 | 66 | @OptIn(ExperimentalMaterial3Api::class) 67 | @Composable 68 | fun NavDrawerExample(activity: Activity) { 69 | val icons = listOf( 70 | Icons.Default.Done, 71 | Icons.Default.Face, 72 | Icons.Default.Lock, 73 | Icons.Default.Search, 74 | Icons.Default.ThumbUp, 75 | Icons.Default.Warning, 76 | Icons.Default.Star 77 | ) 78 | 79 | var selectedIcon by remember { mutableStateOf(icons[0]) } 80 | 81 | ModalNavigationDrawer( 82 | drawerContent = { 83 | ModalDrawerSheet { 84 | FoldAwareColumn( 85 | displayFeatures = calculateDisplayFeatures(activity), 86 | foldPadding = PaddingValues(vertical = 10.dp) 87 | ) { 88 | icons.forEach { 89 | NavigationDrawerItem( 90 | modifier = Modifier 91 | .padding(5.dp) 92 | .border(2.dp, MaterialTheme.colorScheme.primary, CircleShape), 93 | icon = { Icon(imageVector = it, contentDescription = it.name) }, 94 | label = { Text(it.name.substringAfter('.')) }, 95 | selected = it == selectedIcon, 96 | onClick = { selectedIcon = it } 97 | ) 98 | } 99 | } 100 | } 101 | }, 102 | content = { Surface(modifier = Modifier.fillMaxSize()) {} } 103 | ) 104 | } 105 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/adaptive/NavRailFoldAwareColumnSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.adaptive 18 | 19 | import android.app.Activity 20 | import android.os.Bundle 21 | import androidx.activity.ComponentActivity 22 | import androidx.activity.compose.setContent 23 | import androidx.compose.foundation.border 24 | import androidx.compose.foundation.layout.Row 25 | import androidx.compose.foundation.layout.fillMaxSize 26 | import androidx.compose.foundation.layout.padding 27 | import androidx.compose.material.icons.Icons 28 | import androidx.compose.material.icons.filled.Done 29 | import androidx.compose.material.icons.filled.Face 30 | import androidx.compose.material.icons.filled.Lock 31 | import androidx.compose.material.icons.filled.Search 32 | import androidx.compose.material.icons.filled.Star 33 | import androidx.compose.material.icons.filled.ThumbUp 34 | import androidx.compose.material.icons.filled.Warning 35 | import androidx.compose.material3.Icon 36 | import androidx.compose.material3.MaterialTheme 37 | import androidx.compose.material3.NavigationRail 38 | import androidx.compose.material3.NavigationRailItem 39 | import androidx.compose.material3.Surface 40 | import androidx.compose.material3.Text 41 | import androidx.compose.runtime.Composable 42 | import androidx.compose.runtime.getValue 43 | import androidx.compose.runtime.mutableStateOf 44 | import androidx.compose.runtime.remember 45 | import androidx.compose.runtime.setValue 46 | import androidx.compose.ui.Modifier 47 | import androidx.compose.ui.unit.dp 48 | import com.google.accompanist.adaptive.FoldAwareColumn 49 | import com.google.accompanist.adaptive.calculateDisplayFeatures 50 | import com.google.accompanist.sample.AccompanistSample 51 | 52 | class NavRailFoldAwareColumnSample : ComponentActivity() { 53 | override fun onCreate(savedInstanceState: Bundle?) { 54 | super.onCreate(savedInstanceState) 55 | setContent { 56 | AccompanistSample { 57 | Row { 58 | NavRail(this@NavRailFoldAwareColumnSample) 59 | Surface(modifier = Modifier.fillMaxSize()) {} 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | @Composable 67 | fun NavRail(activity: Activity) { 68 | val icons = listOf( 69 | Icons.Default.Done, 70 | Icons.Default.Face, 71 | Icons.Default.Lock, 72 | Icons.Default.Search, 73 | Icons.Default.ThumbUp, 74 | Icons.Default.Warning, 75 | Icons.Default.Star 76 | ) 77 | 78 | var selectedIcon by remember { mutableStateOf(icons[0]) } 79 | 80 | NavigationRail { 81 | FoldAwareColumn(displayFeatures = calculateDisplayFeatures(activity)) { 82 | icons.forEach { 83 | NavigationRailItem( 84 | modifier = Modifier 85 | .padding(5.dp) 86 | .border(2.dp, MaterialTheme.colorScheme.primary), 87 | selected = it == selectedIcon, 88 | onClick = { selectedIcon = it }, 89 | icon = { Icon(imageVector = it, contentDescription = it.name) }, 90 | label = { Text(it.name.substringAfter('.')) } 91 | ) 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/adaptive/VerticalTwoPaneSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.adaptive 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import androidx.compose.foundation.layout.Box 23 | import androidx.compose.foundation.layout.fillMaxSize 24 | import androidx.compose.foundation.layout.padding 25 | import androidx.compose.material3.Card 26 | import androidx.compose.material3.Text 27 | import androidx.compose.ui.Alignment 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.unit.dp 30 | import com.google.accompanist.adaptive.FoldAwareConfiguration 31 | import com.google.accompanist.adaptive.TwoPane 32 | import com.google.accompanist.adaptive.VerticalTwoPaneStrategy 33 | import com.google.accompanist.adaptive.calculateDisplayFeatures 34 | import com.google.accompanist.sample.AccompanistSample 35 | 36 | class VerticalTwoPaneSample : ComponentActivity() { 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | setContent { 40 | AccompanistSample { 41 | val displayFeatures = calculateDisplayFeatures(this) 42 | 43 | TwoPane( 44 | first = { 45 | Card( 46 | modifier = Modifier.padding(8.dp) 47 | ) { 48 | Box( 49 | contentAlignment = Alignment.Center, 50 | modifier = Modifier.fillMaxSize() 51 | ) { 52 | Text("First") 53 | } 54 | } 55 | }, 56 | second = { 57 | Card( 58 | modifier = Modifier.padding(8.dp) 59 | ) { 60 | Box( 61 | contentAlignment = Alignment.Center, 62 | modifier = Modifier.fillMaxSize() 63 | ) { 64 | Text("Second") 65 | } 66 | } 67 | }, 68 | strategy = VerticalTwoPaneStrategy( 69 | splitOffset = 200.dp, 70 | ), 71 | displayFeatures = displayFeatures, 72 | foldAwareConfiguration = FoldAwareConfiguration.HorizontalFoldsOnly, 73 | modifier = Modifier.padding(8.dp) 74 | ) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/drawablepainter/DocSamples.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.drawablepainter 18 | 19 | import androidx.compose.foundation.Image 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.ui.platform.LocalContext 22 | import androidx.core.content.ContextCompat 23 | import com.google.accompanist.drawablepainter.rememberDrawablePainter 24 | import com.google.accompanist.sample.R 25 | 26 | @Composable 27 | fun BasicSample() { 28 | val drawable = ContextCompat.getDrawable(LocalContext.current, R.drawable.rectangle) 29 | 30 | Image( 31 | painter = rememberDrawablePainter(drawable = drawable), 32 | contentDescription = "content description", 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/permissions/RequestLocationPermissionsSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.permissions 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import androidx.compose.foundation.layout.Column 23 | import androidx.compose.foundation.layout.Spacer 24 | import androidx.compose.foundation.layout.height 25 | import androidx.compose.material3.Button 26 | import androidx.compose.material3.Text 27 | import androidx.compose.runtime.Composable 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.unit.dp 30 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 31 | import com.google.accompanist.permissions.rememberMultiplePermissionsState 32 | import com.google.accompanist.sample.AccompanistSample 33 | 34 | class RequestLocationPermissionsSample : ComponentActivity() { 35 | override fun onCreate(savedInstanceState: Bundle?) { 36 | super.onCreate(savedInstanceState) 37 | 38 | setContent { 39 | AccompanistSample { 40 | Sample() 41 | } 42 | } 43 | } 44 | } 45 | 46 | @OptIn(ExperimentalPermissionsApi::class) 47 | @Composable 48 | private fun Sample() { 49 | val locationPermissionsState = rememberMultiplePermissionsState( 50 | listOf( 51 | android.Manifest.permission.ACCESS_COARSE_LOCATION, 52 | android.Manifest.permission.ACCESS_FINE_LOCATION, 53 | ) 54 | ) 55 | 56 | if (locationPermissionsState.allPermissionsGranted) { 57 | Text("Thanks! I can access your exact location :D") 58 | } else { 59 | Column { 60 | val allPermissionsRevoked = 61 | locationPermissionsState.permissions.size == 62 | locationPermissionsState.revokedPermissions.size 63 | 64 | val textToShow = if (!allPermissionsRevoked) { 65 | // If not all the permissions are revoked, it's because the user accepted the COARSE 66 | // location permission, but not the FINE one. 67 | "Yay! Thanks for letting me access your approximate location. " + 68 | "But you know what would be great? If you allow me to know where you " + 69 | "exactly are. Thank you!" 70 | } else if (locationPermissionsState.shouldShowRationale) { 71 | // Both location permissions have been denied 72 | "Getting your exact location is important for this app. " + 73 | "Please grant us fine location. Thank you :D" 74 | } else { 75 | // First time the user sees this feature or the user doesn't want to be asked again 76 | "This feature requires location permission" 77 | } 78 | 79 | val buttonText = if (!allPermissionsRevoked) { 80 | "Allow precise location" 81 | } else { 82 | "Request permissions" 83 | } 84 | 85 | Text(text = textToShow) 86 | Spacer(modifier = Modifier.height(8.dp)) 87 | Button(onClick = { locationPermissionsState.launchMultiplePermissionRequest() }) { 88 | Text(buttonText) 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/permissions/RequestMultiplePermissionsSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.permissions 18 | 19 | import android.Manifest 20 | import android.os.Bundle 21 | import androidx.activity.ComponentActivity 22 | import androidx.activity.compose.setContent 23 | import androidx.compose.foundation.layout.Column 24 | import androidx.compose.foundation.layout.Spacer 25 | import androidx.compose.foundation.layout.height 26 | import androidx.compose.material3.Button 27 | import androidx.compose.material3.Text 28 | import androidx.compose.runtime.Composable 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.unit.dp 31 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 32 | import com.google.accompanist.permissions.MultiplePermissionsState 33 | import com.google.accompanist.permissions.PermissionState 34 | import com.google.accompanist.permissions.rememberMultiplePermissionsState 35 | import com.google.accompanist.sample.AccompanistSample 36 | 37 | @OptIn(ExperimentalPermissionsApi::class) 38 | class RequestMultiplePermissionsSample : ComponentActivity() { 39 | override fun onCreate(savedInstanceState: Bundle?) { 40 | super.onCreate(savedInstanceState) 41 | 42 | setContent { 43 | AccompanistSample { 44 | val multiplePermissionsState = rememberMultiplePermissionsState( 45 | listOf( 46 | Manifest.permission.RECORD_AUDIO, 47 | Manifest.permission.CAMERA, 48 | ) 49 | ) 50 | Sample(multiplePermissionsState) 51 | } 52 | } 53 | } 54 | } 55 | 56 | @OptIn(ExperimentalPermissionsApi::class) 57 | @Composable 58 | private fun Sample(multiplePermissionsState: MultiplePermissionsState) { 59 | if (multiplePermissionsState.allPermissionsGranted) { 60 | // If all permissions are granted, then show screen with the feature enabled 61 | Text("Camera and Read storage permissions Granted! Thank you!") 62 | } else { 63 | Column { 64 | Text( 65 | getTextToShowGivenPermissions( 66 | multiplePermissionsState.revokedPermissions, 67 | multiplePermissionsState.shouldShowRationale 68 | ) 69 | ) 70 | Spacer(modifier = Modifier.height(8.dp)) 71 | Button(onClick = { multiplePermissionsState.launchMultiplePermissionRequest() }) { 72 | Text("Request permissions") 73 | } 74 | } 75 | } 76 | } 77 | 78 | @OptIn(ExperimentalPermissionsApi::class) 79 | private fun getTextToShowGivenPermissions( 80 | permissions: List, 81 | shouldShowRationale: Boolean 82 | ): String { 83 | val revokedPermissionsSize = permissions.size 84 | if (revokedPermissionsSize == 0) return "" 85 | 86 | val textToShow = StringBuilder().apply { 87 | append("The ") 88 | } 89 | 90 | for (i in permissions.indices) { 91 | textToShow.append(permissions[i].permission) 92 | when { 93 | revokedPermissionsSize > 1 && i == revokedPermissionsSize - 2 -> { 94 | textToShow.append(", and ") 95 | } 96 | i == revokedPermissionsSize - 1 -> { 97 | textToShow.append(" ") 98 | } 99 | else -> { 100 | textToShow.append(", ") 101 | } 102 | } 103 | } 104 | textToShow.append(if (revokedPermissionsSize == 1) "permission is" else "permissions are") 105 | textToShow.append( 106 | if (shouldShowRationale) { 107 | " important. Please grant all of them for the app to function properly." 108 | } else { 109 | " denied. The app cannot function without them." 110 | } 111 | ) 112 | return textToShow.toString() 113 | } 114 | -------------------------------------------------------------------------------- /sample/src/main/java/com/google/accompanist/sample/permissions/RequestPermissionSample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.accompanist.sample.permissions 18 | 19 | import android.os.Bundle 20 | import androidx.activity.ComponentActivity 21 | import androidx.activity.compose.setContent 22 | import androidx.compose.foundation.layout.Column 23 | import androidx.compose.foundation.layout.Spacer 24 | import androidx.compose.foundation.layout.height 25 | import androidx.compose.material3.Button 26 | import androidx.compose.material3.Text 27 | import androidx.compose.runtime.Composable 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.unit.dp 30 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 31 | import com.google.accompanist.permissions.isGranted 32 | import com.google.accompanist.permissions.rememberPermissionState 33 | import com.google.accompanist.permissions.shouldShowRationale 34 | import com.google.accompanist.sample.AccompanistSample 35 | 36 | class RequestPermissionSample : ComponentActivity() { 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | 40 | setContent { 41 | AccompanistSample { 42 | Sample() 43 | } 44 | } 45 | } 46 | } 47 | 48 | @OptIn(ExperimentalPermissionsApi::class) 49 | @Composable 50 | private fun Sample() { 51 | val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA) 52 | if (cameraPermissionState.status.isGranted) { 53 | Text("Camera permission Granted") 54 | } else { 55 | Column { 56 | val textToShow = if (cameraPermissionState.status.shouldShowRationale) { 57 | "The camera is important for this app. Please grant the permission." 58 | } else { 59 | "Camera not available" 60 | } 61 | 62 | Text(textToShow) 63 | Spacer(modifier = Modifier.height(8.dp)) 64 | Button(onClick = { cameraPermissionState.launchPermissionRequest() }) { 65 | Text("Request permission") 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-nodpi/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/drawable-nodpi/placeholder.jpg -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | 28 | 29 | 35 | 38 | 41 | 42 | 43 | 44 | 50 | 51 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/drawable/alpha.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable/rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/accompanist/fda7f06a24a42b0b1afa75591e768d82d2941984/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sample/src/main/res/values-ar/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | هذا مضمون \n%s 19 | 20 | -------------------------------------------------------------------------------- /scripts/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2021 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Fail on error and print out commands 18 | set -ex 19 | 20 | # By default we don't shard 21 | SHARD_COUNT=0 22 | SHARD_INDEX=0 23 | # By default we don't log 24 | LOG_FILE="" 25 | # By default we run tests on device 26 | DEVICE=true 27 | 28 | # Parse parameters 29 | for i in "$@"; do 30 | case $i in 31 | --shard-count=*) 32 | SHARD_COUNT="${i#*=}" 33 | shift 34 | ;; 35 | --unit-tests) 36 | DEVICE=false 37 | shift 38 | ;; 39 | --shard-index=*) 40 | SHARD_INDEX="${i#*=}" 41 | shift 42 | ;; 43 | --log-file=*) 44 | LOG_FILE="${i#*=}" 45 | shift 46 | ;; 47 | --run-affected) 48 | RUN_AFFECTED=true 49 | shift 50 | ;; 51 | --run-flaky-tests) 52 | RUN_FLAKY=true 53 | shift 54 | ;; 55 | --affected-base-ref=*) 56 | BASE_REF="${i#*=}" 57 | shift 58 | ;; 59 | *) 60 | echo "Unknown option" 61 | exit 1 62 | ;; 63 | esac 64 | done 65 | 66 | # Start logcat if we have a file to log to 67 | if [[ ! -z "$LOG_FILE" ]]; then 68 | adb logcat >$LOG_FILE & 69 | fi 70 | 71 | FILTER_OPTS="" 72 | # Filter out flaky tests if we're not set to run them 73 | if [[ -z "$RUN_FLAKY" ]]; then 74 | FILTER_OPTS="$FILTER_OPTS -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.FlakyTest" 75 | fi 76 | 77 | # If we're set to only run affected test, update the Gradle task 78 | if [[ ! -z "$RUN_AFFECTED" ]]; then 79 | if [ "$DEVICE" = true ]; then 80 | TASK="runAffectedAndroidTests" 81 | else 82 | TASK="runAffectedUnitTests" 83 | fi 84 | TASK="$TASK -Paffected_module_detector.enable" 85 | 86 | # If we have a base branch set, add the Gradle property 87 | if [[ ! -z "$BASE_REF" ]]; then 88 | TASK="$TASK -Paffected_base_ref=$BASE_REF" 89 | fi 90 | fi 91 | 92 | # If we don't have a task yet, use the defaults 93 | if [[ -z "$TASK" ]]; then 94 | if [ "$DEVICE" = true ]; then 95 | TASK="connectedCheck" 96 | else 97 | TASK="testDebug" 98 | fi 99 | fi 100 | 101 | SHARD_OPTS="" 102 | if [ "$SHARD_COUNT" -gt "0" ]; then 103 | # If we have a shard count value, create the necessary Gradle property args. 104 | # We assume that SHARD_INDEX has been set too 105 | SHARD_OPTS="$SHARD_OPTS -Pandroid.testInstrumentationRunnerArguments.numShards=$SHARD_COUNT" 106 | SHARD_OPTS="$SHARD_OPTS -Pandroid.testInstrumentationRunnerArguments.shardIndex=$SHARD_INDEX" 107 | fi 108 | 109 | ./gradlew --scan --continue --no-configuration-cache --stacktrace $TASK $FILTER_OPTS $SHARD_OPTS 110 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | pluginManagement { 18 | includeBuild("build-logic") 19 | repositories { 20 | google() 21 | mavenCentral() 22 | gradlePluginPortal() 23 | } 24 | } 25 | 26 | dependencyResolutionManagement { 27 | repositories { 28 | google() 29 | mavenCentral() 30 | } 31 | } 32 | 33 | include(":adaptive") 34 | include(":internal-testutils") 35 | include(":drawablepainter") 36 | include(":permissions") 37 | include(":permissions-lint") 38 | include(":sample") 39 | 40 | rootProject.name = "accompanist" 41 | 42 | check(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) { 43 | """ 44 | Accompanist requires JDK 17+ but it is currently using JDK ${JavaVersion.current()}. 45 | Java Home: [${System.getProperty("java.home")}] 46 | https://developer.android.com/build/jdks#jdk-config-in-studio 47 | """.trimIndent() 48 | } -------------------------------------------------------------------------------- /spotless/copyright.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright $YEAR The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /spotless/greclipse.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2020 The Android Open Source Project 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | #Whether to use 'space', 'tab' or 'mixed' (both) characters for indentation. 18 | #The default value is 'tab'. 19 | org.eclipse.jdt.core.formatter.tabulation.char=space 20 | 21 | #Number of spaces used for indentation in case 'space' characters 22 | #have been selected. The default value is 4. 23 | org.eclipse.jdt.core.formatter.tabulation.size=4 24 | 25 | #Number of spaces used for indentation in case 'mixed' characters 26 | #have been selected. The default value is 4. 27 | org.eclipse.jdt.core.formatter.indentation.size=4 28 | 29 | #Whether or not indentation characters are inserted into empty lines. 30 | #The default value is 'true'. 31 | org.eclipse.jdt.core.formatter.indent_empty_lines=false 32 | 33 | #Number of spaces used for multiline indentation. 34 | #The default value is 2. 35 | groovy.formatter.multiline.indentation=2 36 | 37 | #Length after which list are considered too long. These will be wrapped. 38 | #The default value is 30. 39 | groovy.formatter.longListLength=30 40 | 41 | #Whether opening braces position shall be the next line. 42 | #The default value is 'same'. 43 | groovy.formatter.braces.start=same 44 | 45 | #Whether closing braces position shall be the next line. 46 | #The default value is 'next'. 47 | groovy.formatter.braces.end=next 48 | 49 | #Remove unnecessary semicolons. The default value is 'false'. 50 | groovy.formatter.remove.unnecessary.semicolons=false --------------------------------------------------------------------------------