Showing preview only (367K chars total). Download the full file or copy to clipboard to get everything.
Repository: uber/artist
Branch: master
Commit: 000f33e4812d
Files: 129
Total size: 327.5 KB
Directory structure:
gitextract_ob0qypit/
├── .buildscript/
│ └── deploy_snapshot.sh
├── .editorconfig
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── RELEASING.md
├── artist/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── kotlin/
│ │ └── com/
│ │ └── uber/
│ │ └── artist/
│ │ ├── ArtistExtension.kt
│ │ ├── ArtistPlugin.kt
│ │ ├── ArtistTask.kt
│ │ └── internal/
│ │ └── util/
│ │ └── Util.kt
│ └── resources/
│ └── META-INF/
│ └── gradle-plugins/
│ └── com.uber.artist.properties
├── artist-api/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ └── kotlin/
│ └── com/
│ └── uber/
│ └── artist/
│ └── api/
│ ├── JavaTrait.kt
│ ├── JavaTraitService.kt
│ ├── JavaTypeNames.kt
│ ├── JavaViewStencil.kt
│ ├── JavaViewStencilProvider.kt
│ ├── JavaViewStencilService.kt
│ ├── KotlinTrait.kt
│ ├── KotlinTraitService.kt
│ ├── KotlinTypeNames.kt
│ ├── KotlinViewStencil.kt
│ ├── KotlinViewStencilProvider.kt
│ ├── KotlinViewStencilService.kt
│ ├── Trait.kt
│ ├── TraitService.kt
│ ├── TypeNames.kt
│ ├── ViewStencil.kt
│ ├── ViewStencilProvider.kt
│ ├── ViewStencilService.kt
│ └── alias/
│ └── AliasTypeNames.kt
├── artist-core/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ ├── main/
│ │ └── kotlin/
│ │ └── com/
│ │ └── uber/
│ │ └── artist/
│ │ ├── Artist.kt
│ │ ├── ArtistCodeGenerator.kt
│ │ ├── FormattingFileWriter.kt
│ │ ├── JavaArtistCodeGenerator.kt
│ │ ├── JavaFormattingFileWriter.kt
│ │ └── KotlinArtistCodeGenerator.kt
│ └── test/
│ └── kotlin/
│ └── com/
│ └── uber/
│ └── artist/
│ └── ArtistTest.kt
├── artist-traits/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ └── kotlin/
│ └── com/
│ └── uber/
│ └── artist/
│ └── traits/
│ ├── JavaForegroundTrait.kt
│ ├── JavaSuppressNullabilityInitializerTrait.kt
│ ├── JavaVisibilityTrait.kt
│ ├── KotlinForegroundTrait.kt
│ ├── KotlinSuppressNullabilityInitializerTrait.kt
│ └── KotlinVisibilityTrait.kt
├── artist-traits-rx/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ └── kotlin/
│ └── com/
│ └── uber/
│ └── artist/
│ └── traits/
│ └── rx/
│ ├── JavaApiHelper.kt
│ ├── JavaCheckableTrait.kt
│ ├── JavaRxTypeNames.kt
│ ├── JavaScrollableTrait.kt
│ ├── JavaTextInputTrait.kt
│ ├── JavaViewTrait.kt
│ ├── KotlinApiHelper.kt
│ ├── KotlinCheckableTrait.kt
│ ├── KotlinRxTypeNames.kt
│ ├── KotlinScrollableTrait.kt
│ ├── KotlinTextInputTrait.kt
│ ├── KotlinViewTrait.kt
│ └── config/
│ ├── ArtistRxConfig.kt
│ ├── ArtistRxConfigService.kt
│ ├── JavaArtistRxConfig.kt
│ ├── JavaArtistRxConfigService.kt
│ ├── JavaDefaultArtistRxConfig.kt
│ ├── KotlinArtistRxConfig.kt
│ ├── KotlinArtistRxConfigService.kt
│ └── KotlinDefaultArtistRxConfig.kt
├── build.gradle
├── buildSrc/
│ ├── build.gradle
│ └── settings.gradle
├── config/
│ ├── checkstyle/
│ │ ├── checkstyle-suppressions.xml
│ │ ├── checkstyle-test.xml
│ │ └── checkstyle.xml
│ └── lint/
│ └── lint.xml
├── gradle/
│ ├── dependencies.gradle
│ ├── gradle-mvn-push.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── sample/
│ ├── app/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── uber/
│ │ │ └── artist/
│ │ │ └── myapplication/
│ │ │ └── MainActivity.kt
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── divider.xml
│ │ ├── layout/
│ │ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ └── values/
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── demo/
│ │ └── java/
│ │ ├── MyButton.java
│ │ ├── MyEditText.java
│ │ ├── MyImageView.java
│ │ ├── MyLinearLayout.java
│ │ ├── MyNestedScrollView.java
│ │ ├── MySwitch.java
│ │ └── MyTextView.java
│ ├── library/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── uber/
│ │ │ └── artist/
│ │ │ └── mylibrary/
│ │ │ ├── MyUtils.java
│ │ │ ├── MyView.java
│ │ │ └── Signal.java
│ │ └── res/
│ │ └── values/
│ │ ├── attrs_foreground_view.xml
│ │ └── strings.xml
│ ├── providers/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── com/
│ │ └── uber/
│ │ └── artist/
│ │ └── myproviders/
│ │ ├── JavaSampleRxConfig.java
│ │ ├── JavaSampleTypeNames.java
│ │ ├── JavaSampleViewStencilProvider.java
│ │ └── trait/
│ │ └── JavaSampleTrait.java
│ └── providers-kotlin/
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── kotlin/
│ └── com/
│ └── uber/
│ └── artist/
│ └── myproviders/
│ ├── KotlinSampleRxConfig.kt
│ ├── KotlinSampleTypeNames.kt
│ ├── KotlinSampleViewStencilProvider.kt
│ └── trait/
│ └── KotlinSampleTrait.kt
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .buildscript/deploy_snapshot.sh
================================================
#!/bin/bash
#
# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.
#
# Adapted from https://coderwall.com/p/9b_lfq and
# http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
SLUG="uber/artist"
JDK="oraclejdk8"
BRANCH="master"
set -e
if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then
echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'."
elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then
echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'."
elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo "Skipping snapshot deployment: was pull request."
elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then
echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'."
else
echo "Deploying snapshot..."
./gradlew clean uploadArchives -Dorg.gradle.parallel=false
echo "Snapshot deployed!"
fi
================================================
FILE: .editorconfig
================================================
[*.{kt,kts}]
# possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
indent_size=2
continuation_indent_size=4
insert_final_newline=true
# possible values: number (e.g. 120) (package name, imports & comments are ignored), "off"
max_line_length=off
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
Thanks for using Artist. Before you create an issue, please consider the following points:
- [ ] If you think you found a bug, please include a code sample that reproduces the problem. A test case that reproduces the issue is preferred. A stack trace alone is ok but may not contain enough context for us to address the issue.
- [ ] Please include the library version number, including the minor and patch version, in the issue text.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Thank you for contributing to Artist. Before pressing the "Create Pull Request" button, please provide the following:
- [ ] A description about what and why you are contributing, even if it's trivial.
- [ ] The issue number(s) or PR number(s) in the description if you are contributing in response to those.
- [ ] If applicable, unit tests.
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on: [push, pull_request]
jobs:
build:
name: JDK ${{ matrix.java_version }}
runs-on: ubuntu-latest
strategy:
matrix:
java_version: [8]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install JDK ${{ matrix.java_version }}
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: ${{ matrix.java_version }}
- name: Install Android SDK
uses: malinskiy/action-android/install-sdk@release/0.1.1
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1
- name: Configure Gradle
# Install Gradle Deps
run: ./gradlew help
- name: Build project
run: ./gradlew assemble --stacktrace
- name: Run tests
run: ./gradlew test --stacktrace
- name: Gradle Check
run: ./gradlew check --stacktrace
- name: Upload snapshot (main only)
run: ./gradlew uploadArchives -PSONATYPE_NEXUS_USERNAME="$SONATYPE_NEXUS_USERNAME" -PSONATYPE_NEXUS_PASSWORD="$SONATYPE_NEXUS_PASSWORD"
env:
SONATYPE_NEXUS_USERNAME: ${{ secrets.SonatypeUsername }}
SONATYPE_NEXUS_PASSWORD: ${{ secrets.SonatypePassword }}
if: success() && github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && matrix.java_version == '8'
================================================
FILE: .gitignore
================================================
###OSX###
.DS_Store
.AppleDouble
.LSOverride
# Icon must ends with two \r.
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
###Linux###
*~
# KDE directory preferences
.directory
###Android###
# Built application files
*.apk
*.ap_
# Files for ART and Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Gradle files
.gradle/
.gradletasknamecache
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Lint
lint-report.html
lint-report_files/
lint_result.txt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
###IntelliJ###
*.iml
*.ipr
*.iws
.idea/
###Eclipse###
*.pydevproject
.metadata
tmp/
*.tmp
*.bak
*.swp
*~.nib
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
# kotlin
annotations/
================================================
FILE: CHANGELOG.md
================================================
Changelog
=========
Version 0.4.9
-------------
_2023-07-27_
* Update version upstreaming unmerged fixes
Version 0.4.8
-------------
_2023-07-18_
* Fixes Rx Kotlin nulls [issue](https://github.com/uber/artist/issues/49)
Version 0.4.7
-------------
_2021-02-06_
* Add support for AndroidX
Version 0.4.6
-------------
_2021-02-08_
Version 0.4.5
-------------
_2018-12-19_
* **Breaking change:** API classes renamed with "Java" prefixes. Class names and AutoService annotations will need to be updated
* Added Kotlin-prefixed API classes modules for generating Kotlin views
Version 0.3.0
-------------
_2018-12-03_
* **Breaking change:** Project migrated to [AndroidX](https://developer.android.com/jetpack/androidx/). See the [class and package mappings](https://developer.android.com/jetpack/androidx/migrate) for help migrating
Version 0.2.2
-------------
_2018-11-07_
* **Note:** This is the final version that uses the non-AndroidX Support Library
* Annotate underlying `setOnClickListener(listener)` method param as `@Nullable` to match AOSP
* Dependency updates including using Support Library, Kotlin, RxJava, and RxBinding
Version 0.2.1
-------------
_2018-07-01_
* Separated core artist functionality into a module
Version 0.2.0
-------------
_2018-04-23_
* **Breaking change:** Removed `TraitProvider` in favor of annotating with `AutoService(Trait.class)`
* `ViewStencilProvider` and `ArtistRxConfig` implementations can also be annotated with `AutoService`
Version 0.1.0
-------------
_2017-12-11_
* Initial release
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mobile-open-source@uber.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.md
================================================
Contributing to Uber's Android Template
=======================
Uber welcomes contributions of all kinds and sizes. This includes everything from from simple bug reports to large features.
Workflow
--------
We love GitHub issues!
For small feature requests, an issue first proposing it for discussion or demo implementation in a PR suffice.
For big features, please open an issue so that we can agree on the direction, and hopefully avoid investing a lot of time on a feature that might need reworking.
Small pull requests for things like typos, bug fixes, etc are always welcome.
DOs and DON'Ts
--------------
* DO follow our [coding style](https://github.com/uber/java-code-styles)
* DO include tests when adding new features. When fixing bugs, start with adding a test that highlights how the current behavior is broken.
* DO keep the discussions focused. When a new or related topic comes up it's often better to create new issue than to side track the discussion.
* DON'T submit PRs that alter licensing related files or headers. If you believe there's a problem with them, file an issue and we'll be happy to discuss it.
Guiding Principles
------------------
* We allow anyone to participate in our projects. Tasks can be carried out by anyone that demonstrates the capability to complete them
* Always be respectful of one another. Assume the best in others and act with empathy at all times
* Collaborate closely with individuals maintaining the project or experienced users. Getting ideas out in the open and seeing a proposal before it's a pull request helps reduce redundancy and ensures we're all connected to the decision making process
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# Artist [](https://travis-ci.org/uber/artist)
As Android apps grow, providing common features and consistent functionality across Views becomes challenging. Typically, this results in copy-pasting features across views, monolithic classes, or complicated inheritance trees. Artist is a highly-extensible platform for creating and maintaining an app’s base set of Android views.
## Overview
Artist is a Gradle plugin written in Kotlin that generates a base set of Android `View`s. Artist-generated views are created using a stencil and trait system. Each view type is declared with a single stencil, which is comprised of a set of traits. All of this comes together to create an easily maintainable system of stencils and traits.
*Stencils*: A `Stencil` defines a View class to be generated. Each `Stencil` has some properties that can be configured and declares a set of traits they exhibit.
*Traits*: A `Trait` defines the new functionality that should be added to a view. It is a hook into the `Stencil`’s codegen process that is called during each `Stencil`’s generation. It is responsible for generating the code that implements `Trait`'s functionality. This could be used to do things like add automatic view analytics to every view or add first-party support for RxBinding APIs (clicks, attach events, visibility changes, etc.) on all your views.
A simple `Trait` that adds visibility helper methods would look like:
```kotlin
@AutoService(JavaTrait::class)
class VisibilityTrait : JavaTrait {
override fun generateFor(type: Builder, initMethod: MethodSpec.Builder, rClass: ClassName, baseType: String) {
arrayOf("visible", "invisible", "gone")
.forEach { type.addMethod(createVisibilityConvenienceMethod(it)) }
}
private fun createVisibilityConvenienceMethod(type: String): MethodSpec {
return MethodSpec.methodBuilder("is${type.capitalize()}")
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.BOOLEAN)
.addStatement("return getVisibility() == \$T.${type.toUpperCase()}", TypeNames.Android.View)
.build()
}
}
```
A simple `ViewStencil` to generate a `Switch` with visibility helper methods would look like:
```kotlin
class SwitchStencil : JavaViewStencil(
extendedType = "android.support.v7.widget.SwitchCompat",
constructorCount = 3,
defaultAttrRes = "switchStyle",
addedTraits = VisibilityTrait::class.java) {
override fun name() = "MySwitch"
}
```
Finally leaving you with a generated view like this:
```java
public class MySwitch extends SwitchCompat {
// Constructors
// protected init method - provided in every stencil
public boolean isVisible() {
return getVisibility() == View.VISIBLE;
}
public boolean isGone() {
return getVisibility() == View.GONE;
}
public boolean isInvisible() {
return getVisibility() == View.INVISIBLE;
}
}
```
This may look like a lot of boilerplate for simple helpers, but it scales quite well when you want to have these methods on _all_ your base views.
## Motivation
#### Common Façade
Everything is behind the façade of commonly named classes, basically "[YOUR_PREFIX]ViewName". This allows you to push as much functionality as you want behind them whilst not changing the front facing entry point. Things we can push behind them include new functionality, other base classes, framework bug fixes, etc.
#### Sane, simple maintainability
The stencil and trait system ensures that base views are defined in one place and that extra functionality is divided up into single-focus traits.
#### Reactive Semantics
Artist-generated views can have [RxBinding](https://github.com/JakeWharton/RxBinding) APIs as first class citizens in their public APIs. In a increasingly reactive world, this gracefully bridges common UI listener interactions to RxJava streams. This can optionally be brought in via the `artist-traits-rx` module.
#### Intelligence
Artist-generated views have deep internal knowledge of their internal state and interactions. This gives you flexibility to do a number of interesting, contextual actions under the hood.
*Automatic Instrumentation*: Artist-generated views know when they're being attached, changed to visible, clicked, etc. This allows you to do automatic instrumentation of impressions and taps in views when they occur, provided the developer has provided an ID. You can also detect and signal a developer if an ID is missing where there should be one.
*Accessibility*: This intelligence gives you enough insight into the state of the view hierarchy to make accessibility a first class citizen in the daily development cycle. Artist-generated views can intelligently infer if there are content description errors associated with them, and signal them to developers in the apps.
For more examples of things you can do with Artist, check out the [Recipes](https://github.com/uber/artist/wiki/Recipes) wiki page.
## Usage
#### Create the Provider module
- Create a new plain Java/Kotlin module (non-Android)
- Add Artist dependencies (API, Traits, Traits-Rx)
#### Implement the Stencil Provider
- Create a class that implements `JavaViewStencilProvider`
- Annotate your class with `@AutoService(JavaViewStencilProvider::class)`
#### Implement Custom Traits (Optional)
- If you have custom traits, then create classes that implement `JavaTrait`
- Annotate those classes with `@AutoService(JavaTrait::class)`
#### Add Provider module to Plugin Classpath
_Option #1_
If your provider module is in it's own project, then you can add the JAR to the buildscript classpath in your main project's root `build.gradle` like:
```groovy
buildscript {
dependencies {
classpath <include for your jar>
}
}
```
_Option #2_
Otherwise, if your provider module is in your primary project, then in order for Artist to find the classes on the plugin classpath during code generation, we must leverage Gradle's `buildSrc`. We use this project within your project to build the classes that will be added to the plugin classpath. This will run before your primary project is built.
- Create a dir at root of project named `buildSrc`
- Navigate to `buildSrc` and add a relative symlink to the provider module `cd $PROJECT_ROOT/buildSrc; ln -s ../path/to/provider/module/root custom-artist-providers`
- Create a `settings.gradle` in `buildSrc` and add `include :custom-artist-providers`
- Update the `build.gradle` for the `buildSrc` project to ensure that the `custom-artist-providers` module is added the buildScript classpath so it is available to the Artist plugin:
```groovy
subprojects { subproject ->
if (subproject.buildFile.exists()) {
repositories {
jcenter()
google()
}
rootProject.dependencies {
runtime project(path)
}
}
subproject.afterEvaluate {
// Disable useless tasks in buildSrc
if (subproject.plugins.hasPlugin("kotlin")) {
subproject.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions.suppressWarnings = true
}
}
subproject.tasks.findAll {
it.name.toLowerCase().contains("test") ||
it.name.toLowerCase().contains("lint") ||
it.name.toLowerCase().contains("checkstyle") }.each {
it.enabled = false
}
}
}
```
#### Use the Generated Views
The [generated views](https://github.com/uber/artist/tree/master/sample/demo/java) will be added to the library's source files. They can then be consumed as regular views. To add even more consistency, you can write a lint rule or ErrorProne check to ensure that all `View` subclasses use your Artist-generated views.
## Further examples
The set of `JavaViewStencil`s that Artist should process are provided via the `JavaViewStencilProvider`. The [sample's ViewStencilProvider](https://github.com/uber/artist/blob/master/sample/providers/src/main/java/com/uber/artist/myproviders/SampleViewStencilProvider.java) would configure Artist to generate [these Views](https://github.com/uber/artist/tree/master/sample/library/build/generated/source/artist/release/com/uber/artist/mylibrary).
## Download
Artist Plugin [](https://mvnrepository.com/artifact/com.uber.artist/artist)
```gradle
classpath 'com.uber.artist:artist:0.4.9'
```
Artist API [](https://mvnrepository.com/artifact/com.uber.artist/artist-api)
```gradle
classpath 'com.uber.artist:artist-api:0.4.9'
```
Artist Traits [](https://mvnrepository.com/artifact/com.uber.artist/artist-traits)
```gradle
classpath 'com.uber.artist:artist-traits:0.4.9'
```
Artist Rx Traits [](https://mvnrepository.com/artifact/com.uber.artist/artist-traits-rx)
```gradle
classpath 'com.uber.artist:artist-traits-rx:0.4.9'
```
## License
```
Copyright (C) 2017 Uber Technologies
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
================================================
FILE: RELEASING.md
================================================
Releasing
=========
1. Change the version in `gradle.properties` to a non-SNAPSHOT version.
2. Update the `CHANGELOG.md` for the impending release.
3. Update the `README.md` with the new version.
4. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version)
5. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version)
6. `./gradlew clean uploadArchives`
7. Update the `gradle.properties` to the next SNAPSHOT version.
8. `git commit -am "Prepare next development version."`
9. `git push && git push --tags`
10. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact.
================================================
FILE: artist/build.gradle
================================================
apply plugin: "org.jetbrains.kotlin.jvm"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
compileOnly gradleApi()
implementation deps.build.androidPlugin
implementation deps.kotlin.stdLibJdk7
implementation project(":artist-core")
}
if (rootProject.projectDir.name != "buildSrc") {
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
}
================================================
FILE: artist/gradle.properties
================================================
#
# Copyright (C) 2017. Uber Technologies
#
# 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.
#
POM_NAME=artist
POM_ARTIFACT_ID=artist
POM_PACKAGING=jar
================================================
FILE: artist/src/main/kotlin/com/uber/artist/ArtistExtension.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist
class ArtistExtension {
/**
* Optional override of the package name for the generated views. Defaults to variant.applicationId
*/
var viewPackageName: String? = null
/**
* Optional override of the consuming module's package name. Defaults to variant.applicationId
*/
var rPackageName: String? = null
/**
* Optional fully qualified class name for an interface that all generated views should be marked as implementing
*/
var interfaceClassName: String? = null
/**
* Optional prefix to append to the beginning of each generated view's name.
*/
var viewNamePrefix: String = ""
/**
* Optional setting to control whether the source is formatted with Google Java Format. Defaults to true.
*/
var formatSource: Boolean = true
/**
* Optional setting to control whether the source is generated in Java or Kotlin. Defaults to false for Java.
*/
var generateKotlin: Boolean = false
}
================================================
FILE: artist/src/main/kotlin/com/uber/artist/ArtistPlugin.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*
*/
package com.uber.artist
import com.android.build.gradle.AppExtension
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.LibraryPlugin
import com.android.build.gradle.api.BaseVariant
import com.uber.artist.internal.util.resolveVariantOutputDir
import org.gradle.api.DomainObjectSet
import org.gradle.api.Plugin
import org.gradle.api.Project
class ArtistPlugin : Plugin<Project> {
companion object {
private const val ARTIST = "artist"
}
private val artistExtension = ArtistExtension()
override fun apply(project: Project) {
project.extensions.add(ARTIST, artistExtension)
project.afterEvaluate {
project.plugins.all {
when (it) {
is AppPlugin -> with(project.extensions.getByType(AppExtension::class.java)) {
configureAndroid(project, applicationVariants)
}
is LibraryPlugin -> with(project.extensions.getByType(LibraryExtension::class.java)) {
configureAndroid(project, libraryVariants)
}
}
}
}
}
private fun <T : BaseVariant> configureAndroid(
project: Project,
variants: DomainObjectSet<T>) {
variants.all { variant ->
val outputDir = resolveVariantOutputDir(project, variant, ARTIST)
val artistTask = project.tasks.create(
"generate${variant.name.capitalize()}Views", ArtistTask::class.java)
.apply {
group = ARTIST
outputDirectory = outputDir
description = "Generate ${variant.name} base views."
viewPackageName = artistExtension.viewPackageName ?: variant.applicationId
rPackageName = artistExtension.rPackageName ?: (artistExtension.viewPackageName ?: variant.applicationId)
superinterfaceClassName = artistExtension.interfaceClassName
viewNamePrefix = artistExtension.viewNamePrefix
formatSource = artistExtension.formatSource
generateKotlin = artistExtension.generateKotlin
}
artistTask.outputs.dir(outputDir)
if (artistExtension.generateKotlin) {
val kotlinCompileTask = project.tasks.findByName("compile${variant.name.capitalize()}Kotlin")
if (kotlinCompileTask != null) {
kotlinCompileTask.dependsOn(artistTask)
}
}
variant.registerJavaGeneratingTask(artistTask, artistTask.outputDirectory)
}
}
}
================================================
FILE: artist/src/main/kotlin/com/uber/artist/ArtistTask.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.incremental.IncrementalTaskInputs
import java.io.File
open class ArtistTask : DefaultTask() {
@Input
lateinit var outputDirectory: File
@Input
lateinit var viewPackageName: String
@Input
lateinit var rPackageName: String
@Input
var superinterfaceClassName: String? = null
@Input
var viewNamePrefix: String = ""
@Input
var formatSource: Boolean = true
@Input
var generateKotlin: Boolean = false
@TaskAction
fun execute(inputs: IncrementalTaskInputs) {
generateViewsFor(
outputDirectory,
viewPackageName,
rPackageName,
superinterfaceClassName,
viewNamePrefix,
formatSource,
generateKotlin
)
}
}
================================================
FILE: artist/src/main/kotlin/com/uber/artist/internal/util/Util.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.internal.util
import com.android.build.gradle.api.BaseVariant
import org.gradle.api.Project
import java.io.File
fun resolveVariantOutputDir(project: Project, variant: BaseVariant, plugin: String): File = project.file(
"${project.projectDir}/build/generated/source/$plugin/${variant.flavorName}/${variant.buildType.name}".sanitize()
)
fun String.sanitize(): String = replace('/', File.separatorChar)
================================================
FILE: artist/src/main/resources/META-INF/gradle-plugins/com.uber.artist.properties
================================================
implementation-class=com.uber.artist.ArtistPlugin
================================================
FILE: artist-api/build.gradle
================================================
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
api deps.apt.javapoet
api deps.apt.kotlinPoet
implementation deps.kotlin.stdLibJdk7
}
if (rootProject.projectDir.name != "buildSrc") {
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
}
================================================
FILE: artist-api/gradle.properties
================================================
#
# Copyright (C) 2017. Uber Technologies
#
# 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.
#
POM_NAME=artist-api
POM_ARTIFACT_ID=artist-api
POM_PACKAGING=jar
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/JavaTrait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
/**
* A [JavaTrait] defines code that must be generated in order for a [View] to receive new functionality.
* Each [JavaTrait] can be declared by multiple [ViewStencil]s, and generate otherwise duplicate code across all views
* that exhibit them. Common examples include clicks, attach events, visibility changes, etc. They are a hook into the
* [ViewStencil]’s code gen process that are called during each [ViewStencil]’s generation.
*/
interface JavaTrait : Trait<TypeSpec.Builder, MethodSpec.Builder, ClassName> {
override fun generateFor(type: TypeSpec.Builder, initMethod: MethodSpec.Builder, rClass: ClassName, sourceType: String)
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/JavaTraitService.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import java.util.ServiceLoader
class JavaTraitService private constructor() : TraitService<JavaTrait> {
private val serviceLoader = ServiceLoader.load(JavaTrait::class.java)
/**
* Gets the [Trait] implementations loaded.
*
* @return The located [Trait]s.
*/
override fun get(): Set<JavaTrait> {
val traits = LinkedHashSet<JavaTrait>()
serviceLoader.iterator()
.forEach { traits.add(it) }
return traits
}
companion object {
fun newInstance(): JavaTraitService {
return JavaTraitService()
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/JavaTypeNames.kt
================================================
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import com.squareup.javapoet.ClassName
class JavaTypeNames {
class Android {
companion object {
val AccessibilityEvent: ClassName = ClassName.get("android.view.accessibility", "AccessibilityEvent")
val AccessibilityNodeInfo: ClassName = ClassName.get("android.view.accessibility", "AccessibilityNodeInfo")
val AttributeSet: ClassName = ClassName.get("android.util", "AttributeSet")
val Canvas: ClassName = ClassName.get("android.graphics", "Canvas")
val Context: ClassName = ClassName.get("android.content", "Context")
val Drawable: ClassName = ClassName.get("android.graphics.drawable", "Drawable")
val Gravity: ClassName = ClassName.get("android.view", "Gravity")
val GravityCompat: ClassName = ClassName.get("androidx.core.view", "GravityCompat")
val MenuItem: ClassName = ClassName.get("android.view", "MenuItem")
val Rect: ClassName = ClassName.get("android.graphics", "Rect")
val TabLayout: ClassName = ClassName.get("com.google.android.material.tabs", "TabLayout")
val TabLayoutTab: ClassName = TabLayout.nestedClass("Tab")
val TypedArray: ClassName = ClassName.get("android.content.res", "TypedArray")
val View: ClassName = ClassName.get("android.view", "View")
val MotionEvent: ClassName = ClassName.get("android.view", "MotionEvent")
}
}
class Annotations {
companion object {
val AttrRes: ClassName = ClassName.get("androidx.annotation", "AttrRes")
val CallSuper: ClassName = ClassName.get("androidx.annotation", "CallSuper")
val IdRes: ClassName = ClassName.get("androidx.annotation", "IdRes")
val Nullable: ClassName = ClassName.get("androidx.annotation", "Nullable")
val StyleRes: ClassName = ClassName.get("androidx.annotation", "StyleRes")
val TargetApi: ClassName = ClassName.get("android.annotation", "TargetApi")
val VisibleForTesting: ClassName = ClassName.get("androidx.annotation", "VisibleForTesting")
}
}
class Java {
companion object {
val Map: ClassName = ClassName.get(java.util.Map::class.java)
val String: ClassName = ClassName.get(java.lang.String::class.java)
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/JavaViewStencil.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
open class JavaViewStencil(
extendedType: String,
constructorCount: Int = 4,
defaultAttrRes: String? = null,
vararg addedTraits: Class<out JavaTrait> = emptyArray()
) : ViewStencil<TypeSpec.Builder, MethodSpec.Builder, ClassName, CodeBlock>(
extendedType, constructorCount, defaultAttrRes, addedTraits.toSet()
) {
val sourceType = ClassName.get(
extendedType.substringBeforeLast('.'),
extendedType.split('.').last()
)
/**
* Hook for when attributes are being pulled out of the attribute set.
* Can safely assume the following values exist:
* - context: Context
* - attrs: AttributeSet
* - defStyleAttr: int
* - a: TypedArray
*
* Should *not* recycle `a`. Safe to assume `a` is null-checked before code would execute.
*/
override fun attrsHook(type: TypeSpec.Builder, initMethod: MethodSpec.Builder): CodeBlock? = null
/**
* Hook for implementing the `init()` method.
*/
override fun initMethodHook(type: TypeSpec.Builder, initMethod: MethodSpec.Builder) {}
/**
* Hook for the type builder implementation.
*/
override fun typeHook(type: TypeSpec.Builder) {}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/JavaViewStencilProvider.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
interface JavaViewStencilProvider : ViewStencilProvider<JavaViewStencil, JavaTrait> {
/**
* Provide a set of [ViewStencil]s to be used during code generation.
*
* @return The set of [ViewStencil]s.
*/
override fun stencils(): Set<JavaViewStencil>
/**
* Provide a set of [Trait] classes that should be applied to all [ViewStencil]s.
*
* @return The set of [Trait] classes.
*/
override fun globalTraits(): Set<Class<out JavaTrait>>
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/JavaViewStencilService.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import java.util.ServiceLoader
class JavaViewStencilService private constructor() : ViewStencilService<JavaViewStencil, JavaTrait> {
private val serviceLoader = ServiceLoader.load(JavaViewStencilProvider::class.java)
/**
* Gets the [ViewStencil] implementations loaded.
*
* @return The located [ViewStencil]s.
*/
override fun getStencils(): Set<JavaViewStencil> {
val stencils = LinkedHashSet<JavaViewStencil>()
serviceLoader.iterator()
.forEach { stencils.addAll(it.stencils()) }
return stencils
}
/**
* Gets the [Trait] implementations that should be applied to every [ViewStencil].
*
* @return The located global [Trait]s.
*/
override fun getGlobalTraits(): Set<Class<out JavaTrait>> {
val globalTraits = LinkedHashSet<Class<out JavaTrait>>()
serviceLoader.iterator()
.forEach { globalTraits.addAll(it.globalTraits()) }
return globalTraits
}
companion object {
fun newInstance(): JavaViewStencilService {
return JavaViewStencilService()
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/KotlinTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
/**
* A [KotlinTrait] defines code that must be generated in order for a [View] to receive new functionality.
* Each [KotlinTrait] can be declared by multiple [ViewStencil]s, and generate otherwise duplicate code across all views
* that exhibit them. Common examples include clicks, attach events, visibility changes, etc. They are a hook into the
* [ViewStencil]’s code gen process that are called during each [ViewStencil]’s generation.
*/
interface KotlinTrait : Trait<TypeSpec.Builder, FunSpec.Builder, ClassName> {
override fun generateFor(type: TypeSpec.Builder, initMethod: FunSpec.Builder, rClass: ClassName, sourceType: String)
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/KotlinTraitService.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import java.util.ServiceLoader
class KotlinTraitService private constructor() : TraitService<KotlinTrait> {
private val serviceLoader = ServiceLoader.load(KotlinTrait::class.java)
/**
* Gets the [Trait] implementations loaded.
*
* @return The located [Trait]s.
*/
override fun get(): Set<KotlinTrait> {
val traits = LinkedHashSet<KotlinTrait>()
serviceLoader.iterator()
.forEach { traits.add(it) }
return traits
}
companion object {
fun newInstance(): KotlinTraitService {
return KotlinTraitService()
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/KotlinTypeNames.kt
================================================
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.asClassName
class KotlinTypeNames {
class Android {
companion object {
val AccessibilityEvent: ClassName = ClassName("android.view.accessibility", "AccessibilityEvent")
val AccessibilityNodeInfo: ClassName = ClassName("android.view.accessibility", "AccessibilityNodeInfo")
val AttributeSet: ClassName = ClassName("android.util", "AttributeSet")
val Canvas: ClassName = ClassName("android.graphics", "Canvas")
val Context: ClassName = ClassName("android.content", "Context")
val Drawable: ClassName = ClassName("android.graphics.drawable", "Drawable")
val Gravity: ClassName = ClassName("android.view", "Gravity")
val GravityCompat: ClassName = ClassName("androidx.core.view", "GravityCompat")
val MenuItem: ClassName = ClassName("android.view", "MenuItem")
val Rect: ClassName = ClassName("android.graphics", "Rect")
val TabLayout: ClassName = ClassName("com.google.android.material.tabs", "TabLayout")
val TabLayoutTab: ClassName = TabLayout.nestedClass("Tab")
val TypedArray: ClassName = ClassName("android.content.res", "TypedArray")
val View: ClassName = ClassName("android.view", "View")
val MotionEvent: ClassName = ClassName("android.view", "MotionEvent")
}
}
class Annotations {
companion object {
val AttrRes: ClassName = ClassName("androidx.annotation", "AttrRes")
val CallSuper: ClassName = ClassName("androidx.annotation", "CallSuper")
val IdRes: ClassName = ClassName("androidx.annotation", "IdRes")
val StyleRes: ClassName = ClassName("androidx.annotation", "StyleRes")
val TargetApi: ClassName = ClassName("android.annotation", "TargetApi")
val VisibleForTesting: ClassName = ClassName("androidx.annotation", "VisibleForTesting")
}
}
class Java {
companion object {
val Map: ClassName = java.util.Map::class.asClassName()
val Object: ClassName = java.lang.Object::class.asClassName()
val String: ClassName = java.lang.String::class.asClassName()
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/KotlinViewStencil.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import AliasTypeNames.Rx.Companion.rxExtensionFunctionToAlias
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
open class KotlinViewStencil(
extendedType: String,
constructorCount: Int = 4,
defaultAttrRes: String? = null,
vararg addedTraits: Class<out KotlinTrait> = emptyArray()
) : ViewStencil<TypeSpec.Builder, FunSpec.Builder, ClassName, CodeBlock>(
extendedType, constructorCount, defaultAttrRes, addedTraits.toSet()
) {
val sourceType = ClassName(
extendedType.substringBeforeLast('.'),
extendedType.split('.').last()
)
/**
* Hook for when attributes are being pulled out of the attribute set.
* Can safely assume the following values exist:
* - context: Context
* - attrs: AttributeSet
* - defStyleAttr: int
* - a: TypedArray
*
* Should *not* recycle `a`. Safe to assume `a` is null-checked before code would execute.
*/
override fun attrsHook(type: TypeSpec.Builder, initMethod: FunSpec.Builder): CodeBlock? = null
/**
* Hook for implementing the `init()` method.
*/
override fun initMethodHook(type: TypeSpec.Builder, initMethod: FunSpec.Builder) {}
/**
* Hook for the type builder implementation.
*/
override fun typeHook(type: TypeSpec.Builder) {}
/**
* In an attempt to allow for newly created extension functions to co-exist alongside legacy
* code and not conflict in the naming, especially for the migration from rxbinding2 to
* rxbinding3, we provide an ability for stencils to provide their own aliases.
*
* This corresponds to the syntax import x as y where x is the extension function and y is the
* alias
*/
fun extensionFunctionToAlias(): Map<AliasTypeNames.Rx.Companion.ExtensionFunctionAlias, String> {
return rxExtensionFunctionToAlias
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/KotlinViewStencilProvider.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
interface KotlinViewStencilProvider : ViewStencilProvider<KotlinViewStencil, KotlinTrait> {
/**
* Provide a set of [ViewStencil]s to be used during code generation.
*
* @return The set of [ViewStencil]s.
*/
override fun stencils(): Set<KotlinViewStencil>
/**
* Provide a set of [Trait] classes that should be applied to all [ViewStencil]s.
*
* @return The set of [Trait] classes.
*/
override fun globalTraits(): Set<Class<out KotlinTrait>>
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/KotlinViewStencilService.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import java.util.ServiceLoader
class KotlinViewStencilService private constructor() : ViewStencilService<KotlinViewStencil, KotlinTrait> {
private val serviceLoader = ServiceLoader.load(KotlinViewStencilProvider::class.java)
/**
* Gets the [ViewStencil] implementations loaded.
*
* @return The located [ViewStencil]s.
*/
override fun getStencils(): Set<KotlinViewStencil> {
val stencils = LinkedHashSet<KotlinViewStencil>()
serviceLoader.iterator()
.forEach { stencils.addAll(it.stencils()) }
return stencils
}
/**
* Gets the [Trait] implementations that should be applied to every [ViewStencil].
*
* @return The located global [Trait]s.
*/
override fun getGlobalTraits(): Set<Class<out KotlinTrait>> {
val globalTraits = LinkedHashSet<Class<out KotlinTrait>>()
serviceLoader.iterator()
.forEach { globalTraits.addAll(it.globalTraits()) }
return globalTraits
}
companion object {
fun newInstance(): KotlinViewStencilService {
return KotlinViewStencilService()
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/Trait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
/**
* A [Trait] defines code that must be generated in order for a [View] to receive new functionality.
* Each [Trait] can be declared by multiple [ViewStencil]s, and generate otherwise duplicate code across all views
* that exhibit them. Common examples include clicks, attach events, visibility changes, etc. They are a hook into the
* [ViewStencil]’s code gen process that are called during each [ViewStencil]’s generation.
*/
interface Trait<OutputType, FunType, ClassType> {
fun generateFor(type: OutputType, initMethod: FunType, rClass: ClassType, sourceType: String)
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/TraitService.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
interface TraitService<TraitType> {
/**
* Gets the [Trait] implementations loaded.
*
* @return The located [Trait]s.
*/
fun get(): Set<TraitType>
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/TypeNames.kt
================================================
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
import com.squareup.javapoet.ClassName
class TypeNames {
class Android {
companion object {
val AccessibilityEvent: ClassName = ClassName.get("android.view.accessibility", "AccessibilityEvent")
val AccessibilityNodeInfo: ClassName = ClassName.get("android.view.accessibility", "AccessibilityNodeInfo")
val AttributeSet: ClassName = ClassName.get("android.util", "AttributeSet")
val Canvas: ClassName = ClassName.get("android.graphics", "Canvas")
val Context: ClassName = ClassName.get("android.content", "Context")
val Drawable: ClassName = ClassName.get("android.graphics.drawable", "Drawable")
val Gravity: ClassName = ClassName.get("android.view", "Gravity")
val GravityCompat: ClassName = ClassName.get("androidx.core.view", "GravityCompat")
val MenuItem: ClassName = ClassName.get("android.view", "MenuItem")
val Rect: ClassName = ClassName.get("android.graphics", "Rect")
val TabLayout: ClassName = ClassName.get("com.google.android.material.tabs", "TabLayout")
val TabLayoutTab: ClassName = TabLayout.nestedClass("Tab")
val TypedArray: ClassName = ClassName.get("android.content.res", "TypedArray")
val View: ClassName = ClassName.get("android.view", "View")
val MotionEvent: ClassName = ClassName.get("android.view", "MotionEvent")
}
}
class Annotations {
companion object {
val AttrRes: ClassName = ClassName.get("androidx.annotation", "AttrRes")
val CallSuper: ClassName = ClassName.get("androidx.annotation", "CallSuper")
val IdRes: ClassName = ClassName.get("androidx.annotation", "IdRes")
val Nullable: ClassName = ClassName.get("androidx.annotation", "Nullable")
val StyleRes: ClassName = ClassName.get("androidx.annotation", "StyleRes")
val TargetApi: ClassName = ClassName.get("android.annotation", "TargetApi")
val VisibleForTesting: ClassName = ClassName.get("androidx.annotation", "VisibleForTesting")
}
}
class Java {
companion object {
val Map: ClassName = ClassName.get(java.util.Map::class.java)
val String: ClassName = ClassName.get(java.lang.String::class.java)
}
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/ViewStencil.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
abstract class ViewStencil<OutputType, FunType, ClassName, CodeBlock>(
val extendedType: String,
val constructorCount: Int = 4,
val defaultAttrRes: String? = null,
protected val addedTraits: Set<Class<out Trait<*, *, *>>>
) {
val globalTraits = mutableSetOf<Class<out Trait<*, *, *>>>()
var namePrefix: String = ""
open fun traits(): Set<Class<out Trait<*, *, *>>> = globalTraits.plus(addedTraits)
/**
* The name of the view class.
*/
open fun name(): String {
val sourceName = extendedType.split('.').last()
return "$namePrefix${sourceName.removePrefix("AppCompat")}"
}
/**
* Hook for when attributes are being pulled out of the attribute set.
* Can safely assume the following values exist:
* - context: Context
* - attrs: AttributeSet
* - defStyleAttr: int
* - a: TypedArray
*
* Should *not* recycle `a`. Safe to assume `a` is null-checked before code would execute.
*/
abstract fun attrsHook(type: OutputType, initMethod: FunType): CodeBlock?
/**
* Hook for implementing the `init()` method.
*/
abstract fun initMethodHook(type: OutputType, initMethod: FunType)
/**
* Hook for the type builder implementation.
*/
abstract fun typeHook(type: OutputType)
fun setGlobalTraits(traits: Set<Class<out Trait<*, *, *>>>) {
globalTraits.addAll(traits)
}
fun setPrefix(namePrefix: String) {
this.namePrefix = namePrefix
}
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/ViewStencilProvider.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
interface ViewStencilProvider<ViewStencilType, TraitType> {
/**
* Provide a set of [ViewStencil]s to be used during code generation.
*
* @return The set of [ViewStencil]s.
*/
fun stencils(): Set<ViewStencilType>
/**
* Provide a set of [Trait] classes that should be applied to all [ViewStencil]s.
*
* @return The set of [Trait] classes.
*/
fun globalTraits(): Set<Class<out TraitType>>
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/ViewStencilService.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.api
interface ViewStencilService<ViewStencilType, TraitType> {
/**
* Gets the [ViewStencil] implementations loaded.
*
* @return The located [ViewStencil]s.
*/
fun getStencils(): Set<ViewStencilType>
/**
* Gets the [Trait] implementations that should be applied to every [ViewStencil].
*
* @return The located global [Trait]s.
*/
fun getGlobalTraits(): Set<Class<out TraitType>>
}
================================================
FILE: artist-api/src/main/kotlin/com/uber/artist/api/alias/AliasTypeNames.kt
================================================
import com.squareup.kotlinpoet.ClassName
class AliasTypeNames {
open class Rx {
companion object {
val RecyclerViewScrollEvent = ClassName("com.jakewharton.rxbinding3.recyclerview", "RecyclerViewScrollEvent")
val RxView = ClassName("com.jakewharton.rxbinding3.view", "RxView")
val RxCompoundButton = ClassName("com.jakewharton.rxbinding3.widget", "RxCompoundButton")
val RxNestedScrollView = ClassName("com.jakewharton.rxbinding3.core", "RxNestedScrollView")
val RxRecyclerView = ClassName("com.jakewharton.rxbinding3.recyclerview", "RxRecyclerView")
val RxSearchView = ClassName("com.jakewharton.rxbinding3.appcompat", "RxSearchView")
val RxSeekBar = ClassName("com.jakewharton.rxbinding3.widget", "RxSeekBar")
val SeekBarChangeEvent = ClassName("com.jakewharton.rxbinding3.widget", "SeekBarChangeEvent")
val SeekBarProgressChangeEvent = ClassName("com.jakewharton.rxbinding3.widget", "SeekBarProgressChangeEvent")
val SeekBarStartChangeEvent = ClassName("com.jakewharton.rxbinding3.widget", "SeekBarStartChangeEvent")
val RxSwipeRefreshLayout = ClassName("com.jakewharton.rxbinding3.swiperefreshlayout", "RxSwipeRefreshLayout")
val RxTabLayout = ClassName("com.jakewharton.rxbinding3.material", "RxTabLayout")
val RxTextView = ClassName("com.jakewharton.rxbinding3.widget", "RxTextView")
val RxToolbar = ClassName("com.jakewharton.rxbinding3.appcompat", "RxToolbar")
val RxViewPager = ClassName("com.jakewharton.rxbinding3.viewpager", "RxViewPager")
val RxViewAttachEvent = ClassName("com.jakewharton.rxbinding3.view", "ViewAttachEvent")
val RxViewAttachAttachedEvent = ClassName("com.jakewharton.rxbinding3.view", "ViewAttachAttachedEvent")
val RxViewAttachDetachedEvent = ClassName("com.jakewharton.rxbinding3.view", "ViewAttachDetachedEvent")
val SearchViewQueryTextEvent = ClassName("com.jakewharton.rxbinding3.appcompat", "SearchViewQueryTextEvent")
val ViewScrollChangeEvent = ClassName("com.jakewharton.rxbinding3.view", "ViewScrollChangeEvent")
data class ExtensionFunctionAlias(
val className: ClassName,
val methodName: String
)
val list = listOf<ExtensionFunctionAlias>(
ExtensionFunctionAlias(RxToolbar, "itemClicks"),
ExtensionFunctionAlias(RxView, "layoutChanges"),
ExtensionFunctionAlias(RxView, "clicks"),
ExtensionFunctionAlias(RxView, "attachEvents"),
ExtensionFunctionAlias(RxView, "longClicks"),
ExtensionFunctionAlias(RxToolbar, "navigationClicks"),
ExtensionFunctionAlias(RxView, "scrollChangeEvents"),
ExtensionFunctionAlias(RxNestedScrollView, "scrollChangeEvents"),
ExtensionFunctionAlias(RxCompoundButton, "checkedChanges"),
ExtensionFunctionAlias(RxSwipeRefreshLayout, "refreshes"),
ExtensionFunctionAlias(RxSeekBar, "changeEvents"),
ExtensionFunctionAlias(RxTabLayout, "selections"),
ExtensionFunctionAlias(RxSearchView, "queryTextChangeEvents"),
ExtensionFunctionAlias(RxTabLayout, "selections"),
ExtensionFunctionAlias(RxRecyclerView, "scrollEvents"),
ExtensionFunctionAlias(RxTextView, "textChanges"),
ExtensionFunctionAlias(RxView, "attachEvents")
)
val rxExtensionFunctionToAlias = list.map { it to it.className.simpleName.toLowerCase() + "_" + it
.methodName.dropLast(4) }.toMap()
}
}
}
================================================
FILE: artist-core/build.gradle
================================================
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
implementation deps.apt.javapoet
implementation deps.apt.kotlinPoet
implementation deps.build.googleJavaFormatter
implementation deps.kotlin.stdLibJdk7
implementation project(":artist-api")
// Dont need to run tests in buildSrc
if (rootProject.projectDir.name != "buildSrc") {
testImplementation deps.androidx.annotations
testImplementation deps.test.compileTesting
testImplementation deps.test.junit
testImplementation deps.test.robolectric
testImplementation deps.test.truth
}
}
if (rootProject.projectDir.name != "buildSrc") {
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
}
================================================
FILE: artist-core/gradle.properties
================================================
#
# Copyright (C) 2017. Uber Technologies
#
# 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.
#
POM_NAME=artist-core
POM_ARTIFACT_ID=artist-core
POM_PACKAGING=jar
================================================
FILE: artist-core/src/main/kotlin/com/uber/artist/Artist.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist
import java.io.File
fun generateViewsFor(
outputDir: File,
viewPackageName: String,
rPackageName: String,
superinterfaceClassName: String?,
viewNamePrefix: String,
formatSource: Boolean,
generateKotlin: Boolean = false) {
val artistCodeGenerator = if (generateKotlin) KotlinArtistCodeGenerator() else JavaArtistCodeGenerator()
artistCodeGenerator.generateViews(outputDir, viewPackageName, rPackageName, superinterfaceClassName, viewNamePrefix, formatSource)
}
================================================
FILE: artist-core/src/main/kotlin/com/uber/artist/ArtistCodeGenerator.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist
import com.google.common.annotations.VisibleForTesting
import com.uber.artist.api.KotlinViewStencil
import com.uber.artist.api.Trait
import com.uber.artist.api.ViewStencil
import java.io.File
abstract class ArtistCodeGenerator<
OutputFileType,
OutputType,
FunType,
ClassName,
CodeBlock,
ViewStencilType : ViewStencil<OutputType, FunType, ClassName, CodeBlock>,
TraitType : Trait<OutputType, FunType, ClassName>> {
abstract val viewStencils: Set<ViewStencilType>
abstract val traits: Set<TraitType>
abstract val globalTraits: Set<Class<out TraitType>>
fun generateViews(
outputDir: File,
viewPackageName: String,
rPackageName: String,
superinterfaceClassName: String?,
viewNamePrefix: String,
formatSource: Boolean
) {
generateViewsForStencils(viewStencils, traits, globalTraits, outputDir, viewPackageName, rPackageName, superinterfaceClassName, viewNamePrefix, formatSource)
}
@VisibleForTesting
fun generateViewsForStencils(
viewStencils: Set<ViewStencilType>,
traits: Set<TraitType>,
globalTraits: Set<Class<out TraitType>>,
outputDir: File,
viewPackageName: String,
rPackageName: String,
superinterfaceClassName: String?,
viewNamePrefix: String,
formatSource: Boolean) {
val traitMap: Map<Class<out TraitType>, TraitType> = traits.associateBy { it.javaClass }
viewStencils.forEach {
it.setGlobalTraits(globalTraits)
it.setPrefix(viewNamePrefix)
val typeSpecBuilder = generateTypeSpecFor(it, rPackageName, traitMap, superinterfaceClassName)
val fileSpec = generateFileSpecFor(it, viewPackageName, typeSpecBuilder)
if (formatSource) {
writeFileWithFormatting(fileSpec, outputDir, typeSpecBuilder, viewPackageName)
} else {
writeFile(fileSpec, outputDir)
}
}
}
protected abstract fun generateFileSpecFor(stencil: ViewStencilType, viewPackageName: String, typeSpecBuilder: OutputType): OutputFileType
protected abstract fun generateTypeSpecFor(
stencil: ViewStencilType,
rPackageName: String,
traitMap: Map<Class<out TraitType>, TraitType>,
superinterfaceClassName: String?): OutputType
protected abstract fun createInitBuilderFor(stencil: ViewStencilType, type: OutputType): FunType
protected abstract fun generateConstructorsFor(stencil: ViewStencilType, type: OutputType, rClass: ClassName)
protected abstract fun superinterface(className: String): ClassName
protected abstract fun writeFile(fileSpec: OutputFileType, outputDir: File)
protected abstract fun writeFileWithFormatting(fileSpec: OutputFileType, outputDir: File, outputType: OutputType, viewPackageName: String)
protected fun superConstructorStatement(count: Int): String {
when (count) {
1 -> return "super(context)"
2 -> return "super(context, attrs)"
3 -> return "super(context, attrs, defStyleAttr)"
4 -> return "super(context, attrs, defStyleAttr, defStyleRes)"
}
throw IllegalArgumentException()
}
protected fun initStatement(count: Int): String {
when (count) {
1 -> return "init(context, null, 0, 0)"
2 -> return "init(context, attrs, 0, 0)"
3 -> return "init(context, attrs, defStyleAttr, 0)"
4 -> return "init(context, attrs, defStyleAttr, defStyleRes)"
}
throw IllegalArgumentException()
}
}
================================================
FILE: artist-core/src/main/kotlin/com/uber/artist/FormattingFileWriter.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist
import java.io.File
abstract class FormattingFileWriter<OutputFileType, OutputType> {
protected val packageSplitRegex = "\\.".toRegex()
/**
* A rough estimate of the average file size: 80 chars per line, 500 lines.
*/
protected val defaultFileSize = 80 * 500
/**
* A file writer function that formats the code before writing out to the file system.
*/
abstract fun writeWithFormattingTo(directory: File)
}
================================================
FILE: artist-core/src/main/kotlin/com/uber/artist/JavaArtistCodeGenerator.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist
import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterSpec
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
import com.uber.artist.api.JavaTraitService
import com.uber.artist.api.JavaViewStencil
import com.uber.artist.api.JavaViewStencilService
import com.uber.artist.api.TypeNames
import java.io.File
import javax.lang.model.element.Modifier
class JavaArtistCodeGenerator : ArtistCodeGenerator<JavaFile, TypeSpec.Builder, MethodSpec.Builder, ClassName, CodeBlock, JavaViewStencil, JavaTrait>() {
override val viewStencils: Set<JavaViewStencil>
get() = JavaViewStencilService.newInstance().getStencils()
override val traits: Set<JavaTrait>
get() = JavaTraitService.newInstance().get()
override val globalTraits: Set<Class<out JavaTrait>>
get() = JavaViewStencilService.newInstance().getGlobalTraits()
override fun generateFileSpecFor(stencil: JavaViewStencil, viewPackageName: String, typeSpecBuilder: TypeSpec.Builder): JavaFile {
return JavaFile.builder(viewPackageName, typeSpecBuilder.build()).build()
}
override fun generateTypeSpecFor(
stencil: JavaViewStencil,
rPackageName: String,
traitMap: Map<Class<out JavaTrait>, JavaTrait>,
superinterfaceClassName: String?): TypeSpec.Builder {
val rClass = ClassName.get(rPackageName, "R")
val typeBuilder = TypeSpec.classBuilder(stencil.name())
.addModifiers(Modifier.PUBLIC)
.superclass(stencil.sourceType)
superinterfaceClassName?.let { typeBuilder.addSuperinterface(superinterface(superinterfaceClassName)) }
generateConstructorsFor(stencil, typeBuilder, rClass)
val initMethod = createInitBuilderFor(stencil, typeBuilder)
stencil.traits()
.map { traitName -> traitMap[traitName] }
.forEach { it?.generateFor(typeBuilder, initMethod, rClass, stencil.name()) }
typeBuilder.addMethod(initMethod.build())
stencil.typeHook(typeBuilder)
return typeBuilder
}
override fun createInitBuilderFor(
stencil: JavaViewStencil,
type: TypeSpec.Builder): MethodSpec.Builder {
val initMethod = MethodSpec.methodBuilder("init")
.addAnnotation(TypeNames.Annotations.CallSuper)
.addModifiers(Modifier.PROTECTED)
.addParameter(ParameterSpec.builder(TypeNames.Android.Context, "context")
.build())
.addParameter(ParameterSpec.builder(TypeNames.Android.AttributeSet, "attrs")
.addAnnotation(TypeNames.Annotations.Nullable)
.build())
.addParameter(ParameterSpec.builder(ClassName.INT, "defStyleAttr")
.addAnnotation(TypeNames.Annotations.AttrRes)
.build())
.addParameter(ParameterSpec.builder(ClassName.INT, "defStyleRes")
.addAnnotation(TypeNames.Annotations.StyleRes)
.build())
stencil.initMethodHook(type, initMethod)
return initMethod
}
override fun generateConstructorsFor(stencil: JavaViewStencil, type: TypeSpec.Builder, rClass: ClassName) {
val count = stencil.constructorCount
for (i in 1..count) {
when (i) {
1 -> // Context constructor
type.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeNames.Android.Context, "context")
.build())
.addCode(constructorBlock(stencil, rClass, count, i))
.build())
2 -> // Context, AttributeSet constructor
type.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeNames.Android.Context, "context")
.build())
.addParameter(ParameterSpec.builder(TypeNames.Android.AttributeSet, "attrs")
.addAnnotation(TypeNames.Annotations.Nullable)
.build())
.addCode(constructorBlock(stencil, rClass, count, i))
.build())
3 -> // Context, AttributeSet, defStyleAttr constructor
type.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeNames.Android.Context, "context")
.build())
.addParameter(ParameterSpec.builder(TypeNames.Android.AttributeSet, "attrs")
.addAnnotation(TypeNames.Annotations.Nullable)
.build())
.addParameter(ParameterSpec.builder(ClassName.INT, "defStyleAttr")
.addAnnotation(TypeNames.Annotations.AttrRes)
.build())
.addCode(constructorBlock(stencil, rClass, count, i))
.build())
4 -> // Context, AttributeSet, defStyleAttr, defStyleRes constructor
type.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(TypeNames.Android.Context, "context")
.build())
.addParameter(ParameterSpec.builder(TypeNames.Android.AttributeSet, "attrs")
.addAnnotation(TypeNames.Annotations.Nullable)
.build())
.addParameter(ParameterSpec.builder(ClassName.INT, "defStyleAttr")
.addAnnotation(TypeNames.Annotations.AttrRes)
.build())
.addParameter(ParameterSpec.builder(ClassName.INT, "defStyleRes")
.addAnnotation(TypeNames.Annotations.StyleRes)
.build())
.addAnnotation(AnnotationSpec.builder(TypeNames.Annotations.TargetApi)
.addMember("value", "\$T.\$L.\$L",
ClassName.get("android.os", "Build"),
"VERSION_CODES",
"LOLLIPOP")
.build())
.addCode(constructorBlock(stencil, rClass, count, i))
.build())
}
}
}
private fun constructorBlock(stencil: JavaViewStencil, rClass: ClassName, total: Int, currentIndex: Int): CodeBlock {
val builder = CodeBlock.builder()
if (currentIndex == total || currentIndex == 3) {
builder.addStatement(superConstructorStatement(currentIndex))
builder.addStatement(initStatement(currentIndex))
} else {
builder.add(fallthroughConstructorStatement(stencil, rClass, currentIndex))
}
return builder.build()
}
private fun fallthroughConstructorStatement(stencil: JavaViewStencil, rClass: ClassName, count: Int): CodeBlock {
when (count) {
1 -> return CodeBlock.of("this(context, null);\n")
2 -> {
return if (stencil.defaultAttrRes != null) {
if ((stencil.defaultAttrRes as String).startsWith(prefix = "android.R")) {
CodeBlock.of("this(context, attrs, ${stencil.defaultAttrRes});\n")
} else {
CodeBlock.of("this(context, attrs, \$T.attr.${stencil.defaultAttrRes});\n", rClass)
}
} else {
CodeBlock.of("this(context, attrs, 0);\n")
}
}
3 -> return CodeBlock.of("this(context, attrs, defStyleAttr, 0);\n")
}
throw IllegalArgumentException(count.toString())
}
override fun superinterface(className: String): ClassName {
val packageName = className.substring(0, className.lastIndexOf('.'))
val simpleName = className.substring(className.lastIndexOf('.') + 1)
return ClassName.get(packageName, simpleName)
}
override fun writeFile(fileSpec: JavaFile, outputDir: File) {
fileSpec.writeTo(outputDir)
}
override fun writeFileWithFormatting(fileSpec: JavaFile, outputDir: File, outputType: TypeSpec.Builder, packageName: String) {
JavaFormattingFileWriter(fileSpec, outputType, packageName).writeWithFormattingTo(outputDir)
}
}
================================================
FILE: artist-core/src/main/kotlin/com/uber/artist/JavaFormattingFileWriter.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist
import com.google.common.base.Preconditions.checkArgument
import com.google.common.io.CharSink
import com.google.common.io.CharSource
import com.google.googlejavaformat.java.Formatter
import com.google.googlejavaformat.java.FormatterException
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.TypeSpec
import java.io.File
import java.io.IOException
import java.io.OutputStreamWriter
import java.io.Writer
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files
class JavaFormattingFileWriter(val outputFile: JavaFile, val typeSpecBuilder: TypeSpec.Builder, val packageName: String) : FormattingFileWriter<JavaFile, TypeSpec.Builder>() {
private val formatter: Formatter = Formatter()
/**
* A file writer function that formats the code before writing out to the file system.
*/
override fun writeWithFormattingTo(directory: File) {
val directoryPath = directory.toPath()
checkArgument(Files.notExists(directoryPath) || Files.isDirectory(directoryPath),
"path %s exists but is not a directoryPath.", directoryPath)
var outputDirectory = directoryPath
if (!packageName.isEmpty()) {
for (packageComponent in packageName.split(packageSplitRegex)
.filter { !it.isEmpty() }.toTypedArray()) {
outputDirectory = outputDirectory.resolve(packageComponent)
}
Files.createDirectories(outputDirectory)
}
val typeSpec = typeSpecBuilder.build()
val outputPath = outputDirectory.resolve(typeSpec.name + ".java")
try {
OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8).use { writer ->
val stringBuilder = StringBuilder(defaultFileSize)
outputFile.writeTo(stringBuilder)
formatter.formatSource(
CharSource.wrap(stringBuilder),
object : CharSink() {
@Throws(IOException::class)
override fun openStream(): Writer {
return writer
}
})
}
} catch (e: FormatterException) {
throw IOException("Error formatting " + outputPath.fileName.toString(), e)
}
}
}
================================================
FILE: artist-core/src/main/kotlin/com/uber/artist/KotlinArtistCodeGenerator.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist
import AliasTypeNames.Rx.Companion.rxExtensionFunctionToAlias
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
import com.uber.artist.api.KotlinTraitService
import com.uber.artist.api.KotlinTypeNames
import com.uber.artist.api.KotlinViewStencil
import com.uber.artist.api.KotlinViewStencilService
import java.io.File
class KotlinArtistCodeGenerator : ArtistCodeGenerator<FileSpec, TypeSpec.Builder, FunSpec.Builder, ClassName, CodeBlock, KotlinViewStencil, KotlinTrait>() {
override val viewStencils: Set<KotlinViewStencil>
get() = KotlinViewStencilService.newInstance().getStencils()
override val traits: Set<KotlinTrait>
get() = KotlinTraitService.newInstance().get()
override val globalTraits: Set<Class<out KotlinTrait>>
get() = KotlinViewStencilService.newInstance().getGlobalTraits()
override fun generateFileSpecFor(stencil: KotlinViewStencil, viewPackageName: String,
typeSpecBuilder: TypeSpec.Builder): FileSpec {
val typeSpec = typeSpecBuilder.build()
var builder = FileSpec.builder(viewPackageName, typeSpec.name
?: throw IllegalStateException("No name for type: $typeSpec"))
for ((extensionFunctionAlias, alias) in stencil.extensionFunctionToAlias()) {
builder = builder.addAliasedImport(extensionFunctionAlias.toClassName(), "", alias)
}
return builder.addType(typeSpec).build()
}
fun AliasTypeNames.Rx.Companion.ExtensionFunctionAlias.toClassName(): ClassName {
return ClassName(this.className.packageName, this.methodName)
}
override fun generateTypeSpecFor(stencil: KotlinViewStencil, rPackageName: String, traitMap: Map<Class<out KotlinTrait>, KotlinTrait>, superinterfaceClassName: String?): TypeSpec.Builder {
val rClass = ClassName(rPackageName, "R")
val typeBuilder = TypeSpec.classBuilder(stencil.name())
.addModifiers(KModifier.OPEN)
.superclass(stencil.sourceType)
superinterfaceClassName?.let { typeBuilder.addSuperinterface(superinterface(superinterfaceClassName)) }
generateConstructorsFor(stencil, typeBuilder, rClass)
val initMethod = createInitBuilderFor(stencil, typeBuilder)
stencil.traits()
.map { traitName -> traitMap[traitName] }
.forEach { it?.generateFor(typeBuilder, initMethod, rClass, stencil.name()) }
typeBuilder.addFunction(initMethod.build())
stencil.typeHook(typeBuilder)
return typeBuilder
}
override fun createInitBuilderFor(stencil: KotlinViewStencil, type: TypeSpec.Builder): FunSpec.Builder {
return FunSpec.builder("init")
.addAnnotation(KotlinTypeNames.Annotations.CallSuper)
.addModifiers(KModifier.PROTECTED, KModifier.OPEN)
.addParameter(ParameterSpec.builder("context", KotlinTypeNames.Android.Context)
.build())
.addParameter(ParameterSpec.builder("attrs", KotlinTypeNames.Android.AttributeSet.copy(nullable = true))
.build())
.addParameter(ParameterSpec.builder("defStyleAttr", INT)
.addAnnotation(KotlinTypeNames.Annotations.AttrRes)
.build())
.addParameter(ParameterSpec.builder("defStyleRes", INT)
.addAnnotation(KotlinTypeNames.Annotations.StyleRes)
.build())
.also {
stencil.initMethodHook(type, it)
}
}
override fun generateConstructorsFor(stencil: KotlinViewStencil, type: TypeSpec.Builder, rClass: ClassName) {
val paramContext = ParameterSpec.builder("context", KotlinTypeNames.Android.Context)
.build()
val paramAttrs = ParameterSpec.builder("attrs", KotlinTypeNames.Android.AttributeSet.copy(nullable = true))
.defaultValue("null")
.build()
val paramDefStyleAttr = ParameterSpec.builder("defStyleAttr", INT)
.addAnnotation(KotlinTypeNames.Annotations.AttrRes)
.defaultValue(stencil.defaultAttrRes?.let {
if ("." in it) {
CodeBlock.of(it)
} else {
CodeBlock.of("%T.attr.$it", rClass)
}
} ?: CodeBlock.of("0")
)
.build()
val paramDefStyleRes = ParameterSpec.builder("defStyleRes", INT)
.addAnnotation(KotlinTypeNames.Annotations.StyleRes)
.defaultValue("0")
.build()
val params = listOf(paramContext, paramAttrs, paramDefStyleAttr, paramDefStyleRes)
val superConstructorArgs = listOf("context", "attrs", "defStyleAttr", "defStyleRes")
val ctorOverloadsCount = stencil.constructorCount.coerceAtMost(3)
val overloadsConstructor = FunSpec.constructorBuilder()
.addAnnotation(JvmOverloads::class)
.addParameters(params.subList(0, ctorOverloadsCount))
.callSuperConstructor(*superConstructorArgs.subList(0, ctorOverloadsCount).toTypedArray())
.addStatement(initStatement(ctorOverloadsCount))
.build()
val targetApiConstructor = FunSpec.constructorBuilder()
.addAnnotation(AnnotationSpec.builder(KotlinTypeNames.Annotations.TargetApi)
.addMember("%T.VERSION_CODES.LOLLIPOP", ClassName("android.os", "Build"))
.build())
.addParameters(params)
.callSuperConstructor(*superConstructorArgs.toTypedArray())
.addStatement(initStatement(4))
.build()
type
.addFunction(overloadsConstructor)
.apply {
if (stencil.constructorCount > 3) addFunction(targetApiConstructor)
}
}
override fun superinterface(className: String) = ClassName(
className.substringBeforeLast('.'),
className.substringAfterLast('.')
)
override fun writeFile(fileSpec: FileSpec, outputDir: File) {
fileSpec.writeTo(outputDir)
}
override fun writeFileWithFormatting(fileSpec: FileSpec, outputDir: File, outputType: TypeSpec.Builder, packageName: String) {
fileSpec.writeTo(outputDir)
}
}
================================================
FILE: artist-core/src/test/kotlin/com/uber/artist/ArtistTest.kt
================================================
package com.uber.artist
import com.google.common.io.Files
import com.google.common.truth.Truth.assertAbout
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import com.google.testing.compile.JavaFileObjects.forSourceString
import com.google.testing.compile.JavaSourceSubjectFactory.javaSource
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
import com.uber.artist.api.JavaViewStencil
import org.junit.Test
import javax.lang.model.element.Modifier
class ArtistTest {
companion object {
const val TEST_PACKAGE_NAME = "foo.bar"
val TRAITS = setOf(TestTrait())
const val IMAGE_VIEW_SOURCE_NO_TRAITS = """package foo.bar;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.annotation.AttrRes;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
public class MyImageView extends ImageView {
public MyImageView(Context context) {
this(context, null);
}
public MyImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyImageView(Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
@CallSuper
protected void init(
Context context,
@Nullable AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {}
}
"""
val IMAGE_VIEW_WITH_TEST_TRAIT = """package foo.bar;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
import androidx.annotation.AttrRes;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import java.lang.String;
public class MyImageView extends ImageView {
public MyImageView(Context context) {
this(context, null);
}
public MyImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyImageView(Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
public String testMethod() {
return "foo";
}
@CallSuper
protected void init(
Context context,
@Nullable AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {}
}
"""
}
@Test
fun testArtist_withNoTraits_shouldGenerateViews() {
val outputDir = Files.createTempDir()
val stencils: Set<JavaViewStencil> = setOf(
JavaViewStencil("android.widget.Button", 3),
JavaViewStencil("android.widget.ImageView", 3),
JavaViewStencil("android.widget.TextView", 3))
JavaArtistCodeGenerator().generateViewsForStencils(stencils, TRAITS, emptySet(), outputDir, TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, null, "My", true)
val viewOutputDir = outputDir.resolve(TEST_PACKAGE_NAME.replace('.', '/'))
val viewNames = viewOutputDir.listFiles()
.map { it.name }
.toList()
assertThat(viewNames).containsExactly("MyButton.java", "MyImageView.java", "MyTextView.java")
}
@Test
fun testArtist_withNoTraits_shouldGenerateExpectedSource() {
val outputDir = Files.createTempDir()
val stencils: Set<JavaViewStencil> = setOf(
JavaViewStencil("android.widget.ImageView", 3))
JavaArtistCodeGenerator().generateViewsForStencils(stencils, TRAITS, emptySet(), outputDir, TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, null, "My", true)
val viewOutputDir = outputDir.resolve(TEST_PACKAGE_NAME.replace('.', '/'))
assertWithMessage("$viewOutputDir does not exist").that(viewOutputDir.exists()).isTrue()
val generatedFileName = viewOutputDir.listFiles().first()
val generatedViewContent = generatedFileName.readText()
assertAbout(javaSource())
.that(forSourceString("$TEST_PACKAGE_NAME.MyImageView", generatedViewContent))
.compilesWithoutError()
assertWithMessage("$generatedFileName did not match expected file content")
.that(generatedViewContent)
.isEqualTo(IMAGE_VIEW_SOURCE_NO_TRAITS)
}
@Test
fun testArtist_withAddedTrait_shouldGenerateExpectedSource() {
val outputDir = Files.createTempDir()
val stencils: Set<JavaViewStencil> = setOf(
JavaViewStencil("android.widget.ImageView", 3, addedTraits = *arrayOf(TestTrait::class.java)))
JavaArtistCodeGenerator().generateViewsForStencils(stencils, TRAITS, emptySet(), outputDir, TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, null, "My", true)
val viewOutputDir = outputDir.resolve(TEST_PACKAGE_NAME.replace('.', '/'))
assertWithMessage("$viewOutputDir does not exist").that(viewOutputDir.exists()).isTrue()
val generatedFileName = viewOutputDir.listFiles().first()
val generatedViewContent = generatedFileName.readText()
assertAbout(javaSource())
.that(forSourceString("$TEST_PACKAGE_NAME.MyImageView", generatedViewContent))
.compilesWithoutError()
assertWithMessage("$generatedFileName did not match expected file content")
.that(generatedViewContent)
.isEqualTo(IMAGE_VIEW_WITH_TEST_TRAIT)
}
@Test
fun testArtist_withGlobalTrait_shouldGenerateExpectedSource() {
val outputDir = Files.createTempDir()
val globalTraits: Set<Class<out JavaTrait>> = setOf(TestTrait::class.java)
val stencils: Set<JavaViewStencil> = setOf(
JavaViewStencil("android.widget.ImageView", 3))
JavaArtistCodeGenerator().generateViewsForStencils(stencils, TRAITS, globalTraits, outputDir, TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, null, "My", true)
val viewOutputDir = outputDir.resolve(TEST_PACKAGE_NAME.replace('.', '/'))
assertWithMessage("$viewOutputDir does not exist").that(viewOutputDir.exists()).isTrue()
val generatedFileName = viewOutputDir.listFiles().first()
val generatedViewContent = generatedFileName.readText()
assertAbout(javaSource())
.that(forSourceString("$TEST_PACKAGE_NAME.MyImageView", generatedViewContent))
.compilesWithoutError()
assertWithMessage("$generatedFileName did not match expected file content")
.that(generatedViewContent)
.isEqualTo(IMAGE_VIEW_WITH_TEST_TRAIT)
}
class TestTrait : JavaTrait {
override fun generateFor(type: TypeSpec.Builder, initMethod: MethodSpec.Builder, rClass: ClassName, sourceType: String) {
type.addMethod(MethodSpec.methodBuilder("testMethod")
.addModifiers(Modifier.PUBLIC)
.returns(ClassName.get(String::class.java))
.addStatement("return \$S", "foo")
.build())
}
}
}
================================================
FILE: artist-traits/build.gradle
================================================
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
apply plugin: "org.jetbrains.kotlin.kapt"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
kapt deps.apt.autoService
compileOnly deps.apt.autoService
api project(":artist-api")
implementation deps.kotlin.stdLibJdk7
}
if (rootProject.projectDir.name != "buildSrc") {
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
}
================================================
FILE: artist-traits/gradle.properties
================================================
#
# Copyright (C) 2017. Uber Technologies
#
# 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.
#
POM_NAME=artist-traits
POM_ARTIFACT_ID=artist-traits
POM_PACKAGING=jar
================================================
FILE: artist-traits/src/main/kotlin/com/uber/artist/traits/JavaForegroundTrait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits
import com.google.auto.service.AutoService
import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
import com.uber.artist.api.Trait
import com.uber.artist.api.TypeNames
import javax.lang.model.element.Modifier
/**
* This [Trait] ports [FrameLayout]'s foreground functionality to other views. In order to use this, the module that
* applies that [Artist] plugin must declare the ForegroundView styleable in res/values/attrs_foreground_view.xml.
*
* <?xml version="1.0" encoding="utf-8"?>
* <resources>
* <declare-styleable name="ForegroundView">
* <attr name="android:foreground"/>
* <attr name="android:foregroundGravity"/>
* <attr name="foregroundInsidePadding"/>
* </declare-styleable>
* </resources>
*/
@AutoService(JavaTrait::class)
class JavaForegroundTrait : JavaTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
sourceType: String) {
val isLayout = sourceType.endsWith("Layout")
// The field
type.addField(TypeNames.Android.Drawable, "foreground", Modifier.PRIVATE)
if (isLayout) {
type.addField(FieldSpec.builder(TypeNames.Android.Rect, "selfBounds", Modifier.PRIVATE, Modifier.FINAL)
.initializer("new \$T()", TypeNames.Android.Rect)
.build())
type.addField(FieldSpec.builder(TypeNames.Android.Rect, "overlayBounds", Modifier.PRIVATE, Modifier.FINAL)
.initializer("new \$T()", TypeNames.Android.Rect)
.build())
type.addField(FieldSpec.builder(TypeName.BOOLEAN, "foregroundInPadding", Modifier.PRIVATE)
.initializer("true")
.build())
type.addField(FieldSpec.builder(TypeName.BOOLEAN, "foregroundBoundsChanged", Modifier.PRIVATE)
.initializer("false")
.build())
type.addField(FieldSpec.builder(TypeName.INT, "foregroundGravity", Modifier.PRIVATE)
.initializer("\$T.FILL", TypeNames.Android.Gravity)
.build())
}
// Pull out the value
initMethod.addStatement(
"\$T foregroundTA = context.obtainStyledAttributes(attrs, \$T.styleable.ForegroundView)",
TypeNames.Android.TypedArray,
rClass)
initMethod.addStatement(
"final \$T localForeground = foregroundTA.getDrawable(\$T.styleable.ForegroundView_android_foreground)",
TypeNames.Android.Drawable,
rClass)
initMethod.beginControlFlow("if (localForeground != null)")
initMethod.addCode("//noinspection AndroidLintNewApi\n")
initMethod.addStatement("setForeground(localForeground)")
initMethod.endControlFlow()
if (isLayout) {
initMethod.addStatement(
"foregroundGravity = foregroundTA.getInt(\$T.styleable.ForegroundView_android_foregroundGravity, " +
"foregroundGravity)", rClass)
initMethod.addStatement(
"foregroundInPadding = foregroundTA.getBoolean(" +
"\$T.styleable.ForegroundView_foregroundInsidePadding, " +
"true)", rClass)
}
initMethod.addStatement("foregroundTA.recycle()")
val onSizeChangedMethod = MethodSpec.methodBuilder("onSizeChanged")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PROTECTED)
.addParameter(TypeName.INT, "w")
.addParameter(TypeName.INT, "h")
.addParameter(TypeName.INT, "oldw")
.addParameter(TypeName.INT, "oldh")
.addStatement("super.onSizeChanged(w, h, oldw, oldh)")
if (isLayout) {
onSizeChangedMethod.addStatement("foregroundBoundsChanged = true")
} else {
onSizeChangedMethod.beginControlFlow("if (foreground != null)")
.addStatement("foreground.setBounds(0, 0, w, h)")
.endControlFlow()
}
type.addMethod(onSizeChangedMethod.build())
if (sourceType.endsWith("ImageView")) {
type.addMethod(MethodSpec.methodBuilder("hasOverlappingRendering")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.BOOLEAN)
.addStatement("return false")
.build())
}
if (isLayout) {
type.addMethod(MethodSpec.methodBuilder("getForegroundGravity")
.addJavadoc("""Describes how the foreground is positioned.
@return foreground gravity.
@see #setForegroundGravity(int)
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.INT)
.addStatement("return foregroundGravity")
.build())
type.addMethod(MethodSpec.methodBuilder("setForegroundGravity")
.addJavadoc("""Describes how the foreground is positioned. Defaults to START and TOP.
@param foregroundGravity See {@link android.view.Gravity}
@see #getForegroundGravity()
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.INT, "foregroundGravity")
.beginControlFlow("if (this.foregroundGravity != foregroundGravity)")
.beginControlFlow("if ((foregroundGravity & \$T.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0)",
TypeNames.Android.Gravity)
.addStatement("foregroundGravity |= \$T.START", TypeNames.Android.GravityCompat)
.endControlFlow()
.beginControlFlow("if ((foregroundGravity & \$T.VERTICAL_GRAVITY_MASK) == 0)",
TypeNames.Android.Gravity)
.addStatement("foregroundGravity |= \$T.TOP", TypeNames.Android.Gravity)
.endControlFlow()
.addStatement("this.foregroundGravity = foregroundGravity")
.beginControlFlow("if (this.foregroundGravity == \$T.FILL && foreground != null)",
TypeNames.Android.Gravity)
.addStatement("\$T padding = new \$T()", TypeNames.Android.Rect, TypeNames.Android.Rect)
.addStatement("foreground.getPadding(padding)")
.endControlFlow()
.addStatement("requestLayout()")
.endControlFlow()
.build())
}
type.addMethod(MethodSpec.methodBuilder("verifyDrawable")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PROTECTED)
.returns(TypeName.BOOLEAN)
.addParameter(TypeNames.Android.Drawable, "who")
.addStatement("return super.verifyDrawable(who) || (who == foreground)")
.build())
type.addMethod(MethodSpec.methodBuilder("jumpDrawablesToCurrentState")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PUBLIC)
.addStatement("super.jumpDrawablesToCurrentState()")
.beginControlFlow("if (foreground != null)")
.addStatement("foreground.jumpToCurrentState()")
.endControlFlow()
.build())
type.addMethod(MethodSpec.methodBuilder("drawableStateChanged")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PROTECTED)
.addStatement("super.drawableStateChanged()")
.beginControlFlow("if (foreground != null && foreground.isStateful())")
.addStatement("foreground.setState(getDrawableState())")
.endControlFlow()
.build())
type.addMethod(MethodSpec.methodBuilder("getForeground")
.addJavadoc("""Returns the drawable used as the foreground of this view. The
foreground drawable, if non-null, is always drawn on top of the children.
@return A Drawable or null if no foreground was set.
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
.addModifiers(Modifier.PUBLIC)
.returns(TypeNames.Android.Drawable)
.addStatement("return foreground")
.build())
val setForegroundMethod = MethodSpec.methodBuilder("setForeground")
.addJavadoc("""Supply a Drawable that is to be rendered on top of all of the child
views in this layout. Any padding in the Drawable will be taken
into account by ensuring that the children are inset to be placed
inside of the padding area.
@param drawable The Drawable to be drawn on top of the children.
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("value", "\$S", "MissingOverride").build())
.addAnnotation(AnnotationSpec.builder(ClassName.get("android.annotation", "SuppressLint"))
.addMember("value", "\"NewApi\"")
.build())
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeNames.Android.Drawable, "drawable")
.beginControlFlow("if (foreground != drawable)")
.beginControlFlow("if (foreground != null)")
.addStatement("foreground.setCallback(null)")
.addStatement("unscheduleDrawable(foreground)")
.endControlFlow()
.addStatement("foreground = drawable")
.beginControlFlow("if (drawable != null)")
if (!isLayout) {
setForegroundMethod.addStatement("foreground.setBounds(0, 0, getWidth(), getHeight())")
}
setForegroundMethod.addStatement("setWillNotDraw(false)")
.addStatement("drawable.setCallback(this)")
.beginControlFlow("if (drawable.isStateful())")
.addStatement("drawable.setState(getDrawableState())")
.endControlFlow()
if (isLayout) {
setForegroundMethod.beginControlFlow("if (foregroundGravity == \$T.FILL)", TypeNames.Android.Gravity)
setForegroundMethod.addStatement("\$T padding = new \$T()", TypeNames.Android.Rect, TypeNames.Android.Rect)
setForegroundMethod.addStatement("drawable.getPadding(padding)")
setForegroundMethod.endControlFlow()
}
setForegroundMethod.nextControlFlow("else")
.addStatement("setWillNotDraw(true)")
.endControlFlow()
if (isLayout) {
setForegroundMethod.addStatement("requestLayout()")
}
setForegroundMethod.addStatement("invalidate()")
.endControlFlow()
type.addMethod(setForegroundMethod.build())
if (isLayout) {
type.addMethod(MethodSpec.methodBuilder("onLayout")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PROTECTED)
.addParameter(TypeName.BOOLEAN, "changed")
.addParameter(TypeName.INT, "left")
.addParameter(TypeName.INT, "top")
.addParameter(TypeName.INT, "right")
.addParameter(TypeName.INT, "bottom")
.addStatement("super.onLayout(changed, left, top, right, bottom)")
.beginControlFlow("if (changed)")
.addStatement("foregroundBoundsChanged = true")
.endControlFlow()
.build())
}
val drawMethod = MethodSpec.methodBuilder("draw")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeNames.Android.Canvas, "canvas")
.addStatement("super.draw(canvas)")
.beginControlFlow("if (foreground != null)")
if (isLayout) {
drawMethod.addStatement("final \$T localForeground = foreground", TypeNames.Android.Drawable)
.beginControlFlow("if (foregroundBoundsChanged)")
.addStatement("foregroundBoundsChanged = false")
.addStatement("final \$T localSelfBounds = selfBounds", TypeNames.Android.Rect)
.addStatement("final \$T localOverlayBounds = overlayBounds", TypeNames.Android.Rect)
.addStatement("final int w = getRight() - getLeft()")
.addStatement("final int h = getBottom() - getTop()")
.beginControlFlow("if (foregroundInPadding)")
.addStatement("localSelfBounds.set(0, 0, w, h)")
.nextControlFlow("else")
.addStatement("localSelfBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - " +
"getPaddingBottom())")
.endControlFlow()
.addStatement("\$T.apply(foregroundGravity, localForeground.getIntrinsicWidth(), localForeground" +
".getIntrinsicHeight(), localSelfBounds, localOverlayBounds)", TypeNames.Android.Gravity)
.addStatement("localForeground.setBounds(localOverlayBounds)")
.endControlFlow()
.addStatement("localForeground.draw(canvas)")
} else {
drawMethod.addStatement("foreground.draw(canvas)")
}
drawMethod.endControlFlow()
type.addMethod(drawMethod.build())
type.addMethod(MethodSpec.methodBuilder("drawableHotspotChanged")
.addAnnotation(AnnotationSpec.builder(TypeNames.Annotations.TargetApi)
.addMember("value", "\$L", "android.os.Build.VERSION_CODES.LOLLIPOP")
.build())
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.FLOAT, "x")
.addParameter(TypeName.FLOAT, "y")
.addStatement("super.drawableHotspotChanged(x, y)")
.beginControlFlow("if (foreground != null)")
.addStatement("foreground.setHotspot(x, y)")
.endControlFlow()
.build())
}
}
================================================
FILE: artist-traits/src/main/kotlin/com/uber/artist/traits/JavaSuppressNullabilityInitializerTrait.kt
================================================
package com.uber.artist.traits
import com.google.auto.service.AutoService
import com.squareup.javapoet.AnnotationSpec
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec.Builder
import com.uber.artist.api.JavaTrait
@AutoService(JavaTrait::class)
class JavaSuppressNullabilityInitializerTrait : JavaTrait {
override fun generateFor(
type: Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
sourceType: String) {
initMethod.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java)
.addMember("value", "\$S", "CheckNullabilityTypes")
.build())
}
}
================================================
FILE: artist-traits/src/main/kotlin/com/uber/artist/traits/JavaVisibilityTrait.kt
================================================
package com.uber.artist.traits
import com.google.auto.service.AutoService
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec.Builder
import com.uber.artist.api.JavaTrait
import com.uber.artist.api.TypeNames
import javax.lang.model.element.Modifier
@AutoService(JavaTrait::class)
class JavaVisibilityTrait : JavaTrait {
override fun generateFor(
type: Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
sourceType: String) {
// Visibility convenience methods
arrayOf("visible", "invisible", "gone")
.forEach { type.addMethod(createVisibilityConvenienceMethod(it)) }
}
private fun createVisibilityConvenienceMethod(type: String): MethodSpec {
return MethodSpec.methodBuilder("is${type.capitalize()}")
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.BOOLEAN)
.addStatement("return getVisibility() == \$T.${type.toUpperCase()}", TypeNames.Android.View)
.build()
}
}
================================================
FILE: artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinForegroundTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.BOOLEAN
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FLOAT
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.INT
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
import com.uber.artist.api.KotlinTypeNames
import com.uber.artist.api.Trait
/**
* This [Trait] ports [FrameLayout]'s foreground functionality to other views. In order to use this, the module that
* applies that [Artist] plugin must declare the ForegroundView styleable in res/values/attrs_foreground_view.xml.
*
* <?xml version="1.0" encoding="utf-8"?>
* <resources>
* <declare-styleable name="ForegroundView">
* <attr name="android:foreground"/>
* <attr name="android:foregroundGravity"/>
* <attr name="foregroundInsidePadding"/>
* </declare-styleable>
* </resources>
*/
@AutoService(KotlinTrait::class)
class KotlinForegroundTrait : KotlinTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
sourceType: String) {
val isLayout = sourceType.endsWith("Layout")
// The field
type.addProperty(PropertySpec.builder("foreground", KotlinTypeNames.Android.Drawable.copy(nullable = true), KModifier.PRIVATE)
.initializer("null")
.mutable()
.build())
if (isLayout) {
type.addProperty(PropertySpec.builder("selfBounds", KotlinTypeNames.Android.Rect, KModifier.PRIVATE, KModifier.FINAL)
.initializer("%T()", KotlinTypeNames.Android.Rect)
.build())
type.addProperty(PropertySpec.builder("overlayBounds", KotlinTypeNames.Android.Rect, KModifier.PRIVATE, KModifier.FINAL)
.initializer("%T()", KotlinTypeNames.Android.Rect)
.build())
type.addProperty(PropertySpec.builder("foregroundInPadding", BOOLEAN, KModifier.PRIVATE)
.initializer("true")
.mutable()
.build())
type.addProperty(PropertySpec.builder("foregroundBoundsChanged", BOOLEAN, KModifier.PRIVATE)
.initializer("false")
.mutable()
.build())
type.addProperty(PropertySpec.builder("foregroundGravity", INT, KModifier.PRIVATE)
.initializer("%T.FILL", KotlinTypeNames.Android.Gravity)
.mutable()
.build())
}
// Pull out the value
initMethod.apply {
addStatement("val foregroundTA = context.obtainStyledAttributes(attrs, %T.styleable.ForegroundView)", rClass)
beginControlFlow("foregroundTA.getDrawable(%T.styleable.ForegroundView_android_foreground)?.let", rClass)
addCode("//noinspection AndroidLintNewApi\n")
addStatement("setForeground(it)")
endControlFlow()
if (isLayout) {
addStatement(
"foregroundGravity = foregroundTA.getInt(%T.styleable.ForegroundView_android_foregroundGravity, foregroundGravity)", rClass)
addStatement(
"foregroundInPadding = foregroundTA.getBoolean(%T.styleable.ForegroundView_foregroundInsidePadding, true)", rClass)
}
addStatement("foregroundTA.recycle()")
}
val onSizeChangedMethod = FunSpec.builder("onSizeChanged")
.addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("w", INT)
.addParameter("h", INT)
.addParameter("oldw", INT)
.addParameter("oldh", INT)
.addStatement("super.onSizeChanged(w, h, oldw, oldh)")
if (isLayout) {
onSizeChangedMethod.addStatement("foregroundBoundsChanged = true")
} else {
onSizeChangedMethod.addStatement("foreground?.setBounds(0, 0, w, h)")
}
type.addFunction(onSizeChangedMethod.build())
if (sourceType.endsWith("ImageView")) {
type.addFunction(FunSpec.builder("hasOverlappingRendering")
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.returns(BOOLEAN)
.addStatement("return false")
.build())
}
if (isLayout) {
type.addFunction(FunSpec.builder("getForegroundGravity")
.addKdoc("""Describes how the foreground is positioned.
@return foreground gravity.
@see #setForegroundGravity(int)
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("%S", "MissingOverride").build())
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.returns(INT)
.addStatement("return foregroundGravity")
.build())
type.addFunction(FunSpec.builder("setForegroundGravity")
.addKdoc("""Describes how the foreground is positioned. Defaults to START and TOP.
@param foregroundGravity See {@link android.view.Gravity}
@see #getForegroundGravity()
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("%S", "MissingOverride").build())
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("foregroundGravity", INT)
.beginControlFlow("if (this.foregroundGravity != foregroundGravity)")
.beginControlFlow("if ((foregroundGravity and %T.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0)",
KotlinTypeNames.Android.Gravity)
.addStatement("this.foregroundGravity = foregroundGravity.or(%T.START)", KotlinTypeNames.Android.GravityCompat)
.endControlFlow()
.beginControlFlow("if ((foregroundGravity and %T.VERTICAL_GRAVITY_MASK) == 0)",
KotlinTypeNames.Android.Gravity)
.addStatement("this.foregroundGravity = foregroundGravity.or(%T.TOP)", KotlinTypeNames.Android.Gravity)
.endControlFlow()
.beginControlFlow("if (this.foregroundGravity == %T.FILL && foreground != null)",
KotlinTypeNames.Android.Gravity)
.addStatement("val padding = %T()", KotlinTypeNames.Android.Rect)
.addStatement("foreground?.getPadding(padding)")
.endControlFlow()
.addStatement("requestLayout()")
.endControlFlow()
.build())
}
type.addFunction(FunSpec.builder("verifyDrawable")
.addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
.returns(BOOLEAN)
.addParameter("who", KotlinTypeNames.Android.Drawable)
.addStatement("return super.verifyDrawable(who) || (who == foreground)")
.build())
type.addFunction(FunSpec.builder("jumpDrawablesToCurrentState")
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.addStatement("super.jumpDrawablesToCurrentState()")
.addStatement("foreground?.jumpToCurrentState()")
.build())
type.addFunction(FunSpec.builder("drawableStateChanged")
.addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
.addStatement("super.drawableStateChanged()")
.beginControlFlow("if (foreground?.isStateful() ?: false)")
.addStatement("foreground?.setState(getDrawableState())")
.endControlFlow()
.build())
type.addFunction(FunSpec.builder("getForeground")
.addKdoc("""Returns the drawable used as the foreground of this view. The
foreground drawable, if non-null, is always drawn on top of the children.
@return A Drawable or null if no foreground was set.
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("%S", "MissingOverride").build())
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.returns(KotlinTypeNames.Android.Drawable.copy(nullable = true))
.addStatement("return foreground")
.build())
val setForegroundMethod = FunSpec.builder("setForeground")
.addKdoc("""Supply a Drawable that is to be rendered on top of all of the child
views in this layout. Any padding in the Drawable will be taken
into account by ensuring that the children are inset to be placed
inside of the padding area.
@param drawable The Drawable to be drawn on top of the children.
""")
.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java).addMember("%S", "MissingOverride").build())
.addAnnotation(AnnotationSpec.builder(ClassName("android.annotation", "SuppressLint"))
.addMember("%S", "NewApi")
.build())
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("drawable", KotlinTypeNames.Android.Drawable.copy(nullable = true))
.beginControlFlow("if (foreground != drawable)")
.beginControlFlow("if (foreground != null)")
.addStatement("foreground?.setCallback(null)")
.addStatement("unscheduleDrawable(foreground)")
.endControlFlow()
.addStatement("foreground = drawable")
.beginControlFlow("if (drawable != null)")
if (!isLayout) {
setForegroundMethod.addStatement("foreground?.setBounds(0, 0, getWidth(), getHeight())")
}
setForegroundMethod.addStatement("setWillNotDraw(false)")
.addStatement("drawable.setCallback(this)")
.beginControlFlow("if (drawable.isStateful())")
.addStatement("drawable.setState(getDrawableState())")
.endControlFlow()
if (isLayout) {
setForegroundMethod.beginControlFlow("if (foregroundGravity == %T.FILL)", KotlinTypeNames.Android.Gravity)
setForegroundMethod.addStatement("val padding = %T()", KotlinTypeNames.Android.Rect)
setForegroundMethod.addStatement("drawable.getPadding(padding)")
setForegroundMethod.endControlFlow()
}
setForegroundMethod.nextControlFlow("else")
.addStatement("setWillNotDraw(true)")
.endControlFlow()
if (isLayout) {
setForegroundMethod.addStatement("requestLayout()")
}
setForegroundMethod.addStatement("invalidate()")
.endControlFlow()
type.addFunction(setForegroundMethod.build())
if (isLayout) {
type.addFunction(FunSpec.builder("onLayout")
.addModifiers(KModifier.PROTECTED, KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("changed", BOOLEAN)
.addParameter("left", INT)
.addParameter("top", INT)
.addParameter("right", INT)
.addParameter("bottom", INT)
.addStatement("super.onLayout(changed, left, top, right, bottom)")
.beginControlFlow("if (changed)")
.addStatement("foregroundBoundsChanged = true")
.endControlFlow()
.build())
}
val drawMethod = FunSpec.builder("draw")
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("canvas", KotlinTypeNames.Android.Canvas)
.addStatement("super.draw(canvas)")
if (isLayout) {
drawMethod
.beginControlFlow("foreground?.let")
.addStatement("val localForeground = it")
.beginControlFlow("if (foregroundBoundsChanged)")
.addStatement("foregroundBoundsChanged = false")
.addStatement("val localSelfBounds: %T = selfBounds", KotlinTypeNames.Android.Rect)
.addStatement("val localOverlayBounds: %T = overlayBounds", KotlinTypeNames.Android.Rect)
.addStatement("val w: Int = getRight() - getLeft()")
.addStatement("val h: Int = getBottom() - getTop()")
.beginControlFlow("if (foregroundInPadding)")
.addStatement("localSelfBounds.set(0, 0, w, h)")
.nextControlFlow("else")
.addStatement("localSelfBounds.set(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - " +
"getPaddingBottom())")
.endControlFlow()
.addStatement("%T.apply(foregroundGravity, localForeground.getIntrinsicWidth(), localForeground" +
".getIntrinsicHeight(), localSelfBounds, localOverlayBounds)", KotlinTypeNames.Android.Gravity)
.addStatement("localForeground.setBounds(localOverlayBounds)")
.endControlFlow()
.addStatement("localForeground.draw(canvas)")
.endControlFlow()
} else {
drawMethod.addStatement("foreground?.draw(canvas)")
}
type.addFunction(drawMethod.build())
type.addFunction(FunSpec.builder("drawableHotspotChanged")
.addAnnotation(AnnotationSpec.builder(KotlinTypeNames.Annotations.TargetApi)
.addMember("%T.VERSION_CODES.LOLLIPOP", ClassName("android.os", "Build"))
.build())
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("x", FLOAT)
.addParameter("y", FLOAT)
.addStatement("super.drawableHotspotChanged(x, y)")
.addStatement("foreground?.setHotspot(x, y)")
.build())
}
}
================================================
FILE: artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinSuppressNullabilityInitializerTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
@AutoService(KotlinTrait::class)
class KotlinSuppressNullabilityInitializerTrait : KotlinTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
sourceType: String) {
initMethod.addAnnotation(AnnotationSpec.builder(SuppressWarnings::class.java)
.addMember("%S", "CheckNullabilityTypes")
.build())
}
}
================================================
FILE: artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinVisibilityTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.BOOLEAN
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
import com.uber.artist.api.KotlinTypeNames
@AutoService(KotlinTrait::class)
class KotlinVisibilityTrait : KotlinTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
sourceType: String) {
// Visibility convenience methods
arrayOf("visible", "invisible", "gone")
.forEach { type.addFunction(createVisibilityConvenienceMethod(it)) }
}
private fun createVisibilityConvenienceMethod(type: String): FunSpec {
return FunSpec.builder("is${type.capitalize()}")
.addModifiers(KModifier.OPEN)
.returns(BOOLEAN)
.addStatement("return getVisibility() == %T.${type.toUpperCase()}", KotlinTypeNames.Android.View)
.build()
}
}
================================================
FILE: artist-traits-rx/build.gradle
================================================
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
apply plugin: "org.jetbrains.kotlin.kapt"
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
kapt deps.apt.autoService
compileOnly deps.apt.autoService
// Also requires RxBinding be on the classpath. Since it is an AAR, we can't include it here.
api deps.apt.javapoet
api deps.external.rxjava2
api deps.external.rxrelay2
api project(":artist-api")
implementation deps.kotlin.stdLibJdk7
}
if (rootProject.projectDir.name != "buildSrc") {
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
}
================================================
FILE: artist-traits-rx/gradle.properties
================================================
#
# Copyright (C) 2017. Uber Technologies
#
# 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.
#
POM_NAME=artist-traits-rx
POM_ARTIFACT_ID=artist-traits-rx
POM_PACKAGING=jar
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaApiHelper.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.FieldSpec
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterSpec
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.TypeNames
import com.uber.artist.traits.rx.config.JavaArtistRxConfigService
import javax.lang.model.element.Modifier
data class JavaRxBindingInfo(
val className: ClassName,
val methodName: String,
val methodDoc: String
)
data class JavaSettableApi(
val rxBindingInfo: JavaRxBindingInfo,
val listenerType: TypeName,
val listenerMethod: String,
val observableType: TypeName,
val listenerImpl: MethodSpec.Builder,
val isStateful: Boolean = false,
val relayInitializer: CodeBlock? = null,
val setterCaveats: String? = null,
val isUViewOverride: Boolean = false,
val setListenerMethodAnnotations: List<ClassName> = emptyList())
data class JavaAdditiveApi(
val rxBindingInfo: JavaRxBindingInfo,
val observableType: TypeName,
val isUViewOverride: Boolean = false
)
private fun TypeName.irrelevantIfObject(): TypeName {
val artistRxConfig = JavaArtistRxConfigService.newInstance().getArtistRxConfig()
return if (this == TypeName.OBJECT.box()) artistRxConfig.rxBindingSignalEventTypeName() else this
}
fun addRxBindingApiForAdditive(type: TypeSpec.Builder, api: JavaAdditiveApi) {
val artistRxConfig = JavaArtistRxConfigService.newInstance().getArtistRxConfig()
type.addMethod(MethodSpec.methodBuilder(api.rxBindingInfo.methodName)
.addJavadoc("${api.rxBindingInfo.methodDoc}\n")
.apply {
if (api.isUViewOverride) {
addAnnotation(Override::class.java)
}
}
.addModifiers(Modifier.PUBLIC)
.returns(ParameterizedTypeName.get(JavaRxTypeNames.Rx.Observable, api.observableType.irrelevantIfObject()))
.addCode(CodeBlock.builder()
.add("return \$T.${api.rxBindingInfo.methodName}(this)", api.rxBindingInfo.className)
.apply {
if (api.observableType == TypeName.OBJECT.box()) {
artistRxConfig.processRxBindingSignalEvent(this)
}
if (api.rxBindingInfo.methodName != "attachEvents") {
// Safe to call, otherwise it'd be a recursive stack overflow
artistRxConfig.processRxBindingStream(this, api.observableType.irrelevantIfObject())
}
add(";")
}
.build())
.build())
}
fun addRxBindingApiForSettable(type: TypeSpec.Builder, api: JavaSettableApi, isDebug: Boolean = true) {
val artistRxConfig = JavaArtistRxConfigService.newInstance().getArtistRxConfig()
val rxBindingClassName = api.rxBindingInfo.className
val rxBindingMethod = api.rxBindingInfo.methodName
val rxBindingMethodDoc = api.rxBindingInfo.methodDoc
val isInitting = "${api.rxBindingInfo.methodName}IsInitting"
val disposable = "${api.rxBindingInfo.methodName}Disposable"
// clicksInitting
type.addField(TypeName.BOOLEAN, isInitting, Modifier.PRIVATE)
// internal relay
type.addField(
FieldSpec.builder(ParameterizedTypeName.get(if (api.isStateful) JavaRxTypeNames.Rx.BehaviorRelay else JavaRxTypeNames.Rx.PublishRelay,
api.observableType.irrelevantIfObject()),
rxBindingMethod,
Modifier.PRIVATE)
.addAnnotation(TypeNames.Annotations.Nullable).build())
type.addField(FieldSpec.builder(JavaRxTypeNames.Rx.Disposable, disposable, Modifier.PRIVATE).addAnnotation(TypeNames.Annotations.Nullable).build())
val consumer = TypeSpec.anonymousClassBuilder("")
.addSuperinterface(ParameterizedTypeName.get(JavaRxTypeNames.Rx.Consumer, api.observableType.irrelevantIfObject()))
.addMethod(api.listenerImpl.addAnnotation(Override::class.java).build())
.build()
// Overridden and deprecated setOnClickListener method
type.addMethod(MethodSpec.methodBuilder(api.listenerMethod)
.addJavadoc(StringBuilder().apply {
if (api.setterCaveats != null) {
append(api.setterCaveats)
append("\n\n")
}
}.append("@deprecated Use {@link #$rxBindingMethod()}\n").toString())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addAnnotation(Override::class.java)
.addAnnotation(java.lang.Deprecated::class.java)
.addParameter(
ParameterSpec.builder(api.listenerType, "l", Modifier.FINAL).apply {
api.setListenerMethodAnnotations.forEach {
addAnnotation(it)
}
}.build()
)
.beginControlFlow("if ($isInitting)")
.addStatement("$isInitting = false")
.addStatement("super.${api.listenerMethod}(l)")
.nextControlFlow("else")
.beginControlFlow("if ($disposable != null)")
.addStatement("$disposable.dispose()")
.addStatement("$disposable = null")
.endControlFlow()
.beginControlFlow("if (l != null)")
.addCode(CodeBlock.builder()
.add("$disposable = $rxBindingMethod()")
.add(".subscribe(\$L);", consumer)
.build())
.endControlFlow()
.endControlFlow()
.build())
type.addMethod(MethodSpec.methodBuilder(rxBindingMethod)
.addJavadoc(rxBindingMethodDoc)
.apply {
if (api.isUViewOverride) {
addAnnotation(Override::class.java)
}
}
.addModifiers(Modifier.PUBLIC)
.returns(ParameterizedTypeName.get(JavaRxTypeNames.Rx.Observable, api.observableType.irrelevantIfObject()))
.beginControlFlow("if ($rxBindingMethod == null)")
.addStatement("$isInitting = true")
.apply {
if (api.relayInitializer != null) {
addCode("$rxBindingMethod = ", JavaRxTypeNames.Rx.BehaviorRelay)
addCode(api.relayInitializer)
addCode(";\n")
} else {
addStatement("$rxBindingMethod = \$T.create()",
if (api.isStateful) JavaRxTypeNames.Rx.BehaviorRelay else JavaRxTypeNames.Rx.PublishRelay)
}
}
.addCode(CodeBlock.builder()
.add("\$T.$rxBindingMethod(this)", rxBindingClassName)
.apply {
if (api.observableType == TypeName.OBJECT.box()) {
artistRxConfig.processRxBindingSignalEvent(this)
}
if (rxBindingMethod.contains("click", true)) {
artistRxConfig.processTap(this)
}
}
.addStatement("\n\t.subscribe($rxBindingMethod)")
.build())
.endControlFlow()
.addCode(CodeBlock.builder()
.add("return $rxBindingMethod.hide()")
.apply { artistRxConfig.processRxBindingStream(this, api.observableType.irrelevantIfObject()) }
.add(";")
.build())
.build())
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaCheckableTrait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.CodeBlock
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
import com.uber.artist.api.TypeNames
import javax.lang.model.element.Modifier
@AutoService(JavaTrait::class)
class JavaCheckableTrait : JavaTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
baseType: String) {
val isTextView = baseType.endsWith("TextView")
if (isTextView) {
type.addField(
ParameterizedTypeName.get(JavaRxTypeNames.Rx.BehaviorRelay, TypeName.BOOLEAN.box()), "checkedChanges",
Modifier.PRIVATE)
type.addMethod(MethodSpec.methodBuilder("ensureCheckedChanges")
.addModifiers(Modifier.PRIVATE)
.beginControlFlow("if (checkedChanges == null)")
.addStatement("checkedChanges = \$T.create()", JavaRxTypeNames.Rx.BehaviorRelay)
.endControlFlow()
.build())
type.addMethod(MethodSpec.methodBuilder("checkedChanges")
.addJavadoc("""@return an observable of booleans representing the checked state of this view.
""")
.addModifiers(Modifier.PUBLIC)
.returns(ParameterizedTypeName.get(JavaRxTypeNames.Rx.Observable, TypeName.BOOLEAN.box()))
.addStatement("ensureCheckedChanges()")
.addStatement("return checkedChanges.hide()")
.build())
type.addMethod(MethodSpec.methodBuilder("setChecked")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override::class.java)
.addParameter(TypeName.BOOLEAN, "val")
.addStatement("super.setChecked(val)")
.addStatement("ensureCheckedChanges()")
.addStatement("checkedChanges.accept(val)")
.build())
} else {
addRxBindingApiForSettable(type, JavaSettableApi(
JavaRxBindingInfo(JavaRxTypeNames.Rx.RxCompoundButton,
"checkedChanges",
"""@return an observable of booleans representing the checked state of this view.
"""),
ClassName.bestGuess("OnCheckedChangeListener"),
"setOnCheckedChangeListener",
TypeName.BOOLEAN.box(),
MethodSpec.methodBuilder("accept")
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.BOOLEAN.box(), "isChecked")
.addStatement("l.onCheckedChanged($baseType.this, isChecked)"),
true,
CodeBlock.of("\$T.createDefault(isChecked())", JavaRxTypeNames.Rx.BehaviorRelay),
setListenerMethodAnnotations = listOf(TypeNames.Annotations.Nullable)
))
}
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaRxTypeNames.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.squareup.javapoet.ClassName
class JavaRxTypeNames {
class Rx {
companion object {
// Rx
val Consumer = ClassName.get(io.reactivex.functions.Consumer::class.java)
val Disposable = ClassName.get(io.reactivex.disposables.Disposable::class.java)
val Function = ClassName.get(io.reactivex.functions.Function::class.java)
val Observable = ClassName.get(io.reactivex.Observable::class.java)
// RxRelay
val BehaviorRelay = ClassName.get(com.jakewharton.rxrelay2.BehaviorRelay::class.java)
val PublishRelay = ClassName.get(com.jakewharton.rxrelay2.PublishRelay::class.java)
// RxBinding
val RecyclerViewScrollEvent = ClassName.get("com.jakewharton.rxbinding3.recyclerview", "RecyclerViewScrollEvent")
val RxView = ClassName.get("com.jakewharton.rxbinding3.view", "RxView")
val RxCompoundButton = ClassName.get("com.jakewharton.rxbinding3.widget", "RxCompoundButton")
val RxNestedScrollView = ClassName.get("com.jakewharton.rxbinding3.core", "RxNestedScrollView")
val RxRecyclerView = ClassName.get("com.jakewharton.rxbinding3.recyclerview", "RxRecyclerView")
val RxSearchView = ClassName.get("com.jakewharton.rxbinding3.appcompat", "RxSearchView")
val RxSeekBar = ClassName.get("com.jakewharton.rxbinding3.widget", "RxSeekBar")
val SeekBarChangeEvent = ClassName.get("com.jakewharton.rxbinding3.widget", "SeekBarChangeEvent")
val SeekBarProgressChangeEvent = ClassName.get("com.jakewharton.rxbinding3.widget", "SeekBarProgressChangeEvent")
val SeekBarStartChangeEvent = ClassName.get("com.jakewharton.rxbinding3.widget", "SeekBarStartChangeEvent")
val RxSwipeRefreshLayout = ClassName.get("com.jakewharton.rxbinding3.swiperefreshlayout", "RxSwipeRefreshLayout")
val RxTabLayout = ClassName.get("com.jakewharton.rxbinding3.material", "RxTabLayout")
val RxTextView = ClassName.get("com.jakewharton.rxbinding3.widget", "RxTextView")
val RxToolbar = ClassName.get("com.jakewharton.rxbinding3.widget", "RxToolbar")
val RxViewPager = ClassName.get("com.jakewharton.rxbinding3.viewpager", "RxViewPager")
val RxViewAttachEvent = ClassName.get("com.jakewharton.rxbinding3.view", "ViewAttachEvent")
val RxViewAttachAttachedEvent = ClassName.get("com.jakewharton.rxbinding3.view", "ViewAttachAttachedEvent")
val RxViewAttachDetachedEvent = ClassName.get("com.jakewharton.rxbinding3.view", "ViewAttachDetachedEvent")
val SearchViewQueryTextEvent = ClassName.get("com.jakewharton.rxbinding3.appcompat", "SearchViewQueryTextEvent")
val ViewScrollChangeEvent = ClassName.get("com.jakewharton.rxbinding3.view", "ViewScrollChangeEvent")
}
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaScrollableTrait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
import javax.lang.model.element.Modifier
@AutoService(JavaTrait::class)
class JavaScrollableTrait : JavaTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
sourceType: String) {
// ScrollView overrides
if (sourceType.contains("ScrollView")) {
addRxBindingApiForSettable(type, JavaSettableApi(
JavaRxBindingInfo(JavaRxTypeNames.Rx.RxNestedScrollView,
"scrollChangeEvents",
"""@return an observable of scroll-change events for this NestedScrollView.
"""),
ClassName.bestGuess("OnScrollChangeListener"),
"setOnScrollChangeListener",
JavaRxTypeNames.Rx.ViewScrollChangeEvent,
MethodSpec.methodBuilder("accept")
.addModifiers(Modifier.PUBLIC)
.addParameter(JavaRxTypeNames.Rx.ViewScrollChangeEvent, "event")
.addStatement("l.onScrollChange($sourceType.this, event.getScrollX(), event" +
".getScrollY" +
"(), event.getOldScrollX(), event.getOldScrollY())")))
}
// RecyclerView overrides
if (sourceType.contains("RecyclerView")) {
addRxBindingApiForAdditive(type, JavaAdditiveApi(
JavaRxBindingInfo(JavaRxTypeNames.Rx.RxRecyclerView,
"scrollEvents",
"@return an observable of scroll events on this RecyclerView"),
JavaRxTypeNames.Rx.RecyclerViewScrollEvent))
}
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaTextInputTrait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
@AutoService(JavaTrait::class)
class JavaTextInputTrait : JavaTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
sourceType: String) {
// TextChanges
addRxBindingApiForAdditive(type, JavaAdditiveApi(
JavaRxBindingInfo(JavaRxTypeNames.Rx.RxTextView,
"textChanges",
"""@return an observable of character sequences for text changes on this TextView."""),
ClassName.get(CharSequence::class.java)))
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaViewTrait.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.TypeSpec
import com.uber.artist.api.JavaTrait
import com.uber.artist.api.TypeNames
import com.uber.artist.traits.rx.config.JavaArtistRxConfigService
import javax.lang.model.element.Modifier
@AutoService(JavaTrait::class)
open class JavaViewTrait : JavaTrait {
private val artistRxConfig by lazy { JavaArtistRxConfigService.newInstance().getArtistRxConfig() }
override fun generateFor(
type: TypeSpec.Builder,
initMethod: MethodSpec.Builder,
rClass: ClassName,
sourceType: String) {
clicks(type, sourceType)
longClicks(type, sourceType)
layoutChanges(type)
}
open fun clicks(type: TypeSpec.Builder, sourceType: String) {
addRxBindingApiForSettable(type, JavaSettableApi(
JavaRxBindingInfo(JavaRxTypeNames.Rx.RxView,
"clicks",
"""@return an Observable of click events. The emitted value is unspecified and should only be used as notification.
"""),
ClassName.bestGuess("OnClickListener"),
"setOnClickListener",
TypeName.OBJECT.box(),
MethodSpec.methodBuilder("accept")
.addModifiers(Modifier.PUBLIC)
.addParameter(artistRxConfig.rxBindingSignalEventTypeName(), "ignored")
.addStatement("l.onClick($sourceType.this)"),
setListenerMethodAnnotations = listOf(TypeNames.Annotations.Nullable)
))
}
open fun longClicks(type: TypeSpec.Builder, sourceType: String) {
addRxBindingApiForSettable(type, JavaSettableApi(
JavaRxBindingInfo(JavaRxTypeNames.Rx.RxView,
"longClicks",
"""@return an Observable of longclick events. The emitted value is unspecified and should only be used as notification.
"""),
ClassName.bestGuess("OnLongClickListener"),
"setOnLongClickListener",
TypeName.OBJECT.box(),
MethodSpec.methodBuilder("accept")
.addModifiers(Modifier.PUBLIC)
.addParameter(artistRxConfig.rxBindingSignalEventTypeName(), "ignored")
.addStatement("l.onLongClick($sourceType.this)"),
setListenerMethodAnnotations = listOf(TypeNames.Annotations.Nullable)
))
}
open fun layoutChanges(type: TypeSpec.Builder) {
// Attach state changes observable
addRxBindingApiForAdditive(type, JavaAdditiveApi(
JavaRxBindingInfo(
JavaRxTypeNames.Rx.RxView,
"layoutChanges",
"@return an observable which emits on layout changes. The emitted value is " +
"unspecified and should only be used as notification."),
TypeName.OBJECT.box()))
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinApiHelper.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import AliasTypeNames.Rx.Companion.rxExtensionFunctionToAlias
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.BOOLEAN
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.uber.artist.api.KotlinTypeNames
import com.uber.artist.traits.rx.config.KotlinArtistRxConfigService
data class KotlinRxBindingInfo(
val className: ClassName,
val methodName: String,
val methodDoc: String
)
fun KotlinRxBindingInfo.getRxAlias(): String? {
val rxBindingClassName = className
val rxBindingMethod = methodName
val alias_keys = rxExtensionFunctionToAlias.filter {
it.key.methodName == rxBindingMethod && it.key
.className == rxBindingClassName
}.keys.toList()
val rx_alias = if (alias_keys.size > 0) {
rxExtensionFunctionToAlias[alias_keys[0]]
} else {
null
}
return rx_alias
}
data class KotlinSettableApi(
val rxBindingInfo: KotlinRxBindingInfo,
val listenerType: TypeName,
val listenerMethod: String,
val observableType: TypeName,
val listenerImpl: FunSpec.Builder,
val isStateful: Boolean = false,
val relayInitializer: CodeBlock? = null,
val setterCaveats: String? = null,
val isUViewOverride: Boolean = false,
val setListenerMethodAnnotations: List<ClassName> = emptyList())
data class KotlinAdditiveApi(
val rxBindingInfo: KotlinRxBindingInfo,
val observableType: TypeName,
val isUViewOverride: Boolean = false
)
private fun TypeName.irrelevantIfObject(): TypeName {
val artistRxConfig = KotlinArtistRxConfigService.newInstance().getArtistRxConfig()
return if (this == KotlinTypeNames.Java.Object) artistRxConfig.rxBindingSignalEventTypeName() else this
}
fun addRxBindingApiForAdditive(type: TypeSpec.Builder, api: KotlinAdditiveApi) {
val artistRxConfig = KotlinArtistRxConfigService.newInstance().getArtistRxConfig()
val rx_alias = api.rxBindingInfo.getRxAlias()
type.addFunction(FunSpec.builder(api.rxBindingInfo.methodName)
.addKdoc("${api.rxBindingInfo.methodDoc}\n")
.apply {
if (api.isUViewOverride) {
addModifiers(KModifier.OVERRIDE)
}
}
.addModifiers(KModifier.OPEN)
.returns(KotlinRxTypeNames.Rx.Observable.parameterizedBy(api.observableType.irrelevantIfObject()))
.addCode(CodeBlock.builder()
.apply {
if (rx_alias != null) {
add("return ${rx_alias}()")
} else {
add("return ${api.rxBindingInfo.methodName}()")
}
}
.apply {
if (api.observableType == KotlinTypeNames.Java.Object) {
artistRxConfig.processRxBindingSignalEvent(this)
}
if (api.rxBindingInfo.methodName != "attachEvents") {
// Safe to call, otherwise it'd be a recursive stack overflow
artistRxConfig.processRxBindingStream(this, api.observableType.irrelevantIfObject())
}
}
.add("\n")
.build())
.build())
}
fun addRxBindingApiForSettable(type: TypeSpec.Builder, api: KotlinSettableApi, isDebug: Boolean = true) {
val artistRxConfig = KotlinArtistRxConfigService.newInstance().getArtistRxConfig()
val rxBindingClassName = api.rxBindingInfo.className
val rxBindingMethod = api.rxBindingInfo.methodName
val rxBindingMethodDoc = api.rxBindingInfo.methodDoc
val isInitting = "${api.rxBindingInfo.methodName}IsInitting"
val disposable = "${api.rxBindingInfo.methodName}Disposable"
val rx_alias = api.rxBindingInfo.getRxAlias()
// clicksInitting
type.addProperty(PropertySpec.builder(isInitting, BOOLEAN, KModifier.PRIVATE)
.mutable()
.initializer("false")
.build())
// internal relay
val internalRelayTypeName = if (api.isStateful) KotlinRxTypeNames.Rx.BehaviorRelay else KotlinRxTypeNames.Rx.PublishRelay
type.addProperty(
PropertySpec.builder(rxBindingMethod, internalRelayTypeName.parameterizedBy(api.observableType.irrelevantIfObject()).copy(nullable = true),
KModifier.PRIVATE)
.mutable()
.initializer("null")
.build())
type.addProperty(PropertySpec.builder(disposable, KotlinRxTypeNames.Rx.Disposable.copy(nullable = true), KModifier.PRIVATE)
.mutable()
.initializer("null")
.build())
val consumer = TypeSpec.anonymousClassBuilder()
.addSuperinterface(KotlinRxTypeNames.Rx.Consumer.parameterizedBy(api.observableType.irrelevantIfObject()))
.addFunction(api.listenerImpl.addModifiers(KModifier.OVERRIDE).build())
.build()
// Overridden and deprecated setOnClickListener method
type.addFunction(FunSpec.builder(api.listenerMethod)
.addKdoc(StringBuilder().apply {
if (api.setterCaveats != null) {
append(api.setterCaveats)
append("\n\n")
}
}.append("@deprecated Use [$rxBindingMethod]\n").toString())
.addModifiers(KModifier.FINAL, KModifier.OVERRIDE)
.addAnnotation(AnnotationSpec.builder(Deprecated::class.java)
.addMember("message = %S", "Use $rxBindingMethod()")
.addMember("replaceWith = %T(%S)", ReplaceWith::class.asClassName(), "$rxBindingMethod()")
.addMember("level = %T.ERROR", DeprecationLevel::class.asClassName())
.build())
.addParameter(
ParameterSpec.builder("l", api.listenerType.copy(nullable = true)).apply {
api.setListenerMethodAnnotations.forEach {
addAnnotation(it)
}
}.build()
)
.beginControlFlow("if ($isInitting)")
.addStatement("$isInitting = false")
.addStatement("super.${api.listenerMethod}(l)")
.nextControlFlow("else")
.addStatement("$disposable?.dispose()")
.addStatement("$disposable = null")
.beginControlFlow("if (l != null)")
.addCode(CodeBlock.builder()
.add("$disposable = $rxBindingMethod()")
.add(".subscribe($consumer)\n")
.build())
.endControlFlow()
.endControlFlow()
.build())
type.addFunction(FunSpec.builder(rxBindingMethod)
.addKdoc(rxBindingMethodDoc)
.apply {
if (api.isUViewOverride) {
addModifiers(KModifier.OVERRIDE)
}
}
.addModifiers(KModifier.OPEN)
.returns(KotlinRxTypeNames.Rx.Observable.parameterizedBy(api.observableType.irrelevantIfObject()))
.beginControlFlow("if ($rxBindingMethod == null)")
.addStatement("$isInitting = true")
.apply {
if (api.relayInitializer != null) {
addCode("$rxBindingMethod = ", KotlinRxTypeNames.Rx.BehaviorRelay)
addCode(api.relayInitializer)
} else {
addStatement("$rxBindingMethod = %T.create()",
if (api.isStateful) KotlinRxTypeNames.Rx.BehaviorRelay else KotlinRxTypeNames.Rx.PublishRelay)
}
}
.addCode(CodeBlock.builder()
.apply {
add("$rxBindingMethod?.let {\n")
}
.apply {
if (rx_alias != null) {
add("$rx_alias()")
} else {
add("%T.$rxBindingMethod(this)", rxBindingClassName)
}
}
.apply {
if (api.observableType == KotlinTypeNames.Java.Object) {
artistRxConfig.processRxBindingSignalEvent(this)
}
if (rxBindingMethod.contains("click", true)) {
artistRxConfig.processTap(this)
}
}
.addStatement(".subscribe(it)")
.addStatement(" }\n")
.build())
.endControlFlow()
.addCode(CodeBlock.builder()
.add("return $rxBindingMethod?.hide()?")
.apply { artistRxConfig.processRxBindingStream(this, api.observableType.irrelevantIfObject()) }
.add(" ?: Observable.empty()")
.build())
.build())
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinCheckableTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.BOOLEAN
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
@AutoService(KotlinTrait::class)
class KotlinCheckableTrait : KotlinTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
baseType: String) {
val isTextView = baseType.endsWith("TextView")
if (isTextView) {
type.addProperty(PropertySpec.builder(
"checkedChanges",
KotlinRxTypeNames.Rx.BehaviorRelay.parameterizedBy(BOOLEAN).copy(nullable = true),
KModifier.PRIVATE)
.mutable()
.initializer("null")
.build())
type.addFunction(FunSpec.builder("ensureCheckedChanges")
.addModifiers(KModifier.PRIVATE)
.beginControlFlow("if (checkedChanges == null)")
.addStatement("checkedChanges = %T.create()", KotlinRxTypeNames.Rx.BehaviorRelay)
.endControlFlow()
.build())
type.addFunction(FunSpec.builder("checkedChanges")
.addKdoc("""@return an observable of booleans representing the checked state of this view.
""")
.addModifiers(KModifier.OPEN)
.returns(KotlinRxTypeNames.Rx.Observable.parameterizedBy(BOOLEAN))
.addStatement("ensureCheckedChanges()")
.addStatement("return checkedChanges!!.hide()")
.build())
type.addFunction(FunSpec.builder("setChecked")
.addModifiers(KModifier.OPEN, KModifier.OVERRIDE)
.addParameter("value", BOOLEAN)
.addStatement("super.setChecked(value)")
.addStatement("ensureCheckedChanges()")
.addStatement("checkedChanges!!.accept(value)")
.build())
} else {
addRxBindingApiForSettable(type, KotlinSettableApi(
KotlinRxBindingInfo(KotlinRxTypeNames.Rx.RxCompoundButton,
"checkedChanges",
"""@return an observable of booleans representing the checked state of this view.
"""),
ClassName.bestGuess("android.widget.CompoundButton.OnCheckedChangeListener"),
"setOnCheckedChangeListener",
BOOLEAN,
FunSpec.builder("accept")
.addParameter("isChecked", BOOLEAN)
.addStatement("l.onCheckedChanged(this@$baseType, isChecked)"),
true,
CodeBlock.of("%T.createDefault(isChecked())\n", KotlinRxTypeNames.Rx.BehaviorRelay)
))
}
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinRxTypeNames.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.jakewharton.rxrelay2.BehaviorRelay
import com.jakewharton.rxrelay2.PublishRelay
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.asClassName
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import io.reactivex.functions.Function
class KotlinRxTypeNames {
class Rx {
companion object {
// Rx
val Consumer = Consumer::class.asClassName()
val Disposable = Disposable::class.asClassName()
val Function = Function::class.asClassName()
val Observable = Observable::class.asClassName()
// RxRelay
val BehaviorRelay = BehaviorRelay::class.asClassName()
val PublishRelay = PublishRelay::class.asClassName()
// As a consequence of RxBinding2 migrating to RxBinding3 and replacing its static calls
// with equivalent extension functions, its important that we do not interrupt any existing
// code that was created with RxBinding2 in mind, namely the function names. This can mean
// that function names share the same name as the extension function which is being used in
// lieu of the original static function.
//Ideally, we would come up with a better way of listing these classes so as to keep this DRY.
// RxBinding
val RecyclerViewScrollEvent = AliasTypeNames.Rx.RecyclerViewScrollEvent
val RxView = AliasTypeNames.Rx.RxView
val RxCompoundButton = AliasTypeNames.Rx.RxCompoundButton
val RxNestedScrollView = AliasTypeNames.Rx.RxNestedScrollView
val RxRecyclerView = AliasTypeNames.Rx.RxRecyclerView
val RxSearchView = AliasTypeNames.Rx.RxSearchView
val RxSeekBar = AliasTypeNames.Rx.RxSeekBar
val SeekBarChangeEvent = AliasTypeNames.Rx.SeekBarChangeEvent
val SeekBarProgressChangeEvent = AliasTypeNames.Rx.SeekBarProgressChangeEvent
val SeekBarStartChangeEvent = AliasTypeNames.Rx.SeekBarStartChangeEvent
val RxSwipeRefreshLayout = AliasTypeNames.Rx.RxSwipeRefreshLayout
val RxTabLayout = AliasTypeNames.Rx.RxTabLayout
val RxTextView = AliasTypeNames.Rx.RxTextView
val RxToolbar = AliasTypeNames.Rx.RxToolbar
val RxViewPager = AliasTypeNames.Rx.RxViewPager
val RxViewAttachEvent = AliasTypeNames.Rx.RxViewAttachEvent
val RxViewAttachAttachedEvent = AliasTypeNames.Rx.RxViewAttachAttachedEvent
val RxViewAttachDetachedEvent = AliasTypeNames.Rx.RxViewAttachDetachedEvent
val SearchViewQueryTextEvent = AliasTypeNames.Rx.SearchViewQueryTextEvent
val ViewScrollChangeEvent = AliasTypeNames.Rx.ViewScrollChangeEvent
}
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinScrollableTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
@AutoService(KotlinTrait::class)
class KotlinScrollableTrait : KotlinTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
sourceType: String) {
// ScrollView overrides
if (sourceType.contains("ScrollView")) {
addRxBindingApiForSettable(type, KotlinSettableApi(
KotlinRxBindingInfo(KotlinRxTypeNames.Rx.RxNestedScrollView,
"scrollChangeEvents",
"""@return an observable of scroll-change events for this NestedScrollView.
"""),
ClassName.bestGuess("androidx.core.widget.NestedScrollView.OnScrollChangeListener"),
"setOnScrollChangeListener",
KotlinRxTypeNames.Rx.ViewScrollChangeEvent,
FunSpec.builder("accept")
.addParameter("event", KotlinRxTypeNames.Rx.ViewScrollChangeEvent)
.addStatement("l.onScrollChange(this@$sourceType, event.scrollX, event.scrollY, event.oldScrollX, event.oldScrollY)")))
}
// RecyclerView overrides
if (sourceType.contains("RecyclerView")) {
addRxBindingApiForAdditive(type, KotlinAdditiveApi(
KotlinRxBindingInfo(KotlinRxTypeNames.Rx.RxRecyclerView,
"scrollEvents",
"@return an observable of scroll events on this RecyclerView"),
KotlinRxTypeNames.Rx.RecyclerViewScrollEvent))
}
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinTextInputTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.uber.artist.api.KotlinTrait
@AutoService(KotlinTrait::class)
class KotlinTextInputTrait : KotlinTrait {
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
sourceType: String) {
// TextChanges
addRxBindingApiForAdditive(type, KotlinAdditiveApi(
KotlinRxBindingInfo(KotlinRxTypeNames.Rx.RxTextView,
"textChanges",
"""@return an observable of character sequences for text changes on this TextView."""),
CharSequence::class.asClassName()))
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinViewTrait.kt
================================================
/*
* Copyright (C) 2018. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.TypeSpec
import com.uber.artist.api.KotlinTrait
import com.uber.artist.api.KotlinTypeNames
import com.uber.artist.traits.rx.config.KotlinArtistRxConfigService
@AutoService(KotlinTrait::class)
open class KotlinViewTrait : KotlinTrait {
private val artistRxConfig by lazy { KotlinArtistRxConfigService.newInstance().getArtistRxConfig() }
override fun generateFor(
type: TypeSpec.Builder,
initMethod: FunSpec.Builder,
rClass: ClassName,
sourceType: String) {
clicks(type, sourceType)
longClicks(type, sourceType)
layoutChanges(type)
}
open fun clicks(type: TypeSpec.Builder, sourceType: String) {
addRxBindingApiForSettable(type, KotlinSettableApi(
KotlinRxBindingInfo(KotlinRxTypeNames.Rx.RxView,
"clicks",
"""@return an Observable of click events. The emitted value is unspecified and should only be used as notification.
"""),
ClassName.bestGuess("android.view.View.OnClickListener"),
"setOnClickListener",
KotlinTypeNames.Java.Object,
FunSpec.builder("accept")
.addParameter("ignored", artistRxConfig.rxBindingSignalEventTypeName())
.addStatement("l.onClick(this@$sourceType)")
))
}
open fun longClicks(type: TypeSpec.Builder, sourceType: String) {
addRxBindingApiForSettable(type, KotlinSettableApi(
KotlinRxBindingInfo(KotlinRxTypeNames.Rx.RxView,
"longClicks",
"""@return an Observable of longclick events. The emitted value is unspecified and should only be used as notification.
"""),
ClassName.bestGuess("android.view.View.OnLongClickListener"),
"setOnLongClickListener",
KotlinTypeNames.Java.Object,
FunSpec.builder("accept")
.addParameter("ignored", artistRxConfig.rxBindingSignalEventTypeName())
.addStatement("l.onLongClick(this@$sourceType)")
))
}
open fun layoutChanges(type: TypeSpec.Builder) {
// Attach state changes observable
addRxBindingApiForAdditive(type, KotlinAdditiveApi(
KotlinRxBindingInfo(
KotlinRxTypeNames.Rx.RxView,
"layoutChanges",
"@return an observable which emits on layout changes. The emitted value is " +
"unspecified and should only be used as notification."),
KotlinTypeNames.Java.Object))
}
}
================================================
FILE: artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/ArtistRxConfig.kt
================================================
/*
* Copyright (C) 2017. Uber Technologies
*
* 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.
*/
package com.uber.artist.traits.rx.config
/**
* This configuration object describes various plugin points for rx-based traits.
*/
interface ArtistRxConfig<CodeBlockType, TypeNameType> {
/**
* Plugin point for generating additional code to invoke when a view has been tapped.
*/
fun processTap(codeBlockBuilder: CodeBlockType) {}
/**
* Plugin point for generating additional code to invoke when a view has attached to the window.
*/
fun processImpression(codeBlockBuilder: CodeBlockType) {}
/**
* Plugin point for generating additional code to invoke when a view has changed visibility.
*/
fun processVisibilityChanges(codeBlockBuilder: CodeBlockType) {}
/**
* Plugin
gitextract_ob0qypit/ ├── .buildscript/ │ └── deploy_snapshot.sh ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── RELEASING.md ├── artist/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── kotlin/ │ │ └── com/ │ │ └── uber/ │ │ └── artist/ │ │ ├── ArtistExtension.kt │ │ ├── ArtistPlugin.kt │ │ ├── ArtistTask.kt │ │ └── internal/ │ │ └── util/ │ │ └── Util.kt │ └── resources/ │ └── META-INF/ │ └── gradle-plugins/ │ └── com.uber.artist.properties ├── artist-api/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ └── kotlin/ │ └── com/ │ └── uber/ │ └── artist/ │ └── api/ │ ├── JavaTrait.kt │ ├── JavaTraitService.kt │ ├── JavaTypeNames.kt │ ├── JavaViewStencil.kt │ ├── JavaViewStencilProvider.kt │ ├── JavaViewStencilService.kt │ ├── KotlinTrait.kt │ ├── KotlinTraitService.kt │ ├── KotlinTypeNames.kt │ ├── KotlinViewStencil.kt │ ├── KotlinViewStencilProvider.kt │ ├── KotlinViewStencilService.kt │ ├── Trait.kt │ ├── TraitService.kt │ ├── TypeNames.kt │ ├── ViewStencil.kt │ ├── ViewStencilProvider.kt │ ├── ViewStencilService.kt │ └── alias/ │ └── AliasTypeNames.kt ├── artist-core/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ ├── main/ │ │ └── kotlin/ │ │ └── com/ │ │ └── uber/ │ │ └── artist/ │ │ ├── Artist.kt │ │ ├── ArtistCodeGenerator.kt │ │ ├── FormattingFileWriter.kt │ │ ├── JavaArtistCodeGenerator.kt │ │ ├── JavaFormattingFileWriter.kt │ │ └── KotlinArtistCodeGenerator.kt │ └── test/ │ └── kotlin/ │ └── com/ │ └── uber/ │ └── artist/ │ └── ArtistTest.kt ├── artist-traits/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ └── kotlin/ │ └── com/ │ └── uber/ │ └── artist/ │ └── traits/ │ ├── JavaForegroundTrait.kt │ ├── JavaSuppressNullabilityInitializerTrait.kt │ ├── JavaVisibilityTrait.kt │ ├── KotlinForegroundTrait.kt │ ├── KotlinSuppressNullabilityInitializerTrait.kt │ └── KotlinVisibilityTrait.kt ├── artist-traits-rx/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ └── kotlin/ │ └── com/ │ └── uber/ │ └── artist/ │ └── traits/ │ └── rx/ │ ├── JavaApiHelper.kt │ ├── JavaCheckableTrait.kt │ ├── JavaRxTypeNames.kt │ ├── JavaScrollableTrait.kt │ ├── JavaTextInputTrait.kt │ ├── JavaViewTrait.kt │ ├── KotlinApiHelper.kt │ ├── KotlinCheckableTrait.kt │ ├── KotlinRxTypeNames.kt │ ├── KotlinScrollableTrait.kt │ ├── KotlinTextInputTrait.kt │ ├── KotlinViewTrait.kt │ └── config/ │ ├── ArtistRxConfig.kt │ ├── ArtistRxConfigService.kt │ ├── JavaArtistRxConfig.kt │ ├── JavaArtistRxConfigService.kt │ ├── JavaDefaultArtistRxConfig.kt │ ├── KotlinArtistRxConfig.kt │ ├── KotlinArtistRxConfigService.kt │ └── KotlinDefaultArtistRxConfig.kt ├── build.gradle ├── buildSrc/ │ ├── build.gradle │ └── settings.gradle ├── config/ │ ├── checkstyle/ │ │ ├── checkstyle-suppressions.xml │ │ ├── checkstyle-test.xml │ │ └── checkstyle.xml │ └── lint/ │ └── lint.xml ├── gradle/ │ ├── dependencies.gradle │ ├── gradle-mvn-push.gradle │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── sample/ │ ├── app/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── uber/ │ │ │ └── artist/ │ │ │ └── myapplication/ │ │ │ └── MainActivity.kt │ │ └── res/ │ │ ├── drawable/ │ │ │ └── divider.xml │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ └── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── demo/ │ │ └── java/ │ │ ├── MyButton.java │ │ ├── MyEditText.java │ │ ├── MyImageView.java │ │ ├── MyLinearLayout.java │ │ ├── MyNestedScrollView.java │ │ ├── MySwitch.java │ │ └── MyTextView.java │ ├── library/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── uber/ │ │ │ └── artist/ │ │ │ └── mylibrary/ │ │ │ ├── MyUtils.java │ │ │ ├── MyView.java │ │ │ └── Signal.java │ │ └── res/ │ │ └── values/ │ │ ├── attrs_foreground_view.xml │ │ └── strings.xml │ ├── providers/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── uber/ │ │ └── artist/ │ │ └── myproviders/ │ │ ├── JavaSampleRxConfig.java │ │ ├── JavaSampleTypeNames.java │ │ ├── JavaSampleViewStencilProvider.java │ │ └── trait/ │ │ └── JavaSampleTrait.java │ └── providers-kotlin/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── kotlin/ │ └── com/ │ └── uber/ │ └── artist/ │ └── myproviders/ │ ├── KotlinSampleRxConfig.kt │ ├── KotlinSampleTypeNames.kt │ ├── KotlinSampleViewStencilProvider.kt │ └── trait/ │ └── KotlinSampleTrait.kt └── settings.gradle
SYMBOL INDEX (184 symbols across 14 files)
FILE: sample/demo/java/MyButton.java
class MyButton (line 25) | public class MyButton extends AppCompatButton implements MyView {
method MyButton (line 40) | public MyButton(Context context) {
method MyButton (line 44) | public MyButton(Context context, @Nullable AttributeSet attrs) {
method MyButton (line 48) | public MyButton(Context context, @Nullable AttributeSet attrs, @AttrRe...
method sampleMethodFromCustomTrait (line 53) | public View sampleMethodFromCustomTrait() {
method isVisible (line 57) | public boolean isVisible() {
method isInvisible (line 61) | public boolean isInvisible() {
method isGone (line 65) | public boolean isGone() {
method onSizeChanged (line 69) | @Override
method verifyDrawable (line 77) | @Override
method jumpDrawablesToCurrentState (line 82) | @Override
method drawableStateChanged (line 90) | @Override
method getForeground (line 104) | @SuppressWarnings("MissingOverride")
method setForeground (line 116) | @SuppressWarnings("MissingOverride")
method draw (line 139) | @Override
method drawableHotspotChanged (line 147) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 157) | @Override
method clicks (line 186) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 199) | @Override
method longClicks (line 228) | public Observable<Signal> longClicks() {
method layoutChanges (line 244) | public Observable<Signal> layoutChanges() {
method init (line 248) | @CallSuper
FILE: sample/demo/java/MyEditText.java
class MyEditText (line 27) | public class MyEditText extends AppCompatEditText implements MyView {
method MyEditText (line 42) | public MyEditText(Context context) {
method MyEditText (line 46) | public MyEditText(Context context, @Nullable AttributeSet attrs) {
method MyEditText (line 50) | public MyEditText(Context context, @Nullable AttributeSet attrs, @Attr...
method sampleMethodFromCustomTrait (line 55) | public View sampleMethodFromCustomTrait() {
method isVisible (line 59) | public boolean isVisible() {
method isInvisible (line 63) | public boolean isInvisible() {
method isGone (line 67) | public boolean isGone() {
method onSizeChanged (line 71) | @Override
method verifyDrawable (line 79) | @Override
method jumpDrawablesToCurrentState (line 84) | @Override
method drawableStateChanged (line 92) | @Override
method getForeground (line 106) | @SuppressWarnings("MissingOverride")
method setForeground (line 118) | @SuppressWarnings("MissingOverride")
method draw (line 141) | @Override
method drawableHotspotChanged (line 149) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 159) | @Override
method clicks (line 188) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 201) | @Override
method longClicks (line 230) | public Observable<Signal> longClicks() {
method layoutChanges (line 246) | public Observable<Signal> layoutChanges() {
method textChanges (line 251) | public Observable<CharSequence> textChanges() {
method init (line 255) | @CallSuper
FILE: sample/demo/java/MyImageView.java
class MyImageView (line 25) | public class MyImageView extends AppCompatImageView implements MyView {
method MyImageView (line 40) | public MyImageView(Context context) {
method MyImageView (line 44) | public MyImageView(Context context, @Nullable AttributeSet attrs) {
method MyImageView (line 48) | public MyImageView(Context context, @Nullable AttributeSet attrs, @Att...
method sampleMethodFromCustomTrait (line 53) | public View sampleMethodFromCustomTrait() {
method isVisible (line 57) | public boolean isVisible() {
method isInvisible (line 61) | public boolean isInvisible() {
method isGone (line 65) | public boolean isGone() {
method onSizeChanged (line 69) | @Override
method hasOverlappingRendering (line 77) | @Override
method verifyDrawable (line 82) | @Override
method jumpDrawablesToCurrentState (line 87) | @Override
method drawableStateChanged (line 95) | @Override
method getForeground (line 109) | @SuppressWarnings("MissingOverride")
method setForeground (line 121) | @SuppressWarnings("MissingOverride")
method draw (line 144) | @Override
method drawableHotspotChanged (line 152) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 162) | @Override
method clicks (line 191) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 204) | @Override
method longClicks (line 233) | public Observable<Signal> longClicks() {
method layoutChanges (line 249) | public Observable<Signal> layoutChanges() {
method init (line 253) | @CallSuper
FILE: sample/demo/java/MyLinearLayout.java
class MyLinearLayout (line 28) | public class MyLinearLayout extends LinearLayout implements MyView {
method MyLinearLayout (line 53) | public MyLinearLayout(Context context) {
method MyLinearLayout (line 57) | public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
method MyLinearLayout (line 61) | public MyLinearLayout(Context context, @Nullable AttributeSet attrs, @...
method sampleMethodFromCustomTrait (line 66) | public View sampleMethodFromCustomTrait() {
method isVisible (line 70) | public boolean isVisible() {
method isInvisible (line 74) | public boolean isInvisible() {
method isGone (line 78) | public boolean isGone() {
method onSizeChanged (line 82) | @Override
method getForegroundGravity (line 94) | @SuppressWarnings("MissingOverride")
method setForegroundGravity (line 105) | @SuppressWarnings("MissingOverride")
method verifyDrawable (line 123) | @Override
method jumpDrawablesToCurrentState (line 128) | @Override
method drawableStateChanged (line 136) | @Override
method getForeground (line 150) | @SuppressWarnings("MissingOverride")
method setForeground (line 162) | @SuppressWarnings("MissingOverride")
method onLayout (line 189) | @Override
method draw (line 197) | @Override
method drawableHotspotChanged (line 226) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 236) | @Override
method clicks (line 265) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 278) | @Override
method longClicks (line 307) | public Observable<Signal> longClicks() {
method layoutChanges (line 323) | public Observable<Signal> layoutChanges() {
method init (line 327) | @CallSuper
FILE: sample/demo/java/MyNestedScrollView.java
class MyNestedScrollView (line 27) | public class MyNestedScrollView extends NestedScrollView implements MyVi...
method MyNestedScrollView (line 48) | public MyNestedScrollView(Context context) {
method MyNestedScrollView (line 52) | public MyNestedScrollView(Context context, @Nullable AttributeSet attr...
method MyNestedScrollView (line 56) | public MyNestedScrollView(
method sampleMethodFromCustomTrait (line 62) | public View sampleMethodFromCustomTrait() {
method isVisible (line 66) | public boolean isVisible() {
method isInvisible (line 70) | public boolean isInvisible() {
method isGone (line 74) | public boolean isGone() {
method onSizeChanged (line 78) | @Override
method verifyDrawable (line 86) | @Override
method jumpDrawablesToCurrentState (line 91) | @Override
method drawableStateChanged (line 99) | @Override
method getForeground (line 113) | @SuppressWarnings("MissingOverride")
method setForeground (line 125) | @SuppressWarnings("MissingOverride")
method draw (line 148) | @Override
method drawableHotspotChanged (line 156) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 166) | @Override
method clicks (line 195) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 208) | @Override
method longClicks (line 237) | public Observable<Signal> longClicks() {
method layoutChanges (line 253) | public Observable<Signal> layoutChanges() {
method setOnScrollChangeListener (line 258) | @Override
method scrollChangeEvents (line 289) | public Observable<ViewScrollChangeEvent> scrollChangeEvents() {
method init (line 298) | @CallSuper
FILE: sample/demo/java/MySwitch.java
class MySwitch (line 28) | public class MySwitch extends SwitchCompat implements MyView {
method MySwitch (line 49) | public MySwitch(Context context) {
method MySwitch (line 53) | public MySwitch(Context context, @Nullable AttributeSet attrs) {
method MySwitch (line 57) | public MySwitch(Context context, @Nullable AttributeSet attrs, @AttrRe...
method sampleMethodFromCustomTrait (line 62) | public View sampleMethodFromCustomTrait() {
method isVisible (line 66) | public boolean isVisible() {
method isInvisible (line 70) | public boolean isInvisible() {
method isGone (line 74) | public boolean isGone() {
method onSizeChanged (line 78) | @Override
method verifyDrawable (line 86) | @Override
method jumpDrawablesToCurrentState (line 91) | @Override
method drawableStateChanged (line 99) | @Override
method getForeground (line 113) | @SuppressWarnings("MissingOverride")
method setForeground (line 125) | @SuppressWarnings("MissingOverride")
method draw (line 148) | @Override
method drawableHotspotChanged (line 156) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 166) | @Override
method clicks (line 195) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 208) | @Override
method longClicks (line 237) | public Observable<Signal> longClicks() {
method layoutChanges (line 253) | public Observable<Signal> layoutChanges() {
method setOnCheckedChangeListener (line 258) | @Override
method checkedChanges (line 284) | public Observable<Boolean> checkedChanges() {
method init (line 293) | @CallSuper
FILE: sample/demo/java/MyTextView.java
class MyTextView (line 25) | public class MyTextView extends TextView implements MyView {
method MyTextView (line 40) | public MyTextView(Context context) {
method MyTextView (line 44) | public MyTextView(Context context, @Nullable AttributeSet attrs) {
method MyTextView (line 48) | public MyTextView(Context context, @Nullable AttributeSet attrs, @Attr...
method sampleMethodFromCustomTrait (line 53) | public View sampleMethodFromCustomTrait() {
method isVisible (line 57) | public boolean isVisible() {
method isInvisible (line 61) | public boolean isInvisible() {
method isGone (line 65) | public boolean isGone() {
method onSizeChanged (line 69) | @Override
method verifyDrawable (line 77) | @Override
method jumpDrawablesToCurrentState (line 82) | @Override
method drawableStateChanged (line 90) | @Override
method getForeground (line 104) | @SuppressWarnings("MissingOverride")
method setForeground (line 116) | @SuppressWarnings("MissingOverride")
method draw (line 139) | @Override
method drawableHotspotChanged (line 147) | @TargetApi(android.os.Build.VERSION_CODES.LOLLIPOP)
method setOnClickListener (line 157) | @Override
method clicks (line 186) | public Observable<Signal> clicks() {
method setOnLongClickListener (line 199) | @Override
method longClicks (line 228) | public Observable<Signal> longClicks() {
method layoutChanges (line 244) | public Observable<Signal> layoutChanges() {
method init (line 248) | @CallSuper
FILE: sample/library/src/main/java/com/uber/artist/mylibrary/MyUtils.java
class MyUtils (line 27) | public final class MyUtils {
method MyUtils (line 29) | private MyUtils() {
method createTapProcessor (line 38) | public static Consumer<Object> createTapProcessor() {
method createRxBindingSignalMapper (line 53) | public static Function<Object, Signal> createRxBindingSignalMapper() {
FILE: sample/library/src/main/java/com/uber/artist/mylibrary/MyView.java
type MyView (line 22) | public interface MyView {
FILE: sample/library/src/main/java/com/uber/artist/mylibrary/Signal.java
type Signal (line 22) | public enum Signal {
method toString (line 25) | @Override
FILE: sample/providers/src/main/java/com/uber/artist/myproviders/JavaSampleRxConfig.java
class JavaSampleRxConfig (line 29) | @AutoService(JavaArtistRxConfig.class)
method processTap (line 32) | @Override
method processRxBindingSignalEvent (line 38) | @Override
method rxBindingSignalEventTypeName (line 44) | @Override
FILE: sample/providers/src/main/java/com/uber/artist/myproviders/JavaSampleTypeNames.java
class JavaSampleTypeNames (line 24) | public final class JavaSampleTypeNames {
method JavaSampleTypeNames (line 30) | private JavaSampleTypeNames() {
FILE: sample/providers/src/main/java/com/uber/artist/myproviders/JavaSampleViewStencilProvider.java
class JavaSampleViewStencilProvider (line 38) | @AutoService(JavaViewStencilProvider.class)
method stencils (line 41) | @Override
method globalTraits (line 56) | @Override
class SwitchStencil (line 66) | private static class SwitchStencil extends JavaViewStencil {
method SwitchStencil (line 68) | public SwitchStencil() {
method name (line 72) | @Override
FILE: sample/providers/src/main/java/com/uber/artist/myproviders/trait/JavaSampleTrait.java
class JavaSampleTrait (line 32) | @AutoService(JavaTrait.class)
method generateFor (line 35) | @Override
Condensed preview — 129 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (359K chars).
[
{
"path": ".buildscript/deploy_snapshot.sh",
"chars": 961,
"preview": "#!/bin/bash\n#\n# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.\n#\n# Adapted from https://coderwal"
},
{
"path": ".editorconfig",
"chars": 280,
"preview": "[*.{kt,kts}]\n# possible values: number (e.g. 2), \"unset\" (makes ktlint ignore indentation completely)\nindent_size=2\ncont"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 440,
"preview": "Thanks for using Artist. Before you create an issue, please consider the following points:\n\n - [ ] If you think you fou"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 350,
"preview": "Thank you for contributing to Artist. Before pressing the \"Create Pull Request\" button, please provide the following:\n\n "
},
{
"path": ".github/workflows/ci.yml",
"chars": 1373,
"preview": "name: CI\n\non: [push, pull_request]\n\njobs: \n build:\n name: JDK ${{ matrix.java_version }}\n runs-on: ubuntu-latest\n"
},
{
"path": ".gitignore",
"chars": 1154,
"preview": "###OSX###\n\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must ends with two \\r.\nIcon\n\n\n# Thumbnails\n._*\n\n# Files that might"
},
{
"path": "CHANGELOG.md",
"chars": 1557,
"preview": "Changelog\n=========\nVersion 0.4.9\n-------------\n_2023-07-27_\n\n* Update version upstreaming unmerged fixes \n\nVersion 0.4."
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3224,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 1662,
"preview": "Contributing to Uber's Android Template\n=======================\n\nUber welcomes contributions of all kinds and sizes. Thi"
},
{
"path": "LICENSE.txt",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 9792,
"preview": "# Artist [](https://travis-ci.org/uber/artist)\n\n\nAs "
},
{
"path": "RELEASING.md",
"chars": 632,
"preview": "Releasing\n=========\n\n 1. Change the version in `gradle.properties` to a non-SNAPSHOT version.\n 2. Update the `CHANGELOG."
},
{
"path": "artist/build.gradle",
"chars": 428,
"preview": "apply plugin: \"org.jetbrains.kotlin.jvm\"\n\nsourceCompatibility = JavaVersion.VERSION_1_7\ntargetCompatibility = JavaVersio"
},
{
"path": "artist/gradle.properties",
"chars": 647,
"preview": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may "
},
{
"path": "artist/src/main/kotlin/com/uber/artist/ArtistExtension.kt",
"chars": 1570,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist/src/main/kotlin/com/uber/artist/ArtistPlugin.kt",
"chars": 3047,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist/src/main/kotlin/com/uber/artist/ArtistTask.kt",
"chars": 1466,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist/src/main/kotlin/com/uber/artist/internal/util/Util.kt",
"chars": 1036,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist/src/main/resources/META-INF/gradle-plugins/com.uber.artist.properties",
"chars": 50,
"preview": "implementation-class=com.uber.artist.ArtistPlugin\n"
},
{
"path": "artist-api/build.gradle",
"chars": 396,
"preview": "apply plugin: \"java-library\"\napply plugin: \"org.jetbrains.kotlin.jvm\"\n\nsourceCompatibility = JavaVersion.VERSION_1_7\ntar"
},
{
"path": "artist-api/gradle.properties",
"chars": 655,
"preview": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may "
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/JavaTrait.kt",
"chars": 1399,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/JavaTraitService.kt",
"chars": 1190,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/JavaTypeNames.kt",
"chars": 2866,
"preview": "@file:Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\")\n\n/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the "
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/JavaViewStencil.kt",
"chars": 1950,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/JavaViewStencilProvider.kt",
"chars": 1101,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/JavaViewStencilService.kt",
"chars": 1682,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/KotlinTrait.kt",
"chars": 1402,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/KotlinTraitService.kt",
"chars": 1204,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/KotlinTypeNames.kt",
"chars": 2808,
"preview": "@file:Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\")\n\n/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the "
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/KotlinViewStencil.kt",
"chars": 2543,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/KotlinViewStencilProvider.kt",
"chars": 1111,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/KotlinViewStencilService.kt",
"chars": 1702,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/Trait.kt",
"chars": 1218,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/TraitService.kt",
"chars": 800,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/TypeNames.kt",
"chars": 2862,
"preview": "@file:Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\")\n\n/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the "
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/ViewStencil.kt",
"chars": 2078,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/ViewStencilProvider.kt",
"chars": 1057,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/ViewStencilService.kt",
"chars": 1045,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-api/src/main/kotlin/com/uber/artist/api/alias/AliasTypeNames.kt",
"chars": 3495,
"preview": "import com.squareup.kotlinpoet.ClassName\n\nclass AliasTypeNames {\n\n open class Rx {\n companion object {\n\n val Re"
},
{
"path": "artist-core/build.gradle",
"chars": 851,
"preview": "apply plugin: \"java-library\"\napply plugin: \"org.jetbrains.kotlin.jvm\"\n\nsourceCompatibility = JavaVersion.VERSION_1_7\ntar"
},
{
"path": "artist-core/gradle.properties",
"chars": 657,
"preview": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may "
},
{
"path": "artist-core/src/main/kotlin/com/uber/artist/Artist.kt",
"chars": 1124,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-core/src/main/kotlin/com/uber/artist/ArtistCodeGenerator.kt",
"chars": 4042,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-core/src/main/kotlin/com/uber/artist/FormattingFileWriter.kt",
"chars": 1063,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-core/src/main/kotlin/com/uber/artist/JavaArtistCodeGenerator.kt",
"chars": 8597,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-core/src/main/kotlin/com/uber/artist/JavaFormattingFileWriter.kt",
"chars": 2745,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-core/src/main/kotlin/com/uber/artist/KotlinArtistCodeGenerator.kt",
"chars": 6800,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-core/src/test/kotlin/com/uber/artist/ArtistTest.kt",
"chars": 7199,
"preview": "package com.uber.artist\n\nimport com.google.common.io.Files\nimport com.google.common.truth.Truth.assertAbout\nimport com.g"
},
{
"path": "artist-traits/build.gradle",
"chars": 483,
"preview": "apply plugin: \"java-library\"\napply plugin: \"org.jetbrains.kotlin.jvm\"\napply plugin: \"org.jetbrains.kotlin.kapt\"\n\nsourceC"
},
{
"path": "artist-traits/gradle.properties",
"chars": 661,
"preview": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may "
},
{
"path": "artist-traits/src/main/kotlin/com/uber/artist/traits/JavaForegroundTrait.kt",
"chars": 13928,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits/src/main/kotlin/com/uber/artist/traits/JavaSuppressNullabilityInitializerTrait.kt",
"chars": 681,
"preview": "package com.uber.artist.traits\n\nimport com.google.auto.service.AutoService\nimport com.squareup.javapoet.AnnotationSpec\ni"
},
{
"path": "artist-traits/src/main/kotlin/com/uber/artist/traits/JavaVisibilityTrait.kt",
"chars": 1064,
"preview": "package com.uber.artist.traits\n\nimport com.google.auto.service.AutoService\nimport com.squareup.javapoet.ClassName\nimport"
},
{
"path": "artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinForegroundTrait.kt",
"chars": 13359,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinSuppressNullabilityInitializerTrait.kt",
"chars": 1287,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits/src/main/kotlin/com/uber/artist/traits/KotlinVisibilityTrait.kt",
"chars": 1671,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/build.gradle",
"chars": 668,
"preview": "apply plugin: \"java-library\"\napply plugin: \"org.jetbrains.kotlin.jvm\"\napply plugin: \"org.jetbrains.kotlin.kapt\"\n\nsourceC"
},
{
"path": "artist-traits-rx/gradle.properties",
"chars": 667,
"preview": "#\n# Copyright (C) 2017. Uber Technologies\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may "
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaApiHelper.kt",
"chars": 7487,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaCheckableTrait.kt",
"chars": 3470,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaRxTypeNames.kt",
"chars": 3358,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaScrollableTrait.kt",
"chars": 2328,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaTextInputTrait.kt",
"chars": 1372,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/JavaViewTrait.kt",
"chars": 3415,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinApiHelper.kt",
"chars": 8882,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinCheckableTrait.kt",
"chars": 3421,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinRxTypeNames.kt",
"chars": 3283,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinScrollableTrait.kt",
"chars": 2231,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinTextInputTrait.kt",
"chars": 1423,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/KotlinViewTrait.kt",
"chars": 3164,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/ArtistRxConfig.kt",
"chars": 2068,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/ArtistRxConfigService.kt",
"chars": 923,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/JavaArtistRxConfig.kt",
"chars": 2256,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/JavaArtistRxConfigService.kt",
"chars": 1348,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/JavaDefaultArtistRxConfig.kt",
"chars": 703,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/KotlinArtistRxConfig.kt",
"chars": 2287,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/KotlinArtistRxConfigService.kt",
"chars": 1364,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "artist-traits-rx/src/main/kotlin/com/uber/artist/traits/rx/config/KotlinDefaultArtistRxConfig.kt",
"chars": 707,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "build.gradle",
"chars": 1589,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "buildSrc/build.gradle",
"chars": 1306,
"preview": "buildscript {\n apply from: project.rootProject.file(\"../gradle/dependencies.gradle\")\n\n repositories {\n goog"
},
{
"path": "buildSrc/settings.gradle",
"chars": 295,
"preview": "// These are not needed if consuming the Artist plugin from Maven Central instead of the local copy from this repo\ninclu"
},
{
"path": "config/checkstyle/checkstyle-suppressions.xml",
"chars": 315,
"preview": "<?xml version=\"1.0\"?>\n\n<!DOCTYPE suppressions PUBLIC\n \"-//Puppy Crawl//DTD Suppressions 1.1//EN\"\n \"http://www.pupp"
},
{
"path": "config/checkstyle/checkstyle-test.xml",
"chars": 12856,
"preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n \"http://www.pupp"
},
{
"path": "config/checkstyle/checkstyle.xml",
"chars": 13221,
"preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n \"http://www.pupp"
},
{
"path": "config/lint/lint.xml",
"chars": 283,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n <issue id=\"AppCompatCustomView\" severity=\"ignore\" />\n <issue id=\"Go"
},
{
"path": "gradle/dependencies.gradle",
"chars": 2645,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "gradle/gradle-mvn-push.gradle",
"chars": 5986,
"preview": "/*\n * Copyright (C) Chris Banes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 200,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 1161,
"preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "sample/app/build.gradle",
"chars": 1645,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/app/src/main/AndroidManifest.xml",
"chars": 724,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "sample/app/src/main/java/com/uber/artist/myapplication/MainActivity.kt",
"chars": 2935,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/app/src/main/res/drawable/divider.xml",
"chars": 899,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (C) 2017. Uber Technologies\n ~\n ~ Licensed under the Apache "
},
{
"path": "sample/app/src/main/res/layout/activity_main.xml",
"chars": 2907,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (C) 2017. Uber Technologies\n ~\n ~ Licensed under the Apache "
},
{
"path": "sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 265,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 265,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "sample/app/src/main/res/values/colors.xml",
"chars": 208,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorPrimary\">#3F51B5</color>\n <color name=\"color"
},
{
"path": "sample/app/src/main/res/values/dimens.xml",
"chars": 258,
"preview": "<resources>\n <!-- Default screen margins, per the Android Design guidelines. -->\n <dimen name=\"activity_horizontal"
},
{
"path": "sample/app/src/main/res/values/ic_launcher_background.xml",
"chars": 120,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"ic_launcher_background\">#333333</color>\n</resources>"
},
{
"path": "sample/app/src/main/res/values/strings.xml",
"chars": 76,
"preview": "<resources>\n <string name=\"app_name\">Artist Sample</string>\n</resources>\n"
},
{
"path": "sample/app/src/main/res/values/styles.xml",
"chars": 383,
"preview": "<resources>\n\n <!-- Base application theme. -->\n <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar"
},
{
"path": "sample/demo/java/MyButton.java",
"chars": 7535,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/demo/java/MyEditText.java",
"chars": 7830,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/demo/java/MyImageView.java",
"chars": 7622,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/demo/java/MyLinearLayout.java",
"chars": 10351,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/demo/java/MyNestedScrollView.java",
"chars": 9387,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/demo/java/MySwitch.java",
"chars": 9046,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/demo/java/MyTextView.java",
"chars": 7532,
"preview": "package com.uber.artist.mylibrary;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport "
},
{
"path": "sample/library/build.gradle",
"chars": 1994,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/library/src/main/AndroidManifest.xml",
"chars": 150,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "sample/library/src/main/java/com/uber/artist/mylibrary/MyUtils.java",
"chars": 1703,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/library/src/main/java/com/uber/artist/mylibrary/MyView.java",
"chars": 749,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/library/src/main/java/com/uber/artist/mylibrary/Signal.java",
"chars": 833,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/library/src/main/res/values/attrs_foreground_view.xml",
"chars": 257,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"ForegroundView\">\n <attr name=\"android:f"
},
{
"path": "sample/library/src/main/res/values/strings.xml",
"chars": 77,
"preview": "<resources>\n <string name=\"app_name\">My Application</string>\n</resources>\n"
},
{
"path": "sample/providers/build.gradle",
"chars": 1201,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers/src/main/java/com/uber/artist/myproviders/JavaSampleRxConfig.java",
"chars": 1590,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers/src/main/java/com/uber/artist/myproviders/JavaSampleTypeNames.java",
"chars": 1068,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers/src/main/java/com/uber/artist/myproviders/JavaSampleViewStencilProvider.java",
"chars": 2718,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers/src/main/java/com/uber/artist/myproviders/trait/JavaSampleTrait.java",
"chars": 1459,
"preview": "/*\n * Copyright (C) 2017. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers-kotlin/build.gradle",
"chars": 1048,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers-kotlin/src/main/kotlin/com/uber/artist/myproviders/KotlinSampleRxConfig.kt",
"chars": 1517,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers-kotlin/src/main/kotlin/com/uber/artist/myproviders/KotlinSampleTypeNames.kt",
"chars": 925,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers-kotlin/src/main/kotlin/com/uber/artist/myproviders/KotlinSampleViewStencilProvider.kt",
"chars": 2639,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "sample/providers-kotlin/src/main/kotlin/com/uber/artist/myproviders/trait/KotlinSampleTrait.kt",
"chars": 1421,
"preview": "/*\n * Copyright (C) 2018. Uber Technologies\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you"
},
{
"path": "settings.gradle",
"chars": 227,
"preview": "include ':artist'\ninclude ':artist-api'\ninclude ':artist-core'\ninclude ':artist-traits'\ninclude ':artist-traits-rx'\nincl"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the uber/artist GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 129 files (327.5 KB), approximately 80.0k tokens, and a symbol index with 184 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.