├── .ci-java-version
├── .editorconfig
├── .github
└── workflows
│ ├── pr-package-lock.yml
│ ├── pr.yml
│ ├── publish_release.yml
│ └── publish_snapshot.yml
├── .gitignore
├── .idea
└── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── Dangerfile.df.kts
├── LICENSE
├── NOTICE
├── README.md
├── build.gradle.kts
├── changelog_config.json
├── detekt.yml
├── format
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── kotlin-js-store
└── package-lock.json
├── library
├── build.gradle.kts
├── gradle.properties
└── src
│ └── commonMain
│ └── kotlin
│ └── com
│ └── eygraber
│ └── compose
│ └── colorpicker
│ ├── ColorPicker.kt
│ ├── ColorWheel.kt
│ ├── Magnifier.kt
│ ├── MagnifierTransitionData.kt
│ └── Utils.kt
├── renovate.json
├── sample
├── android-app
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── eygraber
│ │ │ └── compose
│ │ │ └── colorpicker
│ │ │ └── sample
│ │ │ └── SampleActivity.kt
│ │ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
├── jvm-app
│ ├── build.gradle.kts
│ └── src
│ │ └── main
│ │ └── kotlin
│ │ └── com
│ │ └── eygraber
│ │ └── compose
│ │ └── colorpicker
│ │ └── sample
│ │ └── ColorPickerJvm.kt
├── shared
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── eygraber
│ │ │ └── compose
│ │ │ └── colorpicker
│ │ │ └── sample
│ │ │ └── FixedDecimalCount.android.kt
│ │ ├── commonMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── eygraber
│ │ │ └── compose
│ │ │ └── colorpicker
│ │ │ └── sample
│ │ │ ├── FixedDecimalCount.common.kt
│ │ │ ├── RefreshIcon.kt
│ │ │ └── Sample.kt
│ │ ├── jsMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── eygraber
│ │ │ └── compose
│ │ │ └── colorpicker
│ │ │ └── sample
│ │ │ └── FixedDecimalCount.js.kt
│ │ ├── jvmMain
│ │ └── kotlin
│ │ │ └── com
│ │ │ └── eygraber
│ │ │ └── compose
│ │ │ └── colorpicker
│ │ │ └── sample
│ │ │ └── FixedDecimalCount.jvm.kt
│ │ └── wasmJsMain
│ │ └── kotlin
│ │ └── com
│ │ └── eygraber
│ │ └── compose
│ │ └── colorpicker
│ │ └── sample
│ │ └── FixedDecimalCount.wasm.kt
└── webApp
│ ├── build.gradle.kts
│ ├── detekt.yml
│ └── src
│ └── wasmJsMain
│ ├── kotlin
│ └── main.wasm.kt
│ └── resources
│ ├── index.html
│ └── load.mjs
└── settings.gradle.kts
/.ci-java-version:
--------------------------------------------------------------------------------
1 | 22
2 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # noinspection EditorConfigKeyCorrectness
2 | [*.{kt,kts}]
3 | ij_kotlin_allow_trailing_comma = true
4 | ij_kotlin_allow_trailing_comma_on_call_site = true
5 | ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | ktlint_code_style = android_studio
10 | ktlint_experimental = enabled
11 | ktlint_ignore_back_ticked_identifier = true
12 | ktlint_standard_annotation = disabled
13 | ktlint_standard_blank-line-between-when-conditions = disabled
14 | ktlint_standard_class-signature = disabled
15 | ktlint_standard_comment-wrapping = disabled
16 | # disabled because of https://github.com/pinterest/ktlint/issues/2182#issuecomment-1863408507
17 | ktlint_standard_condition-wrapping = disabled
18 | ktlint_standard_filename = disabled
19 | ktlint_standard_function-naming = disabled
20 | ktlint_standard_function-signature = disabled
21 | ktlint_standard_keyword-spacing = disabled
22 | ktlint_standard_package-name = disabled
23 | ktlint_standard_property-naming = disabled
24 | ktlint_standard_spacing-between-declarations-with-annotations = disabled
25 | max_line_length = 120
26 |
--------------------------------------------------------------------------------
/.github/workflows/pr-package-lock.yml:
--------------------------------------------------------------------------------
1 | name: Upgrade Package Lock
2 |
3 | on:
4 | pull_request
5 |
6 | jobs:
7 | upgrade_package_lock:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v4
11 | with:
12 | repository: ${{ github.event.pull_request.head.repo.full_name }}
13 | ref: ${{ github.event.pull_request.head.ref }}
14 | token: ${{ secrets.PUSH_PAT }}
15 |
16 | - uses: actions/setup-java@v4
17 | with:
18 | distribution: 'zulu'
19 | java-version-file: .ci-java-version
20 |
21 | - name: Setup Gradle
22 | uses: gradle/actions/setup-gradle@v4
23 | with:
24 | gradle-version: wrapper
25 |
26 | - name: Run assemble task
27 | run: ./gradlew kotlinUpgradePackageLock --rerun-tasks
28 |
29 | - name: Commit package lock changes
30 | id: commit_package_lock_changes
31 | uses: EndBug/add-and-commit@v9
32 | with:
33 | add: "['kotlin-js-store/package-lock.json']"
34 | default_author: github_actions
35 | message: "Upgrade package lock"
36 |
37 | env:
38 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1024m"
39 |
--------------------------------------------------------------------------------
/.github/workflows/pr.yml:
--------------------------------------------------------------------------------
1 | name: Check
2 |
3 | on:
4 | pull_request
5 |
6 | jobs:
7 | danger:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v4
11 |
12 | - name: Danger
13 | uses: danger/kotlin@1.3.3
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 |
17 | detekt:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/checkout@v4
21 |
22 | - uses: actions/setup-java@v4
23 | with:
24 | distribution: 'zulu'
25 | java-version-file: .ci-java-version
26 |
27 | - name: Setup Gradle
28 | uses: gradle/actions/setup-gradle@v4
29 | with:
30 | gradle-version: wrapper
31 |
32 | - name: Run detekt
33 | run: ./gradlew detektAll
34 |
35 | ktlint:
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@v4
39 |
40 | - name: Run ktlint
41 | run: ./format --no-format
42 |
43 | lint:
44 | runs-on: ubuntu-latest
45 | steps:
46 | - uses: actions/checkout@v4
47 |
48 | - uses: actions/setup-java@v4
49 | with:
50 | distribution: 'zulu'
51 | java-version-file: .ci-java-version
52 |
53 | - name: Setup Gradle
54 | uses: gradle/actions/setup-gradle@v4
55 | with:
56 | gradle-version: wrapper
57 |
58 | - name: Run checks
59 | run: ./gradlew lintRelease
60 |
61 | test:
62 | strategy:
63 | matrix:
64 | os: [ macos-latest, ubuntu-latest ]
65 | runs-on: ${{matrix.os}}
66 | steps:
67 | - uses: actions/checkout@v4
68 |
69 | - uses: actions/setup-java@v4
70 | with:
71 | distribution: 'zulu'
72 | java-version-file: .ci-java-version
73 |
74 | - name: Setup Gradle
75 | uses: gradle/actions/setup-gradle@v4
76 | with:
77 | gradle-version: wrapper
78 |
79 | - name: Run tests
80 | run: ./gradlew allTests
81 | if: matrix.os == 'ubuntu-latest'
82 |
83 | - name: Run Apple tests
84 | run: ./gradlew iosX64Test
85 | if: matrix.os == 'macos-latest'
86 |
87 | env:
88 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1024m"
89 |
--------------------------------------------------------------------------------
/.github/workflows/publish_release.yml:
--------------------------------------------------------------------------------
1 | name: Publish a release
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | env:
7 | VERSION_FILE: gradle.properties
8 | VERSION_EXTRACT_PATTERN: '(?<=version=).+'
9 | GH_USER_NAME: github.actor
10 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1024m"
11 |
12 | jobs:
13 | publish_artifacts:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | with:
19 | token: ${{ secrets.PUSH_PAT }}
20 |
21 | - name: Generate versions
22 | uses: HardNorth/github-version-generate@v1
23 | with:
24 | version-source: file
25 | version-file: ${{ env.VERSION_FILE }}
26 | version-file-extraction-pattern: ${{ env.VERSION_EXTRACT_PATTERN }}
27 |
28 | - uses: actions/setup-java@v4
29 | with:
30 | distribution: 'zulu'
31 | java-version-file: .ci-java-version
32 |
33 | - name: Setup Gradle
34 | uses: gradle/actions/setup-gradle@v4
35 | with:
36 | gradle-version: wrapper
37 |
38 | - name: Publish the artifacts
39 | env:
40 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
41 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
42 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }}
43 | run: ./gradlew publishAllPublicationsToMavenCentralRepository -Pversion=${{ env.RELEASE_VERSION }}
44 |
45 | - name: Create, checkout, and push release branch
46 | run: |
47 | git config user.name eygraber
48 | git config user.email 1100381+eygraber@users.noreply.github.com
49 | git checkout -b releases/${{ env.RELEASE_VERSION }}
50 | git push origin releases/${{ env.RELEASE_VERSION }}
51 |
52 | - name: Import GPG Key
53 | uses: crazy-max/ghaction-import-gpg@v6
54 | with:
55 | gpg_private_key: ${{ secrets.GIT_SIGNING_PRIVATE_KEY }}
56 | passphrase: ${{ secrets.GIT_SIGNING_PRIVATE_KEY_PASSWORD }}
57 | git_user_signingkey: true
58 | git_commit_gpgsign: true
59 | git_tag_gpgsign: true
60 |
61 | - name: Store SHA of HEAD commit on ENV
62 | run: echo "GIT_HEAD=$(git rev-parse HEAD)" >> $GITHUB_ENV
63 |
64 | - name: Create tag
65 | id: create_tag
66 | uses: actions/github-script@v7
67 | with:
68 | github-token: ${{ secrets.PUSH_PAT }}
69 | script: |
70 | const {GIT_HEAD} = process.env
71 | github.rest.git.createRef({
72 | owner: context.repo.owner,
73 | repo: context.repo.repo,
74 | ref: "refs/tags/${{ env.RELEASE_VERSION }}",
75 | sha: `${GIT_HEAD}`
76 | })
77 |
78 | - name: Build changelog
79 | id: build_changelog
80 | uses: mikepenz/release-changelog-builder-action@v5
81 | with:
82 | configuration: "changelog_config.json"
83 | toTag: ${{ env.RELEASE_VERSION }}
84 | env:
85 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
86 |
87 | - name: Create release
88 | id: create_release
89 | uses: ncipollo/release-action@v1
90 | with:
91 | body: ${{ steps.build_changelog.outputs.changelog }}
92 | name: Release ${{ env.RELEASE_VERSION }}
93 | tag: ${{ env.RELEASE_VERSION }}
94 | token: ${{ secrets.PUSH_PAT }}
95 |
96 | - uses: actions/checkout@v4
97 | with:
98 | ref: ${{ github.event.head_ref }}
99 | token: ${{ secrets.PUSH_PAT }}
100 |
101 | - name: Prepare next dev version
102 | id: prepare_next_dev
103 | run: |
104 | sed -i -e 's/${{ env.CURRENT_VERSION }}/${{ env.NEXT_VERSION }}/g' gradle.properties && \
105 | sed -i -E -e 's/compose-color-picker(:|\/)[0-9]+\.[0-9]+\.[0-9]+/compose-color-picker\1${{ env.RELEASE_VERSION }}/g' README.md
106 |
107 | - name: Update package-lock.json if needed
108 | run: ./gradlew kotlinUpgradePackageLock --no-build-cache --no-configuration-cache --rerun-tasks
109 |
110 | - name: Commit next dev version
111 | id: commit_next_dev
112 | uses: EndBug/add-and-commit@v9
113 | with:
114 | add: "['gradle.properties', 'README.md', 'kotlin-js-store/package-lock.json']"
115 | default_author: github_actions
116 | message: "Prepare next dev version"
117 |
--------------------------------------------------------------------------------
/.github/workflows/publish_snapshot.yml:
--------------------------------------------------------------------------------
1 | name: Publish a snapshot release
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | publish_snapshot:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 |
14 | - uses: actions/setup-java@v4
15 | with:
16 | distribution: 'zulu'
17 | java-version-file: .ci-java-version
18 |
19 | - name: Setup Gradle
20 | uses: gradle/actions/setup-gradle@v4
21 | with:
22 | gradle-version: wrapper
23 | dependency-graph: generate-and-submit
24 |
25 | - name: Publish the artifacts
26 | env:
27 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
28 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
29 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ARTIFACT_SIGNING_PRIVATE_KEY }}
30 | run: ./gradlew publishAllPublicationsToMavenCentralRepository
31 |
32 | env:
33 | GRADLE_OPTS: -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.jvmargs="-Xmx16g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1024m"
34 | DEPENDENCY_GRAPH_INCLUDE_CONFIGURATIONS: runtimeClasspath|releaseRuntimeClasspath
35 |
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .kotlin
4 | /local.properties
5 | /.idea
6 | # Make an exception for the code style
7 | !.idea/codeStyles/
8 | .DS_Store
9 | build/
10 | /captures
11 | .externalNativeBuild
12 | .cxx
13 | tmp
14 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | GETTERS_AND_SETTERS
80 | KEEP
81 |
82 |
83 | DEPENDENT_METHODS
84 | BREADTH_FIRST
85 |
86 |
87 | OVERRIDDEN_METHODS
88 | KEEP
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | xmlns:android
106 | .*
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | xmlns:.*
116 | .*
117 |
118 |
119 | BY_NAME
120 |
121 |
122 |
123 |
124 |
125 |
126 | .*:id
127 | http://schemas.android.com/apk/res/android
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | style
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | .*:theme
146 | http://schemas.android.com/apk/res/android
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | .*:layout_width
156 | http://schemas.android.com/apk/res/android
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | .*:layout_height
166 | http://schemas.android.com/apk/res/android
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | .*:layout_gravity
176 | http://schemas.android.com/apk/res/android
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | .*:layout_weight
186 | http://schemas.android.com/apk/res/android
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | .*:layout_alignParentTop
196 | http://schemas.android.com/apk/res/android
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | .*:layout_alignTop
206 | http://schemas.android.com/apk/res/android
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 | .*:layout_alignParentStart
216 | http://schemas.android.com/apk/res/android
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 | .*:layout_alignStart
226 | http://schemas.android.com/apk/res/android
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | .*:layout_alignParentEnd
236 | http://schemas.android.com/apk/res/android
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 | .*:layout_alignEnd
246 | http://schemas.android.com/apk/res/android
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 | .*:layout_alignParentBottom
256 | http://schemas.android.com/apk/res/android
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 | .*:layout_alignBottom
266 | http://schemas.android.com/apk/res/android
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 | .*:layout_alignBaseline
276 | http://schemas.android.com/apk/res/android
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 | .*:layout_alignWithParentIfMissing
286 | http://schemas.android.com/apk/res/android
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 | .*:layout_below
296 | http://schemas.android.com/apk/res/android
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 | .*:layout_toStartOf
306 | http://schemas.android.com/apk/res/android
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 | .*:layout_toEndOf
316 | http://schemas.android.com/apk/res/android
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 | .*:layout_above
326 | http://schemas.android.com/apk/res/android
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 | .*:layout_centerInParent
336 | http://schemas.android.com/apk/res/android
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 | .*:layout_centerHorizontal
346 | http://schemas.android.com/apk/res/android
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 | .*:layout_centerVertical
356 | http://schemas.android.com/apk/res/android
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 | .*:layout_constraintTop_to.*Of
366 | http://schemas.android.com/apk/res-auto
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 | .*:layout_constraintStart_to.*Of
376 | http://schemas.android.com/apk/res-auto
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 | .*:layout_constraintEnd_to.*Of
386 | http://schemas.android.com/apk/res-auto
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 | .*:layout_constraintBottom_to.*Of
396 | http://schemas.android.com/apk/res-auto
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 | .*:layout_constraint.*
406 | http://schemas.android.com/apk/res-auto
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 | .*:layout_margin
416 | http://schemas.android.com/apk/res/android
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 | .*:layout_marginTop
426 | http://schemas.android.com/apk/res/android
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 | .*:layout_marginStart
436 | http://schemas.android.com/apk/res/android
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 | .*:layout_marginEnd
446 | http://schemas.android.com/apk/res/android
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 | .*:layout_marginBottom
456 | http://schemas.android.com/apk/res/android
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 | .*:layout_.*
466 | http://schemas.android.com/apk/res/android
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 | .*:layout_.*
476 | http://schemas.android.com/apk/res-auto
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 | .*:minWidth
486 | http://schemas.android.com/apk/res/android
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 | .*:minHeight
496 | http://schemas.android.com/apk/res/android
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 | .*:padding
506 | http://schemas.android.com/apk/res/android
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 | .*:paddingTop
516 | http://schemas.android.com/apk/res/android
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 | .*:paddingStart
526 | http://schemas.android.com/apk/res/android
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 | .*:paddingEnd
536 | http://schemas.android.com/apk/res/android
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 | .*:paddingBottom
546 | http://schemas.android.com/apk/res/android
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 | .*:gravity
556 | http://schemas.android.com/apk/res/android
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 | .*:navigationIcon
566 | http://schemas.android.com/apk/res/android
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 | .*:navigationContentDescription
576 | http://schemas.android.com/apk/res/android
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 | .*:navigationIcon
586 | http://schemas.android.com/apk/res-auto
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 | .*:navigationContentDescription
596 | http://schemas.android.com/apk/res-auto
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 | .*:titleTextAppearance
606 | http://schemas.android.com/apk/res/android
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 | .*:titleTextAppearance
616 | http://schemas.android.com/apk/res-auto
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 | .*:titleTextSize
626 | http://schemas.android.com/apk/res/android
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 | .*:titleTextSize
636 | http://schemas.android.com/apk/res-auto
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 | .*:titleTextColor
646 | http://schemas.android.com/apk/res/android
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 | .*:titleTextColor
656 | http://schemas.android.com/apk/res-auto
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 | .*:title
666 | http://schemas.android.com/apk/res/android
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 | .*:title
676 | http://schemas.android.com/apk/res-auto
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 | .*:inputType
686 | http://schemas.android.com/apk/res/android
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 | .*:imeOptions
696 | http://schemas.android.com/apk/res/android
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 | .*:maxLines
706 | http://schemas.android.com/apk/res/android
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 | .*:ellipsize
716 | http://schemas.android.com/apk/res/android
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 | .*scrollHorizontally
726 | http://schemas.android.com/apk/res/android
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 | .*:lineSpacingExtra
736 | http://schemas.android.com/apk/res/android
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 | .*:lineSpacingMultiplier
746 | http://schemas.android.com/apk/res/android
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 | .*:textIsSelectable
756 | http://schemas.android.com/apk/res/android
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 | .*:drawablePadding
766 | http://schemas.android.com/apk/res/android
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 | .*:drawableTop
776 | http://schemas.android.com/apk/res/android
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 | .*:drawableStart
786 | http://schemas.android.com/apk/res/android
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 | .*:drawableEnd
796 | http://schemas.android.com/apk/res/android
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 | .*:drawableBottom
806 | http://schemas.android.com/apk/res/android
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 | .*:textAppearance
816 | http://schemas.android.com/apk/res/android
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 | .*:textSize
826 | http://schemas.android.com/apk/res/android
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 | .*:textColor
836 | http://schemas.android.com/apk/res/android
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 | .*:textColorHint
846 | http://schemas.android.com/apk/res/android
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 | .*:text
856 | http://schemas.android.com/apk/res/android
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 | .*:hint
866 | http://schemas.android.com/apk/res/android
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 | .*:contentDescription
876 | http://schemas.android.com/apk/res/android
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 | .*:scaleType
886 | http://schemas.android.com/apk/res/android
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 | .*:src
896 | http://schemas.android.com/apk/res/android
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 | .*srcCompat
906 | http://schemas.android.com/apk/res-auto
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 | .*:tint
916 | http://schemas.android.com/apk/res/android
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 | .*:enabled
926 | http://schemas.android.com/apk/res/android
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 | .*:visibility
936 | http://schemas.android.com/apk/res/android
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 | .*:background
946 | http://schemas.android.com/apk/res/android
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 | .*:backgroundTint
956 | http://schemas.android.com/apk/res/android
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 | .*:orientation
966 | http://schemas.android.com/apk/res/android
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 | .*:elevation
976 | http://schemas.android.com/apk/res/android
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 | .*:scrollbars
986 | http://schemas.android.com/apk/res/android
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 | .*:popupTheme
996 | http://schemas.android.com/apk/res/android
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 | .*:popupTheme
1006 | http://schemas.android.com/apk/res-auto
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 | .*:gravity
1016 | http://schemas.android.com/tools
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 | .*:titleTextAppearance
1026 | http://schemas.android.com/tools
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 | .*:titleTextSize
1036 | http://schemas.android.com/tools
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 | .*:titleTextColor
1046 | http://schemas.android.com/tools
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 | .*:title
1056 | http://schemas.android.com/tools
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 | .*:ellipsize
1066 | http://schemas.android.com/tools
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 | .*:lineSpacingExtra
1076 | http://schemas.android.com/tools
1077 |
1078 |
1079 |
1080 |
1081 |
1082 |
1083 |
1084 |
1085 | .*:lineSpacingMultiplier
1086 | http://schemas.android.com/tools
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 | .*:drawablePadding
1096 | http://schemas.android.com/tools
1097 |
1098 |
1099 |
1100 |
1101 |
1102 |
1103 |
1104 |
1105 | .*drawableTop
1106 | http://schemas.android.com/tools
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 | .*:drawableStart
1116 | http://schemas.android.com/tools
1117 |
1118 |
1119 |
1120 |
1121 |
1122 |
1123 |
1124 |
1125 | .*:drawableEnd
1126 | http://schemas.android.com/tools
1127 |
1128 |
1129 |
1130 |
1131 |
1132 |
1133 |
1134 |
1135 | .*:drawableBottom
1136 | http://schemas.android.com/tools
1137 |
1138 |
1139 |
1140 |
1141 |
1142 |
1143 |
1144 |
1145 | .*:textAppearance
1146 | http://schemas.android.com/tools
1147 |
1148 |
1149 |
1150 |
1151 |
1152 |
1153 |
1154 |
1155 | .*:textSize
1156 | http://schemas.android.com/tools
1157 |
1158 |
1159 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 | .*:textColor
1166 | http://schemas.android.com/tools
1167 |
1168 |
1169 |
1170 |
1171 |
1172 |
1173 |
1174 |
1175 | .*:textColorHint
1176 | http://schemas.android.com/tools
1177 |
1178 |
1179 |
1180 |
1181 |
1182 |
1183 |
1184 |
1185 | .*:text
1186 | http://schemas.android.com/tools
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 | .*:hint
1196 | http://schemas.android.com/tools
1197 |
1198 |
1199 |
1200 |
1201 |
1202 |
1203 |
1204 |
1205 | .*:scaleType
1206 | http://schemas.android.com/tools
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 | .*:src
1216 | http://schemas.android.com/tools
1217 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 | .*srcCompat
1226 | http://schemas.android.com/tools
1227 |
1228 |
1229 |
1230 |
1231 |
1232 |
1233 |
1234 |
1235 | .*:tint
1236 | http://schemas.android.com/tools
1237 |
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 | .*:enabled
1246 | http://schemas.android.com/tools
1247 |
1248 |
1249 |
1250 |
1251 |
1252 |
1253 |
1254 |
1255 | .*:visibility
1256 | http://schemas.android.com/tools
1257 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 |
1264 |
1265 | .*:background
1266 | http://schemas.android.com/tools
1267 |
1268 |
1269 |
1270 |
1271 |
1272 |
1273 |
1274 |
1275 | .*:backgroundTint
1276 | http://schemas.android.com/tools
1277 |
1278 |
1279 |
1280 |
1281 |
1282 |
1283 |
1284 |
1285 | .*:elevation
1286 | http://schemas.android.com/tools
1287 |
1288 |
1289 |
1290 |
1291 |
1292 |
1293 |
1294 |
1295 | .*:theme
1296 | http://schemas.android.com/tools
1297 |
1298 |
1299 |
1300 |
1301 |
1302 |
1303 |
1304 |
1305 | .:*
1306 | http://schemas.android.com/tools
1307 |
1308 |
1309 |
1310 |
1311 |
1312 |
1313 |
1314 |
1315 |
1316 |
1317 |
1318 |
1319 |
1320 |
1321 |
1322 |
1323 |
1324 |
1325 |
1326 |
1327 |
1328 |
1329 |
1330 |
1331 |
1332 |
1333 |
1334 |
1335 |
1336 |
1337 |
1338 |
1339 |
1340 |
1341 |
1342 |
1343 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Dangerfile.df.kts:
--------------------------------------------------------------------------------
1 | import systems.danger.kotlin.*
2 | import java.util.Locale
3 |
4 | danger(args) {
5 | with(github) {
6 | val labelNames = issue.labels.map { it.name }.toSet()
7 |
8 | /*
9 | # --------------------------------------------------------------------------------------------------------------------
10 | # Check if labels were added to the pull request
11 | #--------------------------------------------------------------------------------------------------------------------
12 | */
13 | val labelsToFilter = setOf("hold", "skip release notes")
14 | val acceptableLabels = labelNames.filter { it !in labelsToFilter }
15 |
16 | if(acceptableLabels.isEmpty()) {
17 | fail("PR needs labels (hold and skip release notes don't count)")
18 | }
19 |
20 | /*
21 | # --------------------------------------------------------------------------------------------------------------------
22 | # Don't merge if there is a WIP or Hold label applied
23 | # --------------------------------------------------------------------------------------------------------------------
24 | */
25 | if("Hold" in labelNames) fail("This PR cannot be merged with a hold label applied")
26 |
27 | /*
28 | # --------------------------------------------------------------------------------------------------------------------
29 | # Check if merge commits were added to the pull request
30 | # --------------------------------------------------------------------------------------------------------------------
31 | */
32 | val mergeCommitRegex = Regex("^Merge branch '${pullRequest.base.ref}'.*")
33 | if(git.commits.any { it.message.matches(mergeCommitRegex) }) {
34 | fail("Please rebase to get rid of the merge commits in this PR")
35 | }
36 | }
37 |
38 | /*
39 | # --------------------------------------------------------------------------------------------------------------------
40 | # Make sure that no crash files or dumps are in the commit
41 | # --------------------------------------------------------------------------------------------------------------------
42 | */
43 | val touchedFiles = git.createdFiles + git.modifiedFiles
44 | if(touchedFiles.any { it.startsWith("hs_err_pid") || it.startsWith("java_pid") }) {
45 | fail("Please remove any error logs (hs_err_pid*.log) or heap dumps (java_pid*.hprof)")
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Eliezer Graber
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | This project makes use of a modified version of the ColorPickerDemo from AOSP
2 |
3 | Copyright 2020 The Android Open Source Project
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Under development
2 |
3 | Based on the [ColorPickerDemo](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ColorPickerDemo.kt) from the AOSP compose samples.
4 |
5 | ```
6 | repositories {
7 | mavenCentral()
8 | }
9 |
10 | dependencies {
11 | implementation("com.eygraber:compose-color-picker:0.0.19")
12 | }
13 | ```
14 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.eygraber.conventions.tasks.deleteRootBuildDirWhenCleaning
2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget
3 |
4 | buildscript {
5 | dependencies {
6 | classpath(libs.buildscript.android)
7 | classpath(libs.buildscript.androidCacheFix)
8 | classpath(libs.buildscript.compose.compiler)
9 | classpath(libs.buildscript.compose.jetbrains)
10 | classpath(libs.buildscript.detekt)
11 | classpath(libs.buildscript.dokka)
12 | classpath(libs.buildscript.kotlin)
13 | classpath(libs.buildscript.publish)
14 | }
15 | }
16 |
17 | plugins {
18 | base
19 | alias(libs.plugins.conventions)
20 | }
21 |
22 | deleteRootBuildDirWhenCleaning()
23 |
24 | gradleConventionsDefaults {
25 | android {
26 | sdkVersions(
27 | compileSdk = libs.versions.android.sdk.compile,
28 | targetSdk = libs.versions.android.sdk.target,
29 | minSdk = libs.versions.android.sdk.min,
30 | )
31 | }
32 |
33 | detekt {
34 | plugins(
35 | libs.detektCompose,
36 | libs.detektEygraber.formatting,
37 | libs.detektEygraber.style,
38 | )
39 | }
40 |
41 | kotlin {
42 | jvmTargetVersion = JvmTarget.JVM_11
43 | }
44 | }
45 |
46 | gradleConventionsKmpDefaults {
47 | targets(
48 | KmpTarget.Android,
49 | KmpTarget.Ios,
50 | KmpTarget.Js,
51 | KmpTarget.Jvm,
52 | KmpTarget.WasmJs,
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/changelog_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "categories": [
3 | {
4 | "title": "## 🚀 Features",
5 | "labels": ["feature"]
6 | },
7 | {
8 | "title": "## ⚙️ Chores",
9 | "labels": ["chore"]
10 | },
11 | {
12 | "title": "## 🐛 Bugs",
13 | "labels": ["bug"]
14 | },
15 | {
16 | "title": "## 🧪 Tests",
17 | "labels": ["test"]
18 | },
19 | {
20 | "title": "## \uD83D\uDCD6 Documentation",
21 | "labels": ["documentation"]
22 | },
23 | {
24 | "title": "\uD83D\uDCE6 Dependencies",
25 | "labels": ["dependencies"]
26 | }
27 | ],
28 | "ignore_labels": [
29 | "duplicate", "good first issue", "help wanted", "invalid", "question", "wontfix", "skip release notes", "hold"
30 | ],
31 | "sort": "ASC",
32 | "template": "${{CHANGELOG}}",
33 | "pr_template": "- ${{TITLE}} (#${{NUMBER}})",
34 | "empty_template": "- no changes",
35 | "label_extractor": [
36 | {
37 | "pattern": "(.) (.+)",
38 | "target": "$1"
39 | },
40 | {
41 | "pattern": "(.) (.+)",
42 | "target": "$1",
43 | "on_property": "title"
44 | }
45 | ],
46 | "transformers": [
47 | {
48 | "pattern": "[\\-\\*] (\\[(...|TEST|CI|SKIP)\\])( )?(.+?)\n(.+?[\\-\\*] )(.+)",
49 | "target": "- $4\n - $6"
50 | }
51 | ],
52 | "max_tags_to_fetch": 200,
53 | "max_pull_requests": 200,
54 | "max_back_track_time_days": 365,
55 | "tag_resolver": {
56 | "method": "semver"
57 | },
58 | "base_branches": [
59 | "master"
60 | ]
61 | }
62 |
--------------------------------------------------------------------------------
/detekt.yml:
--------------------------------------------------------------------------------
1 | build:
2 | maxIssues: 0
3 | excludeCorrectable: false
4 | weights:
5 | # complexity: 2
6 | # LongParameterList: 1
7 | # style: 1
8 | # comments: 1
9 |
10 | config:
11 | validation: true
12 | warningsAsErrors: true
13 | checkExhaustiveness: true
14 |
15 | processors:
16 | active: true
17 | exclude:
18 | # - 'FunctionCountProcessor'
19 | # - 'PropertyCountProcessor'
20 | # - 'ClassCountProcessor'
21 | # - 'PackageCountProcessor'
22 | # - 'KtFileCountProcessor'
23 |
24 | console-reports:
25 | active: true
26 | exclude:
27 | - 'ProjectStatisticsReport'
28 | - 'ComplexityReport'
29 | - 'NotificationReport'
30 | # - 'FindingsReport'
31 | - 'FileBasedFindingsReport'
32 |
33 | comments:
34 | active: false
35 | AbsentOrWrongFileLicense:
36 | active: false
37 | licenseTemplateFile: 'license.template'
38 | licenseTemplateIsRegex: false
39 | CommentOverPrivateFunction:
40 | active: false
41 | CommentOverPrivateProperty:
42 | active: false
43 | DeprecatedBlockTag:
44 | active: false
45 | EndOfSentenceFormat:
46 | active: false
47 | endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
48 | UndocumentedPublicClass:
49 | active: false
50 | searchInNestedClass: true
51 | searchInInnerClass: true
52 | searchInInnerObject: true
53 | searchInInnerInterface: true
54 | UndocumentedPublicFunction:
55 | active: false
56 | UndocumentedPublicProperty:
57 | active: false
58 |
59 | complexity:
60 | active: true
61 | CognitiveComplexMethod:
62 | active: false
63 | ComplexCondition:
64 | active: true
65 | threshold: 4
66 | ComplexInterface:
67 | active: false
68 | threshold: 10
69 | includeStaticDeclarations: false
70 | includePrivateDeclarations: false
71 | CyclomaticComplexMethod:
72 | active: false
73 | threshold: 15
74 | ignoreSingleWhenExpression: false
75 | ignoreSimpleWhenEntries: false
76 | ignoreNestingFunctions: false
77 | nestingFunctions: ['run', 'let', 'apply', 'with', 'also', 'use', 'forEach', 'isNotNull', 'ifNull']
78 | LabeledExpression:
79 | active: true
80 | ignoredLabels: []
81 | LargeClass:
82 | active: false
83 | threshold: 600
84 | LongMethod:
85 | active: false
86 | threshold: 60
87 | LongParameterList:
88 | active: false
89 | functionThreshold: 6
90 | constructorThreshold: 7
91 | ignoreDefaultParameters: false
92 | ignoreDataClasses: true
93 | ignoreAnnotated: []
94 | MethodOverloading:
95 | active: true
96 | threshold: 6
97 | NamedArguments:
98 | active: false
99 | threshold: 3
100 | NestedBlockDepth:
101 | active: true
102 | threshold: 6
103 | NestedScopeFunctions:
104 | active: false
105 | ReplaceSafeCallChainWithRun:
106 | active: false
107 | StringLiteralDuplication:
108 | active: false
109 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
110 | threshold: 3
111 | ignoreAnnotation: true
112 | excludeStringsWithLessThan5Characters: true
113 | ignoreStringsRegex: '$^'
114 | TooManyFunctions:
115 | active: false
116 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
117 | thresholdInFiles: 11
118 | thresholdInClasses: 11
119 | thresholdInInterfaces: 11
120 | thresholdInObjects: 11
121 | thresholdInEnums: 11
122 | ignoreDeprecated: false
123 | ignorePrivate: false
124 | ignoreOverridden: false
125 |
126 | coroutines:
127 | active: true
128 | GlobalCoroutineUsage:
129 | active: false
130 | InjectDispatcher:
131 | active: true
132 | RedundantSuspendModifier:
133 | active: true
134 | SleepInsteadOfDelay:
135 | active: true
136 | SuspendFunSwallowedCancellation:
137 | active: true
138 | SuspendFunWithCoroutineScopeReceiver:
139 | active: true
140 | SuspendFunWithFlowReturnType:
141 | active: true
142 |
143 | empty-blocks:
144 | active: true
145 | EmptyCatchBlock:
146 | active: true
147 | allowedExceptionNameRegex: '_|(ignore|expected).*'
148 | EmptyClassBlock:
149 | active: true
150 | EmptyDefaultConstructor:
151 | active: true
152 | EmptyDoWhileBlock:
153 | active: true
154 | EmptyElseBlock:
155 | active: true
156 | EmptyFinallyBlock:
157 | active: true
158 | EmptyForBlock:
159 | active: true
160 | EmptyFunctionBlock:
161 | active: false
162 | ignoreOverridden: false
163 | EmptyIfBlock:
164 | active: true
165 | EmptyInitBlock:
166 | active: true
167 | EmptyKtFile:
168 | active: true
169 | EmptySecondaryConstructor:
170 | active: true
171 | EmptyTryBlock:
172 | active: true
173 | EmptyWhenBlock:
174 | active: true
175 | EmptyWhileBlock:
176 | active: true
177 |
178 | exceptions:
179 | active: true
180 | ExceptionRaisedInUnexpectedLocation:
181 | active: true
182 | methodNames: ['toString', 'hashCode', 'equals', 'finalize']
183 | InstanceOfCheckForException:
184 | active: false
185 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
186 | NotImplementedDeclaration:
187 | active: true
188 | ObjectExtendsThrowable:
189 | active: false
190 | PrintStackTrace:
191 | active: false
192 | RethrowCaughtException:
193 | active: true
194 | ReturnFromFinally:
195 | active: true
196 | ignoreLabeled: false
197 | SwallowedException:
198 | active: false
199 | ignoredExceptionTypes:
200 | - InterruptedException
201 | - NumberFormatException
202 | - ParseException
203 | - MalformedURLException
204 | allowedExceptionNameRegex: '_|(ignore|expected).*'
205 | ThrowingExceptionFromFinally:
206 | active: false
207 | ThrowingExceptionInMain:
208 | active: false
209 | ThrowingExceptionsWithoutMessageOrCause:
210 | active: false
211 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
212 | exceptions:
213 | - IllegalArgumentException
214 | - IllegalStateException
215 | - IOException
216 | ThrowingNewInstanceOfSameException:
217 | active: true
218 | TooGenericExceptionCaught:
219 | active: false
220 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
221 | exceptionNames:
222 | - ArrayIndexOutOfBoundsException
223 | - Error
224 | - Exception
225 | - IllegalMonitorStateException
226 | - NullPointerException
227 | - IndexOutOfBoundsException
228 | - RuntimeException
229 | - Throwable
230 | allowedExceptionNameRegex: '_|(ignore|expected).*'
231 | TooGenericExceptionThrown:
232 | active: false
233 | exceptionNames:
234 | - Error
235 | - Exception
236 | - Throwable
237 | - RuntimeException
238 |
239 | formatting-eygraber:
240 | NoWhitespaceAfterKeyword:
241 | active: true
242 | autoCorrect: true
243 |
244 | naming:
245 | active: true
246 | BooleanPropertyNaming:
247 | active: true
248 | allowedPattern: '^(is|has|are|should)'
249 | ClassNaming:
250 | active: true
251 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
252 | classPattern: '[A-Z][a-zA-Z0-9]*'
253 | ConstructorParameterNaming:
254 | active: true
255 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
256 | parameterPattern: '[a-z][A-Za-z0-9]*'
257 | privateParameterPattern: '[a-z][A-Za-z0-9]*'
258 | excludeClassPattern: '$^'
259 | EnumNaming:
260 | active: true
261 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
262 | enumEntryPattern: '([A-Z][a-z0-9]+)((\d)|([A-Z0-9][a-z0-9]+))*([A-Z])?'
263 | ForbiddenClassName:
264 | active: false
265 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
266 | forbiddenName: []
267 | FunctionMaxLength:
268 | active: false
269 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
270 | maximumFunctionNameLength: 30
271 | FunctionMinLength:
272 | active: false
273 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
274 | minimumFunctionNameLength: 3
275 | FunctionNaming:
276 | active: true
277 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
278 | functionPattern: '^([[a-z][A-Z]$][a-zA-Z$0-9]*)|(`.*`)$'
279 | excludeClassPattern: '$^'
280 | ignoreAnnotated: ['Composable']
281 | FunctionParameterNaming:
282 | active: true
283 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
284 | parameterPattern: '[a-z][A-Za-z0-9]*'
285 | excludeClassPattern: '$^'
286 | InvalidPackageDeclaration:
287 | active: true
288 | excludes: ['**/*.kts']
289 | rootPackage: ''
290 | LambdaParameterNaming:
291 | active: false
292 | MatchingDeclarationName:
293 | active: false
294 | mustBeFirst: true
295 | MemberNameEqualsClassName:
296 | active: true
297 | ignoreOverridden: true
298 | NoNameShadowing:
299 | active: true
300 | NonBooleanPropertyPrefixedWithIs:
301 | active: true
302 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
303 | ObjectPropertyNaming:
304 | active: true
305 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
306 | constantPattern: '[A-Z][_A-Z0-9]*'
307 | propertyPattern: '[A-Za-z][A-Za-z0-9]*'
308 | privatePropertyPattern: '(_)?[A-Za-z][A-Za-z0-9]*'
309 | PackageNaming:
310 | active: false
311 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
312 | packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
313 | TopLevelPropertyNaming:
314 | active: true
315 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
316 | constantPattern: '[A-Z][_A-Z0-9]*'
317 | propertyPattern: '[A-Za-z][A-Za-z0-9]*'
318 | privatePropertyPattern: '_?[A-Za-z][A-Za-z0-9]*'
319 | VariableMaxLength:
320 | active: false
321 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
322 | maximumVariableNameLength: 64
323 | VariableMinLength:
324 | active: false
325 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
326 | minimumVariableNameLength: 1
327 | VariableNaming:
328 | active: true
329 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
330 | variablePattern: '[a-z][A-Za-z0-9]*'
331 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
332 | excludeClassPattern: '$^'
333 |
334 | performance:
335 | active: true
336 | ArrayPrimitive:
337 | active: true
338 | CouldBeSequence:
339 | active: true
340 | ForEachOnRange:
341 | active: true
342 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
343 | SpreadOperator:
344 | active: false
345 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
346 | UnnecessaryPartOfBinaryExpression:
347 | active: true
348 | UnnecessaryTemporaryInstantiation:
349 | active: true
350 |
351 | potential-bugs:
352 | active: true
353 | AvoidReferentialEquality:
354 | active: true
355 | CastNullableToNonNullableType:
356 | active: true
357 | CastToNullableType:
358 | active: false
359 | Deprecation:
360 | active: true
361 | DontDowncastCollectionTypes:
362 | active: false
363 | DoubleMutabilityForCollection:
364 | active: false
365 | ElseCaseInsteadOfExhaustiveWhen:
366 | active: true
367 | EqualsAlwaysReturnsTrueOrFalse:
368 | active: false
369 | EqualsWithHashCodeExist:
370 | active: true
371 | ExitOutsideMain:
372 | active: false
373 | ExplicitGarbageCollectionCall:
374 | active: true
375 | HasPlatformType:
376 | active: false
377 | IgnoredReturnValue:
378 | active: false
379 | restrictToConfig: true
380 | returnValueAnnotations: ['*.CheckReturnValue', '*.CheckResult']
381 | ImplicitDefaultLocale:
382 | active: true
383 | ImplicitUnitReturnType:
384 | active: false
385 | allowExplicitReturnType: true
386 | InvalidRange:
387 | active: true
388 | IteratorHasNextCallsNextMethod:
389 | active: true
390 | IteratorNotThrowingNoSuchElementException:
391 | active: true
392 | LateinitUsage:
393 | active: false
394 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
395 | ignoreAnnotated: []
396 | ignoreOnClassesPattern: ''
397 | MapGetWithNotNullAssertionOperator:
398 | active: false
399 | MissingPackageDeclaration:
400 | active: true
401 | NullCheckOnMutableProperty:
402 | active: true
403 | NullableToStringCall:
404 | active: false
405 | PropertyUsedBeforeDeclaration:
406 | active: true
407 | UnconditionalJumpStatementInLoop:
408 | active: true
409 | UnnecessaryNotNullCheck:
410 | active: true
411 | UnnecessaryNotNullOperator:
412 | active: true
413 | UnnecessarySafeCall:
414 | active: true
415 | UnreachableCatchBlock:
416 | active: false
417 | UnreachableCode:
418 | active: true
419 | UnsafeCallOnNullableType:
420 | active: true
421 | UnsafeCast:
422 | active: false
423 | UnusedUnaryOperator:
424 | active: false
425 | UselessPostfixExpression:
426 | active: true
427 | WrongEqualsTypeParameter:
428 | active: true
429 |
430 | style:
431 | active: true
432 | AlsoCouldBeApply:
433 | active: true
434 | BracesOnIfStatements:
435 | active: true
436 | BracesOnWhenStatements:
437 | active: true
438 | singleLine: 'necessary'
439 | multiLine: 'necessary'
440 | ClassOrdering:
441 | active: false
442 | CollapsibleIfStatements:
443 | active: false
444 | CanBeNonNullable:
445 | active: true
446 | CascadingCallWrapping:
447 | active: true
448 | DataClassContainsFunctions:
449 | active: false
450 | conversionFunctionPrefix: ['to']
451 | DataClassShouldBeImmutable:
452 | active: true
453 | DestructuringDeclarationWithTooManyEntries:
454 | active: false
455 | maxDestructuringEntries: 3
456 | DoubleNegativeLambda:
457 | active: true
458 | EqualsNullCall:
459 | active: true
460 | EqualsOnSignatureLine:
461 | active: false
462 | ExplicitCollectionElementAccessMethod:
463 | active: true
464 | ExplicitItLambdaParameter:
465 | active: true
466 | ExpressionBodySyntax:
467 | active: true
468 | includeLineWrapping: true
469 | ForbiddenAnnotation:
470 | active: true
471 | ForbiddenComment:
472 | active: true
473 | comments: ['FIXME', 'STOPSHIP']
474 | ForbiddenImport:
475 | active: false
476 | imports: []
477 | forbiddenPatterns: ''
478 | ForbiddenMethodCall:
479 | active: false
480 | methods: ['kotlin.io.println', 'kotlin.io.print']
481 | ForbiddenSuppress:
482 | active: true
483 | rules: []
484 | ForbiddenVoid:
485 | active: true
486 | ignoreOverridden: true
487 | ignoreUsageInGenerics: true
488 | FunctionOnlyReturningConstant:
489 | active: false
490 | ignoreOverridableFunction: true
491 | ignoreActualFunction: true
492 | excludedFunctions: ['describeContents']
493 | ignoreAnnotated: ['dagger.Provides']
494 | LoopWithTooManyJumpStatements:
495 | active: false
496 | maxJumpCount: 1
497 | MagicNumber:
498 | active: false
499 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
500 | ignoreNumbers: ['-1', '0', '1', '2']
501 | ignoreHashCodeFunction: true
502 | ignorePropertyDeclaration: false
503 | ignoreLocalVariableDeclaration: false
504 | ignoreConstantDeclaration: true
505 | ignoreCompanionObjectPropertyDeclaration: true
506 | ignoreAnnotation: false
507 | ignoreNamedArgument: true
508 | ignoreEnums: false
509 | ignoreRanges: false
510 | ignoreExtensionFunctions: true
511 | MandatoryBracesLoops:
512 | active: true
513 | MaxChainedCallsOnSameLine:
514 | active: true
515 | MaxLineLength:
516 | active: false # handled by ktlint
517 | maxLineLength: 120
518 | MayBeConst:
519 | active: true
520 | ModifierOrder:
521 | active: true
522 | MultilineLambdaItParameter:
523 | active: true
524 | MultilineRawStringIndentation:
525 | active: true
526 | indentSize: 4
527 | NestedClassesVisibility:
528 | active: true
529 | NewLineAtEndOfFile:
530 | active: true
531 | NoTabs:
532 | active: true
533 | NullableBooleanCheck:
534 | active: true
535 | ObjectLiteralToLambda:
536 | active: true
537 | OptionalAbstractKeyword:
538 | active: true
539 | OptionalUnit:
540 | active: true
541 | PreferToOverPairSyntax:
542 | active: true
543 | ProtectedMemberInFinalClass:
544 | active: true
545 | RedundantExplicitType:
546 | active: true
547 | RedundantHigherOrderMapUsage:
548 | active: false
549 | RedundantVisibilityModifierRule:
550 | active: false
551 | ReturnCount:
552 | active: true
553 | max: 2
554 | excludedFunctions: ['equals']
555 | excludeLabeled: false
556 | excludeReturnFromLambda: true
557 | excludeGuardClauses: true
558 | SafeCast:
559 | active: true
560 | SerialVersionUIDInSerializableClass:
561 | active: true
562 | SpacingBetweenPackageAndImports:
563 | active: true
564 | StringShouldBeRawString:
565 | active: false
566 | ThrowsCount:
567 | active: true
568 | max: 3
569 | # excludeGuardClauses: true
570 | TrailingWhitespace:
571 | active: true
572 | TrimMultilineRawString:
573 | active: true
574 | UnderscoresInNumericLiterals:
575 | active: true
576 | acceptableLength: 4
577 | UnnecessaryAbstractClass:
578 | active: false
579 | ignoreAnnotated: ['dagger.Module']
580 | UnnecessaryAnnotationUseSiteTarget:
581 | active: true
582 | UnnecessaryApply:
583 | active: true
584 | UnnecessaryBackticks:
585 | active: true
586 | UnnecessaryBracesAroundTrailingLambda:
587 | active: true
588 | UnnecessaryFilter:
589 | active: false
590 | UnnecessaryInheritance:
591 | active: true
592 | UnnecessaryInnerClass:
593 | active: true
594 | UnnecessaryLet:
595 | active: true
596 | UnnecessaryParentheses:
597 | active: true
598 | UntilInsteadOfRangeTo:
599 | active: true
600 | UnusedImports:
601 | active: true
602 | UnusedParameter:
603 | active: true
604 | UnusedPrivateClass:
605 | active: true
606 | UnusedPrivateMember:
607 | active: false
608 | allowedNames: '(_|ignored|expected|serialVersionUID)'
609 | UnusedPrivateProperty:
610 | active: true
611 | UseAnyOrNoneInsteadOfFind:
612 | active: true
613 | UseArrayLiteralsInAnnotations:
614 | active: true
615 | UseCheckOrError:
616 | active: true
617 | UseCheckNotNull:
618 | active: true
619 | UseDataClass:
620 | active: false
621 | ignoreAnnotated: []
622 | allowVars: false
623 | UseEmptyCounterpart:
624 | active: true
625 | UseIfEmptyOrIfBlank:
626 | active: true
627 | UseIfInsteadOfWhen:
628 | active: false
629 | UseIsNullOrEmpty:
630 | active: true
631 | UseLet:
632 | active: true
633 | UseOrEmpty:
634 | active: true
635 | UseRequire:
636 | active: true
637 | UseRequireNotNull:
638 | active: true
639 | UseSumOfInsteadOfFlatMapSize:
640 | active: true
641 | UselessCallOnNotNull:
642 | active: true
643 | UtilityClassWithPublicConstructor:
644 | active: true
645 | VarCouldBeVal:
646 | active: true
647 | WildcardImport:
648 | active: true
649 | excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
650 | excludeImports: []
651 |
652 | style-eygraber:
653 | NewlineForMultilineKeyword:
654 | active: true
655 | autoCorrect: true
656 |
657 | Compose:
658 | CompositionLocalAllowlist:
659 | active: false
660 | ContentEmitterReturningValues:
661 | active: true
662 | # You can optionally add your own composables here
663 | # contentEmitters: MyComposable,MyOtherComposable
664 | Material2:
665 | active: true
666 | ModifierComposable:
667 | active: true
668 | ModifierMissing:
669 | active: true
670 | ModifierReused:
671 | active: true
672 | ModifierWithoutDefault:
673 | active: true
674 | MultipleEmitters:
675 | active: true
676 | # You can optionally add your own composables here
677 | # contentEmitters: MyComposable,MyOtherComposable
678 | MutableParams:
679 | active: true
680 | ComposableNaming:
681 | active: true
682 | # You can optionally disable the checks in this rule for regex matches against the composable name (e.g. molecule presenters)
683 | # allowedComposableFunctionNames: .*Presenter,.*MoleculePresenter
684 | ComposableParamOrder:
685 | active: true
686 | PreviewNaming:
687 | active: true
688 | PreviewPublic:
689 | active: true
690 | # You can optionally disable that only previews with @PreviewParameter are flagged
691 | # previewPublicOnlyIfParams: false
692 | RememberMissing:
693 | active: true
694 | UnstableCollections:
695 | active: true
696 | ViewModelForwarding:
697 | active: true
698 | ViewModelInjection:
699 | active: true
700 |
--------------------------------------------------------------------------------
/format:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | version=$(sed -n 's/ktlint = "\(.*\)"/\1/p' gradle/libs.versions.toml)
4 | url="https://github.com/pinterest/ktlint/releases/download/$version/ktlint"
5 |
6 | # Set the destination directory and file name
7 | destination_dir="tmp"
8 | file_name="ktlint-$version"
9 |
10 | mkdir -p $destination_dir
11 |
12 | # setting nullglob ensures proper behavior if nothing matches the glob
13 | shopt -s nullglob
14 | for file in $destination_dir/ktlint-*; do
15 | if [[ "$file" != "$destination_dir/$file_name" ]]; then
16 | rm "$file"
17 | fi
18 | done
19 | shopt -u nullglob
20 |
21 | # Check if the file already exists in the destination directory
22 | if [ ! -e "$destination_dir/$file_name" ]; then
23 | if command -v curl >/dev/null 2>&1; then
24 | curl -LJO "$url"
25 | mv "ktlint" "$destination_dir/$file_name"
26 | elif command -v wget >/dev/null 2>&1; then
27 | wget -O "$destination_dir/$file_name" "$url"
28 | else
29 | echo "Error: curl or wget not found. Please install either curl or wget."
30 | exit 1
31 | fi
32 |
33 | chmod +x "$destination_dir/$file_name"
34 | fi
35 |
36 | should_format=true
37 | for arg in "$@"; do
38 | if [ "$arg" == "--no-format" ]; then
39 | should_format=false
40 | set -- "${@//--no-format/}"
41 | break
42 | fi
43 | done
44 |
45 | args=()
46 |
47 | if [ "$should_format" = true ]; then
48 | args+=("--format")
49 | fi
50 |
51 | args+=("$@")
52 |
53 | "$destination_dir/$file_name" **/*.kt **/*.kts \!**/build/** \!Dangerfile.df.kts --color --color-name=YELLOW "${args[@]}"
54 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4g -XX:ReservedCodeCacheSize=240m -XX:+UseCompressedOops -XX:+UseParallelGC -XX:MetaspaceSize=256m -Dfile.encoding=UTF-8
2 | kotlin.daemon.jvm.options=-Xmx4g -XX:ReservedCodeCacheSize=240m -XX:+UseCompressedOops -XX:+UseParallelGC -XX:MetaspaceSize=256m -Dfile.encoding=UTF-8
3 |
4 | group=com.eygraber
5 | version=0.0.20-SNAPSHOT
6 |
7 | POM_URL=https://github.com/eygraber/compose-color-picker/
8 | POM_SCM_URL=https://github.com/eygraber/compose-color-picker/
9 | POM_SCM_CONNECTION=scm:git:git://github.com/eygraber/compose-color-picker.git
10 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/eygraber/compose-color-picker.git
11 |
12 | POM_LICENCE_NAME=MIT
13 | POM_LICENCE_URL=https://opensource.org/licenses/mit-license.php
14 | POM_LICENCE_DIST=repo
15 |
16 | POM_DEVELOPER_ID=eygraber
17 | POM_DEVELOPER_NAME=Eliezer Graber
18 | POM_DEVELOPER_URL=https://github.com/eygraber
19 |
20 | #Android
21 | android.useAndroidX=true
22 | android.enableJetifier=false
23 | android.enableR8.fullMode=true
24 | android.nonTransitiveRClass=true
25 |
26 | android.defaults.buildfeatures.aidl=false
27 | android.defaults.buildfeatures.buildconfig=false
28 | android.defaults.buildfeatures.renderscript=false
29 | android.defaults.buildfeatures.resvalues=false
30 | android.defaults.buildfeatures.shaders=false
31 |
32 | android.experimental.cacheCompileLibResources=true
33 | android.experimental.enableSourceSetPathsMap=true
34 |
35 | systemProp.org.gradle.android.cache-fix.ignoreVersionCheck=true
36 |
37 | #Compose
38 | org.jetbrains.compose.experimental.jscanvas.enabled=true
39 | org.jetbrains.compose.experimental.wasm.enabled=true
40 |
41 | # Gradle
42 | org.gradle.caching=true
43 | org.gradle.parallel=true
44 | org.gradle.configuration-cache=false
45 | # https://youtrack.jetbrains.com/issue/KT-55701
46 | org.gradle.configureondemand=false
47 |
48 | #Kotlin
49 | kotlin.incremental.wasm=true
50 |
51 | kotlin.js.yarn=false
52 | kotlin.native.enableKlibsCrossCompilation=true
53 | kotlin.native.ignoreDisabledTargets=true
54 |
55 | #Misc
56 | org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
57 | org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true
58 |
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.10.1"
3 |
4 | android-sdk-compile = "35"
5 | android-sdk-target = "35"
6 | android-sdk-min = "21"
7 |
8 | androidx-activity = "1.10.1"
9 |
10 | composeJetbrains = "1.8.1"
11 |
12 | conventions = "0.0.84"
13 |
14 | detekt = "1.23.8"
15 | detektCompose = "0.4.22"
16 | detektEygraber = "1.0.11"
17 |
18 | dokka = "2.0.0"
19 |
20 | kotlin = "2.1.21"
21 |
22 | ktlint = "1.6.0"
23 |
24 | publish = "0.32.0"
25 |
26 | [plugins]
27 | conventions = { id = "com.eygraber.conventions", version.ref = "conventions" }
28 |
29 | [libraries]
30 | android-desugar = "com.android.tools:desugar_jdk_libs:2.1.5"
31 |
32 | androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
33 | androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
34 | androidx-appCompat = "androidx.appcompat:appcompat:1.7.1"
35 | androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
36 | androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
37 | androidx-compose-uiTooling = { module = "androidx.compose.ui:ui-tooling" }
38 | androidx-compose-uiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview" }
39 |
40 | buildscript-android = { module = "com.android.tools.build:gradle", version.ref = "agp" }
41 | buildscript-androidCacheFix = { module = "gradle.plugin.org.gradle.android:android-cache-fix-gradle-plugin", version = "3.0.1" }
42 | buildscript-compose-compiler = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
43 | buildscript-compose-jetbrains = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "composeJetbrains" }
44 | buildscript-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
45 | buildscript-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
46 | buildscript-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
47 | buildscript-publish = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "publish" }
48 |
49 | composeMaterialIcons = "org.jetbrains.compose.material:material-icons-core:1.7.3"
50 |
51 | detektCompose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
52 | detektEygraber-formatting = { module = "com.eygraber.detekt.rules:formatting", version.ref = "detektEygraber" }
53 | detektEygraber-style = { module = "com.eygraber.detekt.rules:style", version.ref = "detektEygraber" }
54 |
55 | # not actually used; just here so renovate picks it up
56 | ktlint = { module = "com.pinterest.ktlint:ktlint-bom", version.ref = "ktlint" }
57 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 | # SPDX-License-Identifier: Apache-2.0
19 | #
20 |
21 | ##############################################################################
22 | #
23 | # Gradle start up script for POSIX generated by Gradle.
24 | #
25 | # Important for running:
26 | #
27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28 | # noncompliant, but you have some other compliant shell such as ksh or
29 | # bash, then to run this script, type that shell name before the whole
30 | # command line, like:
31 | #
32 | # ksh Gradle
33 | #
34 | # Busybox and similar reduced shells will NOT work, because this script
35 | # requires all of these POSIX shell features:
36 | # * functions;
37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39 | # * compound commands having a testable exit status, especially «case»;
40 | # * various built-in commands including «command», «set», and «ulimit».
41 | #
42 | # Important for patching:
43 | #
44 | # (2) This script targets any POSIX shell, so it avoids extensions provided
45 | # by Bash, Ksh, etc; in particular arrays are avoided.
46 | #
47 | # The "traditional" practice of packing multiple parameters into a
48 | # space-separated string is a well documented source of bugs and security
49 | # problems, so this is (mostly) avoided, by progressively accumulating
50 | # options in "$@", and eventually passing that to Java.
51 | #
52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54 | # see the in-line comments for details.
55 | #
56 | # There are tweaks for specific operating systems such as AIX, CygWin,
57 | # Darwin, MinGW, and NonStop.
58 | #
59 | # (3) This script is generated from the Groovy template
60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61 | # within the Gradle project.
62 | #
63 | # You can find Gradle at https://github.com/gradle/gradle/.
64 | #
65 | ##############################################################################
66 |
67 | # Attempt to set APP_HOME
68 |
69 | # Resolve links: $0 may be a link
70 | app_path=$0
71 |
72 | # Need this for daisy-chained symlinks.
73 | while
74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75 | [ -h "$app_path" ]
76 | do
77 | ls=$( ls -ld "$app_path" )
78 | link=${ls#*' -> '}
79 | case $link in #(
80 | /*) app_path=$link ;; #(
81 | *) app_path=$APP_HOME$link ;;
82 | esac
83 | done
84 |
85 | # This is normally unused
86 | # shellcheck disable=SC2034
87 | APP_BASE_NAME=${0##*/}
88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH="\\\"\\\""
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | if ! command -v java >/dev/null 2>&1
137 | then
138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
139 |
140 | Please set the JAVA_HOME variable in your environment to match the
141 | location of your Java installation."
142 | fi
143 | fi
144 |
145 | # Increase the maximum file descriptors if we can.
146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
147 | case $MAX_FD in #(
148 | max*)
149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
150 | # shellcheck disable=SC2039,SC3045
151 | MAX_FD=$( ulimit -H -n ) ||
152 | warn "Could not query maximum file descriptor limit"
153 | esac
154 | case $MAX_FD in #(
155 | '' | soft) :;; #(
156 | *)
157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
158 | # shellcheck disable=SC2039,SC3045
159 | ulimit -n "$MAX_FD" ||
160 | warn "Could not set maximum file descriptor limit to $MAX_FD"
161 | esac
162 | fi
163 |
164 | # Collect all arguments for the java command, stacking in reverse order:
165 | # * args from the command line
166 | # * the main class name
167 | # * -classpath
168 | # * -D...appname settings
169 | # * --module-path (only if needed)
170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
171 |
172 | # For Cygwin or MSYS, switch paths to Windows format before running java
173 | if "$cygwin" || "$msys" ; then
174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
176 |
177 | JAVACMD=$( cygpath --unix "$JAVACMD" )
178 |
179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
180 | for arg do
181 | if
182 | case $arg in #(
183 | -*) false ;; # don't mess with options #(
184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
185 | [ -e "$t" ] ;; #(
186 | *) false ;;
187 | esac
188 | then
189 | arg=$( cygpath --path --ignore --mixed "$arg" )
190 | fi
191 | # Roll the args list around exactly as many times as the number of
192 | # args, so each arg winds up back in the position where it started, but
193 | # possibly modified.
194 | #
195 | # NB: a `for` loop captures its iteration list before it begins, so
196 | # changing the positional parameters here affects neither the number of
197 | # iterations, nor the values presented in `arg`.
198 | shift # remove old arg
199 | set -- "$@" "$arg" # push replacement arg
200 | done
201 | fi
202 |
203 |
204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
206 |
207 | # Collect all arguments for the java command:
208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
209 | # and any embedded shellness will be escaped.
210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
211 | # treated as '${Hostname}' itself on the command line.
212 |
213 | set -- \
214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
215 | -classpath "$CLASSPATH" \
216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
217 | "$@"
218 |
219 | # Stop when "xargs" is not available.
220 | if ! command -v xargs >/dev/null 2>&1
221 | then
222 | die "xargs is not available"
223 | fi
224 |
225 | # Use "xargs" to parse quoted args.
226 | #
227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
228 | #
229 | # In Bash we could simply go:
230 | #
231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
232 | # set -- "${ARGS[@]}" "$@"
233 | #
234 | # but POSIX shell has neither arrays nor command substitution, so instead we
235 | # post-process each arg (as a line of input to sed) to backslash-escape any
236 | # character that might be a shell metacharacter, then use eval to reverse
237 | # that process (while maintaining the separation between arguments), and wrap
238 | # the whole thing up as a single "set" statement.
239 | #
240 | # This will of course break if any of these variables contains a newline or
241 | # an unmatched quote.
242 | #
243 |
244 | eval "set -- $(
245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
246 | xargs -n1 |
247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
248 | tr '\n' ' '
249 | )" '"$@"'
250 |
251 | exec "$JAVACMD" "$@"
252 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/library/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.eygraber.conventions-kotlin-multiplatform")
3 | id("com.eygraber.conventions-android-library")
4 | id("com.eygraber.conventions-compose-jetbrains")
5 | id("com.eygraber.conventions-detekt")
6 | id("com.eygraber.conventions-publish-maven-central")
7 | }
8 |
9 | android {
10 | namespace = "com.eygraber.compose.colorpicker"
11 | }
12 |
13 | kotlin {
14 | explicitApi()
15 |
16 | defaultKmpTargets(
17 | project = project,
18 | )
19 |
20 | sourceSets {
21 | commonMain {
22 | dependencies {
23 | implementation(compose.foundation)
24 | implementation(compose.material3)
25 | implementation(compose.runtime)
26 | }
27 | }
28 |
29 | commonTest {
30 | dependencies {
31 | implementation(kotlin("test-common"))
32 | implementation(kotlin("test-annotations-common"))
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/library/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_ARTIFACT_ID=compose-color-picker
2 | POM_NAME=Compose Color Picker
3 | POM_DESCRIPTION=A Jetpack Compose color picking component
4 |
--------------------------------------------------------------------------------
/library/src/commonMain/kotlin/com/eygraber/compose/colorpicker/ColorPicker.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.animation.EnterTransition
5 | import androidx.compose.animation.ExitTransition
6 | import androidx.compose.foundation.Image
7 | import androidx.compose.foundation.gestures.awaitEachGesture
8 | import androidx.compose.foundation.gestures.awaitFirstDown
9 | import androidx.compose.foundation.gestures.drag
10 | import androidx.compose.foundation.layout.Box
11 | import androidx.compose.foundation.layout.BoxWithConstraints
12 | import androidx.compose.foundation.layout.PaddingValues
13 | import androidx.compose.foundation.layout.aspectRatio
14 | import androidx.compose.foundation.shape.GenericShape
15 | import androidx.compose.runtime.Composable
16 | import androidx.compose.runtime.Immutable
17 | import androidx.compose.runtime.getValue
18 | import androidx.compose.runtime.mutableIntStateOf
19 | import androidx.compose.runtime.mutableStateOf
20 | import androidx.compose.runtime.remember
21 | import androidx.compose.runtime.setValue
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.geometry.Offset
24 | import androidx.compose.ui.graphics.Color
25 | import androidx.compose.ui.graphics.isSpecified
26 | import androidx.compose.ui.input.pointer.pointerInput
27 | import androidx.compose.ui.unit.Dp
28 | import androidx.compose.ui.unit.dp
29 |
30 | @Immutable
31 | public data class ColorPickerState(
32 | val alpha: Float,
33 | val brightness: Float,
34 | val magnifier: ColorPicker.Magnifier,
35 | @Suppress("BooleanPropertyNaming") val resetSelectedPosition: Boolean,
36 | val colors: List,
37 | )
38 |
39 | @Composable
40 | public fun rememberColorPickerState(
41 | alpha: Float = 1F,
42 | brightness: Float = 1F,
43 | magnifier: ColorPicker.Magnifier = ColorPicker.Magnifier.Default(),
44 | resetSelectedPosition: Boolean = false,
45 | @Suppress("UnstableCollections") colors: List = ColorWheel.DefaultColors,
46 | ): ColorPickerState =
47 | remember(alpha, brightness, magnifier, resetSelectedPosition, colors) {
48 | ColorPickerState(
49 | alpha,
50 | brightness,
51 | magnifier,
52 | resetSelectedPosition,
53 | colors,
54 | )
55 | }
56 |
57 | @Composable
58 | public fun ColorPicker(
59 | onSelectedColor: (Color) -> Unit,
60 | modifier: Modifier = Modifier,
61 | state: ColorPickerState = rememberColorPickerState(),
62 | ) {
63 | BoxWithConstraints(
64 | modifier = modifier
65 | .aspectRatio(1F),
66 | ) {
67 | val diameter = constraints.maxWidth
68 | val radius = diameter / 2F
69 |
70 | var previousDiameter by remember { mutableIntStateOf(diameter) }
71 | var selectedPosition by remember { mutableStateOf(Offset.Zero) }
72 | var selectedPositionBeforeReset by remember { mutableStateOf(Offset.Zero) }
73 | var selectedColor by remember { mutableStateOf(Color.Unspecified) }
74 |
75 | val isSelectedAndDiameterChanged =
76 | selectedPosition != Offset.Zero && diameter != previousDiameter
77 |
78 | if(state.resetSelectedPosition) {
79 | if(selectedPosition != Offset.Zero) {
80 | selectedPositionBeforeReset = selectedPosition
81 | }
82 | selectedPosition = Offset.Zero
83 | }
84 | else if(isSelectedAndDiameterChanged) {
85 | selectedPosition = selectedPosition.translate(
86 | newDiameter = diameter,
87 | oldDiameter = previousDiameter,
88 | )
89 |
90 | previousDiameter = diameter
91 | }
92 |
93 | val colorWheel = remember(diameter, state.alpha, state.brightness) {
94 | ColorWheel(
95 | diameter = diameter,
96 | radius = radius,
97 | alpha = state.alpha,
98 | brightness = state.brightness,
99 | colors = state.colors,
100 | ).apply {
101 | val currentColor = colorForPosition(selectedPosition)
102 | if(currentColor.isSpecified && currentColor != selectedColor) {
103 | selectedColor = currentColor
104 | onSelectedColor(currentColor)
105 | }
106 | }
107 | }
108 |
109 | val inputModifier = Modifier
110 | .pointerInput(diameter, state.alpha, state.brightness) {
111 | fun update(newPosition: Offset) {
112 | val clampedPosition = newPosition.clampToCircle(radius)
113 | if(clampedPosition != selectedPosition) {
114 | val newColor = colorWheel.colorForPosition(clampedPosition)
115 | if(newColor.isSpecified) {
116 | if(selectedColor != newColor) {
117 | selectedColor = newColor
118 | onSelectedColor(newColor)
119 | }
120 | selectedPosition = clampedPosition
121 | }
122 | }
123 | }
124 |
125 | awaitEachGesture {
126 | val down = awaitFirstDown()
127 | update(down.position)
128 | drag(down.id) { change ->
129 | change.consume()
130 | update(change.position)
131 | }
132 | }
133 | }
134 |
135 | Box(
136 | modifier = inputModifier,
137 | ) {
138 | Image(contentDescription = null, bitmap = colorWheel.image)
139 |
140 | if(state.magnifier is ColorPicker.Magnifier.Default) {
141 | val isMagnifierVisible = selectedColor.isSpecified && selectedPosition != Offset.Zero
142 | AnimatedVisibility(
143 | visible = isMagnifierVisible,
144 | enter = EnterTransition.None,
145 | exit = ExitTransition.None,
146 | ) {
147 | Magnifier(
148 | transitionData = updateMagnifierTransitionData(state.magnifier),
149 | magnifier = state.magnifier,
150 | position = {
151 | when(selectedPosition) {
152 | Offset.Zero -> selectedPositionBeforeReset
153 | else -> selectedPosition
154 | }
155 | },
156 | color = { selectedColor },
157 | )
158 | }
159 | }
160 | }
161 | }
162 | }
163 |
164 | @Suppress("ktlint:standard:max-line-length")
165 | @Deprecated(
166 | message = "Use ColorPicker(Modifier, ColorPickerState, (Color) -> Unit)",
167 | level = DeprecationLevel.WARNING,
168 | replaceWith = ReplaceWith(
169 | "ColorPicker(modifier = modifier, state = rememberColorPickerState(alpha = alpha, brightness = brightness, magnifier = magnifier, resetSelectedPosition = resetSelectedPosition, ), onColorSelected, )",
170 | ),
171 | )
172 | @Composable
173 | public fun ColorPicker(
174 | onSelectedColor: (Color) -> Unit,
175 | modifier: Modifier = Modifier,
176 | alpha: Float = 1F,
177 | brightness: Float = 1F,
178 | magnifier: ColorPicker.Magnifier = ColorPicker.Magnifier.Default(),
179 | resetSelectedPosition: Boolean = false,
180 | ) {
181 | ColorPicker(
182 | onSelectedColor = onSelectedColor,
183 | modifier = modifier,
184 | state = rememberColorPickerState(
185 | alpha = alpha,
186 | brightness = brightness,
187 | magnifier = magnifier,
188 | resetSelectedPosition = resetSelectedPosition,
189 | ),
190 | )
191 | }
192 |
193 | public object ColorPicker {
194 | @Immutable
195 | public sealed class Magnifier {
196 | @Immutable
197 | public data object None : Magnifier()
198 |
199 | @Immutable
200 | public data class Default(
201 | val width: Dp = 150.dp,
202 | val height: Dp = 100.dp,
203 | val pillHeight: Dp = 50.dp,
204 | val pillColorWidthWeight: Float = .25F,
205 | val pillHexWidthWeight: Float = .75F,
206 | val shouldShowAlphaHex: Boolean = true,
207 | // more padding on bottom to account for the default shape
208 | val hexPadding: PaddingValues = PaddingValues(
209 | end = 5.dp,
210 | top = 10.dp,
211 | bottom = 20.dp,
212 | ),
213 | val selectionDiameter: Dp = 15.dp,
214 | val popupShape: GenericShape = MagnifierPopupShape,
215 | ) : Magnifier()
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/library/src/commonMain/kotlin/com/eygraber/compose/colorpicker/ColorWheel.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.graphics.Canvas
5 | import androidx.compose.ui.graphics.Color
6 | import androidx.compose.ui.graphics.ImageBitmap
7 | import androidx.compose.ui.graphics.Paint
8 | import androidx.compose.ui.graphics.RadialGradientShader
9 | import androidx.compose.ui.graphics.SweepGradientShader
10 | import androidx.compose.ui.graphics.toPixelMap
11 |
12 | /**
13 | * A color wheel with an [ImageBitmap] that draws a circular color wheel of the specified diameter.
14 | */
15 | internal class ColorWheel(
16 | diameter: Int,
17 | radius: Float,
18 | alpha: Float,
19 | brightness: Float,
20 | colors: List,
21 | ) {
22 | private fun Color.applyBrightnessAndAlpha(alpha: Float, brightness: Float) =
23 | copy(
24 | red = red * brightness,
25 | green = green * brightness,
26 | blue = blue * brightness,
27 | alpha = alpha,
28 | )
29 |
30 | private val sweepGradient = SweepGradientShader(
31 | colors = colors.map { it.applyBrightnessAndAlpha(alpha, brightness) },
32 | colorStops = null,
33 | center = Offset(radius, radius),
34 | )
35 |
36 | private val saturationGradient = RadialGradientShader(
37 | colors = listOf(
38 | Color.White.applyBrightnessAndAlpha(alpha, brightness),
39 | Color.Transparent,
40 | ),
41 | center = Offset(radius, radius),
42 | radius = radius,
43 | )
44 |
45 | val image = ImageBitmap(diameter, diameter).also { imageBitmap ->
46 | val canvas = Canvas(imageBitmap)
47 | val center = Offset(radius, radius)
48 | val sweepPaint = Paint().apply {
49 | shader = sweepGradient
50 | }
51 |
52 | val saturationPaint = Paint().apply {
53 | shader = saturationGradient
54 | }
55 |
56 | canvas.drawCircle(center, radius, sweepPaint)
57 | canvas.drawCircle(center, radius, saturationPaint)
58 | }
59 |
60 | private val pixelMap by lazy {
61 | image.toPixelMap()
62 | }
63 |
64 | /**
65 | * @return the matching color for [position] inside [ColorWheel], or `null` if there is no color
66 | * or the color is partially transparent.
67 | */
68 | fun colorForPosition(position: Offset): Color {
69 | val x = position.x.toInt()
70 | val y = position.y.toInt()
71 | with(pixelMap) {
72 | if(x !in 0 until width || y !in 0 until height) return Color.Unspecified
73 | return this[x, y].takeIf { it.alpha > 0F } ?: Color.Unspecified
74 | }
75 | }
76 |
77 | companion object {
78 | val DefaultColors = listOf(
79 | Color.Red,
80 | Color.Magenta,
81 | Color.Blue,
82 | Color.Cyan,
83 | Color.Green,
84 | Color.Yellow,
85 | Color.Red,
86 | )
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/library/src/commonMain/kotlin/com/eygraber/compose/colorpicker/Magnifier.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker
2 |
3 | import androidx.compose.foundation.BorderStroke
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.border
6 | import androidx.compose.foundation.layout.Box
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.Row
9 | import androidx.compose.foundation.layout.RowScope
10 | import androidx.compose.foundation.layout.height
11 | import androidx.compose.foundation.layout.offset
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.requiredHeight
14 | import androidx.compose.foundation.layout.requiredSize
15 | import androidx.compose.foundation.layout.requiredWidth
16 | import androidx.compose.foundation.layout.width
17 | import androidx.compose.foundation.shape.CircleShape
18 | import androidx.compose.foundation.shape.GenericShape
19 | import androidx.compose.material3.LocalTextStyle
20 | import androidx.compose.material3.Surface
21 | import androidx.compose.material3.Text
22 | import androidx.compose.runtime.Composable
23 | import androidx.compose.ui.Alignment
24 | import androidx.compose.ui.Modifier
25 | import androidx.compose.ui.draw.alpha
26 | import androidx.compose.ui.draw.clip
27 | import androidx.compose.ui.draw.drawBehind
28 | import androidx.compose.ui.geometry.CornerRadius
29 | import androidx.compose.ui.geometry.Offset
30 | import androidx.compose.ui.geometry.RoundRect
31 | import androidx.compose.ui.graphics.Color
32 | import androidx.compose.ui.graphics.SolidColor
33 | import androidx.compose.ui.text.style.TextAlign
34 | import androidx.compose.ui.unit.IntOffset
35 | import androidx.compose.ui.unit.dp
36 |
37 | @Composable
38 | internal fun Magnifier(
39 | transitionData: MagnifierTransitionData,
40 | magnifier: ColorPicker.Magnifier.Default,
41 | position: () -> Offset,
42 | color: () -> Color,
43 | ) {
44 | val offsetModifier = Modifier.offset {
45 | val (x, y) = position()
46 |
47 | IntOffset(
48 | (x - magnifier.width.roundToPx() / 2).toInt(),
49 | // Align with the center of the selection circle
50 | (y - (magnifier.height.roundToPx() + magnifier.selectionDiameter.roundToPx()) / 2).toInt(),
51 | )
52 | }
53 |
54 | Column(
55 | horizontalAlignment = Alignment.CenterHorizontally,
56 | modifier = offsetModifier
57 | .requiredHeight(magnifier.height)
58 | .requiredWidth(magnifier.width)
59 | .alpha(transitionData.alpha),
60 | ) {
61 | MagnifierPill(
62 | magnifier,
63 | color,
64 | modifier = Modifier.width(transitionData.pillWidth),
65 | )
66 |
67 | MagnifierSelectionCircle(
68 | modifier = Modifier.requiredSize(transitionData.selectionDiameter),
69 | )
70 | }
71 | }
72 |
73 | /**
74 | * Label representing the currently selected [colorProvider], with [Text] representing the hex code and a
75 | * square at the start showing the [colorProvider].
76 | */
77 | @Composable
78 | private fun MagnifierPill(
79 | options: ColorPicker.Magnifier.Default,
80 | colorProvider: () -> Color,
81 | modifier: Modifier = Modifier,
82 | ) {
83 | Surface(
84 | modifier = modifier,
85 | shape = options.popupShape,
86 | ) {
87 | Row {
88 | Box(
89 | modifier = Modifier
90 | .height(options.pillHeight)
91 | .weight(options.pillColorWidthWeight)
92 | .drawBehind {
93 | drawRect(colorProvider())
94 | },
95 | )
96 |
97 | MagnifierLabel(options, colorProvider)
98 | }
99 | }
100 | }
101 |
102 | @Composable
103 | private fun RowScope.MagnifierLabel(
104 | options: ColorPicker.Magnifier.Default,
105 | colorProvider: () -> Color,
106 | ) {
107 | val text = colorProvider().toHexString(options.shouldShowAlphaHex)
108 | val textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center)
109 | Text(
110 | text = text,
111 | modifier = Modifier
112 | .weight(options.pillHexWidthWeight)
113 | .padding(options.hexPadding),
114 | style = textStyle,
115 | maxLines = 1,
116 | )
117 | }
118 |
119 | /**
120 | * Selection circle drawn over the currently selected pixel of the color wheel.
121 | */
122 | @Composable
123 | private fun MagnifierSelectionCircle(
124 | modifier: Modifier = Modifier,
125 | ) {
126 | Box(
127 | modifier = modifier
128 | .clip(CircleShape)
129 | .background(Color.Transparent)
130 | .border(
131 | border = BorderStroke(2.dp, SolidColor(Color.Black.copy(alpha = 0.75f))),
132 | shape = CircleShape,
133 | ),
134 | )
135 | }
136 |
137 | /**
138 | * A [GenericShape] that draws a box with a triangle at the bottom center to indicate a popup.
139 | */
140 | internal val MagnifierPopupShape = GenericShape { size, _ ->
141 | val width = size.width
142 | val height = size.height
143 |
144 | val arrowY = height * 0.8f
145 | val arrowXOffset = width * 0.4f
146 |
147 | addRoundRect(RoundRect(0f, 0f, width, arrowY, cornerRadius = CornerRadius(20f, 20f)))
148 |
149 | moveTo(arrowXOffset, arrowY)
150 | lineTo(width / 2f, height)
151 | lineTo(width - arrowXOffset, arrowY)
152 | close()
153 | }
154 |
--------------------------------------------------------------------------------
/library/src/commonMain/kotlin/com/eygraber/compose/colorpicker/MagnifierTransitionData.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker
2 |
3 | import androidx.compose.animation.AnimatedVisibilityScope
4 | import androidx.compose.animation.EnterExitState
5 | import androidx.compose.animation.ExperimentalAnimationApi
6 | import androidx.compose.animation.core.animateDp
7 | import androidx.compose.animation.core.animateFloat
8 | import androidx.compose.animation.core.tween
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.State
11 | import androidx.compose.runtime.getValue
12 | import androidx.compose.runtime.remember
13 | import androidx.compose.ui.unit.Dp
14 | import androidx.compose.ui.unit.dp
15 |
16 | internal class MagnifierTransitionData(
17 | pillWidth: State,
18 | selectionDiameter: State,
19 | alpha: State,
20 | ) {
21 | val pillWidth: Dp by pillWidth
22 | val selectionDiameter: Dp by selectionDiameter
23 | val alpha: Float by alpha
24 | }
25 |
26 | @OptIn(ExperimentalAnimationApi::class)
27 | @Composable
28 | internal fun AnimatedVisibilityScope.updateMagnifierTransitionData(
29 | options: ColorPicker.Magnifier.Default,
30 | ): MagnifierTransitionData {
31 | val labelWidth = transition.animateDp(transitionSpec = { tween() }) {
32 | if(it == EnterExitState.Visible) options.width else 0.dp
33 | }
34 |
35 | val magnifierDiameter = transition.animateDp(transitionSpec = { tween() }) {
36 | if(it == EnterExitState.Visible) options.selectionDiameter else 0.dp
37 | }
38 |
39 | val alpha = transition.animateFloat(transitionSpec = { tween() }) {
40 | if(it == EnterExitState.Visible) 1f else 0f
41 | }
42 |
43 | return remember(transition) {
44 | MagnifierTransitionData(labelWidth, magnifierDiameter, alpha)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/library/src/commonMain/kotlin/com/eygraber/compose/colorpicker/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.graphics.Color
5 | import kotlin.math.hypot
6 |
7 | internal fun Color.toHexString(includeAlpha: Boolean = true): String = buildString {
8 | append("#")
9 | if(includeAlpha) {
10 | append((alpha * 255).toInt().toString(radix = 16).padStart(length = 2, padChar = '0').uppercase())
11 | }
12 | append((red * 255).toInt().toString(radix = 16).padStart(length = 2, padChar = '0').uppercase())
13 | append((green * 255).toInt().toString(radix = 16).padStart(length = 2, padChar = '0').uppercase())
14 | append((blue * 255).toInt().toString(radix = 16).padStart(length = 2, padChar = '0').uppercase())
15 | }
16 |
17 | internal fun Offset.translate(
18 | newDiameter: Int,
19 | oldDiameter: Int,
20 | ): Offset {
21 | val multiplier = newDiameter / oldDiameter.toFloat()
22 | return Offset(
23 | x = x * multiplier,
24 | y = y * multiplier,
25 | )
26 | }
27 |
28 | internal fun Offset.clampToCircle(radius: Float): Offset {
29 | val dx = x - radius
30 | val dy = y - radius
31 | val d = hypot(dx, dy)
32 | return when {
33 | d > radius -> Offset(
34 | x = radius + dx * (radius - 2) / d,
35 | y = radius + dy * (radius - 2) / d,
36 | )
37 | else -> this
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "enabledManagers": ["gradle", "gradle-wrapper", "github-actions"],
6 | "labels": ["dependencies"],
7 | "prHourlyLimit": 3,
8 | "packageRules": [
9 | {
10 | "groupName": "gradle-conventions",
11 | "matchPackagePrefixes": ["com.eygraber.conventions"],
12 | "automerge": true
13 | },
14 | {
15 | "groupName": "gradle-develocity-plugin",
16 | "matchPackagePrefixes": ["com.gradle.develocity"],
17 | "automerge": true,
18 | "registryUrls": [
19 | "https://plugins.gradle.org/m2"
20 | ]
21 | },
22 | {
23 | "matchDatasources": ["maven"],
24 | "depType": "dependencies",
25 | "registryUrls": [
26 | "https://repo.maven.apache.org/maven2/",
27 | "https://dl.google.com/dl/android/maven2/",
28 | "https://plugins.gradle.org/m2"
29 | ]
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/sample/android-app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/sample/android-app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("android")
3 | id("com.android.application")
4 | id("com.eygraber.conventions-kotlin-library")
5 | id("com.eygraber.conventions-detekt")
6 | id("com.eygraber.conventions-compose-jetbrains")
7 | }
8 |
9 | android {
10 | namespace = "com.eygraber.compose.colorpicker.sample"
11 |
12 | compileSdk = libs.versions.android.sdk.compile.get().toInt()
13 |
14 | defaultConfig {
15 | applicationId = "com.eygraber.compose.colorpicker.sample"
16 | targetSdk = libs.versions.android.sdk.target.get().toInt()
17 | minSdk = libs.versions.android.sdk.min.get().toInt()
18 |
19 | versionCode = 1
20 | versionName = "1.0.0"
21 |
22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
23 | multiDexEnabled = true
24 | }
25 |
26 | buildTypes {
27 | named("release") {
28 | isMinifyEnabled = true
29 | isShrinkResources = true
30 | proguardFiles.clear()
31 | proguardFiles += project.file("proguard-rules.pro")
32 | }
33 |
34 | named("debug") {
35 | applicationIdSuffix = ".debug"
36 |
37 | versionNameSuffix = "-DEBUG"
38 |
39 | isMinifyEnabled = false
40 | }
41 | }
42 |
43 | compileOptions {
44 | isCoreLibraryDesugaringEnabled = true
45 | }
46 |
47 | packaging {
48 | resources.pickFirsts += "META-INF/*"
49 | }
50 |
51 | dependencies {
52 | implementation(projects.sample.shared)
53 |
54 | coreLibraryDesugaring(libs.android.desugar)
55 |
56 | implementation(libs.androidx.activity)
57 | implementation(libs.androidx.activityCompose)
58 | implementation(libs.androidx.appCompat)
59 |
60 | implementation(libs.androidx.compose.foundation)
61 | implementation(libs.androidx.compose.material3)
62 | implementation(libs.androidx.compose.uiTooling)
63 | implementation(libs.androidx.compose.uiToolingPreview)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/sample/android-app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/java/com/eygraber/compose/colorpicker/sample/SampleActivity.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | import android.os.Bundle
4 | import androidx.activity.compose.setContent
5 | import androidx.appcompat.app.AppCompatActivity
6 |
7 | class SampleActivity : AppCompatActivity() {
8 | override fun onCreate(savedInstanceState: Bundle?) {
9 | super.onCreate(savedInstanceState)
10 |
11 | setContent {
12 | Sample()
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eygraber/compose-color-picker/1219d4362c706cf5e44d82b51b0b9c04df4baa1a/sample/android-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
11 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Compose Color Picker Sample
3 |
4 |
--------------------------------------------------------------------------------
/sample/android-app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/sample/jvm-app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | kotlin("jvm")
3 | id("com.eygraber.conventions-compose-jetbrains")
4 | id("com.eygraber.conventions-kotlin-library")
5 | id("com.eygraber.conventions-detekt")
6 | }
7 |
8 | kotlin {
9 | dependencies {
10 | implementation(compose.desktop.currentOs)
11 | implementation(projects.sample.shared)
12 | }
13 | }
14 |
15 | compose.desktop {
16 | application {
17 | mainClass = "com.eygraber.compose.colorpicker.sample.ColorPickerJvmKt"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sample/jvm-app/src/main/kotlin/com/eygraber/compose/colorpicker/sample/ColorPickerJvm.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | import androidx.compose.ui.window.singleWindowApplication
4 |
5 | fun main() {
6 | singleWindowApplication(title = "Color Picker Sample") {
7 | Sample()
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/sample/shared/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.eygraber.conventions-kotlin-multiplatform")
3 | id("com.eygraber.conventions-android-library")
4 | id("com.eygraber.conventions-compose-jetbrains")
5 | id("com.eygraber.conventions-detekt")
6 | }
7 |
8 | android {
9 | namespace = "com.eygraber.compose.colorpicker.sample.shared"
10 | }
11 |
12 | kotlin {
13 | kmpTargets(
14 | KmpTarget.Android,
15 | KmpTarget.Js,
16 | KmpTarget.Jvm,
17 | KmpTarget.WasmJs,
18 | project = project,
19 | ignoreDefaultTargets = true,
20 | )
21 |
22 | sourceSets {
23 | commonMain {
24 | dependencies {
25 | implementation(projects.library)
26 |
27 | api(compose.foundation)
28 | api(compose.material3)
29 | api(compose.runtime)
30 |
31 | api(libs.composeMaterialIcons)
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sample/shared/src/androidMain/kotlin/com/eygraber/compose/colorpicker/sample/FixedDecimalCount.android.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | import java.text.DecimalFormat
4 |
5 | private val digitCache = mutableMapOf()
6 |
7 | actual fun Float.toFixedDecimalCount(digits: Int): String =
8 | digitCache.getOrPut(digits) { DecimalFormat("0.${"0".repeat(digits)}") }.format(this)
9 |
--------------------------------------------------------------------------------
/sample/shared/src/commonMain/kotlin/com/eygraber/compose/colorpicker/sample/FixedDecimalCount.common.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | expect fun Float.toFixedDecimalCount(digits: Int): String
4 |
--------------------------------------------------------------------------------
/sample/shared/src/commonMain/kotlin/com/eygraber/compose/colorpicker/sample/RefreshIcon.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | import androidx.compose.material.icons.materialIcon
4 | import androidx.compose.material.icons.materialPath
5 |
6 | internal val RefreshIcon by lazy {
7 | materialIcon(name = "Rounded.Refresh") {
8 | materialPath {
9 | moveTo(17.65f, 6.35f)
10 | curveToRelative(-1.63f, -1.63f, -3.94f, -2.57f, -6.48f, -2.31f)
11 | curveToRelative(-3.67f, 0.37f, -6.69f, 3.35f, -7.1f, 7.02f)
12 | curveTo(3.52f, 15.91f, 7.27f, 20.0f, 12.0f, 20.0f)
13 | curveToRelative(3.19f, 0.0f, 5.93f, -1.87f, 7.21f, -4.56f)
14 | curveToRelative(0.32f, -0.67f, -0.16f, -1.44f, -0.9f, -1.44f)
15 | curveToRelative(-0.37f, 0.0f, -0.72f, 0.2f, -0.88f, 0.53f)
16 | curveToRelative(-1.13f, 2.43f, -3.84f, 3.97f, -6.8f, 3.31f)
17 | curveToRelative(-2.22f, -0.49f, -4.01f, -2.3f, -4.48f, -4.52f)
18 | curveTo(5.31f, 9.44f, 8.26f, 6.0f, 12.0f, 6.0f)
19 | curveToRelative(1.66f, 0.0f, 3.14f, 0.69f, 4.22f, 1.78f)
20 | lineToRelative(-1.51f, 1.51f)
21 | curveToRelative(-0.63f, 0.63f, -0.19f, 1.71f, 0.7f, 1.71f)
22 | horizontalLineTo(19.0f)
23 | curveToRelative(0.55f, 0.0f, 1.0f, -0.45f, 1.0f, -1.0f)
24 | verticalLineTo(6.41f)
25 | curveToRelative(0.0f, -0.89f, -1.08f, -1.34f, -1.71f, -0.71f)
26 | lineToRelative(-0.64f, 0.65f)
27 | close()
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/shared/src/commonMain/kotlin/com/eygraber/compose/colorpicker/sample/Sample.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.BoxScope
7 | import androidx.compose.foundation.layout.Column
8 | import androidx.compose.foundation.layout.ColumnScope
9 | import androidx.compose.foundation.layout.Row
10 | import androidx.compose.foundation.layout.fillMaxSize
11 | import androidx.compose.foundation.layout.fillMaxWidth
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.size
14 | import androidx.compose.foundation.layout.width
15 | import androidx.compose.foundation.shape.CornerSize
16 | import androidx.compose.material3.Card
17 | import androidx.compose.material3.Icon
18 | import androidx.compose.material3.IconButton
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Slider
21 | import androidx.compose.material3.SliderDefaults
22 | import androidx.compose.material3.Text
23 | import androidx.compose.material3.darkColorScheme
24 | import androidx.compose.runtime.Composable
25 | import androidx.compose.runtime.getValue
26 | import androidx.compose.runtime.mutableFloatStateOf
27 | import androidx.compose.runtime.mutableStateOf
28 | import androidx.compose.runtime.remember
29 | import androidx.compose.runtime.setValue
30 | import androidx.compose.ui.Alignment
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.graphics.Color
33 | import androidx.compose.ui.graphics.isSpecified
34 | import androidx.compose.ui.unit.dp
35 | import com.eygraber.compose.colorpicker.ColorPicker
36 | import com.eygraber.compose.colorpicker.rememberColorPickerState
37 | import kotlinx.coroutines.DelicateCoroutinesApi
38 | import kotlinx.coroutines.GlobalScope
39 | import kotlinx.coroutines.delay
40 | import kotlinx.coroutines.launch
41 |
42 | @OptIn(DelicateCoroutinesApi::class)
43 | @Composable
44 | fun Sample(
45 | modifier: Modifier = Modifier,
46 | ) {
47 | SampleCard(
48 | modifier = modifier,
49 | ) {
50 | val defaultColor = MaterialTheme.colorScheme.onSurface
51 |
52 | var selectedColor by remember { mutableStateOf(defaultColor) }
53 | var alpha by remember { mutableFloatStateOf(1F) }
54 | var brightness by remember { mutableFloatStateOf(1F) }
55 | var shouldResetSelectedPosition by remember { mutableStateOf(false) }
56 |
57 | Column(
58 | modifier = Modifier.fillMaxSize(),
59 | horizontalAlignment = Alignment.CenterHorizontally,
60 | ) {
61 | Controls(
62 | selectedColor = selectedColor,
63 | alpha = alpha,
64 | brightness = brightness,
65 | defaultColor = defaultColor,
66 | onSelectedColorChange = { newSelectedColor ->
67 | selectedColor = newSelectedColor
68 | },
69 | onAlphaChange = { newAlpha ->
70 | alpha = newAlpha
71 | },
72 | onBrightnessChange = { newBrightness ->
73 | brightness = newBrightness
74 | },
75 | resetSelectedPosition = {
76 | shouldResetSelectedPosition = true
77 |
78 | GlobalScope.launch {
79 | delay(500)
80 | shouldResetSelectedPosition = false
81 | }
82 | },
83 | )
84 |
85 | ColorPicker(
86 | onSelectedColor = { newSelectedColor ->
87 | selectedColor = when {
88 | newSelectedColor.isSpecified -> newSelectedColor
89 | else -> defaultColor
90 | }
91 | },
92 | modifier = Modifier.weight(.66F),
93 | state = rememberColorPickerState(
94 | alpha = alpha,
95 | brightness = brightness,
96 | resetSelectedPosition = shouldResetSelectedPosition,
97 | ),
98 | )
99 | }
100 | }
101 | }
102 |
103 | @Composable
104 | private fun ColumnScope.Controls(
105 | selectedColor: Color,
106 | defaultColor: Color,
107 | alpha: Float,
108 | brightness: Float,
109 | onSelectedColorChange: (Color) -> Unit,
110 | onAlphaChange: (Float) -> Unit,
111 | onBrightnessChange: (Float) -> Unit,
112 | resetSelectedPosition: () -> Unit,
113 | ) {
114 | Row(
115 | modifier = Modifier.weight(.33F).fillMaxWidth(),
116 | horizontalArrangement = Arrangement.SpaceBetween,
117 | ) {
118 | Column(
119 | verticalArrangement = Arrangement.SpaceAround,
120 | ) {
121 | Row(
122 | verticalAlignment = Alignment.CenterVertically,
123 | ) {
124 | Text(
125 | text = "Pick a color",
126 | color = selectedColor,
127 | )
128 |
129 | IconButton(
130 | onClick = {
131 | onSelectedColorChange(defaultColor)
132 | onAlphaChange(1F)
133 | onBrightnessChange(1F)
134 | resetSelectedPosition()
135 | },
136 | ) {
137 | Icon(
138 | imageVector = RefreshIcon,
139 | contentDescription = "Back to defaults",
140 | )
141 | }
142 | }
143 |
144 | Box(
145 | modifier = Modifier
146 | .size(75.dp)
147 | .background(selectedColor),
148 | )
149 | }
150 |
151 | Column(
152 | modifier = Modifier.align(Alignment.CenterVertically),
153 | ) {
154 | Text(text = "Alpha: ${alpha.toFixedDecimalCount(1)}")
155 |
156 | Slider(
157 | value = alpha,
158 | onValueChange = {
159 | onAlphaChange(it)
160 | },
161 | valueRange = 0F..1F,
162 | steps = 0,
163 | colors = SliderDefaults.colors(thumbColor = selectedColor, activeTrackColor = selectedColor),
164 | modifier = Modifier.width(200.dp),
165 | )
166 |
167 | Text(text = "Brightness: ${brightness.toFixedDecimalCount(1)}")
168 |
169 | Slider(
170 | value = brightness,
171 | onValueChange = {
172 | onBrightnessChange(it)
173 | },
174 | valueRange = 0F..1F,
175 | steps = 0,
176 | colors = SliderDefaults.colors(thumbColor = selectedColor, activeTrackColor = selectedColor),
177 | modifier = Modifier.width(200.dp),
178 | )
179 | }
180 | }
181 | }
182 |
183 | @Composable
184 | private fun SampleCard(
185 | modifier: Modifier = Modifier,
186 | content: @Composable BoxScope.() -> Unit,
187 | ) {
188 | MaterialTheme(
189 | colorScheme = darkColorScheme(),
190 | ) {
191 | Box(
192 | modifier = modifier
193 | .fillMaxSize()
194 | .background(
195 | color = MaterialTheme.colorScheme.background,
196 | ),
197 | ) {
198 | Card(
199 | shape = MaterialTheme.shapes.small.copy(all = CornerSize(8.dp)),
200 | modifier = Modifier
201 | .fillMaxSize()
202 | .padding(16.dp),
203 | ) {
204 | Box(
205 | modifier = Modifier.padding(16.dp),
206 | ) {
207 | content()
208 | }
209 | }
210 | }
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/sample/shared/src/jsMain/kotlin/com/eygraber/compose/colorpicker/sample/FixedDecimalCount.js.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | actual fun Float.toFixedDecimalCount(digits: Int): String = asDynamic().toFixed(digits).toString()
4 |
--------------------------------------------------------------------------------
/sample/shared/src/jvmMain/kotlin/com/eygraber/compose/colorpicker/sample/FixedDecimalCount.jvm.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | import java.text.DecimalFormat
4 |
5 | private val digitCache = mutableMapOf()
6 |
7 | actual fun Float.toFixedDecimalCount(digits: Int): String =
8 | digitCache.getOrPut(digits) { DecimalFormat("0.${"0".repeat(digits)}") }.format(this)
9 |
--------------------------------------------------------------------------------
/sample/shared/src/wasmJsMain/kotlin/com/eygraber/compose/colorpicker/sample/FixedDecimalCount.wasm.kt:
--------------------------------------------------------------------------------
1 | package com.eygraber.compose.colorpicker.sample
2 |
3 | @JsFun("(f, d) => f.toFixed(d)")
4 | external fun toFixed(f: Float, d: Int): String
5 |
6 | actual fun Float.toFixedDecimalCount(digits: Int): String = toFixed(this, digits)
7 |
--------------------------------------------------------------------------------
/sample/webApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
2 |
3 | plugins {
4 | id("com.eygraber.conventions-kotlin-multiplatform")
5 | id("com.eygraber.conventions-detekt")
6 | id("com.eygraber.conventions-compose-jetbrains")
7 | }
8 |
9 | kotlin {
10 | kmpTargets(
11 | KmpTarget.WasmJs,
12 | project = project,
13 | binaryType = BinaryType.Executable,
14 | webOptions = KmpTarget.WebOptions(
15 | isNodeEnabled = false,
16 | isBrowserEnabled = true,
17 | moduleName = "color-picker-wasm",
18 | ),
19 | ignoreDefaultTargets = true,
20 | )
21 | @OptIn(ExperimentalWasmDsl::class)
22 | wasmJs {
23 | browser {
24 | commonWebpackConfig {
25 | outputFileName = "color-picker-wasm.js"
26 | experiments += "topLevelAwait"
27 | }
28 | }
29 | }
30 |
31 | sourceSets {
32 | wasmJsMain {
33 | dependencies {
34 | implementation(compose.foundation)
35 | implementation(compose.material3)
36 |
37 | implementation(projects.sample.shared)
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/sample/webApp/detekt.yml:
--------------------------------------------------------------------------------
1 | potential-bugs:
2 | active: true
3 | MissingPackageDeclaration:
4 | active: false
5 |
--------------------------------------------------------------------------------
/sample/webApp/src/wasmJsMain/kotlin/main.wasm.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.ExperimentalComposeUiApi
2 | import androidx.compose.ui.window.CanvasBasedWindow
3 | import com.eygraber.compose.colorpicker.sample.Sample
4 |
5 | @OptIn(ExperimentalComposeUiApi::class)
6 | fun main() {
7 | CanvasBasedWindow("ColorPicker") {
8 | Sample()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sample/webApp/src/wasmJsMain/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Color Picker
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/webApp/src/wasmJsMain/resources/load.mjs:
--------------------------------------------------------------------------------
1 | import { instantiate } from 'color-picker-wasm.uninstantiated.mjs';
2 |
3 | await wasmSetup;
4 |
5 | let te = null;
6 | try {
7 | await instantiate({ skia: Module['asm'] });
8 | } catch (e) {
9 | te = e;
10 | }
11 |
12 | if (te != null) {
13 | throw te;
14 | }
15 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.eygraber.conventions.Env
2 | import com.eygraber.conventions.repositories.addCommonRepositories
3 |
4 | pluginManagement {
5 | repositories {
6 | google {
7 | content {
8 | includeGroupByRegex("com\\.google.*")
9 | includeGroupByRegex("com\\.android.*")
10 | includeGroupByRegex("androidx.*")
11 | }
12 | }
13 |
14 | mavenCentral()
15 |
16 | maven("https://oss.sonatype.org/content/repositories/snapshots") {
17 | mavenContent {
18 | snapshotsOnly()
19 | }
20 | }
21 |
22 | maven("https://s01.oss.sonatype.org/content/repositories/snapshots") {
23 | mavenContent {
24 | snapshotsOnly()
25 | }
26 | }
27 |
28 | gradlePluginPortal()
29 | }
30 | }
31 |
32 | @Suppress("UnstableApiUsage")
33 | dependencyResolutionManagement {
34 | // comment this out for now because it doesn't work with KMP js
35 | // https://youtrack.jetbrains.com/issue/KT-55620/KJS-Gradle-plugin-doesnt-support-repositoriesMode
36 | // repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
37 |
38 | repositories {
39 | addCommonRepositories(
40 | includeMavenCentral = true,
41 | includeMavenCentralSnapshots = true,
42 | includeGoogle = true,
43 | includeJetbrainsComposeDev = false,
44 | )
45 | }
46 | }
47 |
48 | plugins {
49 | id("com.eygraber.conventions.settings") version "0.0.84"
50 | id("com.gradle.develocity") version "4.0.2"
51 | }
52 |
53 | rootProject.name = "compose-color-picker"
54 |
55 | include(":library")
56 | include(":sample:android-app")
57 | include(":sample:jvm-app")
58 | include(":sample:shared")
59 | include(":sample:webApp")
60 |
61 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
62 |
63 | develocity {
64 | buildScan {
65 | termsOfUseUrl = "https://gradle.com/terms-of-service"
66 | publishing.onlyIf { Env.isCI }
67 | if(Env.isCI) {
68 | termsOfUseAgree = "yes"
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------