commit 8750da4c241c8b46219c2b8bc7b38a3607b0d59c Author: Volkova2007. Date: Fri Jun 26 03:26:01 2026 +0400 first commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..83be1d5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +# https://editorconfig.org/ +# This configuration is used by ktlint when spotless invokes it + +[*.{kt,kts}] +ij_kotlin_allow_trailing_comma=true +ij_kotlin_allow_trailing_comma_on_call_site=true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd9488b --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +# Gradle +.gradle +build/ + +captures + +**/local.properties + +# IntelliJ .idea folder +.idea/workspace.xml +.idea/libraries +.idea/caches +.idea/navEditor.xml +.idea/tasks.xml +.idea/modules.xml +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/deploymentTargetDropDown.xml +.idea/deploymentTargetSelector.xml +.idea/misc.xml +.idea/androidTestResultsUserPreferences.xml +.idea/inspectionProfiles +.idea/vcs.xml + +# Ignore all auto-generated configs except for app.xml. It's needed as template for the build +.idea/runConfigurations +!.idea/runConfigurations/app.xml + +gradle.xml +*.iml + +# General +.DS_Store +.externalNativeBuild + +# Do not commit plain-text signing info +release/*.properties +release/*.gpg + +# VS Code config +org.eclipse.buildship.core.prefs +.classpath +.project + +# Temporary API docs +docs/api +package-list-coil-base + +# Mkdocs temporary serving folder +docs-gen +site +*.bak +.idea/appInsightsSettings.xml + +#TFLite +*.tflite diff --git a/.google/packaging.yaml b/.google/packaging.yaml new file mode 100644 index 0000000..2aacc25 --- /dev/null +++ b/.google/packaging.yaml @@ -0,0 +1,41 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# GOOGLE SAMPLE PACKAGING DATA +# +# This file is used by Google as part of our samples packaging process. +# End users may safely ignore this file. It has no relevance to other systems. +--- +status: PUBLISHED +technologies: [Android, Platform] +categories: + - Permissions + - Camera + - Accessibility + - Connectivity + - Location + - Privacy + - UserInterface + - AndroidPlatform + - JetpackComposeA11y + - JetpackComposePermissions +solutions: + - Mobile + - Platform + - SDK + - Jetpack +languages: [Kotlin] +github: android/platform-samples +level: INTERMEDIATE +license: apache2 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..43514b5 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Platform Samples \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml new file mode 100644 index 0000000..1661ec9 --- /dev/null +++ b/.idea/assetWizardSettings.xml @@ -0,0 +1,45 @@ + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..ab75be2 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,124 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/AOSP.xml b/.idea/copyright/AOSP.xml new file mode 100644 index 0000000..58b8ac7 --- /dev/null +++ b/.idea/copyright/AOSP.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..d6e60d4 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..c22b6fa --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/markdown.xml b/.idea/markdown.xml new file mode 100644 index 0000000..c61ea33 --- /dev/null +++ b/.idea/markdown.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..6218e23 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @yrezgui @ashnohe @madebymozart @alabiaga \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f8b12cb --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,63 @@ +# Google Open Source Community Guidelines + +At Google, we recognize and celebrate the creativity and collaboration of open +source contributors and the diversity of skills, experiences, cultures, and +opinions they bring to the projects and communities they participate in. + +Every one of Google's open source projects and communities are inclusive +environments, based on treating all individuals respectfully, regardless of +gender identity and expression, sexual orientation, disabilities, +neurodiversity, physical appearance, body size, ethnicity, nationality, race, +age, religion, or similar personal characteristic. + +We value diverse opinions, but we value respectful behavior more. + +Respectful behavior includes: + +* Being considerate, kind, constructive, and helpful. +* Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or + physically threatening behavior, speech, and imagery. +* Not engaging in unwanted physical contact. + +Some Google open source projects [may adopt][] an explicit project code of +conduct, which may have additional detailed expectations for participants. Most +of those projects will use our [modified Contributor Covenant][]. + +[may adopt]: https://opensource.google/docs/releasing/preparing/#conduct +[modified Contributor Covenant]: https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/ + +## Resolve peacefully + +We do not believe that all conflict is necessarily bad; healthy debate and +disagreement often yields positive results. However, it is never okay to be +disrespectful. + +If you see someone behaving disrespectfully, you are encouraged to address the +behavior directly with those involved. Many issues can be resolved quickly and +easily, and this gives people more control over the outcome of their dispute. +If you are unable to resolve the matter for any reason, or if the behavior is +threatening or harassing, report it. We are dedicated to providing an +environment where participants feel welcome and safe. + +## Reporting problems + +Some Google open source projects may adopt a project-specific code of conduct. +In those cases, a Google employee will be identified as the Project Steward, +who will receive and handle reports of code of conduct violations. In the event +that a project hasn’t identified a Project Steward, you can report problems by +emailing opensource@google.com. + +We will investigate every complaint, but you may not receive a direct response. +We will use our discretion in determining when and how to follow up on reported +incidents, which may range from not taking action to permanent expulsion from +the project and project-sponsored spaces. We will notify the accused of the +report and provide them an opportunity to discuss it before any action is +taken. The identity of the reporter will be omitted from the details of the +report supplied to the accused. In potentially harmful situations, such as +ongoing harassment or threats to anyone's safety, we may take action without +notice. + +*This document was adapted from the [IndieWeb Code of Conduct][] and can also +be found at .* + +[IndieWeb Code of Conduct]: https://indieweb.org/code-of-conduct diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ebbb59e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution; +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..59a9ea4 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +![Build](https://github.com/android/platform-samples/actions/workflows/build.yml/badge.svg) + +# Android Platform Samples + +This repository contains a collection of samples that demonstrate the use of different Android OS platform APIs. The samples are organized into folders by topic, and each folder contains a README file that provides more information about the samples in that folder. + +> **Note:** These samples are intended to showcase specific functionality in isolation, and they may use +> simplified code. They are not intended to be used as production-ready code. +> For best practices follow our documentation and check +> [Now In Android](https://github.com/android/nowinandroid) + +Browse the samples inside each topic samples folder: + +- [Accessibility](https://github.com/android/platform-samples/tree/main/samples/accessibility) +- [Camera](https://github.com/android/platform-samples/tree/main/samples/camera) +- [Connectivity](https://github.com/android/platform-samples/tree/main/samples/connectivity) +- [Graphics](https://github.com/android/platform-samples/tree/main/samples/graphics) +- [Location](https://github.com/android/platform-samples/tree/main/samples/location) +- [Privacy](https://github.com/android/platform-samples/tree/main/samples/privacy) +- [Storage](https://github.com/android/platform-samples/tree/main/samples/storage) +- [User-interface](https://github.com/android/platform-samples/tree/main/samples/user-interface) +- More to come... + +We are constantly adding new samples to this repository. You can find a list of all the available samples [here](https://github.com/android/platform-samples/tree/main/samples). + +## How to run + +1. Clone the repository +2. Open the whole project in Android Studio. +3. Sync & Run `app` configuration + +The app will open with the samples list screen that allows you to navigate throughout the different +categories and available samples. + +> **Note:** the `app` module is required to bring together all the samples but it's not relevant +> for their functionality, you can simply ignore it. The wiring is done under the hood and an +> implementation detail not needed to understand any sample functionality. + +## Reporting Issues + +You can report an [issue with a sample](https://github.com/android/platform-samples/issues) using +this repository. When doing so, make sure to specify which sample you are referring to. + +## Contributions + +Please contribute! We will gladly review any pull requests. +Make sure to read the [Contributing](CONTRIBUTING.md) page first though. + +> Note: make sure to run `./gradlew --init-script gradle/spotless-init.gradle.kts spotlessApply` before +> submitting PRs. + +## License + +``` +Copyright 2023 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..0a7a4ae --- /dev/null +++ b/app/README.md @@ -0,0 +1,24 @@ +# Platform Samples app + +This module contains the necessary infrastructure using the [casa-android framework](https://github.com/google/casa-android) +to wire all the samples together and offer a launch point. + +**You can ignore the code in this modules since it's not relevant for the samples** + +## License + +``` +Copyright 2023 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..f63d052 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,113 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + kotlin("plugin.serialization") version "2.1.10" +} + +android { + namespace = "com.example.platform" + compileSdk = 37 + + defaultConfig { + applicationId = "com.example.platform" + minSdk = 24 + targetSdk = 37 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.androidx.navigation.compose) + + implementation(libs.kotlinx.serialization.json) + implementation(libs.androidx.fragment.compose) + + implementation(project(":shared")) + implementation(project(":samples:accessibility")) + implementation(project(":samples:camera:camera2")) + implementation(project(":samples:camera:camerax")) + implementation(project(":samples:connectivity:audio")) + implementation(project(":samples:connectivity:bluetooth:ble")) + implementation(project(":samples:connectivity:bluetooth:companion")) + implementation(project(":samples:connectivity:callnotification")) + implementation(project(":samples:connectivity:telecom")) + implementation(project(":samples:connectivity:UwbRanging")) + implementation(project(":samples:graphics:pdf")) + implementation(project(":samples:graphics:ultrahdr")) + implementation(project(":samples:location")) + implementation(project(":samples:media:ultrahdr")) + implementation(project(":samples:media:video")) + implementation(project(":samples:privacy:data")) + implementation(project(":samples:privacy:permissions")) + implementation(project(":samples:privacy:transparency")) + implementation(project(":samples:storage")) + implementation(project(":samples:user-interface:appwidgets")) + implementation(project(":samples:user-interface:constraintlayout")) + implementation(project(":samples:user-interface:draganddrop")) + implementation(project(":samples:user-interface:haptics")) + implementation(project(":samples:user-interface:live-updates")) + implementation(project(":samples:user-interface:picture-in-picture")) + implementation(project(":samples:user-interface:predictiveback")) + implementation(project(":samples:user-interface:quicksettings")) + implementation(project(":samples:user-interface:share")) + implementation(project(":samples:user-interface:text")) + implementation(project(":samples:user-interface:window-insets")) + implementation(project(":samples:user-interface:windowmanager")) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c9899e0 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/example/platform/app/ApiSurface.kt b/app/src/main/java/com/example/platform/app/ApiSurface.kt new file mode 100644 index 0000000..23cbaee --- /dev/null +++ b/app/src/main/java/com/example/platform/app/ApiSurface.kt @@ -0,0 +1,238 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.app + +import kotlinx.serialization.Serializable + +@Serializable +data class ApiSurface(override val id: String, override val name: String, override val description: String? = null) : CatalogItem + +val AccessiblityApiSurface = ApiSurface( + "accessiblity", + "Accessibility", + null, +) + +val CameraCamera2ApiSurface = ApiSurface( + "camera-camera2", + "Camera2", + null, +) + +val CameraCameraXApiSurface = ApiSurface( + "camera-camerax", + "CameraX", + null, +) + +val ConnectivityAudioApiSurface = ApiSurface( + "connectivity-audio", + "Connectivity Audio", + null, +) + +val ConnectivityBluetoothBleApiSurface = ApiSurface( + "connectivity-bluetooth-ble", + "Connectivity Bluetooth BLE", + null, +) + +val ConnectivityBluetoothCompanionApiSurface = ApiSurface( + "connectivity-bluetooth-companion", + "Connectivity Bluetooth Companion", + null, +) + +val ConnectivityCallNotificationApiSurface = ApiSurface( + "connectivity-call-notification", + "Connectivity Call Notification", + null, +) + +val ConnectivityTelecomApiSurface = ApiSurface( + "connectivity-telecom", + "Connectivity Telecom", + null, +) + +val GraphicsPdfApiSurface = ApiSurface( + "graphics-pdf", + "Graphics PDF", + null, +) + +val GraphicsUltraHdrApiSurface = ApiSurface( + "graphics-ultrahdr", + "Graphics UltraHDR", + null, +) + +val LocationApiSurface = ApiSurface( + "location", + "Location", + null, +) + +val MediaUltraHdrApiSurface = ApiSurface( + "media-ultrahdr", + "Media UltraHDR", + null, +) + +val MediaVideoApiSurface = ApiSurface( + "media-video", + "Media Video", + null, +) + +val PrivacyDataApiSurface = ApiSurface( + "privacy-data", + "Privacy Data", + null, +) + +val PrivacyPermissionsApiSurface = ApiSurface( + "privacy-permissions", + "Privacy Permissions", + null, +) + +val PrivacyTransparencyApiSurface = ApiSurface( + "privacy-transparency", + "Privacy Transparency", + null, +) + +val StorageApiSurface = ApiSurface( + "storage", + "Storage", + """Android photo library access capabilities. + Photo Picker for unified device and cloud photo access, and MediaStore for detailed local media querying. + + WARNING: These samples only work on devices with Android 14 or higher.""".trimMargin() +) + +val UserInterfaceAppWidgetsApiSurface = ApiSurface( + "user-interface-app-widgets", + "User Interface - App Widgets", + null, +) + +val UserInterfaceConstraintLayoutApiSurface = ApiSurface( + "user-interface-constraint-layout", + "User Interface - Constraint Layout", + null, +) + +val UserInterfaceDragAndDropApiSurface = ApiSurface( + "user-interface-draganddrop", + "User Interface - Drag and Drop", + null, +) + +val UserInterfaceHapticsApiSurface = ApiSurface( + "user-interface-haptics", + "User Interface - Haptics", + null, +) + +val UserInterfaceLiveUpdatesApiSurface = ApiSurface( + "live-updates", + "User Interface - Live Updates", + null, +) + +val UserInterfacePictureInPictureApiSurface = ApiSurface( + "user-interface-picture-in-picture", + "User Interface - Picture In Picture", + null, +) + +val UserInterfacePredictiveBackApiSurface = ApiSurface( + "user-interface-predictive-back", + "User Interface - Predictive Back", + null, +) + +val UserInterfaceQuickSettingsApiSurface = ApiSurface( + "user-interface-quick-settings", + "User Interface - Quick Settings", + null, +) + +val UserInterfaceShareApiSurface = ApiSurface( + "user-interface-share", + "User Interface - Share", + null, +) + +val UserInterfaceTextApiSurface = ApiSurface( + "user-interface-text", + "User Interface - Text", + null, +) + +val UserInterfaceWindowInsetsApiSurface = ApiSurface( + "user-interface-window-insets", + "User Interface - Window Insets", + null, +) + +val UserInterfaceWindowManagerApiSurface = ApiSurface( + "user-interface-window-manager", + "User Interface - Window Manager", + null, +) + +val ConnectivityUwbRangingApiSurface = ApiSurface( + "connectivity-uwb-ranging", + "Connectivity UWB Ranging", + null, +) + +val API_SURFACES = listOf( + AccessiblityApiSurface, + CameraCamera2ApiSurface, + CameraCameraXApiSurface, + ConnectivityAudioApiSurface, + ConnectivityBluetoothBleApiSurface, + ConnectivityBluetoothCompanionApiSurface, + ConnectivityCallNotificationApiSurface, + ConnectivityTelecomApiSurface, + ConnectivityUwbRangingApiSurface, + GraphicsPdfApiSurface, + GraphicsUltraHdrApiSurface, + LocationApiSurface, + MediaUltraHdrApiSurface, + MediaVideoApiSurface, + PrivacyDataApiSurface, + PrivacyPermissionsApiSurface, + PrivacyTransparencyApiSurface, + StorageApiSurface, + UserInterfaceAppWidgetsApiSurface, + UserInterfaceConstraintLayoutApiSurface, + UserInterfaceDragAndDropApiSurface, + UserInterfaceHapticsApiSurface, + UserInterfaceLiveUpdatesApiSurface, + UserInterfacePictureInPictureApiSurface, + UserInterfacePredictiveBackApiSurface, + UserInterfaceQuickSettingsApiSurface, + UserInterfaceShareApiSurface, + UserInterfaceTextApiSurface, + UserInterfaceWindowInsetsApiSurface, + UserInterfaceWindowManagerApiSurface +).associateBy { it.id } \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/CatalogScreen.kt b/app/src/main/java/com/example/platform/app/CatalogScreen.kt new file mode 100644 index 0000000..1a6e57a --- /dev/null +++ b/app/src/main/java/com/example/platform/app/CatalogScreen.kt @@ -0,0 +1,122 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.app + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + + +/** + * Represents an API surface or sample. + */ +interface CatalogItem { + val id: String + val name: String + val description: String? +} + +/** + * Screen rendering list of items with their descriptions (API surfaces or samples) + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CatalogScreen( + item: CatalogItem, + subItems: Map, + onNavigateToSubItem: (CatalogItem) -> Unit = {}, +) { + val list = remember { subItems.values.toList() } + + Scaffold( + topBar = { + TopAppBar( + title = { Text(item.name) }, + ) + }, + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp), + ) { + item { + Text( + text = "Samples", + style = MaterialTheme.typography.bodyLarge, + ) + Spacer(modifier = Modifier.height(16.dp)) + } + items(list) { subItem -> + CatalogScreenItem(item = subItem, onClick = onNavigateToSubItem) + Spacer(modifier = Modifier.height(16.dp)) + } + } + } +} + +@Composable +fun CatalogScreenItem( + item: CatalogItem, + onClick: (CatalogItem) -> Unit, +) { + OutlinedCard(onClick = { onClick(item) }, modifier = Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.padding(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text(text = item.name, style = MaterialTheme.typography.titleSmall) + if (item.description != null) { + Text( + text = item.description!!, + style = MaterialTheme.typography.bodySmall, + ) + } + } + Icon( + imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, + contentDescription = null, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/MainActivity.kt b/app/src/main/java/com/example/platform/app/MainActivity.kt new file mode 100644 index 0000000..b1d066a --- /dev/null +++ b/app/src/main/java/com/example/platform/app/MainActivity.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.app + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.fragment.app.FragmentActivity +import com.example.platform.shared.theme.CatalogTheme + +class MainActivity : FragmentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + CatalogTheme { + NavGraph() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/MainApp.kt b/app/src/main/java/com/example/platform/app/MainApp.kt new file mode 100644 index 0000000..751b61c --- /dev/null +++ b/app/src/main/java/com/example/platform/app/MainApp.kt @@ -0,0 +1,19 @@ +import android.app.Application + +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class MainApp : Application() \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/NavGraph.kt b/app/src/main/java/com/example/platform/app/NavGraph.kt new file mode 100644 index 0000000..c5ba5fc --- /dev/null +++ b/app/src/main/java/com/example/platform/app/NavGraph.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.app + +import android.content.Intent +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.navigation.NavType +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument +import androidx.navigation.toRoute +import kotlinx.serialization.Serializable + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun NavGraph() { + val context = LocalContext.current + val navController = rememberNavController() + + NavHost(navController = navController, startDestination = MainScreen) { + composable { + CatalogScreen( + item = MainScreen, + subItems = API_SURFACES, + onNavigateToSubItem = { apiSurface -> navController.navigate(apiSurface as ApiSurface) }, + ) + } + composable { backStackEntry -> + val apiSurface = backStackEntry.toRoute() + val subItems = remember { SAMPLE_DEMOS.filter { it.value.apiSurface == apiSurface } } + + CatalogScreen( + item = apiSurface, + subItems = subItems, + onNavigateToSubItem = { sampleDemo -> + if (sampleDemo is ActivitySampleDemo) { + context.startActivity(Intent(context, sampleDemo.content)) + } else { + navController.navigate("$SAMPLE_DEMO_ROUTE/${sampleDemo.id}") + } + }, + ) + } + + composable( + "$SAMPLE_DEMO_ROUTE/{$SAMPLE_DEMO_ID}", + listOf(navArgument(SAMPLE_DEMO_ID) { type = NavType.StringType }), + ) { backStackEntry -> + val arguments = requireNotNull(backStackEntry.arguments) + val sampleDemoId = requireNotNull(arguments.getString(SAMPLE_DEMO_ID)) + val sampleDemo = SAMPLE_DEMOS.getValue(sampleDemoId) as ComposableSampleDemo + + Scaffold( + topBar = { TopAppBar(title = { Text(sampleDemo.name) }) }, + ) { paddingValues -> + Box( + modifier = Modifier + .padding(paddingValues) + .padding(8.dp) + .fillMaxSize(), + ) { + sampleDemo.content() + } + } + } + } +} + + +@Serializable +data object MainScreen : CatalogItem { + override val id = "main" + override val name = "Platform Samples" + override val description = null +} + +const val SAMPLE_DEMO_ROUTE = "sampledemo" +const val SAMPLE_DEMO_ID = "id" \ No newline at end of file diff --git a/app/src/main/java/com/example/platform/app/SampleDemo.kt b/app/src/main/java/com/example/platform/app/SampleDemo.kt new file mode 100644 index 0000000..c61fb30 --- /dev/null +++ b/app/src/main/java/com/example/platform/app/SampleDemo.kt @@ -0,0 +1,1204 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.app + +import android.os.Build +import androidx.compose.runtime.Composable +import androidx.fragment.compose.AndroidFragment +import com.example.android.pip.PiPMovieActivity +import com.example.android.pip.PiPSampleActivity +import com.example.platform.accessibility.ColorContrast +import com.example.platform.accessibility.LiveRegionView +import com.example.platform.accessibility.SpeakableText +import com.example.platform.camera.imagecapture.Camera2ImageCapture +import com.example.platform.camera.imagecapture.Camera2UltraHDRCapture +import com.example.platform.camera.preview.Camera2Preview +import com.example.platform.camerax.video.CameraXVideo +import com.example.platform.camerax.basic.CameraXBasic +import com.example.platform.camerax.extensions.CameraXExtensions +import com.example.platform.camerax.mlkit.CameraXMlKit +import com.example.platform.connectivity.audio.AudioCommsSample +import com.example.platform.connectivity.bluetooth.ble.BLEScanIntentSample +import com.example.platform.connectivity.bluetooth.ble.ConnectGATTSample +import com.example.platform.connectivity.bluetooth.ble.FindBLEDevicesSample +import com.example.platform.connectivity.bluetooth.ble.server.GATTServerSample +import com.example.platform.connectivity.bluetooth.cdm.CompanionDeviceManagerSample +import com.example.platform.connectivity.callnotification.CallNotificationSample +import com.example.platform.connectivity.telecom.TelecomCallSample +import com.example.platform.graphics.pdf.PdfRendererScreen +import com.example.platform.graphics.ultrahdr.display.CompressingUltraHDRImages +import com.example.platform.graphics.ultrahdr.display.DisplayUltraHDRScreen +import com.example.platform.graphics.ultrahdr.display.DisplayingUltraHDR +import com.example.platform.graphics.ultrahdr.display.DisplayingUltraHDRUsing3PLibrary +import com.example.platform.graphics.ultrahdr.display.VisualizingAnUltraHDRGainmap +import com.example.platform.graphics.ultrahdr.edit.EditingUltraHDR +import com.example.platform.graphics.ultrahdr.opengl.UltraHDRWithOpenGL +import com.example.platform.location.bglocationaccess.BgLocationAccessScreen +import com.example.platform.location.currentLocation.CurrentLocationScreen +import com.example.platform.location.geofencing.GeofencingScreen +import com.example.platform.location.locationupdates.LocationUpdatesScreen +import com.example.platform.location.permission.LocationPermissionScreen +import com.example.platform.location.useractivityrecog.UserActivityRecognitionScreen +import com.example.platform.media.ultrahdr.video.UltraHDRToHDRVideo +import com.example.platform.media.video.TransformerTFLite +import com.example.platform.media.video.TransformerVideoComposition +import com.example.platform.privacy.data.PackageVisibility +import com.example.platform.privacy.permissions.ComposePermissions +import com.example.platform.privacy.permissions.MultiplePermissions +import com.example.platform.privacy.permissions.Permissionless +import com.example.platform.privacy.permissions.SinglePermission +import com.example.platform.privacy.transparency.DataAccessSample +import com.example.platform.privacy.transparency.ScreenshotDetectionSample +import com.example.platform.shared.MinSdkBox +import com.example.platform.storage.mediastore.MediaStoreQuerySample +import com.example.platform.storage.mediastore.SelectedPhotosAccessSample +import com.example.platform.storage.photopicker.PhotoPickerSample +import com.example.platform.storage.storageaccessframework.GetContentSample +import com.example.platform.ui.appwidgets.AppWidgets +import com.example.platform.ui.constraintlayout.AdvancedArrangementFragment +import com.example.platform.ui.constraintlayout.AdvancedChainsFragment +import com.example.platform.ui.constraintlayout.AspectRatioFragment +import com.example.platform.ui.constraintlayout.BasicArrangementFragment +import com.example.platform.ui.constraintlayout.BasicChainFragment +import com.example.platform.ui.constraintlayout.CenteringViewsFragment +import com.example.platform.ui.constraintlayout.ComplexMotion1Fragment +import com.example.platform.ui.constraintlayout.ComplexMotion2Fragment +import com.example.platform.ui.constraintlayout.ComplexMotion3Fragment +import com.example.platform.ui.constraintlayout.ComplexMotion4Fragment +import com.example.platform.ui.constraintlayout.ConstraintSetFragment +import com.example.platform.ui.constraintlayout.Coordinator1Fragment +import com.example.platform.ui.constraintlayout.Coordinator2Fragment +import com.example.platform.ui.constraintlayout.Coordinator3Fragment +import com.example.platform.ui.constraintlayout.CustomAttributeFragment +import com.example.platform.ui.constraintlayout.Drawer1Fragment +import com.example.platform.ui.constraintlayout.Drawer2Fragment +import com.example.platform.ui.constraintlayout.FragmentTransition2Fragment +import com.example.platform.ui.constraintlayout.FragmentTransitionFragment +import com.example.platform.ui.constraintlayout.GuidelinesFragment +import com.example.platform.ui.constraintlayout.ImageFilter1Fragment +import com.example.platform.ui.constraintlayout.ImageFilter2Fragment +import com.example.platform.ui.constraintlayout.KeyTriggerFragment +import com.example.platform.ui.constraintlayout.KeyframeCycleFragment +import com.example.platform.ui.constraintlayout.KeyframeInterpolationFragment +import com.example.platform.ui.constraintlayout.KeyframePositionFragment +import com.example.platform.ui.constraintlayout.LottieFragment +import com.example.platform.ui.constraintlayout.MotionBasic01Fragment +import com.example.platform.ui.constraintlayout.MotionBasic02Fragment +import com.example.platform.ui.constraintlayout.MotionBasic02NoAutoCompleteFragment +import com.example.platform.ui.constraintlayout.MultiStateFragment +import com.example.platform.ui.constraintlayout.ParallaxFragment +import com.example.platform.ui.constraintlayout.SidePanelFragment +import com.example.platform.ui.constraintlayout.ViewPagerFragment +import com.example.platform.ui.constraintlayout.YoutubeFragment +import com.example.platform.ui.draganddrop.DragAndDropCompose +import com.example.platform.ui.draganddrop.DragAndDropMultiWindow +import com.example.platform.ui.draganddrop.DragAndDropRichContentReceiverFragment +import com.example.platform.ui.draganddrop.DragAndDropWithHelper +import com.example.platform.ui.draganddrop.DragAndDropWithViews +import com.example.platform.ui.haptics.Bounce +import com.example.platform.ui.haptics.Expand +import com.example.platform.ui.haptics.HapticsBasic +import com.example.platform.ui.haptics.Resist +import com.example.platform.ui.haptics.Wobble +import com.example.platform.ui.insets.ImmersiveMode +import com.example.platform.ui.insets.WindowInsetsAnimationActivity +import com.example.platform.ui.live_updates.LiveUpdateSample +import com.example.platform.ui.predictiveback.PBHostingActivity +import com.example.platform.ui.quicksettings.QuickSettings +import com.example.platform.ui.share.receiver.ShareReceiverActivity +import com.example.platform.ui.share.sender.ShareSender +import com.example.platform.ui.text.ConversionSuggestions +import com.example.platform.ui.text.DownloadableFontsFragment +import com.example.platform.ui.text.Hyphenation +import com.example.platform.ui.text.LineBreak +import com.example.platform.ui.text.Linkify +import com.example.platform.ui.text.TextSpanFragment +import com.example.platform.ui.windowmanager.demos.WindowDemosActivity +import com.google.uwb.hellouwb.ui.UwbRangingActivity + +interface SampleDemo : CatalogItem { + override val id: String + override val name: String + override val description: String? + val documentation: String? + val minSdk: Int + val tags: List + val apiSurface: ApiSurface + val content: Any +} + +data class ComposableSampleDemo( + override val id: String, + override val name: String, + override val description: String? = null, + override val documentation: String? = null, + override val minSdk: Int = Build.VERSION_CODES.LOLLIPOP, + override val apiSurface: ApiSurface, + override val tags: List = emptyList(), + override val content: @Composable () -> Unit, +) : SampleDemo + +data class ActivitySampleDemo( + override val id: String, + override val name: String, + override val description: String? = null, + override val documentation: String? = null, + override val minSdk: Int = Build.VERSION_CODES.LOLLIPOP, + override val apiSurface: ApiSurface, + override val tags: List = emptyList(), + override val content: Class<*>, +) : SampleDemo + +val SAMPLE_DEMOS by lazy { + listOf( + ComposableSampleDemo( + id = "color-contrast", + name = "Color Contrast", + description = "This sample demonstrates the importance of proper color contrast and how to " + + "audit your app to ensure proper color contrast.", + documentation = "https://support.google.com/accessibility/android/answer/7158390", + apiSurface = AccessiblityApiSurface, + content = { ColorContrast() }, + ), + ComposableSampleDemo( + id = "live-region-view", + name = "Live Region (View)", + description = "Utilize LiveRegion to automatically notify users of accessibility services" + + " about changes to a view", + documentation = "https://developer.android.com/reference/android/view/View#attr_android:accessibilityLiveRegion", + apiSurface = AccessiblityApiSurface, + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "speakable-text", + name = "Speakable Text", + description = "The sample demonstrates the importance of having proper labels for" + + " interactive elements and how to audit your app for content label related " + + "improvements.", + documentation = "https://developer.android.com/guide/topics/ui/accessibility/apps#describe-ui-element", + apiSurface = AccessiblityApiSurface, + content = { SpeakableText() }, + ), + ComposableSampleDemo( + id = "image-capture", + name = "Image Capture", + description = "This sample demonstrates how to capture an image using Camera2 and encode it " + + "into a JPEG container.", + documentation = "https://developer.android.com/training/camera2/capture-sessions-requests", + apiSurface = CameraCamera2ApiSurface, + tags = listOf("Camera2"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "ultrahdr-image-capture", + name = "UltraHDR Image Capture", + description = "This sample demonstrates how to capture a 10-bit compressed still image and " + + "store it using the new UltraHDR image format using Camera2.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = CameraCamera2ApiSurface, + tags = listOf("UltraHDR", "Camera2"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "ultrahdr-image-capture", + name = "Camera2 Preview", + description = "Demonstrates displaying processed pixel data directly from the camera sensor " + + "to the screen using Camera2.", + documentation = "https://developer.android.com/training/camera2", + apiSurface = CameraCamera2ApiSurface, + tags = listOf("Camera2"), + content = { AndroidFragment() }, + ), + + // CameraX Samples + ComposableSampleDemo( + id = "camerax-basic", + name = "CameraX • Basic Image Capture", + description = "This sample demonstrates how to capture an image & tap-to-focus using CameraX", + documentation = "https://developer.android.com/training/camerax", + apiSurface = CameraCameraXApiSurface, + tags = listOf("CameraX"), + content = { CameraXBasic() }, + ), + ComposableSampleDemo( + id = "camerax-video-capture", + name = "CameraX • Basic Video Capture", + description = "This sample demonstrates how to capture a video using CameraX", + documentation = "https://developer.android.com/training/camerax", + apiSurface = CameraCameraXApiSurface, + tags = listOf("CameraX"), + content = { CameraXVideo() }, + ), + ComposableSampleDemo( + id = "camerax-extensions", + name = "CameraX • Extensions", + description = "This sample demonstrates how to check for and utilize CameraX Extensions", + documentation = "https://developer.android.com/training/camerax", + apiSurface = CameraCameraXApiSurface, + tags = listOf("CameraX"), + content = { CameraXExtensions() }, + ), + ComposableSampleDemo( + id = "camerax-ml-kit", + name = "CameraX • MLKit Sample", + description = "This sample demonstrates how to use MLKit with CameraX", + documentation = "https://developer.android.com/training/camerax", + apiSurface = CameraCameraXApiSurface, + tags = listOf("CameraX"), + content = { CameraXMlKit() }, + ), + + ComposableSampleDemo( + id = "communication-audio-manager", + name = "Communication Audio Manager", + description = "This sample shows how to use audio manager to for Communication application that self-manage the call.", + documentation = "https://developer.android.com/guide/topics/connectivity/ble-audio/audio-manager", + minSdk = Build.VERSION_CODES.S, + apiSurface = ConnectivityAudioApiSurface, + tags = listOf("Audio"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.S) { + //noinspection NewApi + AudioCommsSample() + } + }, + ), + ComposableSampleDemo( + id = "create-gatt-server", + name = "Create a GATT server", + description = "Shows how to create a GATT server and communicate with the GATT client", + documentation = "https://developer.android.com/reference/android/bluetooth/BluetoothGattServer", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.M) { + //noinspection NewApi + GATTServerSample() + } + }, + ), + ComposableSampleDemo( + id = "scan-with-ble-intent", + name = "Scan with BLE Intent", + description = "This samples shows how to use the BLE intent to scan for devices", + documentation = "https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.app.PendingIntent)", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + BLEScanIntentSample() + } + }, + ), + ComposableSampleDemo( + id = "connect-gatt-server", + name = "Connect to a GATT server", + description = "Shows how to connect to a GATT server hosted by the BLE device and perform simple operations", + documentation = "https://developer.android.com/guide/topics/connectivity/bluetooth/connect-gatt-server", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.M) { + //noinspection NewApi + ConnectGATTSample() + } + }, + ), + ComposableSampleDemo( + id = "find-devices", + name = "Find devices", + description = "This example will demonstrate how to scanning for Low Energy Devices", + documentation = "https://developer.android.com/guide/topics/connectivity/bluetooth", + apiSurface = ConnectivityBluetoothBleApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.M) { + //noinspection NewApi + FindBLEDevicesSample() + } + }, + ), + ComposableSampleDemo( + id = "companion-device-manager", + name = "Companion Device Manager", + description = "This samples shows how to use the CDM to pair and connect with BLE devices", + documentation = "https://developer.android.com/guide/topics/connectivity/companion-device-pairing", + apiSurface = ConnectivityBluetoothCompanionApiSurface, + tags = listOf("Bluetooth"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + CompanionDeviceManagerSample() + } + }, + ), + ActivitySampleDemo( + id = "call-notification", + name = "Call Notification", + description = "Sample demonstrating how to make incoming call notifications and in call notifications", + documentation = "https://developer.android.com/reference/android/app/Notification.CallStyle", + apiSurface = ConnectivityCallNotificationApiSurface, + content = CallNotificationSample::class.java, + ), + ComposableSampleDemo( + id = "telecom-call", + name = "Telecom Call", + description = "A sample showcasing how to handle calls with the Jetpack Telecom API", + documentation = "https://developer.android.com/guide/topics/connectivity/telecom", + apiSurface = ConnectivityTelecomApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + TelecomCallSample() + } + }, + ), + ActivitySampleDemo( + id = "connectivity-uwb-ranging", + name = "UWB Ranging", + description = "Demonstrates how to use the UWB APIs to perform ranging.", + documentation = "https://developer.android.com/guide/topics/connectivity/uwb", + apiSurface = ConnectivityUwbRangingApiSurface, + content = UwbRangingActivity::class.java + ), + ComposableSampleDemo( + id = "pdf-renderer", + name = "PDF Renderer", + description = "Demonstrates how to use PdfRenderer to display PDF documents on the screen.", + documentation = null, + apiSurface = GraphicsPdfApiSurface, + content = { PdfRendererScreen() }, + ), + ComposableSampleDemo( + id = "compressing-ultrahdr-images", + name = "Compressing UltraHDR Images", + description = "This sample demonstrates displaying an UltraHDR image in a Compose View and an Android View", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "displaying-ultrahdr", + name = "Displaying UltraHDR", + description = "This sample demonstrates displaying an UltraHDR image.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "displaying-ultrahdr", + name = "Displaying UltraHDR (3P Libraries)", + description = "This sample demonstrates using the various popular image loading library to" + + " detect the presence of a gainmap to enable HDR mode when displaying an UltraHDR image", + documentation = "https://github.com/bumptech/glide", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "displaying-ultrahdr-compose", + name = "Displaying UltraHDR (Compose)", + description = "This sample demonstrates displaying an UltraHDR image in a Compose View and an Android View", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR", "Compose"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + //noinspection NewApi + DisplayUltraHDRScreen() + } + }, + ), + ComposableSampleDemo( + id = "visualizing-ultrahdr-gainmap", + name = "Visualizing an UltraHDR Gainmap", + description = "This sample demonstrates visualizing the underlying gainmap of an UltraHDR " + + "image, which reveals which parts of the image are enhanced by the gainmap.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "editing-ultrahdr", + name = "Editing UltraHDR", + description = "This sample demonstrates editing an UltraHDR image and the resulting gainmap as well. Spatial edit operations like crop, rotate, scale are supported", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "ultrahdr-opengles-surfaceview", + name = "UltraHDR x OpenGLES SurfaceView", + description = "This sample demonstrates displaying an UltraHDR image via and OpenGL Pipeline " + + "and control the SurfaceView's rendering brightness.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = GraphicsUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "location-background-location-updates", + name = "Location - Background Location updates", + description = "This Sample demonstrate how to access location and get location updates when app is in background", + documentation = "https://developer.android.com/training/location/background", + apiSurface = LocationApiSurface, + content = { BgLocationAccessScreen() }, + ), + ComposableSampleDemo( + id = "location-getting-current-location", + name = "Location - Getting Current Location", + description = "This Sample demonstrate how to request of current location", + documentation = "https://developer.android.com/training/location/retrieve-current", + apiSurface = LocationApiSurface, + content = { CurrentLocationScreen() }, + ), + ComposableSampleDemo( + id = "location-create-monitor-geofence", + name = "Location - Create and monitor Geofence", + description = "This Sample demonstrate best practices for Creating and monitoring geofence", + documentation = "https://developer.android.com/training/location/geofencing", + apiSurface = LocationApiSurface, + content = { GeofencingScreen() }, + ), + ComposableSampleDemo( + id = "location-updates", + name = "Location - Updates", + description = "This Sample demonstrate how to get location updates", + documentation = "https://developer.android.com/training/location/request-updates", + apiSurface = LocationApiSurface, + content = { LocationUpdatesScreen() }, + ), + ComposableSampleDemo( + id = "location-permissions", + name = "Location - Permissions", + description = "This Sample demonstrate best practices for Location Permission", + documentation = "https://developer.android.com/training/location/permissions", + apiSurface = LocationApiSurface, + tags = listOf("permissions"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.Q) { + //noinspection NewApi + LocationPermissionScreen() + } + }, + ), + ComposableSampleDemo( + id = "location-user-activity-recognition", + name = "Location - User Activity Recognition", + description = "This Sample demonstrate detection of user activity like walking, driving, etc.", + documentation = "https://developer.android.com/training/location/transitions", + apiSurface = LocationApiSurface, + content = { UserActivityRecognitionScreen() }, + ), + ComposableSampleDemo( + id = "ultrahdr-to-hdr-video", + name = "UltraHDR to HDR Video", + description = "This sample demonstrates converting a series of UltraHDR images into a HDR " + + "video." + "The sample leverages GPU hardware acceleration to render and encode the " + + "images.", + documentation = "https://developer.android.com/guide/topics/media/hdr-image-format", + apiSurface = MediaUltraHdrApiSurface, + tags = listOf("UltraHDR"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "transformer-tflite", + name = "Transformer and TFLite", + description = "This sample demonstrates using Transformer with TFLite/RTLite by applying a selected art style to a video.", + documentation = "https://developer.android.com/guide/topics/media/transformer", + apiSurface = MediaVideoApiSurface, + tags = listOf("Transformer"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "video-composition-using-media3-transformer", + name = "Video Composition using Media3 Transformer", + description = "This sample demonstrates concatenation of two video assets and an image using Media3 Transformer library.", + documentation = "https://developer.android.com/guide/topics/media/transformer", + apiSurface = MediaVideoApiSurface, + tags = listOf("Transformer"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "package-visibility", + name = "Package Visibility", + description = "A sample that showcase how the package visibility queries affects the available packages", + documentation = "https://developer.android.com/training/package-visibility", + apiSurface = PrivacyDataApiSurface, + content = { PackageVisibility() }, + ), + ComposableSampleDemo( + id = "permissions-compose", + name = "Permissions using Compose", + description = "This sample showcases how to request permission using Accompanist in Compose", + documentation = "https://google.github.io/accompanist/permissions/", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { ComposePermissions() }, + ), + ComposableSampleDemo( + id = "permissions-compose", + name = "Multiple Permissions", + description = "Shows the recommended flow to request multiple RELATED runtime permissions", + documentation = "https://developer.android.com/training/permissions/requesting", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "permissionless", + name = "Permissionless", + description = "This sample demonstrate how you can avoid requesting permission for certain actions by leveraging System APIs", + documentation = "https://developer.android.com/training/permissions/evaluating", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { Permissionless() }, + ), + ComposableSampleDemo( + id = "single-permission", + name = "Single Permission", + description = "Shows the recommended flow to request single runtime permissions", + documentation = "https://developer.android.com/training/permissions/requesting", + apiSurface = PrivacyPermissionsApiSurface, + tags = listOf("Permissions"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "data-access", + name = "Data Access", + description = "Demonstrates how to implement data access auditing for your app to identify " + + "unexpected data access, even from third-party SDKs and libraries.", + documentation = "https://developer.android.com/guide/topics/data/audit-access", + apiSurface = PrivacyTransparencyApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.R) { + //noinspection NewApi + DataAccessSample() + } + }, + ), + ComposableSampleDemo( + id = "screenshot-detection", + name = "Screenshot Detection", + description = "This sample shows how to detect that the user capture the screen in Android 14 onwards", + documentation = null, + apiSurface = PrivacyTransparencyApiSurface, + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "photo-picker", + name = "PhotoPicker", + description = "Select images/videos in a privacy-friendly way using the photo picker", + documentation = "https://developer.android.com/training/data-storage/shared/photopicker", + apiSurface = StorageApiSurface, + content = { PhotoPickerSample() }, + ), + ComposableSampleDemo( + id = "mediastore-query", + name = "MediaStore - Query", + description = "Query files indexed by MediaStore", + documentation = "https://developer.android.com/training/data-storage/shared/media#media_store", + apiSurface = StorageApiSurface, + content = { MediaStoreQuerySample() }, + ), + ComposableSampleDemo( + id = "storageaccessframework-getcontent", + name = "Storage Access Framework - GET_CONTENT", + description = "Open a document using the Storage Access Framework", + documentation = "https://developer.android.com/training/data-storage/shared/documents-files#open-file", + apiSurface = StorageApiSurface, + content = { GetContentSample() }, + ), + ComposableSampleDemo( + id = "selected-photos-access", + name = "Selected Photos Access", + description = "Check and request storage permissions", + documentation = "https://developer.android.com/about/versions/14/changes/partial-photo-video-access", + apiSurface = StorageApiSurface, + content = { SelectedPhotosAccessSample() }, + ), + ComposableSampleDemo( + id = "app-widgets", + name = "App Widgets", + description = "Showcases how to pin widgets within the app and provides a catalog of well-designed canonical widget layouts for inspiration.", + documentation = "https://developer.android.com/develop/ui/views/appwidgets/overview", + apiSurface = UserInterfaceAppWidgetsApiSurface, + tags = listOf("App Widgets"), + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.O) { + //noinspection NewApi + AppWidgets() + } + }, + ), + ComposableSampleDemo( + id = "constraintlayout-centering-views", + name = "ConstraintLayout - 1. Centering Views", + description = "Center child views horizontally or vertically.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-basic-arrangement", + name = "ConstraintLayout - 2. Basic arrangement", + description = "Arrange positions of child views relative to other views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-advanced-arrangement", + name = "ConstraintLayout - 3. Advanced arrangement", + description = "More arrangement options.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-aspect-ratio", + name = "ConstraintLayout - 4. Aspect ratio", + description = "Specify aspect ratio for the dimensions of the child views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-basic-chains", + name = "ConstraintLayout - 5. Basic chains", + description = "Use chains to arrange multiple child views horizontally or vertically.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-advanced-chains", + name = "ConstraintLayout - 5. Advanced chains", + description = "Use chains to arrange multiple child views horizontally or vertically.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-constraintset", + name = "ConstraintLayout - 7. ConstraintSet", + description = "Use ConstraintSet to specify multiple constraints to all the child views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "constraintlayout-guidelines", + name = "ConstraintLayout - 8. Guidelines", + description = "Use a horizontal or vertical guideline to apply constraints to child views.", + documentation = "https://developer.android.com/develop/ui/views/layout/constraint-layout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("ConstraintLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-01-basic", + name = "MotionLayout - 01. Basic", + description = "Basic motion example using referenced ConstraintLayout files", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-02-basic", + name = "MotionLayout - 02. Basic", + description = "Basic motion example using ConstraintSets defined in the MotionScene file", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-02-basic-no-auto-complete", + name = "MotionLayout - 02. Basic, no auto complete", + description = "Basic motion example same as 2, but autoComplete is set to false in onSwipe", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-03-custom-attribute", + name = "MotionLayout - 03. Custom attribute", + description = "Show color interpolation (custom attribute)", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-04-imagefilterview-1", + name = "MotionLayout - 04. ImageFilterView 1", + description = "Show image cross-fade (using ML's ImageFilterView + custom attribute)", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-05-imagefilterview-2", + name = "MotionLayout - 05. ImageFilterView 2", + description = "Show image saturation transition (using ML's ImageFilterView + custom attribute)", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-06-keyframe-position", + name = "MotionLayout - 06. Keyframe position", + description = "Use a simple keyframe to change the interpolated motion", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-07-keyframe-interpolation", + name = "MotionLayout - 07. Keyframe interpolation", + description = "More complex keyframe, adding rotation interpolation", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-08-keyframe-cycle", + name = "MotionLayout - 08. Keyframe cycle", + description = "Basic example of using a keyframe cycle", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-09-coordinatorlayout-1", + name = "MotionLayout - 09. CoordinatorLayout 1", + description = "Basic example of using MotionLayout instead of AppBarLayout", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-10-coordinatorlayout-2", + name = "MotionLayout - 10. CoordinatorLayout 2", + description = "Slightly more complex example of MotionLayout replacing AppBarLayout, with multiple elements and parallax background", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-11-coordinatorlayout-3", + name = "MotionLayout - 11. CoordinatorLayout 3", + description = "Another AppBarLayout replacement example", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-12-drawerlayout-1", + name = "MotionLayout - 12. DrawerLayout 1", + description = "Basic DrawerLayout with motionlayout", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-13-drawerlayout-2", + name = "MotionLayout - 13. DrawerLayout 2", + description = "Advanced DrawerLayout with motionlayout", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-14-sidepanel", + name = "MotionLayout - 14. SidePanel", + description = "Side Panel, implemented with MotionLayout only", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-15-parallax", + name = "MotionLayout - 15. Parallax", + description = "Parallax background. Drag the car.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-16-viewpager", + name = "MotionLayout - 16. ViewPager", + description = "Using MotionLayout with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-17-complex-motion-1", + name = "MotionLayout - 17. Complex Motion 1", + description = "Basic CoordinatorLayout-like behavior. Implemented with MotionLayout only, using a moving guideline. Note the view isn't resized.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-18-complex-motion-2", + name = "MotionLayout - 18. Complex Motion 2", + description = "Advanced CoordinatorLayout-like behavior (adding a FAB). Implemented with MotionLayout only, using a moving guideline. Note the view isn't resized.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-19-complex-motion-3", + name = "MotionLayout - 19. Complex Motion 3", + description = "Advanced CoordinatorLayout-like behavior (adding a FAB). Implemented with MotionLayout only, using direct resizing of the view.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-20-complex-motion-4", + name = "MotionLayout - 20. Complex Motion 4", + description = "Advanced Synchronized reveal motion + helper (bounce). Implemented with MotionLayout only.", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-21-fragment-transition-1", + name = "MotionLayout - 21. Fragment transition 1", + description = "Using MotionLayout with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-22-fragment-transition-2", + name = "MotionLayout - 22. Fragment transition 2", + description = "Using MotionLayout with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-23-lottie", + name = "MotionLayout - 23. Lottie", + description = "Using MotionLayout and Lottie with ViewPager", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-24-youtube-like-motion", + name = "MotionLayout - 24. YouTube-like motion", + description = "Example showing a transition like YouTube", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-25-keytrigger", + name = "MotionLayout - 25. KeyTrigger", + description = "Example that calls a method using KeyTrigger", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "motionlayout-26-multi-state", + name = "MotionLayout - 26. Multi-state", + description = "Example that transitions between multiple states", + documentation = "https://developer.android.com/develop/ui/views/animations/motionlayout", + apiSurface = UserInterfaceConstraintLayoutApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-in-multiwindow-mode", + name = "Drag and Drop in MultiWindow mode", + description = "Drag and drop to another app visible in multiwindow mode", + documentation = "https://developer.android.com/develop/ui/views/touch-and-input/drag-drop/multi-window", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-richcontentreceiver", + name = "Drag and Drop using the RichContentReceiver", + description = "Using RichContentReceiverInterface for implementing Drop for rich data types", + documentation = "https://developer.android.com/develop/ui/views/receive-rich-content", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-compose", + name = "Drag and Drop in Compose", + description = "Drag and drop in Compose", + documentation = null, + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { DragAndDropCompose() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-helper", + name = "Drag and Drop - Helper", + description = "Drag and Drop using the DragHelper and DropHelper from DragAndDropHelper library", + documentation = "https://developer.android.com/develop/ui/views/touch-and-input/drag-drop#drophelper", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "drag-and-drop-views", + name = "Drag and Drop using views", + description = "Drag and Drop using the views", + documentation = "https://developer.android.com/develop/ui/views/touch-and-input/drag-drop/view", + apiSurface = UserInterfaceDragAndDropApiSurface, + tags = listOf("MotionLayout"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "haptics-1-vibration-effects", + name = "Haptics - 1. Vibration effects", + description = "Shows various vibration effects.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { HapticsBasic() }, + ), + ComposableSampleDemo( + id = "haptics-2-resist", + name = "Haptics - 2. Resist", + description = "Simulates resistance by increasing the intensity and reducing the duration between vibration effects.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Resist() }, + ), + ComposableSampleDemo( + id = "haptics-3-expand", + name = "Haptics - 3. Expand", + description = "Expands and collapses a circle with haptics with an added tick to sharpen that the animation has ended.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Expand() }, + ), + ComposableSampleDemo( + id = "haptics-4-bounce", + name = "Haptics - 4. Bounce", + description = "Play primitive effects to simulate physical interactions.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Bounce() }, + ), + ComposableSampleDemo( + id = "haptics-5-wobble", + name = "Haptics - 5. Wobble", + description = "Play primitive effects to simulate physical interactions.", + documentation = "https://source.android.com/docs/core/interaction/haptics", + apiSurface = UserInterfaceHapticsApiSurface, + tags = listOf("Haptics"), + content = { Wobble() }, + ), + ComposableSampleDemo( + id = "live-updates", + name = "Live Updates - ProgressStyle implementation", + description = "Usage of ProgressStyle with Live update treatment", + documentation = "https://developer.android.com/about/versions/16/features/progress-centric-notifications", + minSdk = Build.VERSION_CODES.BAKLAVA, + apiSurface = UserInterfaceLiveUpdatesApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.BAKLAVA) { + //noinspection NewApi + LiveUpdateSample() + } + }, + ), + ActivitySampleDemo( + id = "picture-in-picture-video-playback", + name = "Picture in Picture (PiP) - Video playback", + description = "Basic usage of Picture-in-Picture mode showcasing video playback", + documentation = "https://developer.android.com/develop/ui/views/picture-in-picture", + apiSurface = UserInterfacePictureInPictureApiSurface, + content = PiPMovieActivity::class.java, + ), + ActivitySampleDemo( + id = "picture-in-picture-stopwatch", + name = "Picture in Picture (PiP) - Stopwatch", + description = "Basic usage of Picture-in-Picture mode showcasing a stopwatch", + documentation = "https://developer.android.com/develop/ui/views/picture-in-picture", + apiSurface = UserInterfacePictureInPictureApiSurface, + content = PiPSampleActivity::class.java, + ), + ActivitySampleDemo( + id = "predictive-back", + name = "Predictive Back", + description = "Shows Predictive Back animations.", + documentation = "https://developer.android.com/about/versions/14/features/predictive-back", + apiSurface = UserInterfacePredictiveBackApiSurface, + content = PBHostingActivity::class.java, + ), + ComposableSampleDemo( + id = "quick-settings", + name = "Quick Settings", + description = "Add your custom tile to the Quick Settings.", + documentation = "https://developer.android.com/develop/ui/views/quicksettings-tiles", + apiSurface = UserInterfaceQuickSettingsApiSurface, + content = { + MinSdkBox(minSdk = Build.VERSION_CODES.N) { + //noinspection NewApi + QuickSettings() + } + }, + ), + ActivitySampleDemo( + id = "receive-data-shared-by-other-apps", + name = "Receive data shared by other apps", + description = "Receive texts and images from other apps.", + documentation = null, + apiSurface = UserInterfaceShareApiSurface, + content = ShareReceiverActivity::class.java, + ), + ComposableSampleDemo( + id = "send-data-with-sharesheet", + name = "Send data with sharesheet", + description = "Send texts and images to other apps using the Android Sharesheet.", + documentation = null, + apiSurface = UserInterfaceShareApiSurface, + content = { ShareSender() }, + ), + ComposableSampleDemo( + id = "conversion-suggestions", + name = "Conversion suggestions", + description = "Demonstrates how to implement the incremental search feature for non-alphabet languages with the Conversion Suggestions API.", + documentation = "https://developer.android.com/about/versions/13/features#text-conversion", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "downloadable-fonts", + name = "Downloadable Fonts", + description = "Download fonts instead of bundling them in the app resources.", + documentation = "https://developer.android.com/develop/ui/views/text-and-emoji/downloadable-fonts", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "hyphenation", + name = "Hyphenation", + description = "Demonstrates different options for the `android:hyphenationFrequency` attribute", + documentation = "https://developer.android.com/reference/android/widget/TextView#attr_android:hyphenationFrequency", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "line-break", + name = "LineBreak", + description = "Demonstrates different options for the `android:lineBreakWordStyle` attribute.", + documentation = "https://developer.android.com/about/versions/13/features#japanese-wrapping", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "linkify", + name = "Linkify", + description = "Linkify is useful for creating links in TextViews.", + documentation = "https://developer.android.com/reference/kotlin/androidx/core/text/util/LinkifyCompat", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "text-span", + name = "TextSpan", + description = "buildSpannedString is useful for quickly building a rich text.", + documentation = "https://developer.android.com/kotlin/ktx#core", + apiSurface = UserInterfaceTextApiSurface, + tags = listOf("Text"), + content = { AndroidFragment() }, + ), + ComposableSampleDemo( + id = "immersive-mode", + name = "Immersive mode", + description = "Immersive mode enables your app to display full-screen by hiding system bars.", + documentation = "https://developer.android.com/develop/ui/views/layout/immersive", + apiSurface = UserInterfaceWindowInsetsApiSurface, + content = { ImmersiveMode() }, + ), + ActivitySampleDemo( + id = "window-insets-animation", + name = "WindowInsetsAnimation", + description = "Shows how to react to the on-screen keyboard (IME) changing visibility, and also controlling the IME's visibility.", + documentation = "https://developer.android.com/develop/ui/views/layout/sw-keyboard", + apiSurface = UserInterfaceWindowInsetsApiSurface, + content = WindowInsetsAnimationActivity::class.java, + ), + ActivitySampleDemo( + id = "window-manager", + name = "WindowManager", + description = "Demonstrates how to use the Jetpack WindowManager library.", + documentation = "https://developer.android.com/jetpack/androidx/releases/window", + apiSurface = UserInterfaceWindowManagerApiSurface, + content = WindowDemosActivity::class.java, + ), + ).associateBy { it.id } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_background.xml b/app/src/main/res/drawable-v24/ic_launcher_background.xml new file mode 100644 index 0000000..59087dc --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_background.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..41a35e0 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml b/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml new file mode 100644 index 0000000..8754bdd --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_monochrome.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..cb2e01c --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..c301006 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..7bd5c90 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..426fed3 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..e5f7b6e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..070cba7 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..79d063a --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,22 @@ + + + + Platform Samples + https://github.com/android/platform-samples/tree/main/samples/%1$s + https://developer.android.com/about + https://github.com/android/platform-samples/issues/new?template=bug_report.md&title=%1$s + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..6cb398d --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values-v31/themes.xml b/samples/user-interface/appwidgets/src/main/res/values-v31/themes.xml new file mode 100644 index 0000000..fc3e2c9 --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values-v31/themes.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values-xlarge/dimens.xml b/samples/user-interface/appwidgets/src/main/res/values-xlarge/dimens.xml new file mode 100644 index 0000000..3fafe67 --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values-xlarge/dimens.xml @@ -0,0 +1,130 @@ + + + + + + + + + 2 + 2 + 180dp + 184dp + 56dp + 56dp + 488dp + 304dp + + + 3 + 1 + 298dp + 64dp + 48dp + 48dp + 488dp + 672dp + + + 2 + 2 + 180dp + 184dp + 48dp + 48dp + 488dp + 672dp + + + 2 + 2 + 180dp + 184dp + 180dp + 184dp + 488dp + 488dp + + + 3 + 2 + 298dp + 184dp + 180dp + 184dp + 488dp + 488dp + + + 2 + 2 + 180dp + 184dp + 180dp + 184dp + 488dp + 488dp + + + 2 + 2 + 180dp + 184dp + 180dp + 1840dp + 488dp + 488dp + + + 2 + 2 + 180dp + 184dp + 180dp + 184dp + 488dp + 488dp + + + 4 + 3 + 298dp + 304dp + 180dp + 184dp + 488dp + 672dp + + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/attrs.xml b/samples/user-interface/appwidgets/src/main/res/values/attrs.xml new file mode 100644 index 0000000..c0b945a --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/attrs.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/colors.xml b/samples/user-interface/appwidgets/src/main/res/values/colors.xml new file mode 100644 index 0000000..95c4e0e --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/colors.xml @@ -0,0 +1,29 @@ + + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #FFE1F5FE + #FF81D4FA + #FF039BE5 + #FF01579B + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/dimens.xml b/samples/user-interface/appwidgets/src/main/res/values/dimens.xml new file mode 100644 index 0000000..50d6bdb --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/dimens.xml @@ -0,0 +1,146 @@ + + + + 0dp + 48dp + + + + + + + + 2 + 2 + 120dp + 115dp + 56dp + 56dp + 624dp + 200dp + + + 4 + 1 + 256dp + 48dp + 48dp + 48dp + 624dp + 422dp + + + 2 + 2 + 120dp + 115dp + 48dp + 48dp + 624dp + 422dp + + + 2 + 2 + 120dp + 115dp + 120dp + 115dp + 624dp + 276dp + + + 4 + 2 + 256dp + 115dp + 120dp + 115dp + 624dp + 422dp + + + 4 + 2 + 256dp + 115dp + 120dp + 115dp + 624dp + 422dp + + + 2 + 2 + 120dp + 115dp + 120dp + 115dp + 624dp + 422dp + + + 4 + 2 + 256dp + 115dp + 120dp + 115dp + 624dp + 422dp + + + 4 + 3 + 256dp + 185dp + 120dp + 115dp + 624dp + 422dp + + + 2 + 2 + 120dp + 48dp + 120dp + 48dp + + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/flags.xml b/samples/user-interface/appwidgets/src/main/res/values/flags.xml new file mode 100644 index 0000000..ac48b30 --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/flags.xml @@ -0,0 +1,19 @@ + + + + false + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/glance_colors.xml b/samples/user-interface/appwidgets/src/main/res/values/glance_colors.xml new file mode 100644 index 0000000..5b9c0de --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/glance_colors.xml @@ -0,0 +1,51 @@ + + + + + @color/m3_sys_color_light_primary + @color/m3_sys_color_light_on_primary + @color/m3_sys_color_light_inverse_primary + @color/m3_sys_color_light_primary_container + @color/m3_sys_color_light_on_primary_container + @color/m3_sys_color_light_secondary + @color/m3_sys_color_light_on_secondary + @color/m3_sys_color_light_secondary_container + @color/m3_sys_color_light_on_secondary_container + @color/m3_sys_color_light_tertiary + @color/m3_sys_color_light_on_tertiary + @color/m3_sys_color_light_tertiary_container + @color/m3_sys_color_light_on_tertiary_container + @color/m3_sys_color_light_background + @color/m3_sys_color_light_on_background + @color/m3_sys_color_light_surface + @color/m3_sys_color_light_on_surface + @color/m3_sys_color_light_surface_variant + @color/m3_sys_color_light_on_surface_variant + @color/m3_sys_color_light_inverse_surface + @color/m3_sys_color_light_inverse_on_surface + @color/m3_sys_color_light_outline + @color/m3_sys_color_light_error + @color/m3_sys_color_light_on_error + @color/m3_sys_color_light_error_container + @color/m3_sys_color_light_on_error_container + #ffe0f3ff + + @color/m3_default_color_primary_text + @color/m3_dark_default_color_primary_text + @color/m3_default_color_secondary_text + @color/m3_dark_default_color_secondary_text + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/strings.xml b/samples/user-interface/appwidgets/src/main/res/values/strings.xml new file mode 100644 index 0000000..e53e802 --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/strings.xml @@ -0,0 +1,96 @@ + + + AppWidget + Pin an App Widget: + Pinning is not supported in the default launcher + Sample to demonstrate how to get the app\'s appwidgets info and request the user to pin them in the launcher. + Tap \"Add to home\" button on any of the items in the canonical widget layouts demo page: + Or tap on any of the items below to request the launcher to pin them: + ☆ Canonical widget layouts + Explore and pin the recommended, intentionally designed widget samples + RemoteViews Weather + Glance Weather + Widget for weather forecast + Icon for weather + Tokyo + Mostly cloudy + 10° + 11° + 12° + 13° + 14° + 15° + 16° + 17° + 4pm + 5pm + 6pm + 7pm + Tuesday + Wednesday + Thursday + Friday + + LayoutSamples + Settings + Refresh + No data + + + Long Text + Text & Image + Text and Image + Image Text List + Checklist + Action List + Image Grid + Expressive Toolbar + Toolbar + Search Toolbar + + + Add + Learn more + See more + Search + + + sample_mark_done + Create visually impactful, scrollable image galleries using image-only grids. Rows and columns automatically adapt to various screen sizes for optimal presentation. + Grid + Text and Image + Include an image for added visual impact. For more information, see Breakpoints to learn how to adapt this layout for different screen sizes. + Easily scannable text and image lists are perfect for showcasing multiple content types, such as news headlines, playlists with album art, or messages. + Image Text List + Provide intuitive control grouping with action lists, where visual on/off states offer immediate feedback on item statuses. + Action List + Long Text + Ideal for titles, status updates, short descriptions, or any scenario where a single line of text effectively conveys the message. + Checklist + The checklist layout is perfect for displaying tasks, providing clear tap targets for users to easily mark items as done. + Toolbar + Increase user engagement and streamline key workflows by providing instant access to your app\'s most important features with a toolbar widget. + Expressive Toolbar + Increase user engagement and streamline key workflows by providing instant access to your app\'s most important features with an expressive shaped toolbar widget. + Search Toolbar + Ideal for apps where search is paramount, this layout provides a dedicated search entry point while allowing for additional shortcuts based on available widget space. + + Full Bleed Snap Scrolling + Full Bleed Snap Scrolling + A full bleed gallery widget demonstrating snap scrolling. Scroll vertically between featured items with an overlaid title and caption. + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/styles.xml b/samples/user-interface/appwidgets/src/main/res/values/styles.xml new file mode 100644 index 0000000..d3980aa --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/styles.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/appwidgets/src/main/res/values/themes.xml b/samples/user-interface/appwidgets/src/main/res/values/themes.xml new file mode 100644 index 0000000..2e42aad --- /dev/null +++ b/samples/user-interface/appwidgets/src/main/res/values/themes.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + diff --git a/samples/user-interface/predictiveback/README.md b/samples/user-interface/predictiveback/README.md new file mode 100644 index 0000000..e635d36 --- /dev/null +++ b/samples/user-interface/predictiveback/README.md @@ -0,0 +1,289 @@ +# Predictive Back Samples + +Shows different types of predictive back animations for a Views based app, including: + ++ Back-to-home ++ Cross-activity ++ Custom cross-activity ++ Cross-fragment animation ++ Shared element cross-fragment animation ++ Custom Progress API animation ++ Custom AndroidX Transition ++ Cross-fragment animation with MaterialSharedAxis ++ setCustomAnimations + +For predictive back on Compose, see [Predictive back in Compose](https://developer.android.com/develop/ui/compose/system/predictive-back) +and [Predictive back Compose snippets](https://github.com/android/snippets/blob/030dddb8f6f319ffb1b3809fe71add6417531fe2/compose/snippets/src/main/java/com/example/compose/snippets/predictiveback/PredictiveBackSnippets.kt) + +## Custom cross-activity + +In general, rely on the default cross-activity animation; however, if required use +`overrideActivityTransition` instead of `overridePendingTransition`. Although animation resources are +expected for `overrideActivityTransition`, we strongly recommend to stop using animation and to +instead use animator and androidx transitions for most use cases. For more details see the +[developer documentation](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture). + +```kotlin + override fun onCreate(savedInstanceState: Bundle?) { + "..." + + overrideActivityTransition( + OVERRIDE_TRANSITION_OPEN, + android.R.anim.fade_in, + 0 + ) + + overrideActivityTransition( + OVERRIDE_TRANSITION_CLOSE, + 0, + android.R.anim.fade_out + ) + } +``` + +## Cross-fragment animation + +Example code uses navigation component default animations. + +```xml + +``` + +## Shared element cross-fragment animation + +Example code shows a simple cross-fragment animation between two [shared elements](https://developer.android.com/guide/fragments/animate#shared) +that automatically works with predictive back gesture navigation provided fragment and androidx +transitions dependencies are upgraded to +[1.7.0](https://developer.android.com/jetpack/androidx/releases/fragment#1.7.0-alpha08) and +[1.5.0](https://developer.android.com/jetpack/androidx/releases/transition#1.5.0-alpha06) respectively. + +On the first fragment, where "second_card" is the `android:transitionName` of a view in the second +fragment: +```kotlin +binding.sharedElementCrossFragment.setOnClickListener { + findNavController().navigate( + R.id.show_PBSharedElementTransitionFragment, + null, + null, + FragmentNavigatorExtras(it to "second_card") + ) +} +``` + +On the second fragment: +```kotlin +override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enterTransition = AutoTransition() + sharedElementEnterTransition = AutoTransition() +} +``` + +Note that `enterTransition` must be specified in addition to `sharedElementEnterTransition`, which +will mark the shared element as seekable and therefore enable the predictive back gesture to play. + +## Custom Progress API animation + +The following example using the Progress API follows the +[Predictive Back Design Guidance](https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back). + +```kotlin +override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + "..." + + val windowWidth = requireActivity().windowManager.currentWindowMetrics.bounds.width() + val maxXShift = windowWidth / 20 + + val predictiveBackCallback = object: OnBackPressedCallback(enabled=false) { + + override fun handleOnBackProgressed(backEvent: BackEventCompat) { + when (backEvent.swipeEdge) { + BackEventCompat.EDGE_LEFT -> + binding.box.translationX = backEvent.progress * maxXShift + BackEventCompat.EDGE_RIGHT -> + binding.box.translationX = -(backEvent.progress * maxXShift) + } + binding.box.scaleX = 1F - (0.1F * backEvent.progress) + binding.box.scaleY = 1F - (0.1F * backEvent.progress) + } + + override fun handleOnBackPressed() { + // your back handling logic + } + + override fun handleOnBackCancelled() { + binding.box.scaleX = 1F + binding.box.scaleY = 1F + binding.box.translationX = 0F + } + } + + requireActivity().onBackPressedDispatcher.addCallback( + this.viewLifecycleOwner, + predictiveBackCallback + ) +} +``` + +## Custom AndroidX Transition +For more details see the +[developer documentation](https://developer.android.com/about/versions/14/features/predictive-back#androidx-transitions). + +```kotlin +class MyFragment : Fragment() { + + val transitionSet = TransitionSet().apply { + addTransition(Fade(Fade.MODE_OUT)) + addTransition(ChangeBounds()) + addTransition(Fade(Fade.MODE_IN)) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val callback = object : OnBackPressedCallback(enabled = false) { + + var controller: TransitionSeekController? = null + + override fun handleOnBackStarted(backEvent: BackEvent) { + // Create the transition + controller = TransitionManager.controlDelayedTransition( + // textContainer is a FrameLayout containing the shortText and longText TextViews + binding.textContainer, + transitionSet + ) + changeTextVisibility(ShowText.SHORT) + } + + override fun handleOnBackProgressed(backEvent: BackEvent) { + // Play the transition as the user swipes back + if (controller?.isReady == true) { + controller?.currentFraction = backEvent.progress + } + } + + override fun handleOnBackPressed() { + // Finish playing the transition when the user commits back + controller?.animateToEnd() + this.isEnabled = false + } + + override fun handleOnBackCancelled() { + // If the user cancels the back gesture, reset the state + transition(ShowText.LONG) + } + } + + binding.shortText.setOnClickListener { + transition(ShowText.LONG) + callback.isEnabled = true + } + + this.requireActivity().onBackPressedDispatcher.addCallback(callback) + } + + private fun transition(showText: ShowText) { + TransitionManager.beginDelayedTransition( + binding.textContainer, + transitionSet + ) + changeTextVisibility(showText) + } + + enum class ShowText { SHORT, LONG } + private fun changeTextVisibility(showText: ShowText) { + when (showText) { + ShowText.SHORT -> { + binding.shortText.isVisible = true + binding.longText.isVisible = false + } + ShowText.LONG -> { + binding.shortText.isVisible = false + binding.longText.isVisible = true + } + } + } +} +``` + +## Cross-fragment animation with MaterialSharedAxis + +MaterialSharedAxis is a Visibility transition. A Visibility transition is triggered when the target +Fragment's visibility is changed or when the Fragment is added or removed. This means MaterialSharedAxis +requires a View to be changing in visibility or to be added or removed to trigger its animation. + +For more details see the +[developer documentation](https://m2.material.io/develop/android/theming/motion#shared-axis). + +```kotlin +// FragmentA.kt + +override fun onCreateView(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false) +} + +// FragmentB.kt + +override fun onCreateView(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false) +} +``` +## setCustomAnimations + +Use `setEnterTransition`, `setExitTransition`, `setReenterTransition`, `setReturnTransition`, +`setSharedElementEnterTransition`, `setSharedElementReturnTransition` instead of `setCustomAnimations` +where possible. + +However, if you are using `setCustomAnimations`, here's a code sample showing +predictive back working with animators and fragment manager. + +```kotlin +// PBSetCustomAnimationsActivity.kt + +override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + "..." + supportFragmentManager.commit { + replace(R.id.fragment_container, PBSetCustomAnimationsFirstFragment()) + } + +} + +// PBSetCustomAnimationsFirstFragment.kt + +override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, +): View { + _binding = FragmentSetCustomAnimationsBinding + .inflate(inflater, container, false) + + binding.box.setOnClickListener { + parentFragmentManager.commit { + setCustomAnimations( + android.R.animator.fade_in, // enter + android.R.animator.fade_out, // exit + android.R.animator.fade_in, // popEnter + android.R.animator.fade_out) // popExit + replace(R.id.fragment_container,PBSetCustomAnimationsSecondFragment()) + setReorderingAllowed(true) + addToBackStack(null) + } + } + return binding.root +} +``` + diff --git a/samples/user-interface/predictiveback/build.gradle.kts b/samples/user-interface/predictiveback/build.gradle.kts new file mode 100644 index 0000000..f8bf8a6 --- /dev/null +++ b/samples/user-interface/predictiveback/build.gradle.kts @@ -0,0 +1,60 @@ + +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.example.platform.ui.predictiveback" + compileSdk = 36 + + defaultConfig { + minSdk = 21 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = "11" + } + + buildFeatures { + viewBinding = true + } + lint { + targetSdk = 35 + } + testOptions { + targetSdk = 35 + } +} + +dependencies { + implementation(libs.androidx.appcompat) + implementation(libs.androidx.constraintlayout) + implementation(libs.material) + implementation(libs.androidx.navigation.fragment) + implementation(libs.androidx.navigation.ui) + implementation(libs.androidx.fragment) + implementation(libs.androidx.transition) +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/AndroidManifest.xml b/samples/user-interface/predictiveback/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a547e25 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBAnimation.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBAnimation.kt new file mode 100644 index 0000000..20f5e14 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBAnimation.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +enum class PBAnimation { + SYS_UI, + BACK_TO_HOME, + CROSS_ACTIVITY, + CUSTOM_CROSS_ACTIVITY, + CROSS_FRAGMENT, + SHARED_ELEMENT_CROSS_FRAGMENT, + PROGRESS_API, + TRANSITION, + MATERIAL_SHARED_AXIS, + SET_CUSTOM_ANIMATIONS +} +data class PBAnimationText(val title: String, val description: String) + +val animations = mapOf( + PBAnimation.SYS_UI to PBAnimationText( + "Enable System Animations", + "The system animations are back-to-home and default cross-activity. To see them: (1) Enable gesture navigation. (2) In Android 14, enable the Predictive Back Developer Option." + ), + PBAnimation.BACK_TO_HOME to PBAnimationText( + "Back-to-Home", + "To see the back-to-home animation, go to the root activity and swipe back from either the left or right edge to exit the app." + ), + PBAnimation.CROSS_ACTIVITY to PBAnimationText( + "Default Cross-Activity", + "To see the default cross-activity animation, on this screen swipe back from either the left or right edge." + ), + PBAnimation.CUSTOM_CROSS_ACTIVITY to PBAnimationText( + "Custom Cross-Activity", + "Click to see a custom cross-activity animation." + ), + PBAnimation.CROSS_FRAGMENT to PBAnimationText( + "Cross-Fragment", + "Click to see cross-fragment animations with Navigation Component's default animators." + ), + PBAnimation.SHARED_ELEMENT_CROSS_FRAGMENT to PBAnimationText( + title = "Shared Element Cross-Fragment", + description = "Click to see a shared element cross-fragment animation." + ), + PBAnimation.PROGRESS_API to PBAnimationText( + "Progress API", + "Click to see an animation created with the Predictive Back Progress API." + ), + PBAnimation.TRANSITION to PBAnimationText( + "Transition", + "Click to see an animation created with AndroidX Transitions and the Predictive Back Progress API." + ), + PBAnimation.MATERIAL_SHARED_AXIS to PBAnimationText( + "Material Shared Axis", + "Click to see an animation created with Material Shared Axis." + ), + PBAnimation.SET_CUSTOM_ANIMATIONS to PBAnimationText( + "setCustomAnimations", + "Click to see an animation created with setCustomAnimations." + ) + +) \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBCustomCrossActivityAnimation.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBCustomCrossActivityAnimation.kt new file mode 100644 index 0000000..73a8224 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBCustomCrossActivityAnimation.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatActivity +import com.example.platform.ui.predictiveback.databinding.ActivityCustomCrossAnimationBinding + +class PBCustomCrossActivityAnimation : AppCompatActivity() { + + private lateinit var binding: ActivityCustomCrossAnimationBinding + + @RequiresApi(34) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + + binding = ActivityCustomCrossAnimationBinding.inflate(layoutInflater) + setContentView(binding.root) + + overrideActivityTransition( + OVERRIDE_TRANSITION_OPEN, + android.R.anim.fade_in, + 0 + ) + + + overrideActivityTransition( + OVERRIDE_TRANSITION_CLOSE, + 0, + android.R.anim.fade_out + ) + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBHostingActivity.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBHostingActivity.kt new file mode 100644 index 0000000..8377419 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBHostingActivity.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity +import com.example.platform.ui.predictiveback.databinding.HostingActivityBinding + +class PBHostingActivity : FragmentActivity() { + + private lateinit var binding: HostingActivityBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + + binding = HostingActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBListFragment.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBListFragment.kt new file mode 100644 index 0000000..d766cf4 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBListFragment.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.FragmentNavigatorExtras +import androidx.navigation.fragment.findNavController +import com.example.platform.ui.predictiveback.databinding.FragmentAnimationListBinding +import com.google.android.material.transition.MaterialSharedAxis + +class PBListFragment : Fragment() { + + private var _binding: FragmentAnimationListBinding? = null + private val binding get() = _binding!! + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentAnimationListBinding.inflate(inflater, container, false) + + setAnimationText() + + exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false) + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.customCrossActivityCard.setOnClickListener { + findNavController().navigate(R.id.show_PBCustomCrossActivityAnimation) + } + binding.crossFragmentCard.setOnClickListener { + findNavController().navigate(R.id.show_PBNavigationComponentDefaultAnimations) + } + binding.sharedElementCrossFragment.setOnClickListener { + findNavController().navigate( + R.id.show_PBSharedElementTransitionFragment, + null, + null, + FragmentNavigatorExtras(it to "second_card") + ) + } + binding.progressApiCard.setOnClickListener { + findNavController().navigate(R.id.show_PBProgressAPI) + } + binding.transitionsCard.setOnClickListener { + findNavController().navigate(R.id.show_PBTransition) + } + binding.materialSharedAxisCard.setOnClickListener { + findNavController().navigate(R.id.show_PBMaterialSharedAxisAnimations) + } + binding.setCustomAnimationsCard.setOnClickListener { + findNavController().navigate(R.id.show_PBSetCustomAnimationsActivity) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun setAnimationText() { + binding.sysUiTitle.text = animations[PBAnimation.SYS_UI]?.title ?: "" + binding.sysUiDescription.text = animations[PBAnimation.SYS_UI]?.description ?: "" + binding.backToHomeTitle.text = animations[PBAnimation.BACK_TO_HOME]?.title ?: "" + binding.backToHomeDescription.text = animations[PBAnimation.BACK_TO_HOME]?.description ?: "" + binding.crossActivityTitle.text = animations[PBAnimation.CROSS_ACTIVITY]?.title ?: "" + binding.crossActivityDescription.text = animations[PBAnimation.CROSS_ACTIVITY]?.description ?: "" + binding.customCrossActivityTitle.text = animations[PBAnimation.CUSTOM_CROSS_ACTIVITY]?.title ?: "" + binding.customCrossActivityDescription.text = animations[PBAnimation.CUSTOM_CROSS_ACTIVITY]?.description ?: "" + binding.crossFragmentTitle.text = animations[PBAnimation.CROSS_FRAGMENT]?.title ?: "" + binding.crossFragmentDescription.text = animations[PBAnimation.CROSS_FRAGMENT]?.description ?: "" + binding.sharedElementCrossFragmentTitle.text = animations[PBAnimation.SHARED_ELEMENT_CROSS_FRAGMENT]?.title ?: "" + binding.sharedElementCrossFragmentDescription.text = animations[PBAnimation.SHARED_ELEMENT_CROSS_FRAGMENT]?.description ?: "" + binding.progressApiTitle.text = animations[PBAnimation.PROGRESS_API]?.title ?: "" + binding.progressApiDescription.text = animations[PBAnimation.PROGRESS_API]?.description ?: "" + binding.transitionsTitle.text = animations[PBAnimation.TRANSITION]?.title ?: "" + binding.transitionsDescription.text = animations[PBAnimation.TRANSITION]?.description ?: "" + binding.materialSharedAxisTitle.text = animations[PBAnimation.MATERIAL_SHARED_AXIS]?.title ?: "" + binding.materialSharedAxisDescription.text = animations[PBAnimation.MATERIAL_SHARED_AXIS]?.description ?: "" + binding.setCustomAnimationsTitle.text = animations[PBAnimation.SET_CUSTOM_ANIMATIONS]?.title ?: "" + binding.setCustomAnimationsDescription.text = animations[PBAnimation.SET_CUSTOM_ANIMATIONS]?.description ?: "" + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBMaterialSharedAxisAnimations.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBMaterialSharedAxisAnimations.kt new file mode 100644 index 0000000..94fe80a --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBMaterialSharedAxisAnimations.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.platform.ui.predictiveback.databinding.FragmentMaterialSharedAxisBinding +import com.google.android.material.transition.MaterialSharedAxis + +class PBMaterialSharedAxisAnimations : Fragment() { + + private var _binding: FragmentMaterialSharedAxisBinding? = null + private val binding get() = _binding!! + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentMaterialSharedAxisBinding + .inflate(inflater, container, false) + + enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ true) + returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, /* forward= */ false) + + return binding.root + } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBNavigationComponentDefaultAnimations.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBNavigationComponentDefaultAnimations.kt new file mode 100644 index 0000000..c3c102e --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBNavigationComponentDefaultAnimations.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.platform.ui.predictiveback.databinding.FragmentNavigationComponentDefaultAnimationsBinding + +class PBNavigationComponentDefaultAnimations : Fragment() { + + private var _binding: FragmentNavigationComponentDefaultAnimationsBinding? = null + private val binding get() = _binding!! + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentNavigationComponentDefaultAnimationsBinding + .inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBProgressAPI.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBProgressAPI.kt new file mode 100644 index 0000000..86cced3 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBProgressAPI.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Build +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.Interpolator +import android.view.animation.PathInterpolator +import android.widget.Toast +import androidx.activity.BackEventCompat +import androidx.activity.OnBackPressedCallback +import com.example.platform.ui.predictiveback.databinding.FragmentProgressApiBinding + +class PBProgressAPI : Fragment() { + companion object { + val STANDARD_DECELERATE: Interpolator = PathInterpolator(0f, 0f, 0f, 1f) + } + private var _binding: FragmentProgressApiBinding? = null + private val binding get() = _binding!! + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentProgressApiBinding + .inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val windowWidth = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + requireActivity().windowManager.currentWindowMetrics.bounds.width() + } else { + 0 + } + val maxXShift = windowWidth / 20 + + val predictiveBackCallback = object: OnBackPressedCallback(enabled=false) { + + // Follows Predictive Back Design Guidance: https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back + override fun handleOnBackProgressed(backEvent: BackEventCompat) { + // Applies a decelerate interpolator + val backProgress = backEvent.progress + val interpolatedProgress = STANDARD_DECELERATE.getInterpolation(backProgress) + + when (backEvent.swipeEdge) { + BackEventCompat.EDGE_LEFT -> + binding.box.translationX = interpolatedProgress * maxXShift + BackEventCompat.EDGE_RIGHT -> + binding.box.translationX = -(interpolatedProgress * maxXShift) + } + binding.box.scaleX = 1F - (0.1F * interpolatedProgress) + binding.box.scaleY = 1F - (0.1F * interpolatedProgress) + } + + override fun handleOnBackPressed() { + Toast.makeText( + context, + "Disable the callback to go back.", + Toast.LENGTH_SHORT + ).show() + resetCallbackState() + } + + override fun handleOnBackCancelled() { + resetCallbackState() + } + + private fun resetCallbackState() { + binding.box.scaleX = 1F + binding.box.scaleY = 1F + binding.box.translationX = 0F + } + + } + requireActivity().onBackPressedDispatcher.addCallback( + this.viewLifecycleOwner, + predictiveBackCallback + ) + updateCallbackState(predictiveBackCallback) + + binding.button.setOnClickListener { + predictiveBackCallback.isEnabled = !predictiveBackCallback.isEnabled + updateCallbackState(predictiveBackCallback) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun updateCallbackState(callback: OnBackPressedCallback) { + when (callback.isEnabled) { + true -> { + binding.button.text = "disable callback" + binding.body.text = + "Swipe back to see the box move using the Predictive Back" + + " Progress APIs on Android 14+ devices." + } + false -> { + binding.button.text = "enable callback" + binding.body.text = + "Enable the callback so that it can intercept your back gestures." + } + } + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsActivity.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsActivity.kt new file mode 100644 index 0000000..463604f --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsActivity.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.fragment.app.commit +import com.example.platform.ui.predictiveback.databinding.ActivitySetCustomAnimationsBinding + +class PBSetCustomAnimationsActivity : AppCompatActivity() { + private lateinit var binding: ActivitySetCustomAnimationsBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + + binding = ActivitySetCustomAnimationsBinding.inflate(layoutInflater) + setContentView(binding.root) + + supportFragmentManager.commit { + replace(R.id.fragment_container, PBSetCustomAnimationsFirstFragment()) + } + + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsFirstFragment.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsFirstFragment.kt new file mode 100644 index 0000000..8544d76 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsFirstFragment.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.commit +import com.example.platform.ui.predictiveback.databinding.FragmentSetCustomAnimationsBinding + +class PBSetCustomAnimationsFirstFragment : Fragment() { + + private var _binding: FragmentSetCustomAnimationsBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentSetCustomAnimationsBinding + .inflate(inflater, container, false) + + binding.button.setOnClickListener { + parentFragmentManager.commit { + setCustomAnimations( + android.R.animator.fade_in, // enter + android.R.animator.fade_out, // exit + android.R.animator.fade_in, // popEnter + android.R.animator.fade_out) // popExit + replace(R.id.fragment_container,PBSetCustomAnimationsSecondFragment()) + setReorderingAllowed(true) + addToBackStack(null) + } + } + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsSecondFragment.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsSecondFragment.kt new file mode 100644 index 0000000..fc8795e --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSetCustomAnimationsSecondFragment.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.platform.ui.predictiveback.databinding.FragmentSetCustomAnimationsSecondBinding + +class PBSetCustomAnimationsSecondFragment : Fragment() { + + private var _binding: FragmentSetCustomAnimationsSecondBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentSetCustomAnimationsSecondBinding + .inflate(inflater, container, false) + + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSharedElementTransitionFragment.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSharedElementTransitionFragment.kt new file mode 100644 index 0000000..487528f --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBSharedElementTransitionFragment.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.transition.AutoTransition +import com.example.platform.ui.predictiveback.databinding.FragmentSharedElementTransitionBinding + +class PBSharedElementTransitionFragment : Fragment() { + + private var _binding: FragmentSharedElementTransitionBinding? = null + private val binding get() = _binding!! + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enterTransition = AutoTransition() + sharedElementEnterTransition = AutoTransition() + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentSharedElementTransitionBinding + .inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBTransition.kt b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBTransition.kt new file mode 100644 index 0000000..1adfcd1 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/java/com/example/platform/ui/predictiveback/PBTransition.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.platform.ui.predictiveback + +import android.os.Bundle +import android.text.method.ScrollingMovementMethod +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.BackEventCompat +import androidx.activity.OnBackPressedCallback +import androidx.core.view.isVisible +import androidx.transition.ChangeBounds +import androidx.transition.Fade +import androidx.transition.TransitionManager +import androidx.transition.TransitionSeekController +import androidx.transition.TransitionSet +import com.example.platform.ui.predictiveback.databinding.FragmentTransitionBinding + +class PBTransition : Fragment() { + private var _binding: FragmentTransitionBinding? = null + private val binding get() = _binding!! + + val transitionSet = TransitionSet().apply { + addTransition(Fade(Fade.MODE_OUT)) + addTransition(ChangeBounds()) + addTransition(Fade(Fade.MODE_IN)) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = FragmentTransitionBinding + .inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + changeTextVisibility(ShowText.SHORT) + + val callback = object : OnBackPressedCallback(enabled = false) { + + var controller: TransitionSeekController? = null + + override fun handleOnBackStarted(backEvent: BackEventCompat) { + controller = TransitionManager.controlDelayedTransition( + binding.textContainer, + transitionSet + ) + changeTextVisibility(ShowText.SHORT) + } + + override fun handleOnBackProgressed(backEvent: BackEventCompat) { + if (controller?.isReady == true) { + controller?.currentFraction = backEvent.progress + } + } + + override fun handleOnBackPressed() { + controller?.animateToEnd() + this.isEnabled = false + } + + override fun handleOnBackCancelled() { + // If the user cancels the back gesture, reset the state + TransitionManager.beginDelayedTransition( + binding.textContainer, + ChangeBounds() + ) + changeTextVisibility(ShowText.LONG) + } + } + + binding.shortText.setOnClickListener { + TransitionManager.beginDelayedTransition(binding.textContainer, transitionSet) + changeTextVisibility(ShowText.LONG) + callback.isEnabled = true + } + + this.requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback) + + binding.longText.movementMethod = ScrollingMovementMethod() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + enum class ShowText { SHORT, LONG } + private fun changeTextVisibility(showText: ShowText) { + when (showText) { + ShowText.SHORT -> { + binding.shortText.isVisible = true + binding.longText.isVisible = false + binding.body.text = "Click on the box." + } + ShowText.LONG -> { + binding.shortText.isVisible = false + binding.longText.isVisible = true + binding.body.text = "Swipe back slowly to see the Predictive Back AndroidX Transition." + } + } + } +} \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/drawable/android.xml b/samples/user-interface/predictiveback/src/main/res/drawable/android.xml new file mode 100644 index 0000000..02adeae --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/drawable/android.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/samples/user-interface/predictiveback/src/main/res/drawable/box.xml b/samples/user-interface/predictiveback/src/main/res/drawable/box.xml new file mode 100644 index 0000000..cb9828a --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/drawable/box.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/drawable/button.xml b/samples/user-interface/predictiveback/src/main/res/drawable/button.xml new file mode 100644 index 0000000..68a8983 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/drawable/button.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/activity_custom_cross_animation.xml b/samples/user-interface/predictiveback/src/main/res/layout/activity_custom_cross_animation.xml new file mode 100644 index 0000000..73e092e --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/activity_custom_cross_animation.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/activity_set_custom_animations.xml b/samples/user-interface/predictiveback/src/main/res/layout/activity_set_custom_animations.xml new file mode 100644 index 0000000..562c87c --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/activity_set_custom_animations.xml @@ -0,0 +1,35 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/fragment_animation_list.xml b/samples/user-interface/predictiveback/src/main/res/layout/fragment_animation_list.xml new file mode 100644 index 0000000..7ffef21 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/fragment_animation_list.xml @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/fragment_material_shared_axis.xml b/samples/user-interface/predictiveback/src/main/res/layout/fragment_material_shared_axis.xml new file mode 100644 index 0000000..c177ab7 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/fragment_material_shared_axis.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/fragment_navigation_component_default_animations.xml b/samples/user-interface/predictiveback/src/main/res/layout/fragment_navigation_component_default_animations.xml new file mode 100644 index 0000000..a5386f4 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/fragment_navigation_component_default_animations.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/fragment_progress_api.xml b/samples/user-interface/predictiveback/src/main/res/layout/fragment_progress_api.xml new file mode 100644 index 0000000..c1ee265 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/fragment_progress_api.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/user-interface/predictiveback/src/main/res/layout/fragment_set_custom_animations.xml b/samples/user-interface/predictiveback/src/main/res/layout/fragment_set_custom_animations.xml new file mode 100644 index 0000000..bf1e3f2 --- /dev/null +++ b/samples/user-interface/predictiveback/src/main/res/layout/fragment_set_custom_animations.xml @@ -0,0 +1,63 @@ + + + + + +