Compare commits

...

686 Commits

Author SHA1 Message Date
22541f2d5e Update CHANGELOG.md 2024-02-10 15:40:59 +06:00
e235c52b6c Update libs.versions.toml 2024-02-10 15:39:48 +06:00
e89b8c72dd Update gradle-wrapper.properties 2024-02-08 10:40:43 +06:00
a374e53a3a update StringResource 2024-02-06 19:56:44 +06:00
afb066c4ee start 0.20.32 2024-02-06 19:55:34 +06:00
f05761d4a5 Merge pull request #386 from InsanusMokrassar/0.20.31
0.20.31
2024-01-31 18:10:53 +06:00
76adc9ea33 update ktor 2024-01-31 17:37:27 +06:00
99dd291413 start 0.20.31 2024-01-31 17:36:30 +06:00
f904eb27e1 Merge pull request #384 from InsanusMokrassar/0.20.30
0.20.30
2024-01-31 17:27:17 +06:00
eeb8214812 update exposed 2024-01-31 17:05:43 +06:00
f7215b039e start 0.20.30 2024-01-31 17:04:38 +06:00
c07fe5a0f9 Merge pull request #382 from InsanusMokrassar/0.20.29
0.20.29
2024-01-30 23:03:30 +06:00
0d28cb6e20 update dependencies 2024-01-30 21:18:49 +06:00
a1a17bfd1f start 0.20.29 2024-01-30 20:21:45 +06:00
f386f09592 Merge pull request #377 from InsanusMokrassar/0.20.28
0.20.28
2024-01-19 12:59:11 +06:00
a47e17fe6e downgrade kotlin and compose 2024-01-18 23:19:17 +06:00
01dc3b63ff start 0.20.28 2024-01-18 23:12:36 +06:00
2d97e0699e Merge pull request #372 from InsanusMokrassar/0.20.27
0.20.27
2024-01-16 12:43:08 +06:00
75f514d99b update github release 2024-01-16 12:38:53 +06:00
9a687cfc1c update dependencies 2024-01-16 12:19:30 +06:00
14edf8b6b7 start 0.20.27 2024-01-16 12:13:07 +06:00
23aa2d8917 Merge pull request #371 from InsanusMokrassar/0.20.26
0.20.26
2024-01-11 23:29:05 +06:00
7651388b5c update exposed version 2024-01-11 23:09:17 +06:00
805ab32b24 improvements in resources 2024-01-11 23:01:10 +06:00
cc623b1097 start 0.20.26 2024-01-11 23:00:14 +06:00
1420416b3e Merge pull request #369 from InsanusMokrassar/0.20.25
0.20.25
2024-01-04 20:34:15 +06:00
9a0b67f938 fix of tests 2024-01-04 20:30:09 +06:00
303e1e6281 add rgb and rgba representations of HEXAColor 2024-01-04 20:20:55 +06:00
ff59b0cc9c add colors module 2024-01-04 19:53:43 +06:00
be5d2ee715 start 0.20.25 2024-01-04 18:14:09 +06:00
8dd2e3f6f9 Merge pull request #366 from InsanusMokrassar/0.20.24
0.20.24
2024-01-04 10:26:00 +06:00
2eedd196d2 update changelog 2024-01-04 10:25:50 +06:00
759a3f2784 make it possible to use encoder inside of serialize callback 2024-01-04 10:24:39 +06:00
386fa830c3 update soywiz dependencies and downgrade ksp 2024-01-04 09:58:03 +06:00
c382423d77 upgrade MapperSerializer 2023-12-31 13:44:50 +06:00
bb466ce66c start 0.20.24 2023-12-31 13:38:57 +06:00
72cd3dd8a1 Merge pull request #365 from InsanusMokrassar/renovate/ksp
Update ksp to v1.9.22-1.0.16
2023-12-25 21:53:05 +06:00
renovate[bot]
595cedaaf1 Update ksp to v1.9.22-1.0.16 2023-12-25 10:15:44 +00:00
eeaceb6cf5 Merge pull request #362 from InsanusMokrassar/0.20.23
0.20.23
2023-12-22 02:43:14 +06:00
1bd671685b update dependencies and remove redundant usages of old IetfLanguageCode 2023-12-21 23:25:36 +06:00
48d3fe41f2 a lot of improvements in language codes 2023-12-21 15:41:11 +06:00
7ab21871cd Revert "add klock module"
This reverts commit 65d01b1fb3.
2023-12-21 14:05:27 +06:00
65d01b1fb3 add klock module 2023-12-17 23:37:45 +06:00
6230accb68 start 0.20.23 2023-12-17 22:30:25 +06:00
c6ed821934 Merge pull request #360 from InsanusMokrassar/0.20.22
0.20.22
2023-12-14 23:57:51 +06:00
10e03bb951 allow to create own Diff with constructor 2023-12-14 23:56:56 +06:00
aa4f392948 start 0.20.22 2023-12-14 23:52:28 +06:00
f51b59ec02 Update libs.versions.toml 2023-12-14 13:47:34 +06:00
8c76834ae4 Merge pull request #359 from InsanusMokrassar/0.20.21
0.20.21
2023-12-12 23:49:26 +06:00
4a454f3d67 add opportunity to use translation with default locale 2023-12-12 23:45:22 +06:00
151aa1863d update gradle wrapper 2023-12-12 21:41:53 +06:00
3bf6896296 get back translations with android and java resources packages 2023-12-12 21:41:09 +06:00
0d01561476 optimizations and improvements in resources 2023-12-12 21:39:58 +06:00
f6ded92251 init resources module 2023-12-12 20:58:08 +06:00
d01b735cc6 start 0.20.21 2023-12-12 20:21:37 +06:00
6c12001080 Merge pull request #358 from InsanusMokrassar/0.20.20
0.20.20
2023-12-12 00:12:34 +06:00
1afbf03606 improvements in AbstractExposedCRUDRepo 2023-12-12 00:01:58 +06:00
f6ef5c61c5 start 0.20.20 2023-12-11 23:54:25 +06:00
c18fee8107 update gradle publishing scripts
one more potential ix of publishing scripts

drop new configs of publish scripts

revert publish scripts and update gradle properties
2023-12-09 20:38:44 +06:00
d9df7a4384 Revert gradle wrapper version 2023-12-09 01:38:45 +06:00
87c2230e8e revert xmx 2500 2023-12-08 23:42:26 +06:00
da7eb6de0a change xmx 2023-12-08 21:34:05 +06:00
ed3815118f update jvmargs of gradle 2023-12-08 19:34:47 +06:00
be726f42bd Merge pull request #356 from InsanusMokrassar/0.20.19
update publishing scripts
2023-12-08 16:06:22 +06:00
a91006132f update publishing scripts 2023-12-08 16:06:07 +06:00
9a9f741a0b Merge pull request #355 from InsanusMokrassar/0.20.19
0.20.19
2023-12-08 16:03:05 +06:00
5028f130e9 update dependencies 2023-12-08 15:58:16 +06:00
77fa019651 start 0.20.19 2023-12-08 15:56:44 +06:00
9715da9384 Merge pull request #353 from InsanusMokrassar/0.20.18
0.20.18
2023-12-04 16:13:33 +06:00
f6d5035c1a small refactor in SpecialMutableStateFlow 2023-12-04 15:44:06 +06:00
43e782ab6f SpecialMutableStateFlow : MutableStateFlow 2023-12-04 15:37:02 +06:00
f3f9920bfb deprecate FlowState 2023-12-04 15:08:52 +06:00
2bfd615812 start 0.20.18 2023-12-04 15:01:42 +06:00
ebfacb3659 Merge pull request #352 from InsanusMokrassar/0.20.17
0.20.17
2023-12-01 02:51:00 +06:00
c71d557eec update dependencies 2023-12-01 02:46:15 +06:00
e0398cef21 start 0.10.17 2023-12-01 02:44:44 +06:00
f91599e9c6 Merge pull request #348 from InsanusMokrassar/0.20.16
0.20.16
2023-11-30 01:54:31 +06:00
f8f9f93c97 update changelog, rename SpecialMutableStateFlow file and update gradle wrapper 2023-11-30 01:54:04 +06:00
a8a5340d8b kdocs 2023-11-30 01:27:14 +06:00
871b27f37d initialization fixes 2023-11-30 01:22:43 +06:00
6f174cae1d fixes 2023-11-30 01:05:01 +06:00
22d7ac3e22 FlowState 2023-11-29 23:54:27 +06:00
9b30c3a155 small matrix improvements 2023-11-29 20:33:06 +06:00
915bac64b1 small fixes in SpecialStateFlow 2023-11-29 20:26:23 +06:00
9d2b50e55d SpecialStateFlow 2023-11-29 17:13:21 +06:00
bde100f63d start 0.20.16 2023-11-29 17:12:37 +06:00
05b035a13d Merge pull request #345 from InsanusMokrassar/0.20.15
0.20.15
2023-11-24 23:47:02 +06:00
eefb56bed7 Update libs.versions.toml 2023-11-24 20:14:32 +06:00
fcc0dc4189 update dependencies 2023-11-24 16:58:50 +06:00
47ff20317f start 0.20.15 2023-11-24 16:46:22 +06:00
1558b9103d Merge pull request #343 from InsanusMokrassar/0.20.14
0.20.14
2023-11-18 23:27:06 +06:00
7a78742162 update kslog 2023-11-18 18:11:07 +06:00
c01e240f66 update dependencies 2023-11-18 15:40:12 +06:00
fef4fcbac6 start 0.20.14 2023-11-18 15:37:19 +06:00
5ab18bce4b Merge pull request #335 from InsanusMokrassar/0.20.13
0.20.13
2023-11-11 16:37:11 +06:00
24aec7271a update dependencies and changelog 2023-11-11 16:30:59 +06:00
9b19a2cb95 Update libs.versions.toml 2023-11-10 04:29:38 +06:00
efdd7b8a57 Update libs.versions.toml 2023-11-10 04:28:54 +06:00
6df4cc9c3b start 0.20.13 2023-11-06 21:12:28 +06:00
b9d93db0f5 Merge pull request #334 from InsanusMokrassar/0.20.12
0.20.12
2023-11-02 19:16:56 +06:00
d7ee45ca64 update github workflows 2023-11-02 19:01:00 +06:00
d7c31b1b22 update and fix build 2023-11-02 19:00:35 +06:00
7d6794a358 start 0.20.12 2023-11-02 18:26:15 +06:00
473eb87346 Merge pull request #330 from InsanusMokrassar/0.20.11
0.20.11
2023-11-01 12:38:44 +06:00
8b18b07790 Update CHANGELOG.md 2023-11-01 12:37:30 +06:00
ab3c80a5ec update dependencies 2023-10-31 17:53:56 +06:00
075b93ecd6 SmartRWLocker now will wait first unlock of write mutex for acquiring read 2023-10-26 12:28:24 +06:00
f6d0f72e49 start 0.20.11 2023-10-26 12:28:04 +06:00
fcda3af862 Merge pull request #329 from InsanusMokrassar/0.20.10
0.20.10
2023-10-26 10:51:45 +06:00
d5c7a589b1 update dependencies 2023-10-26 10:48:36 +06:00
86e099ed25 start 0.20.10 2023-10-26 10:37:46 +06:00
ebd7befe73 Merge pull request #324 from InsanusMokrassar/0.20.9
0.20.9
2023-10-24 16:25:05 +06:00
b8c7e581a1 add support of linuxArm64 target 2023-10-20 21:53:27 +06:00
8281259179 start 0.20.9 2023-10-17 23:34:53 +06:00
d3e06b07df Update libs.versions.toml 2023-10-14 22:53:02 +06:00
4967018418 Merge pull request #322 from InsanusMokrassar/0.20.8
0.20.8
2023-10-14 21:01:45 +06:00
537a3c38fa update dependencies 2023-10-14 16:22:00 +06:00
0124957833 start 0.20.8 2023-10-14 16:08:51 +06:00
f0420e2d61 Merge pull request #312 from InsanusMokrassar/0.20.7
0.20.7
2023-10-09 18:16:58 +06:00
7090566041 fill changelog 2023-10-09 13:58:17 +06:00
f0d5035cd0 update dependencies 2023-10-09 13:53:44 +06:00
0fb9b8dc30 update compose version 2023-09-27 17:59:09 +06:00
eef6e81134 Update libs.versions.toml 2023-09-26 01:28:36 +06:00
1593159a3f Update libs.versions.toml 2023-09-26 01:27:20 +06:00
3da9eb9dbe temporal update 2023-09-22 14:14:23 +06:00
f17613f3fb update dependencies 2023-09-12 14:11:12 +06:00
14337ccb46 start 0.20.7 2023-09-10 17:09:08 +06:00
1a3913b09c Merge pull request #311 from InsanusMokrassar/0.20.6
0.20.6
2023-09-08 04:20:58 +06:00
039aed2747 improvements in ExposedReadKeyValuesRepo 2023-09-08 02:59:53 +06:00
173991e3cb start 0.20.6 2023-09-08 02:55:25 +06:00
8b3f8cab01 Merge pull request #310 from InsanusMokrassar/0.20.5
0.20.5
2023-09-07 20:09:58 +06:00
2a20d24589 fixes in SmartRWLocker 2023-09-07 16:57:59 +06:00
53c2d552ec start 0.20.5 2023-09-07 16:57:08 +06:00
af11c1a83d Merge pull request #307 from InsanusMokrassar/0.20.4
0.20.4
2023-09-06 19:17:17 +06:00
a65cf1481c fixes in build 2023-09-06 19:15:09 +06:00
0318716236 update kslog version 2023-09-06 19:08:32 +06:00
88eb4b3342 update dependencies 2023-09-06 18:58:10 +06:00
4810d1ef6a start 0.20.4 2023-09-06 18:55:48 +06:00
f2a9514d89 Merge pull request #306 from InsanusMokrassar/renovate/jb.dokka
Update dependency org.jetbrains.dokka:dokka-gradle-plugin to v1.9.0
2023-09-02 04:15:10 +06:00
925ba6ac24 Update build.gradle 2023-09-02 04:04:04 +06:00
renovate[bot]
ef407268a2 Update dependency org.jetbrains.dokka:dokka-gradle-plugin to v1.9.0 2023-09-01 21:04:25 +00:00
cd6c4bbe38 Merge pull request #304 from InsanusMokrassar/0.20.3
0.20.3
2023-09-02 03:03:57 +06:00
c058e18408 update smart rw locker tests 2023-09-01 20:11:04 +06:00
6d3ca565ca update ktor 2023-09-01 19:41:49 +06:00
236a7b4fd2 Update libs.versions.toml 2023-09-01 04:33:36 +06:00
e1f387dbf7 update dependencies and make a lot of fixes in repos and smart lockers/semaphors 2023-08-31 01:37:35 +06:00
3d113dd31e fixes in locks of caches repos 2023-08-29 15:06:55 +06:00
e0e57f0336 start 0.20.3 2023-08-29 14:59:57 +06:00
e775b58d41 Merge pull request #298 from InsanusMokrassar/0.20.2
0.20.2
2023-08-24 20:10:06 +06:00
5b070a8478 add small kdoc for map repos 2023-08-24 17:15:29 +06:00
8a61193500 update korlibs 2023-08-24 14:29:15 +06:00
fad522b8fe Update CHANGELOG.md 2023-08-24 03:00:08 +06:00
0acac205af fix build 2023-08-24 02:17:27 +06:00
069d4c61b7 update android target 2023-08-23 14:52:09 +06:00
7c113f5700 Update libs.versions.toml 2023-08-23 13:27:23 +06:00
dfdaf4225b start 0.20.2 and all main repos uses 'SmartRWLocker' 2023-08-23 03:26:54 +06:00
bd39ab2467 Merge pull request #295 from InsanusMokrassar/0.20.1
0.20.1
2023-08-12 22:47:03 +06:00
6ce1eb3f2d add tests for smartrwlocker 2023-08-12 22:37:35 +06:00
ce7d4fe9a2 fix of #293 2023-08-12 22:08:12 +06:00
a65bb2f419 add number picker, set picker and small text field 2023-08-11 01:51:40 +06:00
cbc868448b start 0.20.1 2023-08-11 00:58:09 +06:00
9c336a0b56 Update CHANGELOG.md 2023-08-09 02:43:53 +06:00
0f0d09399e Update CHANGELOG.md 2023-08-09 02:42:28 +06:00
e13a1162a9 Merge pull request #294 from InsanusMokrassar/0.20.0
0.20.0
2023-08-09 00:32:03 +06:00
57ebed903f done migration 2023-08-08 18:45:36 +06:00
4478193d8a update dependencies 2023-08-05 13:49:45 +06:00
ee948395e3 start 0.20.0 2023-08-02 13:03:40 +06:00
0616b051ae Merge pull request #291 from InsanusMokrassar/0.19.9
0.19.9
2023-07-29 17:37:38 +06:00
4d155d0505 update koin 2023-07-29 16:37:25 +06:00
a169e733d9 fill changelog 2023-07-29 16:31:50 +06:00
f081e237c8 improvements in startup plugin 2023-07-29 16:30:17 +06:00
f412d387fa start 0.19.9 2023-07-29 16:05:24 +06:00
67354b43e2 Merge pull request #287 from InsanusMokrassar/0.19.8
0.19.8
2023-07-27 01:33:49 +06:00
39135a4000 skipStartInvalidate 2023-07-27 00:35:01 +06:00
eaa014cebd update dependencies 2023-07-27 00:30:26 +06:00
856e657f81 fixes in KeyValueRepo.clear 2023-07-23 13:47:20 +06:00
3a609e5b66 start 0.19.8 2023-07-23 13:26:39 +06:00
d0022dd599 Merge pull request #281 from InsanusMokrassar/0.19.7
0.19.7
2023-06-30 14:37:36 +06:00
7ba6eed453 Update CHANGELOG.md 2023-06-30 12:04:42 +06:00
beeb6ecc0a Update CHANGELOG.md 2023-06-30 12:03:17 +06:00
7cdc17a714 Update libs.versions.toml 2023-06-30 12:02:20 +06:00
4765a950a9 start 0.19.7 2023-06-30 12:01:21 +06:00
65e8137e08 Merge pull request #279 from InsanusMokrassar/0.19.6
0.19.6
2023-06-29 20:27:19 +06:00
ae546dd9ad update dependencies 2023-06-29 18:38:58 +06:00
8110c42be0 update compose 2023-06-27 16:53:10 +06:00
bd2b5ae5fc add versions plugin 2023-06-27 16:46:45 +06:00
2ddfffa6a9 start 0.19.6 2023-06-27 16:46:45 +06:00
a4b54e861d Merge pull request #277 from InsanusMokrassar/0.19.5
0.19.5
2023-06-20 20:22:33 +06:00
c6785f1a4f fixes in types generation 2023-06-20 20:20:47 +06:00
83fe621c56 start 0.19.5 2023-06-20 20:16:24 +06:00
b3a93e17eb Merge pull request #275 from InsanusMokrassar/0.19.4
0.19.4
2023-06-19 16:25:43 +06:00
546a391af3 dependencies update 2023-06-19 16:19:14 +06:00
786cf9bd8b fix in koin generator 2023-06-19 13:52:41 +06:00
dfd6fe062d start 0.19.4 2023-06-19 13:45:29 +06:00
b6ef818613 Merge pull request #274 from InsanusMokrassar/0.19.3
0.19.3
2023-06-19 08:45:45 +06:00
b0f9e9c30a generic-oriented koin definitions 2023-06-19 00:32:45 +06:00
7e5c88ddc3 koin generator improvements 2023-06-18 23:38:36 +06:00
9824c3e00f start 0.19.3 2023-06-18 23:11:50 +06:00
9171d5ed11 update dokka version and return js kdocs 2023-06-09 12:01:11 +06:00
a1830ebb82 Merge pull request #269 from InsanusMokrassar/0.19.2
0.19.2
2023-06-02 13:02:38 +06:00
22d2a3d9bf Update CHANGELOG.md 2023-06-02 12:47:45 +06:00
c9b97fc965 Update libs.versions.toml 2023-06-02 12:46:56 +06:00
51f85becd5 Merge pull request #267 from InsanusMokrassar/0.19.2
0.19.2
2023-06-02 12:39:27 +06:00
a8a281cfb4 downgrade kotlin-poet version 2023-06-02 12:19:02 +06:00
ddd1304949 update dependencies 2023-06-02 12:17:21 +06:00
86d70b6c02 start 0.19.2 2023-06-02 12:11:49 +06:00
a22bdb39e7 Merge pull request #261 from InsanusMokrassar/0.19.1
update kotlin poet (0.19.1)
2023-05-29 17:35:29 +06:00
7ae4d5ef95 update kotlin poet 2023-05-29 17:31:40 +06:00
a2038cbefa Merge pull request #259 from InsanusMokrassar/0.19.1
0.19.1
2023-05-29 17:28:19 +06:00
992091eade Update CHANGELOG.md 2023-05-29 17:27:57 +06:00
e3bfead0c5 Update libs.versions.toml 2023-05-29 10:15:19 +06:00
0de96141fd start 0.19.1 2023-05-29 10:14:35 +06:00
fa18d15c3c Merge pull request #257 from InsanusMokrassar/0.19.0
0.19.0
2023-05-26 20:17:30 +06:00
ea9dbf2371 Koin.get and Scope.get with opportunity to get dependency by its type and definition 2023-05-25 21:12:21 +06:00
d34e3ec7a9 update dependencies 2023-05-25 21:08:12 +06:00
c8833a36af start 0.19.0 2023-05-25 20:58:05 +06:00
a067cb0c0f Merge pull request #256 from InsanusMokrassar/0.18.4
0.18.4
2023-05-14 16:51:57 +06:00
999c8327bd lazyInject 2023-05-14 16:36:24 +06:00
c2ec73c70a start 0.18.4 2023-05-14 16:28:03 +06:00
702f782fc1 Merge pull request #254 from InsanusMokrassar/0.18.3
0.18.3
2023-05-12 14:30:37 +06:00
25dbcaaf83 update dependencies 2023-05-12 00:51:38 +06:00
b00d454a24 start 0.18.3 2023-05-12 00:26:16 +06:00
dbc921d56d Merge pull request #250 from InsanusMokrassar/0.18.2
0.18.2
2023-05-10 10:19:47 +06:00
438fefa7a3 fixes 2023-05-09 15:09:50 +06:00
5d74eac814 Update libs.versions.toml 2023-05-09 01:41:25 +06:00
9fb62e1e25 start 0.18.2 and make json of Startup customizable 2023-05-09 01:38:04 +06:00
3e366ea73b Update libs.versions.toml 2023-05-06 01:54:15 +06:00
0ff895bffa Update gradle.properties 2023-05-06 01:53:33 +06:00
c5bb120280 Merge pull request #248 from InsanusMokrassar/0.18.1
0.18.1
2023-05-05 22:26:53 +06:00
4b26a92b37 improve old diff API 2023-05-05 12:01:29 +06:00
0a8453b4d2 add MapDiff#reversed and MutableMap#applyDiff(MapDiff) 2023-05-05 11:58:27 +06:00
c9872a61b6 now applyDiff will return its receiver 2023-05-05 11:50:23 +06:00
149a1aa278 fix in new applyDiff 2023-05-05 11:33:22 +06:00
13d0e1b682 add MapDiff utils 2023-05-04 21:12:26 +06:00
6f9be2a9f8 start 0.18.1 and add SmartMutex 2023-05-03 23:47:05 +06:00
93ba98d993 Merge pull request #246 from InsanusMokrassar/revert-245-renovate/ksp
Revert "Update ksp to v1.8.21-1.0.11"
2023-04-28 00:34:20 +06:00
940ee3df06 Revert "Update ksp to v1.8.21-1.0.11" 2023-04-28 00:34:10 +06:00
2e7bab10fd Merge pull request #245 from InsanusMokrassar/renovate/ksp
Update ksp to v1.8.21-1.0.11
2023-04-28 00:14:25 +06:00
renovate[bot]
3ed70a37ea Update ksp to v1.8.21-1.0.11 2023-04-27 18:10:41 +00:00
fe8f80b9d9 Merge pull request #243 from InsanusMokrassar/0.18.0
0.18.0
2023-04-27 17:08:42 +06:00
d81fb32fb9 remove previous deprecations 2023-04-27 16:29:08 +06:00
2877b5532c Revert "Get back some functional of "trying to update up to gradle wrapper 8.1.1""
This reverts commit b938b21395.
2023-04-27 16:24:58 +06:00
b938b21395 Get back some functional of "trying to update up to gradle wrapper 8.1.1" 2023-04-27 16:24:37 +06:00
58836359cc Revert "trying to update up to gradle wrapper 8.1.1"
This reverts commit 5edb0e1331.
2023-04-27 15:00:43 +06:00
5edb0e1331 trying to update up to gradle wrapper 8.1.1 2023-04-27 14:15:45 +06:00
0f0d0b5d58 update android fragments dependency 2023-04-27 12:16:48 +06:00
46c1887cbe remove usage of cache updates flows in autorecached repos 2023-04-27 12:06:50 +06:00
5f231c2212 ReadKeyValuesRepo#removeWithValue 2023-04-25 19:32:43 +06:00
4e97ce86aa add defauult value to kvCache in fullyCached 2023-04-25 17:39:14 +06:00
315a7cb29e Rename full caching factories functions to fullyCached 2023-04-25 17:23:47 +06:00
aa7cc503f2 improvements in query parameters getting 2023-04-25 13:14:12 +06:00
4bbe7e5a80 update build tools version 2023-04-25 12:23:53 +06:00
d9c05f38d2 start 0.18.0 2023-04-25 12:23:08 +06:00
cd0c4c9650 Merge pull request #237 from InsanusMokrassar/renovate/android-gradle
Update dependency com.android.tools.build:gradle to v7.4.2
2023-04-25 00:38:47 +06:00
fc3407f104 Merge pull request #241 from InsanusMokrassar/0.17.8
0.17.8
2023-04-19 20:16:36 +06:00
3a5544206b update ktor 2023-04-19 19:46:39 +06:00
e17e2f7fb8 start 0.17.8 2023-04-19 19:45:58 +06:00
renovate[bot]
d32c95f143 Update dependency com.android.tools.build:gradle to v7.4.2 2023-04-18 18:12:52 +00:00
6d8a8ab018 Merge pull request #233 from InsanusMokrassar/0.17.7
0.17.7
2023-04-18 19:37:06 +06:00
a7dce8fa78 update dependencies 2023-04-18 19:24:05 +06:00
ca73ff8e19 add support of native in startup 2023-04-18 19:20:39 +06:00
d01ad10d7d start 0.17.7 2023-04-18 19:20:39 +06:00
81041ee43c Merge pull request #232 from InsanusMokrassar/renovate/configure
Configure Renovate
2023-04-18 19:20:15 +06:00
renovate[bot]
6e004c2ae4 Add renovate.json 2023-04-18 08:23:10 +00:00
0e2fac5b22 Merge pull request #231 from InsanusMokrassar/0.17.6
0.17.6
2023-04-13 11:27:18 +06:00
269da7f155 update dependencies and fill changelog 2023-04-13 11:20:34 +06:00
3cb6b73ee0 add preloadImage 2023-04-07 01:17:04 +06:00
a938ee1efb simplify API of MPPFile.input 2023-04-04 01:26:18 +06:00
6ea5e2e5a6 update version 2023-04-03 22:54:57 +06:00
617dfb54e0 experimentally add linuxx64 and mingwx64 as target platforms 2023-04-03 22:35:41 +06:00
d23e005985 preview of files realization 2023-04-03 15:49:46 +06:00
e5207f5bc5 Revert "Revert "trying to add native support""
This reverts commit c96cea8db0.
2023-04-03 10:26:03 +06:00
c96cea8db0 Revert "trying to add native support"
This reverts commit 0a8e71d76a.
2023-04-03 10:22:53 +06:00
0a8e71d76a trying to add native support 2023-04-03 10:22:43 +06:00
cf1fd32b08 Merge pull request #230 from InsanusMokrassar/0.17.5
0.17.5
2023-03-10 22:30:32 +06:00
cc4224ea1f update kv and crud tests 2023-03-10 22:08:35 +06:00
f4c148bc58 add map type info in ktor read kv and crud repos 2023-03-10 21:41:01 +06:00
022297ad3f fixes in AutoRecacheReadKeyValueRepo 2023-03-10 19:37:36 +06:00
5180d6fc3e hotfixes 2023-03-10 18:43:43 +06:00
eeebbff70d getAll 2023-03-10 18:37:48 +06:00
afc6aeea15 fixes 2023-03-10 16:14:51 +06:00
486515eddd update dokka 2023-03-10 16:09:33 +06:00
0e21699cd1 several updates 2023-03-10 15:45:02 +06:00
f1678ef7cf start 0.17.5 2023-03-10 15:36:36 +06:00
cea65fc76e Merge pull request #229 from InsanusMokrassar/0.17.4
0.17.4
2023-03-09 23:23:55 +06:00
c9e320b72a update compose version 2023-03-09 23:22:33 +06:00
555956087d add android manifest to mapper module 2023-03-09 22:23:56 +06:00
b3f468f901 add docs to mapper serialization and allow to use them as supertypes 2023-03-09 22:21:22 +06:00
f5f7511781 add mapper serializer 2023-03-09 21:55:07 +06:00
4be1d93f60 start 0.17.4 2023-03-09 21:49:15 +06:00
7d684608ef Merge pull request #228 from InsanusMokrassar/0.17.3
0.17.3
2023-03-07 23:30:23 +06:00
2c7fd320eb fixed 2023-03-07 22:28:56 +06:00
88ee82e1c6 add Diff#isEmpty 2023-03-07 21:50:23 +06:00
d6402c624e optimization of nonstrict comparison 2023-03-07 19:14:43 +06:00
8b9c93bc10 diffs improvement 2023-03-07 19:12:12 +06:00
4f5e261d01 start 0.17.3 2023-03-07 18:59:34 +06:00
cf455aebe6 Merge pull request #227 from InsanusMokrassar/0.17.2
0.17.2
2023-03-02 21:57:49 +06:00
1380d5f8e1 fill changelog 2023-03-02 21:38:20 +06:00
5f65260bfe Update DefaultStatesManager.kt 2023-03-02 21:35:59 +06:00
11f0dcfc01 Update DefaultStatesManager.kt 2023-03-02 21:32:58 +06:00
555b7886a4 Update gradle.properties 2023-03-02 21:28:50 +06:00
3707a6c0ce Merge pull request #226 from InsanusMokrassar/0.17.1
0.17.1
2023-02-28 19:36:08 +06:00
4c8d92b4c3 update ktor 2023-02-28 19:32:32 +06:00
8bbd33c896 now all android modules depends on jvm 2023-02-28 19:08:39 +06:00
ac33a3580f start 0.17.1 2023-02-28 19:04:04 +06:00
a64a32fbe6 Merge pull request #225 from InsanusMokrassar/0.17.0
0.17.0
2023-02-28 12:15:41 +06:00
9493e97a38 remove redundant part from defaultAndroidSettings.gradle 2023-02-27 22:46:08 +06:00
88bd770260 fill dependencies updates changelog 2023-02-27 22:45:35 +06:00
a7bd33b7bf update kotlin and ksp versions 2023-02-27 17:53:37 +06:00
73c724a2e5 small cleanup in build scripts 2023-02-27 17:03:09 +06:00
d8cf3c6726 update dependencies and at least it is buildable 2023-02-27 16:56:39 +06:00
15dee238b5 start 0.17.0 2023-02-27 15:54:37 +06:00
c70626734e Merge pull request #224 from InsanusMokrassar/0.16.13
0.16.13
2023-02-27 15:51:20 +06:00
5a765ea1bc get back publishing signing 2023-02-27 15:43:52 +06:00
8215f9d2c6 temporal solution of generating problem 2023-02-26 14:37:41 +06:00
d2e6d2ec80 generators for models has been created 2023-02-25 19:56:12 +06:00
3718181e8f start 0.16.13 2023-02-25 18:44:07 +06:00
0d825cf424 Merge pull request #223 from InsanusMokrassar/0.16.12
0.16.12
2023-02-22 10:30:45 +06:00
28a804d988 improve start launcher plugin logs 2023-02-22 10:28:25 +06:00
9e4bb9d678 add readme to generator and add several fixes in processor 2023-02-22 10:20:15 +06:00
9c40d7da3d add generator for koin definitions 2023-02-22 01:34:42 +06:00
2b76ad0aa9 improvements in selectByIds of CommonExposedRepo 2023-02-20 11:55:14 +06:00
e4b619e050 start 0.16.12 2023-02-20 11:54:26 +06:00
36c09feaf2 Merge pull request #222 from InsanusMokrassar/0.16.11
0.16.11
2023-02-20 00:49:12 +06:00
2d68321503 Update build.gradle 2023-02-20 00:19:27 +06:00
85455ab21c improvements in language codes 2023-02-19 19:34:42 +06:00
18d63eb980 start 0.16.11 2023-02-19 19:20:03 +06:00
2e429e9704 Merge pull request #221 from InsanusMokrassar/0.16.10
0.16.10
2023-02-17 13:54:09 +06:00
f4af28059b improvements in ReadKeyValueFromCRUDRepo 2023-02-17 13:45:22 +06:00
c1476bd075 add ReadCRUDFromKeyValueRepo 2023-02-15 20:16:54 +06:00
16c720fddd start 0.16.10 2023-02-15 20:14:55 +06:00
8b4b4a5eca Merge pull request #220 from InsanusMokrassar/0.16.9
0.16.9
2023-02-13 16:09:55 +06:00
32e6e5b7e2 Update CHANGELOG.md 2023-02-13 16:09:44 +06:00
a9f7fd8e32 new extension HttpResponse.bodyOrNullOnNoContent 2023-02-10 15:17:08 +06:00
95be1a26f2 improvements in HttpResponse.bodyOrNull 2023-02-10 15:14:03 +06:00
ef9b31aee0 start 0.16.9 2023-02-05 18:17:20 +06:00
df3c01ff0a Merge pull request #219 from InsanusMokrassar/0.16.8
0.16.8
2023-02-04 01:46:43 +06:00
4704c5a33d Update CHANGELOG.md 2023-02-04 01:39:49 +06:00
225c06550a fixes in FileKeyValueRepo 2023-02-03 10:23:52 +06:00
f0987614c6 fixes in FileKeyValueRepo 2023-02-03 10:07:24 +06:00
269c2876f3 actualize all make it possible to disable clear on actualization 2023-02-02 20:07:31 +06:00
168d6acf7c actualizeAll now is overridable in auto recache repos 2023-02-02 20:05:25 +06:00
a5f718e257 add meppers for kv<->kvs repos 2023-02-02 12:29:00 +06:00
4f68459582 start add kv to kvs transformations 2023-02-02 10:15:37 +06:00
442db122cf remove redundant id getter in keyvalues autocached repo 2023-02-01 23:02:06 +06:00
580d757be2 fixes in download file and upgrade ktor version 2023-02-01 22:28:17 +06:00
47b0f6d2d8 fixes 2023-02-01 13:03:51 +06:00
3f6f6ebc2b realize invalidate method in fallback cache repos 2023-01-31 10:09:55 +06:00
2645ea29d6 Update ActionWrapper.kt 2023-01-31 00:11:45 +06:00
79f2041565 Update AutoRecacheReadCRUDRepo.kt 2023-01-30 23:59:48 +06:00
4a7567f288 Update AutoRecacheWriteCRUDRepo.kt 2023-01-30 23:59:19 +06:00
8a890ed6ed Update AutoRecacheReadKeyValueRepo.kt 2023-01-30 23:58:43 +06:00
3d90df6897 Update AutoRecacheWriteKeyValueRepo.kt 2023-01-30 23:58:11 +06:00
681c13144a Update AutoRecacheReadKeyValuesRepo.kt 2023-01-30 23:57:40 +06:00
b64f2e6d32 Update AutoRecacheWriteKeyValuesRepo.kt 2023-01-30 23:56:57 +06:00
428eabb1bd Create FallbackCacheRepo.kt 2023-01-30 23:54:32 +06:00
2162e83bce fixes in actualizeAll 2023-01-29 22:51:39 +06:00
6142022283 improve actualizeAll to lazily clear repo 2023-01-29 22:46:32 +06:00
e6d9c8250f add all autorecaches repos 2023-01-29 22:18:22 +06:00
46178e723b fixes 2023-01-29 20:54:32 +06:00
605f55acd2 fixes in Write* cache repos 2023-01-29 20:35:10 +06:00
0f8b69aa60 start 0.16.8 2023-01-29 20:35:10 +06:00
551d8ec480 Merge pull request #218 from InsanusMokrassar/0.16.7
0.16.7
2023-01-29 13:20:16 +06:00
fc48446ec4 temporarily remove fallback repo 2023-01-29 13:15:56 +06:00
3644b83ac6 fill changelog 2023-01-29 13:08:41 +06:00
cd73791b6f add docs to the MutableState.asState 2023-01-29 12:46:27 +06:00
03de71df2e add docs to as compose state 2023-01-29 12:43:51 +06:00
83d5d3faf4 improve flow as state functionality 2023-01-29 12:28:22 +06:00
0c8bec4c89 add extension actualizeAll 2023-01-27 15:15:27 +06:00
7fc93817c1 start to add fallback repos 2023-01-27 14:45:31 +06:00
d0a00031a1 Update CHANGELOG.md 2023-01-26 22:54:17 +06:00
6ebc5aa0c2 potential fix of state value change in asMutableComposeState 2023-01-23 15:21:47 +06:00
8a6b4bb49e add alsoIfTrue/alsoIfFalse/letIfTrue/letIfFalse 2023-01-22 23:01:06 +06:00
20799b9a3e add repeat on failure with callback 2023-01-22 22:43:26 +06:00
ec3afc615c add serializable diff 2023-01-20 13:29:45 +06:00
da692ccfc3 add ifTrue/ifFalse 2023-01-19 20:21:35 +06:00
53b89f3a18 start 0.16.7 2023-01-19 20:19:21 +06:00
58cded28d3 Merge pull request #217 from InsanusMokrassar/0.16.6
0.16.6
2023-01-18 22:28:44 +06:00
592c5f3732 Column extensions eqOrIsNull and neqOrIsNotNull 2023-01-14 20:17:57 +06:00
f44a78a5f5 small improvement of openBaseWebSocketFlow 2023-01-14 15:20:52 +06:00
e0bdd5dfdc improvements in microutils 2023-01-14 13:22:07 +06:00
99c0f06b72 repos update 2023-01-14 13:15:59 +06:00
66fc6df3d7 Improvements in 'StartLauncherPlugin#start' methods 2023-01-08 13:52:53 +06:00
a36425a905 start 0.16.6 2023-01-08 13:51:34 +06:00
d920fee6d4 Merge pull request #216 from InsanusMokrassar/0.16.5
0.16.5
2023-01-04 20:21:15 +06:00
23590be5de Update CHANGELOG.md 2023-01-04 20:20:25 +06:00
94acc3c93b Update libs.versions.toml 2023-01-04 08:56:46 +06:00
5616326a3b start 0.16.5 2023-01-04 08:50:19 +06:00
7601860c5c Merge pull request #214 from InsanusMokrassar/0.16.4
0.16.4
2022-12-27 19:03:35 +06:00
8b43d785cc update changelog 2022-12-27 18:51:01 +06:00
b62d3a0b7d launchInCurrentThread 2022-12-27 18:50:41 +06:00
fad73c7213 start 0.16.4 2022-12-27 18:49:04 +06:00
2403c7c2b0 Merge pull request #213 from InsanusMokrassar/0.16.3
0.16.3
2022-12-25 13:24:37 +06:00
fa090bf920 Update StartLauncherPlugin.kt 2022-12-25 10:29:16 +06:00
a83ee86340 improvements in startup launcher 2022-12-25 10:26:44 +06:00
204955bcce add template for startup and readme 2022-12-19 17:51:05 +06:00
ee56e9543a start 0.16.3 2022-12-17 17:27:15 +06:00
96fdff6ffd Merge pull request #212 from InsanusMokrassar/0.16.2
0.16.2
2022-12-16 13:59:47 +06:00
58b007cbb3 fill changelog 2022-12-16 13:59:24 +06:00
4f0c139889 remove redundant stop koin expect-actual 2022-12-16 11:58:18 +06:00
c584c24fce fixes and improvements 2022-12-15 15:07:10 +06:00
85e5cee154 replace createStartupPluginAndRegister 2022-12-15 14:59:07 +06:00
5af91981f1 upgrades and filling of README 2022-12-15 10:26:31 +06:00
2fe4f08059 update compose 2022-12-15 08:51:53 +06:00
83796f345a start fix startup 2022-12-14 22:26:23 +06:00
c1e21364a6 start 0.16.2 2022-12-14 21:40:18 +06:00
067d9d0d3b Merge pull request #211 from InsanusMokrassar/0.16.1
0.16.1
2022-12-09 19:58:54 +06:00
03f527d83e Update CHANGELOG.md 2022-12-09 19:46:58 +06:00
ced05a4586 improve default runCatchingSafely/safelyWithResult and add suspend variances of safe/unsafe SafeWrapper interface 2022-12-09 12:14:24 +06:00
43fe06206a add safe wrapper 2022-12-09 11:09:32 +06:00
023657558e start 0.16.1 2022-12-09 10:52:53 +06:00
9b0b726c80 Merge pull request #210 from InsanusMokrassar/0.16.0
0.16.0
2022-12-08 09:29:12 +06:00
4ee67321c4 fill changelog and update android dependencies 2022-12-08 09:26:59 +06:00
59f1f2e59b Update libs.versions.toml 2022-12-08 08:53:37 +06:00
0766d48b7c Update libs.versions.toml 2022-12-08 07:56:04 +06:00
e18903b9e9 Update gradle.properties 2022-12-08 07:54:53 +06:00
d0eecdead2 Update gradle.properties 2022-12-08 07:53:59 +06:00
cc4a83a033 Update gradle.properties 2022-12-08 07:53:08 +06:00
1cf911bbde Update build.gradle 2022-12-07 19:40:30 +06:00
36d73d5023 Update StartupLaunchingTests.kt 2022-12-07 11:39:45 +06:00
c395242e3e fixes in StartupLauncingTests 2022-12-07 10:50:51 +06:00
cd9cd7cc5d Merge pull request #208 from InsanusMokrassar/0.15.1
0.15.1
2022-12-07 09:43:48 +06:00
acbb8a0c07 complete prepare startup module 2022-12-06 13:15:08 +06:00
b9d8528599 complete kdocs for the startup module 2022-12-06 13:14:05 +06:00
4971326eca add kdocs for the config of startup 2022-12-06 13:08:37 +06:00
09d1047260 add kdocs to the startup module 2022-12-06 13:06:21 +06:00
02dbd493c2 add tests and make several replacements/improvements 2022-12-06 12:38:24 +06:00
b17931e7bd complete startup module 2022-12-05 22:31:15 +06:00
2a4570eafc Update ServerLauncher.kt 2022-12-05 21:52:36 +06:00
c9514d3a6d Create DefaultJson.kt 2022-12-05 21:47:00 +06:00
072805efc7 Update ServerLauncher.kt 2022-12-05 21:45:30 +06:00
369ff26627 initialize startup module 2022-12-05 21:19:38 +06:00
c5abbbbd2d start 0.15.1 2022-12-05 20:04:50 +06:00
d974639f1e fixes in source roots of dokka 2022-12-05 07:47:35 +06:00
26efde316b remove redundant fix of android 32 dx files from worflows 2022-12-05 07:09:31 +06:00
fafe50f80a suppress kdocs of uniupload in actual realizations 2022-12-05 07:00:31 +06:00
41504469db Revert "potential fix of dokka"
This reverts commit 31022733ac.
2022-12-05 06:57:29 +06:00
03b3ddd98b Revert "remove redundant dokka println"
This reverts commit b53cfd5504.
2022-12-05 06:57:27 +06:00
89d919f2be Merge pull request #207 from InsanusMokrassar/0.15.0
0.15.0
2022-12-04 19:26:14 +06:00
b53cfd5504 remove redundant dokka println 2022-12-04 19:03:44 +06:00
31022733ac potential fix of dokka 2022-12-04 19:00:16 +06:00
f9a8c39879 updates and fixes in mime types 2022-12-02 21:07:32 +06:00
a812c2dd2f refresh language codes 2022-12-02 20:03:29 +06:00
217e977f0d ids in crud repos 2022-12-02 13:46:06 +06:00
04c301d1ac upgrade version up to 0.15.0 2022-12-02 13:31:11 +06:00
7f0c425389 start 0.14.5 2022-12-02 13:04:44 +06:00
1ede1c423b Merge pull request #206 from InsanusMokrassar/0.14.4
0.14.4
2022-11-27 17:34:07 +06:00
7cb064896a fill changelog 2022-11-27 14:44:03 +06:00
0c5e2862ca improvements in ktor client-server files handling 2022-11-27 14:38:35 +06:00
30d4507f54 hotfix in handleUniUpload 2022-11-26 12:12:01 +06:00
b6c6d89455 Update gradle.properties 2022-11-25 10:16:31 +06:00
9d218ee534 Update UniloadMultipart.kt 2022-11-25 10:15:59 +06:00
11116d8cab Merge pull request #205 from InsanusMokrassar/0.14.3
0.14.3
2022-11-24 13:15:08 +06:00
58d754bbde handleUniUpload now is inline 2022-11-24 12:29:48 +06:00
8f25c123dd add download and downloadToTemporalFile extensions 2022-11-24 11:27:30 +06:00
76e214fc08 upfixes 2022-11-22 14:39:31 +06:00
2b5380f8d6 add server part of uniUpload, fill changelog 2022-11-22 13:38:36 +06:00
844a129094 complete universal uniupload 2022-11-22 12:57:43 +06:00
a3090cca9d add uniupload for JVM 2022-11-22 10:36:15 +06:00
b7b5159e9c Revert "Revert "start adding uniupload""
This reverts commit 0d1aae0ef7.
2022-11-22 09:51:10 +06:00
0f8bc2c950 fix in workflow build 2022-11-21 20:50:28 +06:00
69f5c49f45 small update of build workflows 2022-11-21 20:49:04 +06:00
9b308e6fb8 update workflow 2022-11-21 20:32:48 +06:00
3e3f91128b add dependency of common android repos onto jvm 2022-11-21 20:23:05 +06:00
0d1aae0ef7 Revert "start adding uniupload"
This reverts commit 4d022f0480.
2022-11-21 20:20:37 +06:00
4d022f0480 start adding uniupload 2022-11-21 13:39:11 +06:00
153e20d00e start 0.14.3 2022-11-21 12:49:04 +06:00
a9a8171dd6 add gitea repo 2022-11-16 21:35:18 +06:00
bf5c3b59b2 Merge pull request #204 from InsanusMokrassar/0.14.2
0.14.2
2022-11-15 10:02:11 +06:00
607c432bdb Update CHANGELOG.md 2022-11-15 10:01:58 +06:00
ae5c010770 Update libs.versions.toml 2022-11-15 09:54:18 +06:00
d5e432437f Update gradle.properties 2022-11-15 09:53:28 +06:00
9f99ebce01 Merge pull request #203 from InsanusMokrassar/0.14.1
0.14.1
2022-11-10 17:19:17 +06:00
64ee899b84 revert koin version change 2022-11-10 17:13:25 +06:00
e0e0c1658b revert kotlin versin version change 2022-11-10 17:11:45 +06:00
2c586f667c update version 2022-11-10 17:09:12 +06:00
64164ef6c1 update dependencies 2022-11-10 17:06:20 +06:00
22343c0731 Merge pull request #202 from InsanusMokrassar/0.14.0
0.14.0
2022-11-08 14:20:12 +06:00
f4ec1a4c60 add deprecations removing note 2022-11-08 14:19:48 +06:00
c1c33cceb1 remove deprecations 2022-11-08 14:17:45 +06:00
a3e975b2ba Update CHANGELOG.md 2022-11-08 14:13:36 +06:00
06e705a687 update klock 2022-11-08 13:12:39 +06:00
d56eb6c867 fixes 2022-11-08 12:47:59 +06:00
9cbca864e3 Update CHANGELOG.md 2022-11-08 08:00:18 +06:00
abb4378694 Update libs.versions.toml 2022-11-08 07:59:50 +06:00
0eb698d9a4 start 0.14.0 2022-11-08 07:57:31 +06:00
15ea9f2093 add documentation for KeyValue repo 2022-11-03 13:01:51 +06:00
d47aca0923 Merge pull request #201 from InsanusMokrassar/0.13.2
0.13.2
2022-10-30 22:35:07 +06:00
1ac50e9959 update several dependencies and fill changelog 2022-10-30 21:45:19 +06:00
6adfbe3a96 Update exposed and revert several dependencies updates 2022-10-24 23:52:04 +06:00
59f36e62e9 update dependencies 2022-10-22 14:25:46 +06:00
54af116009 start 0.13.2 2022-10-22 14:14:12 +06:00
38fbec8e3b Merge pull request #200 from InsanusMokrassar/0.13.1
0.13.1
2022-10-17 15:42:13 +06:00
babbfc55e4 update default of AbstractExposedWriteCRUDRepo 2022-10-17 15:31:27 +06:00
2511e18d69 AbstractExposedWriteCRUDRepo#createAndInsertId now is optional and returns nullable value 2022-10-17 14:42:53 +06:00
29658c70a0 start 0.13.1 2022-10-17 14:35:16 +06:00
96311ee43d Merge pull request #199 from InsanusMokrassar/0.13.0
0.13.0
2022-10-13 16:50:50 +06:00
bd33b09052 A LOT OF KTOR METHODS RELATED TO UnifierRouter/UnifiedRequester HAVE BEEN REMOVED 2022-10-11 13:24:39 +06:00
8055003b47 deprecations removing 2022-10-11 12:58:29 +06:00
1257492f85 changes in insert/update exposed methods and rewriting of read keyvalue(s) exposed repos to be extending abstract key value(s) repos 2022-10-11 12:55:25 +06:00
1107b7f4ef start 0.13.0 2022-10-11 12:07:49 +06:00
a1a1171240 start 0.12.18 2022-10-04 15:36:43 +06:00
46c02e5df1 Merge pull request #198 from InsanusMokrassar/0.12.17
0.12.17
2022-10-01 21:59:42 +06:00
2e9efc57de update dependencies 2022-10-01 21:56:47 +06:00
acecadef17 start 0.12.17 2022-10-01 21:41:34 +06:00
19394b5e69 Merge pull request #197 from InsanusMokrassar/0.12.16
0.12.16
2022-09-26 02:16:17 +06:00
de999e197f fix of setOnHierarchyChangeListenerRecursively 2022-09-26 01:10:10 +06:00
9d95687d3c FlowOnHierarchyChangeListener 2022-09-26 01:02:12 +06:00
aa9dfb4ab8 start 0.12.16 2022-09-26 00:47:32 +06:00
9c5b44efb3 Merge pull request #196 from InsanusMokrassar/0.12.15
0.12.15
2022-09-24 01:04:01 +06:00
ac587a67e6 findViewsByTag 2022-09-23 16:43:33 +06:00
59428140a8 applyDiff: Diff 2022-09-23 16:05:32 +06:00
60bdb59d71 fix version change 2022-09-23 12:41:57 +06:00
be52871de8 flows extensions 2022-09-23 12:41:07 +06:00
b7934cf357 flows extensions 2022-09-23 12:33:00 +06:00
dbfbeef90a start 0.12.15 2022-09-23 12:12:15 +06:00
00943c9cdf add docs for Diff class 2022-09-23 12:10:24 +06:00
8745c6a16a Merge pull request #195 from InsanusMokrassar/0.12.14
0.12.14
2022-09-23 00:19:12 +06:00
433ba4b58f argumentOrNull/argumentOrThrow 2022-09-22 23:39:08 +06:00
d40376e524 Update CHANGELOG.md 2022-09-22 21:51:40 +06:00
a2982f88f5 Update libs.versions.toml 2022-09-21 21:17:34 +06:00
1642f7abd9 improvements in AwaitFirst 2022-09-21 18:00:39 +06:00
a10d2184ff start 0.12.14 2022-09-21 17:53:57 +06:00
522435f096 Merge pull request #194 from InsanusMokrassar/0.12.13
make actor not async
2022-09-15 01:43:21 +06:00
79b30290c0 fix build 2022-09-15 01:19:05 +06:00
f8b8626859 make actor not async 2022-09-15 01:11:20 +06:00
b061b85a08 Merge pull request #193 from InsanusMokrassar/0.12.13
0.12.13
2022-09-14 23:27:59 +06:00
3870db1c88 fix of #155 and build 2022-09-14 22:47:57 +06:00
1be1070eb4 fix of #160 2022-09-14 22:32:00 +06:00
2696e663cf add RandomQualifier 2022-09-14 22:21:30 +06:00
1e1f7df86d create koin module 2022-09-14 22:14:20 +06:00
1d8ded8fd3 start 0.12.13 2022-09-14 21:42:29 +06:00
197825123a Merge pull request #192 from InsanusMokrassar/0.12.12
0.12.12
2022-09-13 22:08:52 +06:00
422b2e6db1 improve SkeletonAnimation 2022-09-13 22:01:02 +06:00
1973e0b5bf SkeletonAnimation 2022-09-13 01:30:36 +06:00
8258cf93a9 start 0.12.12 2022-09-12 21:03:07 +06:00
1d49bd5947 Merge pull request #191 from InsanusMokrassar/0.12.11
0.12.11
2022-09-08 23:46:55 +06:00
44317d1519 start 0.12.11 and add values in key value cache repo 2022-09-08 22:38:24 +06:00
48e08fcc69 Merge pull request #190 from InsanusMokrassar/0.12.10
0.12.10
2022-09-07 23:54:33 +06:00
1a3ce6e623 hotfix 2022-09-07 23:47:13 +06:00
fb122f3e70 0.12.10 2022-09-07 23:45:59 +06:00
7cca6039cc Merge pull request #189 from InsanusMokrassar/0.12.9
fix of cache filling without clearing of values
2022-09-07 22:16:50 +06:00
118e3dba39 fix of cache filling without clearing of values 2022-09-07 21:56:41 +06:00
87070710fa Merge pull request #188 from InsanusMokrassar/0.12.9
update korlibs
2022-09-07 21:14:26 +06:00
38499c3d4a update korlibs 2022-09-07 21:09:57 +06:00
5d754d968b Merge pull request #187 from InsanusMokrassar/0.12.9
0.12.9
2022-09-07 20:26:46 +06:00
d543d436bc Fixes in key values cache 2022-09-07 20:01:13 +06:00
12b54f99af update gradle wrapper 2022-09-07 19:49:29 +06:00
2a95d7e643 start 0.12.9 2022-09-07 19:48:59 +06:00
e3d3cacfa4 Merge pull request #186 from InsanusMokrassar/0.12.8
0.12.8
2022-09-07 00:57:17 +06:00
4b13491a0e Update CHANGELOG.md 2022-09-07 00:56:48 +06:00
85d516d1e9 Update libs.versions.toml 2022-09-06 23:44:02 +06:00
ac58b6a7e3 bodyOrNull/respondOrNoContent 2022-09-06 21:16:41 +06:00
2cc6126765 start 0.12.8 2022-09-06 21:15:01 +06:00
f94b085850 Merge pull request #185 from InsanusMokrassar/0.12.7
0.12.7
2022-09-05 18:35:56 +06:00
c9822a491b WriteCRUDCacheRepo subscribtions and changeResultsUnchecked(Pagination) 2022-09-05 15:07:33 +06:00
23b2d60295 start 0.12.7 2022-09-05 14:20:58 +06:00
f4bc9eed39 Merge pull request #184 from InsanusMokrassar/0.12.6
0.12.6
2022-08-31 04:10:33 +06:00
e310f188b0 fixed absence of image/* in known mime types 2022-08-31 03:01:06 +06:00
6c1571188c start 0.12.6 2022-08-31 02:59:29 +06:00
945d2fa284 Merge pull request #183 from InsanusMokrassar/0.12.5
0.12.5
2022-08-30 17:38:57 +06:00
020095f1ff Update CHANGELOG.md 2022-08-30 17:37:59 +06:00
b165a76e62 fixes in selectPaginated 2022-08-30 15:20:45 +06:00
03c8830672 start 0.12.5 2022-08-30 15:17:09 +06:00
38448da89b Merge pull request #182 from InsanusMokrassar/0.12.4
0.12.4
2022-08-23 13:02:19 +06:00
2ade5aff91 update dependencies 2022-08-23 12:57:16 +06:00
29bf6e80ec start 0.12.4 2022-08-23 12:55:52 +06:00
027e927e1b Merge pull request #181 from InsanusMokrassar/0.12.3
0.12.3
2022-08-22 01:44:17 +06:00
fa061f88e2 refactor 2022-08-22 01:42:04 +06:00
1d01b65b5f fix 2022-08-22 01:26:28 +06:00
2c2b364167 add default unoptimized realization of selectByIds 2022-08-22 01:25:07 +06:00
7c5fc9bf7c selectPaginated and abstract exposed keyvalue(s) repos 2022-08-22 01:11:09 +06:00
193d22ff20 start 0.12.3 2022-08-22 00:02:34 +06:00
0f3b553ba2 Merge pull request #180 from InsanusMokrassar/0.12.2
0.12.2
2022-08-19 16:25:24 +06:00
4eb1013446 Update CHANGELOG.md 2022-08-19 15:54:46 +06:00
be1b2563ec Update libs.versions.toml 2022-08-19 15:54:30 +06:00
1c9c2f1e70 Update libs.versions.toml 2022-08-19 02:00:29 +06:00
52dfded741 start 0.12.2 2022-08-19 01:59:09 +06:00
0a115e5cf4 Merge pull request #179 from InsanusMokrassar/0.12.1
0.12.1
2022-08-11 23:50:12 +06:00
c349af999b Update CHANGELOG.md 2022-08-11 23:36:01 +06:00
9a5709a34d Update libs.versions.toml 2022-08-11 22:52:34 +06:00
28d311160b start 0.12.1 2022-08-11 22:47:56 +06:00
1ca7081a40 Merge pull request #176 from InsanusMokrassar/0.12.0
0.12.0
2022-08-04 13:55:30 +06:00
5ce8ebe82c Merge branch 'master' into 0.12.0 2022-08-04 13:51:12 +06:00
a7a88b29b9 Merge pull request #178 from InsanusMokrassar/0.11.14
0.11.14
2022-08-04 13:48:18 +06:00
cd7b982385 update android version 2022-08-04 12:50:36 +06:00
ee59100075 fix of build 2022-08-04 12:36:23 +06:00
f808ac58ef Delete HmacSHA256.kt 2022-08-04 11:30:15 +06:00
f33ada5396 deprecations handling 2022-08-04 10:59:04 +06:00
984d781f2f fill changelog 2022-08-04 10:14:52 +06:00
dd33e1e8bc change version of kotlin 2022-08-04 10:05:29 +06:00
2950de29e5 fixes 2022-08-04 09:59:05 +06:00
50a8799f9d update serialization 2022-08-04 09:47:29 +06:00
bdb0ce6fc7 Update libs.versions.toml 2022-08-04 09:19:57 +06:00
18ec2bca96 Update libs.versions.toml 2022-08-03 10:09:38 +06:00
937ef48794 Update libs.versions.toml 2022-08-01 21:47:05 +06:00
6331f13e9a actualize changelog 2022-07-26 14:58:28 +06:00
5213a2ff8e add kdocs for pagination 2022-07-26 14:58:14 +06:00
087d7452fd encode by default pages number 2022-07-26 14:54:52 +06:00
703094c924 add several additional constructors for the pagination result 2022-07-26 13:56:47 +06:00
eea645c865 PaginationResult#objectsCount 2022-07-25 13:31:15 +06:00
324832a189 start 0.11.14 2022-07-25 13:14:55 +06:00
d55d735c51 Merge pull request #177 from InsanusMokrassar/0.11.13
0.11.13
2022-07-22 16:34:58 +06:00
e3ff1b9609 fill changelog 2022-07-22 16:31:33 +06:00
70c31966ca Update gradle-wrapper.properties 2022-07-18 14:33:04 +06:00
0e4188882f Update libs.versions.toml 2022-07-18 14:08:23 +06:00
6bf0ce92ba Update libs.versions.toml 2022-07-18 13:51:00 +06:00
d51bdc5086 Update gradle.properties 2022-07-18 13:48:09 +06:00
f04f262cee Update libs.versions.toml 2022-07-18 13:16:56 +06:00
0f172055ef Update gradle.properties 2022-07-18 13:11:02 +06:00
e5dd4363f1 Merge pull request #175 from InsanusMokrassar/0.11.12
0.11.12
2022-07-02 02:50:47 +06:00
a3a48bbaac fixes in file key value repo 2022-07-02 02:28:43 +06:00
5e716fb9a8 start 0.11.12 2022-07-02 02:26:58 +06:00
11a36153cc Merge pull request #174 from InsanusMokrassar/0.11.11
0.11.11: improvements of cache repos
2022-06-30 14:03:29 +06:00
8bee354f04 improvements of cache repos 2022-06-30 13:59:32 +06:00
f7dd2b5ce7 Merge pull request #173 from InsanusMokrassar/0.11.11
0.11.11
2022-06-30 13:41:43 +06:00
8ca10c00bb update dokka workflow 2022-06-30 13:41:12 +06:00
905c7e8eda deprecate hmacSha256 2022-06-30 13:19:37 +06:00
d4c5e849bf deprecate BodyPair 2022-06-30 13:15:19 +06:00
8250a2a021 start 0.11.11 2022-06-30 13:14:06 +06:00
01b3df7b8c Update github_release.gradle 2022-06-30 10:12:54 +06:00
daa6e4aff5 Merge pull request #172 from InsanusMokrassar/0.11.10
0.11.10
2022-06-30 02:56:14 +06:00
23bcb26a58 complete improvements in caches 2022-06-30 02:44:44 +06:00
e55f60c30b rename unlimited kv cache 2022-06-30 00:22:07 +06:00
0d0c16e16d add full cache repos 2022-06-29 23:53:49 +06:00
540d5cce7c start add full repos caches 2022-06-29 19:43:58 +06:00
a548b00979 update repos cache 2022-06-29 19:31:57 +06:00
4b7ca6d565 start 0.11.10 2022-06-29 19:29:38 +06:00
0473fa238c Merge pull request #171 from InsanusMokrassar/0.11.9
0.11.9
2022-06-29 01:56:03 +06:00
cfc7119697 update dependencies 2022-06-28 23:06:09 +06:00
22a6520d3e start 0.11.9 2022-06-28 23:01:32 +06:00
fb25e91191 start 0.12.0 2022-06-28 22:57:21 +06:00
c116b270b6 Merge pull request #170 from InsanusMokrassar/0.11.8
0.11.8
2022-06-28 14:18:11 +06:00
aa2d598689 fixes in FileKeyValueRepo 2022-06-28 14:13:04 +06:00
5ef3bb746b start 0.11.8 2022-06-28 14:12:12 +06:00
037616e271 Merge pull request #169 from InsanusMokrassar/0.11.7
0.11.7
2022-06-28 03:07:54 +06:00
abbea906f1 fixes 2022-06-28 01:46:24 +06:00
9132e216c9 add several extensions for MapperRepo 2022-06-28 01:43:57 +06:00
12a7e3c4af fixes 2022-06-28 01:06:46 +06:00
b40c093917 SimpleMapper, SimpleSuspendableMapper, mappers for CRUDRepo 2022-06-28 00:56:51 +06:00
7ac12455c8 add ReadCRUDRepo mappers 2022-06-28 00:19:25 +06:00
5043eec7a2 start 0.11.7 2022-06-27 23:35:10 +06:00
cf31f53e01 Merge pull request #168 from InsanusMokrassar/0.11.6
0.11.6
2022-06-24 00:19:34 +06:00
cd22d76fa7 fsms fixes 2022-06-23 18:47:59 +06:00
c8759843f7 start 0.11.6 2022-06-23 17:19:07 +06:00
781bbcc012 Merge pull request #167 from InsanusMokrassar/0.11.5
0.11.5
2022-06-23 12:50:31 +06:00
6da29c0686 add note about 0.11.4 2022-06-23 12:49:09 +06:00
fcdb6fc45a start and finish 0.11.5 -.- 2022-06-23 12:48:24 +06:00
e785a99bd7 Merge pull request #166 from InsanusMokrassar/0.11.4
0.11.4
2022-06-22 22:37:30 +06:00
121e513fdd fixes in tempUpload of js part 2022-06-22 22:36:06 +06:00
9cf01ab54f improve compose list state creation 2022-06-22 16:56:51 +06:00
c655107681 renames in compose states creation 2022-06-22 16:52:19 +06:00
91a5af6a9a fix of changelog 2022-06-22 16:10:58 +06:00
86e74c0a6f add extension toMutableListState 2022-06-22 16:09:52 +06:00
d8f01f21a0 start 0.11.4 2022-06-22 16:09:35 +06:00
9fb8626d8c Rewrite crud.yml 2022-06-16 15:51:22 +06:00
1c52e04cdb Merge pull request #164 from InsanusMokrassar/0.11.3
0.11.3
2022-06-14 23:59:49 +06:00
798128256e fixes in websockets 2022-06-14 23:48:10 +06:00
72c2df47fd start 0.11.3 2022-06-14 19:35:17 +06:00
c9c6d4c0c1 Merge pull request #163 from InsanusMokrassar/0.11.2
0.11.2
2022-06-13 23:21:14 +06:00
2f4f9f3003 fixes 2022-06-13 23:20:25 +06:00
22e8f8e5d6 start 0.11.2 2022-06-13 22:19:33 +06:00
04cf8c3d9a Merge pull request #162 from InsanusMokrassar/0.11.1
0.11.1
2022-06-12 16:32:05 +06:00
52198be543 fixes in tempupload 2022-06-12 14:26:41 +06:00
80fd5a489b add pagination type info in crud read repo client 2022-06-11 22:37:32 +06:00
b187043ee1 fixes in kv and kvs of ktor repos 2022-06-10 22:25:16 +06:00
8965752055 start 0.11.1 2022-06-10 22:23:45 +06:00
b796620267 Merge pull request #161 from InsanusMokrassar/0.11.0
0.11.0
2022-06-05 02:54:05 +06:00
62df81bb4e update uuid and partially fill changelog 2022-06-05 00:34:45 +06:00
0a8e0f6178 complete swagger templates 2022-06-04 17:12:05 +06:00
f705020aaa fixes in packages 2022-06-04 16:55:23 +06:00
78bd3b9853 replaces of new API of ktor 2022-06-04 16:52:01 +06:00
992b283597 add kv swagger template 2022-06-04 16:47:22 +06:00
8fc1ff1d59 fixes and filling of cru.yml for swagger 2022-06-04 16:18:48 +06:00
3bbde61f39 renames in crud repos and some little refactoring 2022-06-04 15:15:17 +06:00
8d955c4b9d renames in kevalues repos 2022-06-04 15:00:23 +06:00
18593c530b renames in key value repos 2022-06-04 14:42:16 +06:00
78903cd4eb complete improvements in repos ktor parts 2022-06-04 00:22:36 +06:00
eaa143f7d7 kv rework, fixes in map keyvalue repo, tests 2022-06-03 22:33:26 +06:00
bcb0e42fa2 add new started clients for crud 2022-06-03 17:52:58 +06:00
8eed435302 start improvements in ktor parts 2022-06-03 12:22:51 +06:00
0e4a63057f start 0.11.0 2022-06-02 23:50:06 +06:00
1af5faa440 Merge pull request #159 from InsanusMokrassar/0.10.8
0.10.8
2022-05-29 20:06:14 +06:00
7412217b0c add Element.isOverflow 2022-05-29 10:22:57 +06:00
3e749d75b7 start 0.10.8 2022-05-29 10:19:39 +06:00
846b5c87c9 Merge pull request #158 from InsanusMokrassar/0.10.7
0.10.7
2022-05-28 23:59:54 +06:00
475 changed files with 19861 additions and 6870 deletions

View File

@@ -1,17 +1,14 @@
name: Publish package to GitHub Packages
name: Build
on: [push]
jobs:
publishing:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
java-version: 17
- name: Rewrite version
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -22,7 +19,6 @@ jobs:
run: ./gradlew build
- name: Publish
continue-on-error: true
run: ./gradlew --no-parallel publishAllPublicationsToGithubPackagesRepository
run: ./gradlew publishAllPublicationsToGiteaRepository
env:
GITHUBPACKAGES_USER: ${{ github.actor }}
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}

View File

@@ -10,12 +10,9 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
java-version: 17
- name: Build
run: ./gradlew dokkaHtml
run: ./gradlew build && ./gradlew dokkaHtml
- name: Publish KDocs
uses: peaceiris/actions-gh-pages@v3
with:

View File

@@ -1,8 +0,0 @@
job("Build and run tests") {
container(displayName = "Run gradle build", image = "openjdk:11") {
kotlinScript { api ->
// here can be your complex logic
api.gradlew("build")
}
}
}

View File

@@ -1,5 +1,921 @@
# Changelog
## 0.20.32
* `Versions`:
* `Okio`: `3.7.0` -> `3.8.0`
* `Resources`:
* Make `StringResource` serializable
* Add several variants of builder usages
## 0.20.31
* `Versions`:
* `Ktor`: `2.3.7` -> `2.3.8`
## 0.20.30
* `Versions`:
* `Exposed`: `0.46.0` -> `0.47.0`
## 0.20.29
* `Versions`:
* `Kotlin`: `1.9.21` -> `1.9.22`
* `Compose`: `1.5.11` -> `1.5.12`
* `Korlibs`: `5.3.0` -> `5.3.1`
## 0.20.28
* `Versions`:
* `Kotlin`: `1.9.22` -> `1.9.21` (downgrade)
* `Compose`: `1.6.0-dev13691` -> `1.5.11` (downgrade)
## 0.20.27
* `Versions`:
* `Kotlin`: `1.9.21` -> `1.9.22`
* `Compose`: `1.5.11` -> `1.6.0-dev13691`
## 0.20.26
* `Versions`:
* `Exposed`: `0.45.0` -> `0.46.0`. **This update brinds new api deprecations in exposed**
* `Resources`:
* Add opportunity to get default translation by passing `null` as `IetfLang` argument
* Add several useful extensions to get translations in `JS` target
## 0.20.25
* `Colors`:
* `Common`:
* Module inited
## 0.20.24
**Since this version depdendencies of klock and krypto replaced with `com.soywiz.korge:korlibs-time` and `com.soywiz.korge:korlibs-crypto`**
* `Versions`:
* `Klock` (since now `KorlibsTime`): `4.0.10` -> `5.3.0`
* `Krypto` (since now `KorlibsCrypto`): `4.0.10` -> `5.3.0`
* `Serialization`:
* `Mapper`:
* `Mapper` pass decoder into callback of deserialization strategy
* `Mapper` pass encoder into callback of serialization strategy
## 0.20.23
* `Versions`:
* `Koin`: `3.5.0` -> `3.5.3`
* `Okio`: `3.6.0` -> `3.7.0`
* `LanguageCodes`:
* Fixes in intermediate language codes (like `Chinese.Hans`)
* Rename `IetfLanguageCode` to `IetfLang`
* Rename all subsequent functions (including serializer)
* New lazy properties `knownLanguageCodesMap`, `knownLanguageCodesMapByLowerCasedKeys` and several others
## 0.20.22
* `Common`:
* Add opportunity to create own `Diff` with base constructor
## 0.20.21
* `Resources`:
* Inited
## 0.20.20
* `Repos`:
* `Exposed`:
* Add opportunity for setup flows in `AbstractExposedCRUDRepo`
## 0.20.19
* `Versions`:
* `Ktor`: `2.3.6` -> `2.3.7`
## 0.20.18
* `Coroutines`:
* `SpecialMutableStateFlow` now extends `MutableStateFlow`
* `Compose`:
* Deprecate `FlowState` due to its complexity in fixes
## 0.20.17
* `Versions`:
* `Serialization`: `1.6.1` -> `1.6.2`
## 0.20.16
* `Versions`:
* `Exposed`: `0.44.1` -> `0.45.0`
* `Coroutines`:
* Add `SpecialMutableStateFlow`
* `Compose`:
* Add `FlowState`
## 0.20.15
* `Versions`:
* `Kotlin`: `1.9.20` -> `1.9.21`
* `KSLog`: `1.3.0` -> `1.3.1`
* `Compose`: `1.5.10` -> `1.5.11`
## 0.20.14
* `Versions`:
* `Serialization`: `1.6.0` -> `1.6.1`
* `KSLog`: `1.2.4` -> `1.3.0`
## 0.20.13
* `Versions`:
* `Ktor`: `2.3.5` -> `2.3.6`
* `UUID`: `0.8.1` -> `0.8.2`
## 0.20.12
**It is experimental migration onto new gradle version. Be careful in use of this version**
**This update have JDK 17 in `compatibility` and `target` versions**
## 0.20.11
* `Versions`:
* `Kotlin`: `1.9.20-RC2` -> `1.9.20`
* `Exposed`: `0.44.0` -> `0.44.1`
* `Compose`: `1.5.10-rc02` -> `1.5.10`
* `Coroutines`:
* `SmartRWLocker` now will wait first unlock of write mutex for acquiring read
## 0.20.10
* `Versions`:
* `Kotlin`: `1.9.20-RC` -> `1.9.20-RC1`
* `KSLog`: `1.2.1` -> `1.2.2`
* `Compose`: `1.5.10-rc01` -> `1.5.10-rc02`
* `RecyclerView`: `1.3.1` -> `1.3.2`
## 0.20.9
* Most of common modules now supports `linuxArm64` target
## 0.20.8
**THIS VERSION CONTAINS UPDATES OF DEPENDENCIES UP TO RC VERSIONS. USE WITH CAUTION**
* `Versions`:
* `Kotlin`: `1.9.20-Beta2` -> `1.9.20-RC`
* `Compose`: `1.5.10-beta02` -> `1.5.10-rc01`
## 0.20.7
**THIS VERSION CONTAINS UPDATES OF DEPENDENCIES UP TO BETA VERSIONS. USE WITH CAUTION**
* `Versions`:
* `Kotlin`: `1.9.10` -> `1.9.20-Beta2`
* `Compose`: `1.5.1` -> `1.5.10-beta02`
* `Exposed`: `0.43.0` -> `0.44.0`
* `Ktor`: `2.3.4` -> `2.3.5`
* `Koin`: `3.4.3` -> `3.5.0`
* `Okio`: `3.5.0` -> `3.6.0`
* `Android Core`: `1.10.1` -> `1.12.0`
* `Android Compose Material`: `1.1.1` -> `1.1.2`
## 0.20.6
* `Repos`:
* `Exposed`
* Fixes in exposed key-values repos
## 0.20.5
* `Coroutines`:
* Fixes in `SmartRWLocker`
## 0.20.4
* `Versions`:
* `Kotlin`: `1.9.0` -> `1.9.10`
* `KSLog`: `1.2.0` -> `1.2.1`
* `Compose`: `1.5.0` -> `1.5.1`
* `UUID`: `0.8.0` -> `0.8.1`
## 0.20.3
* `Versions`:
* `Compose`: `1.4.3` -> `1.5.0`
* `Exposed`: `0.42.1` -> `0.43.0`
* `Ktor`: `2.3.3` -> `2.3.4`
* `Repos`:
* `Cache`:
* Fixes in locks of caches
## 0.20.2
* All main repos uses `SmartRWLocker`
* `Versions`:
* `Serialization`: `1.5.1` -> `1.6.0`
* `Exposed`: `0.42.0` -> `0.42.1`
* `Korlibs`: `4.0.9` -> `4.0.10`
* `Androis SDK`: `33` -> `34`
## 0.20.1
* `SmallTextField`:
* Module is initialized
* `Pickers`:
* Module is initialized
* `Coroutines`:
* Add `SmartSemaphore`
* Add `SmartRWLocker`
## 0.20.0
* `Versions`:
* `Kotlin`: `1.8.22` -> `1.9.0`
* `KSLog`: `1.1.1` -> `1.2.0`
* `Exposed`: `0.41.1` -> `0.42.0`
* `UUID`: `0.7.1` -> `0.8.0`
* `Korlibs`: `4.0.3` -> `4.0.9`
* `Ktor`: `2.3.2` -> `2.3.3`
* `Okio`: `3.4.0` -> `3.5.0`
## 0.19.9
* `Versions`:
* `Koin`: `3.4.2` -> `3.4.3`
* `Startup`:
* Now it is possible to start application in synchronous way
## 0.19.8
* `Versions`:
* `Coroutines`: `1.7.2` -> `1.7.3`
* `Kotlin`: `1.8.20` -> `1.8.22`
* `Compose`: `1.4.1` -> `1.4.3`
* `Okio`: `3.3.0` -> `3.4.0`
* `RecyclerView`: `1.3.0` -> `1.3.1`
* `Fragment`: `1.6.0` -> `1.6.1`
* `Repos`:
* Fixes In `KeyValueRepo.clear()` of almost all inheritors of `KeyValueRepo`
* `Cache`:
* All full caches got `skipStartInvalidate` property. By default, this property is `false` and fully caching repos
will be automatically invalidated on start of their work
## 0.19.7
* `Versions`:
* `Coroutines`: `1.7.1` -> `1.7.2`
## 0.19.6
* `Versions`:
* `Coroutines`: `1.6.4` -> `1.7.1`
* `Ktor`: `2.3.1` -> `2.3.2`
* `Compose`: `1.4.0` -> `1.4.1`
## 0.19.5
* `Repos`:
* `Generator`:
* Fixes in new type generation
## 0.19.4
* `Versions`:
* `Koin`: `3.4.1` -> `3.4.2`
* `Android Fragments`: `1.5.7` -> `1.6.0`
* `Koin`
* `Generator`
* Fixes in new generic generator part
## 0.19.3
* `Koin`
* `Generator`
* New getter methods now available with opportunity to use parameters
* Old notation `*Single` and `*Factory` is deprecated since this release. With old
will be generated new `single*` and `factory*` notations for new generations
* Add opportunity to use generic-oriented koin definitions
## 0.19.2
* `Versions`:
* `Ktor`: `2.3.0` -> `2.3.1`
* `Koin`: `3.4.0` -> `3.4.1`
* `Uuid`: `0.7.0` -> `0.7.1`
## 0.19.1
* `Versions`:
* `Korlibs`: `4.0.1` -> `4.0.3`
* `Kotlin Poet`: `1.13.2` -> `1.14.0`
## 0.19.0
* `Versions`:
* `Korlibs`: `3.4.0` -> `4.0.1`
## 0.18.4
* `Koin`:
* New extension `lazyInject`
## 0.18.3
* `Versions`:
* `Serialization`: `1.5.0` -> `1.5.1`
* `Android Cor Ktx`: `1.10.0` -> `1.10.1`
## 0.18.2
* `Startup`:
* Now internal `Json` is fully customizable
## 0.18.1
* `Common`:
* Add `MapDiff`
* `Coroutines`:
* Add `SmartMutex`
## 0.18.0
**ALL PREVIOUSLY DEPRECATED FUNCTIONALITY HAVE BEEN REMOVED**
* `Versions`:
* `Android Fragments`: `1.5.6` -> `1.5.7`
* `Ktor`:
* `Server`:
* Now it is possible to take query parameters as list
* `Repos`:
* `Common`:
* New `WriteKeyValuesRepo.removeWithValue`
* `Cache`:
* Rename full caching factories functions to `fullyCached`
## 0.17.8
* `Versions`:
* `Ktor`: `2.2.4` -> `2.3.0`
## 0.17.7
* `Versions`:
* `Android CoreKtx`: `1.9.0` -> `1.10.0`
* `Startup`:
* Add support of `linuxX64` and `mingwX64` platforms
## 0.17.6
* `Versions`:
* `Kotlin`: `1.8.10` -> `1.8.20`
* `KSLog`: `1.0.0` -> `1.1.1`
* `Compose`: `1.3.1` -> `1.4.0`
* `Koin`: `3.3.2` -> `3.4.0`
* `RecyclerView`: `1.2.1` -> `1.3.0`
* `Fragment`: `1.5.5` -> `1.5.6`
* Experimentally (`!!!`) add `linuxX64` and `mingwX64` targets
## 0.17.5
* `Common`:
* Conversations of number primitives with bounds care
* `Repos`:
* `Common`:
* By default, `getAll` for repos will take all the size of repo as page size
* New extension for all built-in repos `maxPagePagination`
* All the repos got `getAll` functions
## 0.17.4
* `Serialization`:
* `Mapper`:
* Module inited
* `Versions`:
* `Compose`: `1.3.1-rc02` -> `1.3.1`
## 0.17.3
* `Common`:
* Add `fixed` extensions for `Float` and `Double`
* New function `emptyDiff`
* Now you may pass custom `comparisonFun` to all `diff` functions
## 0.17.2
* `FSM`:
* `DefaultStatesManager.onUpdateContextsConflictResolver` and `DefaultStatesManager.onStartContextsConflictResolver` now return `false` by default
## 0.17.1
* **Hotfix** for absence of jvm dependencies in android modules
* `Versions`:
* `Ktor`: `2.2.3` -> `2.2.4`
## 0.17.0
* `Versions`:
* `Kotlin`: `1.7.20` -> `1.8.10`
* `Serialization`: `1.4.1` -> `1.5.0`
* `KSLog`: `0.5.4` -> `1.0.0`
* `AppCompat`: `1.6.0` -> `1.6.1`
## 0.16.13
* `Repos`:
* `Generator`:
* Module has been created
## 0.16.12
* `Repos`:
* `Exposed`:
* `CommonExposedRepo.selectByIds` uses `foldRight` by default instead of raw foreach
* `Koin`:
* `Generator`:
* Module has been created
## 0.16.11
* `LanguageCodes`:
* In android and JVM targets now available `toJavaLocale` and from Java `Locale` conversations from/to
`IetfLanguageCode`
## 0.16.10
* `Repos`:
* `Cache`:
* New transformer type: `ReadCRUDFromKeyValueRepo`
* New transformer type: `ReadKeyValueFromCRUDRepo`
* `Pagination`:
* New `paginate` extensions with `reversed` support for `List`/`Set`
## 0.16.9
* `Versions`:
* `Koin`: `3.2.2` -> `3.3.2`
* `AppCompat`: `1.5.1` -> `1.6.0`
* `Ktor`:
* `Client`
* `HttpResponse.bodyOrNull` now retrieve callback to check if body should be received or null
* New extension `HttpResponse.bodyOrNullOnNoContent`
## 0.16.8
* `Versions`:
* `Ktor`: `2.2.2` -> `2.2.3`
* `Ktor`:
* `Client`
* Fixes in `HttpClient.uniUpload`
* `Server`
* Fixes in `PartData.FileItem.download`
* `Repos`:
* `Cache`:
* New type of caches: `FallbackCacheRepo`
* Fixes in `Write*` variants of cached repos
* New type `ActionWrapper`
* New `AutoRecache*` classes for all types of repos as `FallbackCacheRepo`s
* `Common`:
* New transformations for key-value and key-values vice-verse
* Fixes in `FileReadKeyValueRepo`
## 0.16.7
* `Common`:
* New extensions `ifTrue`/`ifFalse`/`alsoIfTrue`/`alsoIfFalse`/`letIfTrue`/`letIfFalse`
* `Diff` now is serializable
* Add `IndexedValue` serializer
* `repeatOnFailure` extending: now you may pass any lambda to check if continue to try/do something
* `Compose`:
* New extension `MutableState.asState`
* `Coroutines`:
* `Compose`:
* All the `Flow` conversations to compose `State`/`MutableState`/`SnapshotStateList`/`List` got several new
parameters
* `Flow.toMutableState` now is deprecated in favor to `asMutableComposeState`
* `Repos`:
* `Cache`:
* New type `FullCacheRepo`
* New type `CommonCacheRepo`
* `CacheRepo` got `invalidate` method. It will fully reload `FullCacheRepo` and just clear `CommonCacheRepo`
* New extensions `KVCache.actualizeAll`
## 0.16.6
* `Startup`:
* `Launcher`:
* Improvements in `StartLauncherPlugin#start` methods
* Add opportunity to pass second argument on `JVM` platform as log level
* `Repos`:
* `Ktor`:
* `Client`:
* All clients repos got opportunity to customize their flows
* `Exposed`:
* Extensions `eqOrIsNull` and `neqOrIsNotNull` for `Column`
## 0.16.5
* `Versions`:
* `Ktor`: `2.2.1` -> `2.2.2`
## 0.16.4
* `Coroutines`:
* Create `launchInCurrentThread`
## 0.16.3
* `Startup`:
* `Launcher`:
* All starting API have been moved into `StartLauncherPlugin` and do not require serialize/deserialize cycle for now
## 0.16.2
* `Versions`:
* `Compose`: `1.2.1` -> `1.2.2`
* `Startup`:
* Module become available on `JS` target
## 0.16.1
* `Coroutines`:
* New `runCatchingSafely`/`safelyWithResult` with receivers
* `SafeWrapper`:
* Module inited
## 0.16.0
* `Versions`:
* `Ktor`: `2.1.3` -> `2.2.1`
* `Android Fragment`: `1.5.3` -> `1.5.5`
## 0.15.1
* `Startup`:
* Inited :)
* `Plugin`:
* Inited :)
* `Launcher`:
* Inited :)
## 0.15.0
* `Repos`:
* `CRUD`:
* `Common`:
* New method `ReadCRUDRepo#getIdsByPagination`
* `Android`:
* `AbstractAndroidCRUDRepo` got new abstract method `toId`
* `Exposed`:
* `CommonExposedRepo` new abstract property `asId`
* `Ktor`:
* `Client`:
* `KtorReadCRUDRepoClient` now requires `paginationIdType`
* `LanguageCodes`:
* Updates and fixes in generation
* `MimeTypes`:
* Updates and fixes in generation
## 0.14.4
* `Common`:
* `JVM`:
* New extension `downloadToTempFile`
* `Ktor`:
* `Server`:
* Small fix in `handleUniUpload`
* `ApplicationCall#uniloadMultipartFile` now uses `uniloadMultipart`
* `Common`:
* New extension `downloadToTempFile`
* `Client`:
* New extensions on top of `uniUpload`
## 0.14.3
* `Common`:
* New type `Progress`
* `Ktor`:
* `Client`:
* New universal `uniUpload` extension for `HttpClient`
* `Server`:
* New universal `handleUniUpload` extension for `ApplicationCall`
* Add extensions `download` and `downloadToTemporalFile`
## 0.14.2
* `Versions`:
* `Exposed`: `0.40.1` -> `0.41.1`
## 0.14.1
* `Versions`:
* `Klock`: `3.3.1` -> `3.4.0`
* `UUID`: `0.5.0` -> `0.6.0`
## 0.14.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
* `Versions`:
* `Kotlin`: `1.7.10` -> `1.7.20`
* `Klock`: `3.3.0` -> `3.3.1`
* `Compose`: `1.2.0` -> `1.2.1`
* `Exposed`: `0.39.2` -> `0.40.1`
## 0.13.2
* `Versions`:
* `Klock`: `3.1.0` -> `3.3.0`
* `Ktor`: `2.1.2` -> `2.1.3`
## 0.13.1
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo#createAndInsertId` now is optional and returns nullable value
## 0.13.0
**ALL DEPRECATIONS HAVE BEEN REMOVED**
**A LOT OF KTOR METHODS RELATED TO UnifierRouter/UnifiedRequester HAVE BEEN REMOVED**
* `Repos`:
* `Exposed`:
* `AbstractExposedWriteCRUDRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `createAndInsertId`
* Old `update` method has been deprecated and not recommended to override anymore in realizations
* Old `insert` method now is `open` instead of `abstract` and can be omitted
* `AbstractExposedKeyValueRepo` got two new methods: `update` with `it` as `UpdateBuilder<Int>` and `insertKey`
* Old `update` method has been deprecated and not recommended to override anymore
* Old `insert` method now is `open` instead of `abstract` and can be omitted in realizations
## 0.12.17
* `Versions`:
* `JB Compose`: `1.2.0-alpha01-dev774` -> `1.2.0-beta02`
* `Ktor`: `2.1.1` -> `2.1.2`
* `Koin`: `3.2.0` -> `3.2.2`
## 0.12.16
* `Coroutines`:
* `Android`:
* Add class `FlowOnHierarchyChangeListener`
* Add `ViewGroup#setOnHierarchyChangeListenerRecursively(OnHierarchyChangeListener)`
## 0.12.15
* `Common`:
* `applyDiff` will return `Diff` object since this release
* `Android`:
* New functions/extensions `findViewsByTag` and `findViewsByTagInActivity`
* `Coroutines`:
* Add `Flow` extensions `flatMap`, `flatMapNotNull` and `flatten`
* Add `Flow` extensions `takeNotNull` and `filterNotNull`
## 0.12.14
* `Versions`:
* `Android CoreKTX`: `1.8.0` -> `1.9.0`
* `Android AppCompat`: `1.4.2` -> `1.5.1`
* Android Compile SDK: 32 -> 33
* Android Build Tools: 32.0.0 -> 33.0.0
* `Common`:
* `Android`:
* Add `argumentOrNull`/`argumentOrThrow` delegates for fragments
* `Coroutines`:
* Rewrite `awaitFirstWithDeferred` onto `CompletableDeferred` instead of coroutines suspending
## 0.12.13
* `Coroutines`:
* Add opportunity to use markers in actors (solution of [#160](https://github.com/InsanusMokrassar/MicroUtils/issues/160))
* `Koin`:
* Module inited :)
* `Repos`:
* `Android`:
* Add typealias `KeyValueSPRepo` and opportunity to create shared preferences `KeyValue` repo with `KeyValueStore(...)` (fix of [#155](https://github.com/InsanusMokrassar/MicroUtils/issues/155))
## 0.12.12
* `Common`:
* `Compose`:
* `JS`:
* Add `SkeletonAnimation` stylesheet
## 0.12.11
* `Repos`:
* `Cache`:
* Override `KeyValue` cache method `values`
## 0.12.10
* `Repos`:
* `Cache`:
* Hotfix in key values `get`
## 0.12.9
* `Versions`:
* `Klock`: `3.0.0` -> `3.1.0`
* `Repos`:
* `Cache`:
* Fixes in key values cache
## 0.12.8
* `Versions`:
* `Ktor`: `2.1.0` -> `2.1.1`
* `Compose`: `1.2.0-alpha01-dev764` -> `1.2.0-alpha01-dev774`
* `Ktor`:
* `Client`:
* New extension `HttpClient#bodyOrNull` which returns `null` in case when server responded with `No Content` (204)
* `Server`:
* New extension `ApplicationCall#respondOrNoContent` which responds `No Content` (204) when passed data is null
## 0.12.7
* `Repos`:
* `Cache`:
* Force `WriteCRUDCacheRepo` to subscribe on new and updated objects of parent repo
* `Pagination`:
* New function `changeResultsUnchecked(Pagination)`
## 0.12.6
* `MimeeTypes>`:
* Fixed absence of `image/*` in known mime types
## 0.12.5
* `Repos`:
* `Exposed`:
* Fixes in `paginate` extensions
## 0.12.4
* `Versions`:
* `Kotlin`: `1.7.0` -> `1.7.10`
* `Compose`: `1.2.0-alpha01-dev755` -> `1.2.0-alpha01-dev764`
## 0.12.3
* `Repos`:
* `Exposed`:
* Add abstract exposed variants of `KeyValue` and `KeyValues` repos
* Add new extension `Query#selectPaginated`
## 0.12.2
* `Versions`:
* `Serialization`: `1.4.0-RC` -> `1.4.0`
* `Compose`: `1.2.0-alpha01-dev753` -> `1.2.0-alpha01-dev755`
## 0.12.1
* `Versions`:
* `Ktor`: `2.0.3` -> `2.1.0`
## 0.12.0
**OLD DEPRECATIONS HAVE BEEN REMOVED**
**MINIMAL ANDROID API HAS BEEN ENLARGED UP TO API 21 (Android 5.0)**
* `Versions`
* `Kotlin`: `1.6.21` -> `1.7.0`
* `Coroutines`: `1.6.3` -> `1.6.4`
* `Exposed`: `0.38.2` -> `0.39.2`
* `Compose`: `1.2.0-alpha01-dev729` -> `1.2.0-alpha01-dev753`
* `Klock`: `2.7.0` -> `3.0.0`
* `uuid`: `0.4.1` -> `0.5.0`
* `Android Core KTX`: `1.7.0` -> `1.8.0`
* `Android AppCompat`: `1.4.1` -> `1.4.2`
* `Ktor`:
* All previously standard functions related to work with binary data by default have been deprecated
## 0.11.14
* `Pagination`:
* `PaginationResult` got new field `objectsNumber` which by default is a times between `pagesNumber` and `size`
## 0.11.13
* `Versions`:
* `Coroutines`: `1.6.3` -> `1.6.4`
* `Compose`: `1.2.0-alpha01-dev629` -> `1.2.0-alpha01-dev731`
## 0.11.12
* `Repos`:
* `Common`:
* `JVM`:
* Fixes in `ReadFileKeyValueRepo` methods (`values`/`keys`)
## 0.11.11
* `Crypto`:
* `hmacSha256` has been deprecated
* `Ktor`:
* `Client`:
* `BodyPair` has been deprecated
* `Repos`:
* `Cache`:
* New interface `CacheRepo`
* New interface `FullCacheRepo`
* `actualize*` methods inside of full cache repos now open for overriding
## 0.11.10
* `Repos`:
* `Cache`:
* `KVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
* `SimpleKVCache` has been replaced to the package `dev.inmo.micro_utils.repos.cache`
* New `KVCache` subtype - `FullKVCache`
* Add `Full*` variants of standard repos
* Add `cached`/`caching` (for write repos) extensions for all standard types of repos
## 0.11.9
* `Versions`
* `Coroutines`: `1.6.1` -> `1.6.3`
* `Ktor`: `2.0.2` -> `2.0.3`
* `Compose`: `1.2.0-alpha01-dev686` -> `1.2.0-alpha01-dev729`
## 0.11.8
* `Repos`:
* `Common`:
* Fixes in `FileKeyValueRepo`
## 0.11.7
* `Common`:
* New abstractions `SimpleMapper` and `SimpleSuspendableMapper`
* `Repos`:
* `Common`:
* Add mappers for `CRUDRepo`
## 0.11.6
* `FSM`:
* `Common`
* Several fixes related to the jobs handling
## 0.11.5
* `Coroutines`:
* `Compose`:
* Add extension `StateFlow#asMutableComposeListState` and `StateFlow#asComposeList`
* Add extension `StateFlow#asMutableComposeState`/`StateFlow#asComposeState`
## 0.11.4
**THIS VERSION HAS BEEN BROKEN, DO NOT USE IT**
## 0.11.3
* `Ktor`:
* Support of `WebSockets` has been improved
* `Client`:
* New extensions: `HttpClient#openBaseWebSocketFlow`, `HttpClient#openWebSocketFlow`, `HttpClient#openSecureWebSocketFlow`
## 0.11.2
* `Ktor`:
* Support of `WebSockets` has been improved and added fixes inside of clients
## 0.11.1
* `Repos`
* `Ktor`
* In `configureReadKeyValueRepoRoutes` and `configureReadKeyValuesRepoRoutes` configurators fixed requiring of `reversed` property
## 0.11.0
* `Versions`
* `UUID`: `0.4.0` -> `0.4.1`
* `Ktor`
* `Client`:
* New extension fun `HttpResponse#throwOnUnsuccess`
* All old functions, classes and extensions has been rewritten with new ktor-way with types info and keeping `ContentNegotiation` in mind
* `Server`:
* All old functions, classes and extensions has been rewritten with new ktor-way with types info and keeping `ContentNegotiation` in mind
* `Repos`
* `Ktor`:
* Fully rewritten work with all declared repositories
* All old functions, classes and extensions has been rewritten with new ktor-way with types info and keeping `ContentNegotiation` in mind
## 0.10.8
* `Common`
* Add `Element.isOverflow*` extension properties
## 0.10.7
* `Pagination`:

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.android.alerts.common"/>

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.android.alerts.recyclerview"/>

View File

@@ -0,0 +1,18 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
alias(libs.plugins.jb.compose)
}
apply from: "$mppProjectWithSerializationAndComposePresetPath"
kotlin {
sourceSets {
androidMain {
dependencies {
api project(":micro_utils.android.smalltextfield")
}
}
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,27 @@
package dev.inmo.micro_utils.android.pickers
import androidx.compose.animation.core.*
internal suspend fun Animatable<Float, AnimationVector1D>.fling(
initialVelocity: Float,
animationSpec: DecayAnimationSpec<Float>,
adjustTarget: ((Float) -> Float)?,
block: (Animatable<Float, AnimationVector1D>.() -> Unit)? = null,
): AnimationResult<Float, AnimationVector1D> {
val targetValue = animationSpec.calculateTargetValue(value, initialVelocity)
val adjustedTarget = adjustTarget?.invoke(targetValue)
return if (adjustedTarget != null) {
animateTo(
targetValue = adjustedTarget,
initialVelocity = initialVelocity,
block = block
)
} else {
animateDecay(
initialVelocity = initialVelocity,
animationSpec = animationSpec,
block = block,
)
}
}

View File

@@ -0,0 +1,222 @@
package dev.inmo.micro_utils.android.pickers
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.exponentialDecay
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ContentAlpha
import androidx.compose.material.IconButton
import androidx.compose.material.ProvideTextStyle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.center
import androidx.compose.ui.unit.dp
import dev.inmo.micro_utils.android.smalltextfield.SmallTextField
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.absoluteValue
import kotlin.math.roundToInt
private inline fun PointerInputScope.checkContains(offset: Offset): Boolean {
return ((size.center.x - offset.x).absoluteValue < size.width / 2) && ((size.center.y - offset.y).absoluteValue < size.height / 2)
}
// src: https://gist.github.com/vganin/a9a84653a9f48a2d669910fbd48e32d5
@OptIn(ExperimentalTextApi::class, ExperimentalComposeUiApi::class)
@Composable
fun NumberPicker(
number: Int,
modifier: Modifier = Modifier,
range: IntRange? = null,
textStyle: TextStyle = LocalTextStyle.current,
arrowsColor: Color = MaterialTheme.colorScheme.primary,
allowUseManualInput: Boolean = true,
onStateChanged: (Int) -> Unit = {},
) {
val coroutineScope = rememberCoroutineScope()
val numbersColumnHeight = 36.dp
val halvedNumbersColumnHeight = numbersColumnHeight / 2
val halvedNumbersColumnHeightPx = with(LocalDensity.current) { halvedNumbersColumnHeight.toPx() }
fun animatedStateValue(offset: Float): Int = number - (offset / halvedNumbersColumnHeightPx).toInt()
val animatedOffset = remember { Animatable(0f) }.apply {
if (range != null) {
val offsetRange = remember(number, range) {
val value = number
val first = -(range.last - value) * halvedNumbersColumnHeightPx
val last = -(range.first - value) * halvedNumbersColumnHeightPx
first..last
}
updateBounds(offsetRange.start, offsetRange.endInclusive)
}
}
val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
val animatedStateValue = animatedStateValue(animatedOffset.value)
val disabledArrowsColor = arrowsColor.copy(alpha = ContentAlpha.disabled)
val inputFieldShown = if (allowUseManualInput) {
remember { mutableStateOf(false) }
} else {
null
}
Column(
modifier = modifier
.wrapContentSize()
.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState { deltaY ->
if (inputFieldShown ?.value != true) {
coroutineScope.launch {
animatedOffset.snapTo(animatedOffset.value + deltaY)
}
}
},
onDragStopped = { velocity ->
if (inputFieldShown ?.value != true) {
coroutineScope.launch {
val endValue = animatedOffset.fling(
initialVelocity = velocity,
animationSpec = exponentialDecay(frictionMultiplier = 20f),
adjustTarget = { target ->
val coercedTarget = target % halvedNumbersColumnHeightPx
val coercedAnchors =
listOf(-halvedNumbersColumnHeightPx, 0f, halvedNumbersColumnHeightPx)
val coercedPoint = coercedAnchors.minByOrNull { abs(it - coercedTarget) }!!
val base =
halvedNumbersColumnHeightPx * (target / halvedNumbersColumnHeightPx).toInt()
coercedPoint + base
}
).endState.value
onStateChanged(animatedStateValue(endValue))
animatedOffset.snapTo(0f)
}
}
}
),
horizontalAlignment = Alignment.CenterHorizontally
) {
val spacing = 4.dp
val upEnabled = range == null || range.first < number
IconButton(
{
onStateChanged(number - 1)
inputFieldShown ?.value = false
},
enabled = upEnabled
) {
Icon(Icons.Default.KeyboardArrowUp, "", tint = if (upEnabled) arrowsColor else disabledArrowsColor)
}
Spacer(modifier = Modifier.height(spacing))
Box(
modifier = Modifier
.offset { IntOffset(x = 0, y = coercedAnimatedOffset.roundToInt()) },
contentAlignment = Alignment.Center
) {
val baseLabelModifier = Modifier.align(Alignment.Center)
ProvideTextStyle(textStyle) {
Text(
text = (animatedStateValue - 1).toString(),
modifier = baseLabelModifier
.offset(y = -halvedNumbersColumnHeight)
.alpha(coercedAnimatedOffset / halvedNumbersColumnHeightPx)
)
if (inputFieldShown ?.value == true) {
val currentValue = remember { mutableStateOf(number.toString()) }
val focusRequester = remember { FocusRequester() }
SmallTextField(
currentValue.value,
{
val asDigit = it.toIntOrNull()
when {
(asDigit == null && it.isEmpty()) -> currentValue.value = (range ?.first ?: 0).toString()
(asDigit != null && (range == null || asDigit in range)) -> currentValue.value = it
else -> { /* do nothing */ }
}
},
baseLabelModifier.focusRequester(focusRequester).width(IntrinsicSize.Min).pointerInput(number) {
detectTapGestures {
if (!checkContains(it)) {
currentValue.value.toIntOrNull() ?.let(onStateChanged)
inputFieldShown.value = false
}
}
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
),
keyboardActions = KeyboardActions {
currentValue.value.toIntOrNull() ?.let(onStateChanged)
inputFieldShown.value = false
},
singleLine = true,
textStyle = textStyle
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
} else {
Text(
text = animatedStateValue.toString(),
modifier = baseLabelModifier
.alpha(1 - abs(coercedAnimatedOffset) / halvedNumbersColumnHeightPx)
.clickable {
if (inputFieldShown ?.value == false) {
inputFieldShown.value = true
}
}
)
}
Text(
text = (animatedStateValue + 1).toString(),
modifier = baseLabelModifier
.offset(y = halvedNumbersColumnHeight)
.alpha(-coercedAnimatedOffset / halvedNumbersColumnHeightPx)
)
}
}
Spacer(modifier = Modifier.height(spacing))
val downEnabled = range == null || range.last > number
IconButton(
{
onStateChanged(number + 1)
inputFieldShown ?.value = false
},
enabled = downEnabled
) {
Icon(Icons.Default.KeyboardArrowDown, "", tint = if (downEnabled) arrowsColor else disabledArrowsColor)
}
}
}

View File

@@ -0,0 +1,156 @@
package dev.inmo.micro_utils.android.pickers
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.exponentialDecay
import androidx.compose.foundation.gestures.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import kotlin.math.*
@OptIn(ExperimentalTextApi::class, ExperimentalComposeUiApi::class)
@Composable
fun <T> SetPicker(
current: T,
dataList: List<T>,
modifier: Modifier = Modifier,
textStyle: TextStyle = LocalTextStyle.current,
arrowsColor: Color = MaterialTheme.colorScheme.primary,
dataToString: @Composable (T) -> String = { it.toString() },
onStateChanged: (T) -> Unit = {},
) {
val coroutineScope = rememberCoroutineScope()
val numbersColumnHeight = 8.dp + with(LocalDensity.current) {
textStyle.lineHeight.toDp()
}
val numbersColumnHeightPx = with(LocalDensity.current) { numbersColumnHeight.toPx() }
val halvedNumbersColumnHeight = numbersColumnHeight / 2
val halvedNumbersColumnHeightPx = with(LocalDensity.current) { halvedNumbersColumnHeight.toPx() }
val index = dataList.indexOfFirst { it === current }.takeIf { it > -1 } ?: dataList.indexOf(current)
val lastIndex = dataList.size - 1
fun animatedStateValue(offset: Float): Int = index - (offset / halvedNumbersColumnHeightPx).toInt()
val animatedOffset = remember { Animatable(0f) }.apply {
val offsetRange = remember(index, lastIndex) {
val value = index
val first = -(lastIndex - value) * halvedNumbersColumnHeightPx
val last = value * halvedNumbersColumnHeightPx
first..last
}
updateBounds(offsetRange.start, offsetRange.endInclusive)
}
val indexAnimatedOffset = if (animatedOffset.value > 0) {
(index - floor(animatedOffset.value / halvedNumbersColumnHeightPx).toInt())
} else {
(index - ceil(animatedOffset.value / halvedNumbersColumnHeightPx).toInt())
}
val coercedAnimatedOffset = animatedOffset.value % halvedNumbersColumnHeightPx
val boxOffset = (indexAnimatedOffset * halvedNumbersColumnHeightPx) - coercedAnimatedOffset
val disabledArrowsColor = arrowsColor.copy(alpha = ContentAlpha.disabled)
val scrollState = rememberScrollState()
Column(
modifier = modifier
.wrapContentSize()
.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState { deltaY ->
coroutineScope.launch {
animatedOffset.snapTo(animatedOffset.value + deltaY)
}
},
onDragStopped = { velocity ->
coroutineScope.launch {
val endValue = animatedOffset.fling(
initialVelocity = velocity,
animationSpec = exponentialDecay(frictionMultiplier = 20f),
adjustTarget = { target ->
val coercedTarget = target % halvedNumbersColumnHeightPx
val coercedAnchors =
listOf(-halvedNumbersColumnHeightPx, 0f, halvedNumbersColumnHeightPx)
val coercedPoint = coercedAnchors.minByOrNull { abs(it - coercedTarget) }!!
val base =
halvedNumbersColumnHeightPx * (target / halvedNumbersColumnHeightPx).toInt()
coercedPoint + base
}
).endState.value
onStateChanged(dataList.elementAt(animatedStateValue(endValue)))
animatedOffset.snapTo(0f)
}
}
),
horizontalAlignment = Alignment.CenterHorizontally
) {
val spacing = 4.dp
val upEnabled = index > 0
IconButton(
{
onStateChanged(dataList.elementAt(index - 1))
},
enabled = upEnabled
) {
Icon(Icons.Default.KeyboardArrowUp, "", tint = if (upEnabled) arrowsColor else disabledArrowsColor)
}
Spacer(modifier = Modifier.height(spacing))
Box(
modifier = Modifier,
contentAlignment = Alignment.Center
) {
ProvideTextStyle(textStyle) {
dataList.forEachIndexed { i, t ->
val alpha = when {
i == indexAnimatedOffset - 1 -> coercedAnimatedOffset / halvedNumbersColumnHeightPx
i == indexAnimatedOffset -> 1 - (abs(coercedAnimatedOffset) / halvedNumbersColumnHeightPx)
i == indexAnimatedOffset + 1 -> -coercedAnimatedOffset / halvedNumbersColumnHeightPx
else -> return@forEachIndexed
}
val offset = when {
i == indexAnimatedOffset - 1 && coercedAnimatedOffset > 0 -> coercedAnimatedOffset - halvedNumbersColumnHeightPx
i == indexAnimatedOffset -> coercedAnimatedOffset
i == indexAnimatedOffset + 1 && coercedAnimatedOffset < 0 -> coercedAnimatedOffset + halvedNumbersColumnHeightPx
else -> return@forEachIndexed
}
Text(
text = dataToString(t),
modifier = Modifier
.alpha(alpha)
.offset(y = with(LocalDensity.current) { offset.toDp() })
)
}
}
}
Spacer(modifier = Modifier.height(spacing))
val downEnabled = index < lastIndex
IconButton(
{
onStateChanged(dataList.elementAt(index + 1))
},
enabled = downEnabled
) {
Icon(Icons.Default.KeyboardArrowDown, "", tint = if (downEnabled) arrowsColor else disabledArrowsColor)
}
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.android.recyclerview"/>

View File

@@ -0,0 +1,18 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
alias(libs.plugins.jb.compose)
}
apply from: "$mppProjectWithSerializationAndComposePresetPath"
kotlin {
sourceSets {
androidMain {
dependencies {
api libs.android.compose.material3
}
}
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,66 @@
package dev.inmo.micro_utils.android.smalltextfield
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.takeOrElse
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.VisualTransformation
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SmallTextField(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
readOnly: Boolean = false,
textStyle: TextStyle = LocalTextStyle.current,
textColor: Color = textStyle.color.takeOrElse {
LocalContentColor.current
},
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
singleLine: Boolean = false,
maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE,
minLines: Int = 1,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
BasicTextField(
value = value,
modifier = modifier,
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
textStyle = textStyle.copy(
color = textColor
),
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
interactionSource = interactionSource,
singleLine = singleLine,
maxLines = maxLines,
minLines = minLines,
cursorBrush = SolidColor(
textStyle.color.takeOrElse {
LocalContentColor.current
}
)
)
}

View File

@@ -9,6 +9,7 @@ buildscript {
dependencies {
classpath libs.buildscript.kt.gradle
classpath libs.buildscript.kt.serialization
classpath libs.buildscript.kt.ksp
classpath libs.buildscript.jb.dokka
classpath libs.buildscript.gh.release
classpath libs.buildscript.android.gradle
@@ -16,12 +17,17 @@ buildscript {
}
}
plugins {
alias(libs.plugins.versions)
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
google()
maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" }
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
}
// temporal crutch until legacy tests will be stabled or legacy target will be removed
@@ -36,3 +42,4 @@ allprojects {
apply from: "./extensions.gradle"
apply from: "./github_release.gradle"
apply from: "./versions_plugin_setup.gradle"

View File

@@ -0,0 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"

View File

@@ -0,0 +1,141 @@
package dev.inmo.micro_utils.colors.common
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
import kotlin.math.floor
/**
* Wrapper for RGBA colors. Receiving [UInt] in main constructor. Each part in main constructor
* configured with `0x00 - 0xff` range. Examples:
*
* * Red: `0xff0000ffu`
* * Red (0.5 capacity): `0xff000088u`
*
* Anyway it is recommended to use
*/
@Serializable
@JvmInline
value class HEXAColor (
val uint: UInt
) : Comparable<HEXAColor> {
val hexa: String
get() = "#${uint.toString(16).padStart(8, '0')}"
val hex: String
get() = hexa.take(7)
val rgba: String
get() = "rgba($r,$g,$b,${aOfOne.toString().take(5)})"
val rgb: String
get() = "rgb($r,$g,$b)"
val shortHex: String
get() = "#${r.shortPart()}${g.shortPart()}${b.shortPart()}"
val shortHexa: String
get() = "$shortHex${a.shortPart()}"
val rgbInt: Int
get() = (uint shr 2).toInt()
val r: Int
get() = ((uint and 0xff000000u) / 0x1000000u).toInt()
val g: Int
get() = ((uint and 0x00ff0000u) / 0x10000u).toInt()
val b: Int
get() = ((uint and 0x0000ff00u) / 0x100u).toInt()
val a: Int
get() = ((uint and 0x000000ffu)).toInt()
val aOfOne: Float
get() = a.toFloat() / (0xff)
init {
require(uint in 0u ..0xffffffffu)
}
constructor(r: Int, g: Int, b: Int, a: Int) : this(
((r * 0x1000000).toLong() + g * 0x10000 + b * 0x100 + a).toUInt()
) {
require(r in 0 ..0xff)
require(g in 0 ..0xff)
require(b in 0 ..0xff)
require(a in 0 ..0xff)
}
constructor(r: Int, g: Int, b: Int, aOfOne: Float = 1f) : this(
r = r, g = g, b = b, a = (aOfOne * 0xff).toInt()
)
override fun toString(): String {
return hexa
}
override fun compareTo(other: HEXAColor): Int = (uint - other.uint).coerceIn(Int.MIN_VALUE.toUInt(), Int.MAX_VALUE.toLong().toUInt()).toInt()
fun copy(
r: Int = this.r,
g: Int = this.g,
b: Int = this.b,
aOfOne: Float = this.aOfOne
) = HEXAColor(r = r, g = g, b = b, aOfOne = aOfOne)
fun copy(
r: Int = this.r,
g: Int = this.g,
b: Int = this.b,
a: Int
) = HEXAColor(r = r, g = g, b = b, a = a)
companion object {
/**
* Parsing color from [color]
*
* Supported formats samples (on Red color based):
*
* * `#f00`
* * `#f00f`
* * `#ff0000`
* * `#ff0000ff`
* * `rgb(255, 0, 0)`
* * `rgba(255, 0, 0, 1)`
*/
fun parseStringColor(color: String): HEXAColor = when {
color.startsWith("#") -> color.removePrefix("#").let { color ->
when (color.length) {
3 -> color.map { "$it$it" }.joinToString(separator = "", postfix = "ff")
4 -> color.take(3).map { "$it$it" }.joinToString(separator = "", postfix = color.takeLast(1).let { "${it}0" })
6 -> "${color}ff"
8 -> color
else -> error("Malfurmed color string: $color. It is expected that color started with # will contains 3, 6 or 8 valuable parts")
}
}
color.startsWith("rgb(") -> color
.removePrefix("rgb(")
.removeSuffix(")")
.replace(Regex("\\s"), "")
.split(",")
.joinToString("", postfix = "ff") {
it.toInt().toString(16).padStart(2, '0')
}
color.startsWith("rgba(") -> color
.removePrefix("rgba(")
.removeSuffix(")")
.replace(Regex("\\s"), "")
.split(",").let {
it.take(3).map { it.toInt().toString(16).padStart(2, '0') } + (it.last().toFloat() * 0xff).toInt().toString(16).padStart(2, '0')
}
.joinToString("")
else -> color
}.lowercase().toUInt(16).let(::HEXAColor)
/**
* Parsing color from [color]
*
* Supported formats samples (on Red color based):
*
* * `#f00`
* * `#ff0000`
* * `#ff0000ff`
* * `rgb(255, 0, 0)`
* * `rgba(255, 0, 0, 1)`
*/
operator fun invoke(color: String) = parseStringColor(color)
private fun Int.shortPart(): String {
return (floor(toFloat() / 16)).toInt().toString(16)
}
}
}

View File

@@ -0,0 +1,175 @@
package dev.inmo.micro_utils.colors.common
import kotlin.math.floor
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class HexColorTests {
val alphaRgbaPrecision = 5
class TestColor(
val color: HEXAColor,
val shortHex: String,
val shortHexa: String,
val hex: String,
val hexa: String,
val rgb: String,
val rgba: String,
val r: Int,
val g: Int,
val b: Int,
val a: Int,
vararg val additionalRGBAVariants: String
)
val testColors: List<TestColor>
get() = listOf(
TestColor(
color = HEXAColor(uint = 0xff0000ffu),
shortHex = "#f00",
shortHexa = "#f00f",
hex = "#ff0000",
hexa = "#ff0000ff",
rgb = "rgb(255,0,0)",
rgba = "rgba(255,0,0,1.0)",
r = 0xff,
g = 0x00,
b = 0x00,
a = 0xff,
"rgba(255,0,0,1)",
),
TestColor(
color = HEXAColor(uint = 0x00ff00ffu),
shortHex = "#0f0",
shortHexa = "#0f0f",
hex = "#00ff00",
hexa = "#00ff00ff",
rgb = "rgb(0,255,0)",
rgba = "rgba(0,255,0,1.0)",
r = 0x00,
g = 0xff,
b = 0x00,
a = 0xff,
"rgba(0,255,0,1)"
),
TestColor(
color = HEXAColor(0x0000ffffu),
shortHex = "#00f",
shortHexa = "#00ff",
hex = "#0000ff",
hexa = "#0000ffff",
rgb = "rgb(0,0,255)",
rgba = "rgba(0,0,255,1.0)",
r = 0x00,
g = 0x00,
b = 0xff,
a = 0xff,
"rgba(0,0,255,1)"
),
TestColor(
color = HEXAColor(0xff000088u),
shortHex = "#f00",
shortHexa = "#f008",
hex = "#ff0000",
hexa = "#ff000088",
rgb = "rgb(255,0,0)",
rgba = "rgba(255,0,0,0.533)",
r = 0xff,
g = 0x00,
b = 0x00,
a = 0x88,
),
TestColor(
color = HEXAColor(0x00ff0088u),
shortHex = "#0f0",
shortHexa = "#0f08",
hex = "#00ff00",
hexa = "#00ff0088",
rgb = "rgb(0,255,0)",
rgba = "rgba(0,255,0,0.533)",
r = 0x00,
g = 0xff,
b = 0x00,
a = 0x88,
),
TestColor(
color = HEXAColor(0x0000ff88u),
shortHex = "#00f",
shortHexa = "#00f8",
hex = "#0000ff",
hexa = "#0000ff88",
rgb = "rgb(0,0,255)",
rgba = "rgba(0,0,255,0.533)",
r = 0x00,
g = 0x00,
b = 0xff,
a = 0x88,
),
TestColor(
color = HEXAColor(0xff000022u),
shortHex = "#f00",
shortHexa = "#f002",
hex = "#ff0000",
hexa = "#ff000022",
rgb = "rgb(255,0,0)",
rgba = "rgba(255,0,0,0.133)",
r = 0xff,
g = 0x00,
b = 0x00,
a = 0x22,
),
TestColor(
color = HEXAColor(0x00ff0022u),
shortHex = "#0f0",
shortHexa = "#0f02",
hex = "#00ff00",
hexa = "#00ff0022",
rgb = "rgb(0,255,0)",
rgba = "rgba(0,255,0,0.133)",
r = 0x00,
g = 0xff,
b = 0x00,
a = 0x22,
),
TestColor(
color = HEXAColor(0x0000ff22u),
shortHex = "#00f",
shortHexa = "#00f2",
hex = "#0000ff",
hexa = "#0000ff22",
rgb = "rgb(0,0,255)",
rgba = "rgba(0,0,255,0.133)",
r = 0x00,
g = 0x00,
b = 0xff,
a = 0x22,
),
)
@Test
fun baseTest() {
testColors.forEach {
assertEquals(it.hex, it.color.hex)
assertEquals(it.hexa, it.color.hexa)
assertEquals(it.shortHex, it.color.shortHex)
assertEquals(it.shortHexa, it.color.shortHexa)
assertEquals(it.rgb, it.color.rgb)
assertTrue(it.rgba == it.color.rgba || it.color.rgba in it.additionalRGBAVariants)
assertEquals(it.r, it.color.r)
assertEquals(it.g, it.color.g)
assertEquals(it.b, it.color.b)
assertEquals(it.a, it.color.a)
}
}
@Test
fun testHexParseColor() {
testColors.forEach {
assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.hex))
assertEquals(it.color, HEXAColor.parseStringColor(it.hexa))
assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.rgb))
assertTrue(it.color.uint.toInt() - HEXAColor.parseStringColor(it.rgba).uint.toInt() in -0x1 .. 0x1, )
assertEquals(it.color.copy(aOfOne = 1f), HEXAColor.parseStringColor(it.shortHex))
assertEquals(it.color.copy(a = floor(it.color.a.toFloat() / 16).toInt() * 0x10), HEXAColor.parseStringColor(it.shortHexa))
}
}
}

View File

@@ -4,7 +4,7 @@ plugins {
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
kotlin {
sourceSets {
@@ -16,6 +16,24 @@ kotlin {
androidMain {
dependencies {
api project(":micro_utils.coroutines")
api libs.android.fragment
}
dependsOn jvmMain
}
linuxX64Main {
dependencies {
api libs.okio
}
}
mingwX64Main {
dependencies {
api libs.okio
}
}
linuxArm64Main {
dependencies {
api libs.okio
}
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,10 @@
package dev.inmo.micro_utils.common.compose
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
/**
* Converts current [MutableState] to immutable [State] using [derivedStateOf]
*/
fun <T> MutableState<T>.asState(): State<T> = derivedStateOf { this.value }

View File

@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.common.compose
import org.jetbrains.compose.web.css.*
object SkeletonAnimation : StyleSheet() {
val skeletonKeyFrames: CSSNamedKeyframes by keyframes {
to { backgroundPosition("-20% 0") }
}
fun CSSBuilder.includeSkeletonStyle(
duration: CSSSizeValue<out CSSUnitTime> = 2.s,
timingFunction: AnimationTimingFunction = AnimationTimingFunction.EaseInOut,
iterationCount: Int? = null,
direction: AnimationDirection = AnimationDirection.Normal,
keyFrames: CSSNamedKeyframes = skeletonKeyFrames,
hideChildren: Boolean = true,
hideText: Boolean = hideChildren
) {
backgroundImage("linear-gradient(110deg, rgb(236, 236, 236) 40%, rgb(245, 245, 245) 50%, rgb(236, 236, 236) 65%)")
backgroundSize("200% 100%")
backgroundPosition("180% 0")
animation(keyFrames) {
duration(duration)
timingFunction(timingFunction)
iterationCount(iterationCount)
direction(direction)
}
if (hideText) {
property("color", "${Color.transparent} !important")
}
if (hideChildren) {
child(self, universal) style {
property("visibility", "hidden")
}
}
}
val skeleton by style {
includeSkeletonStyle()
}
}

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.common.compose"/>

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,76 @@
package dev.inmo.micro_utils.common
import android.os.Bundle
import android.os.Parcelable
import androidx.fragment.app.Fragment
import java.io.Serializable
import kotlin.reflect.KProperty
object ArgumentPropertyNullableDelegate {
operator fun <T: Any> getValue(thisRef: Fragment, property: KProperty<*>): T? {
val arguments = thisRef.arguments ?: return null
val key = property.name
return when (property.getter.returnType.classifier) {
// Scalars
String::class -> arguments.getString(key)
Boolean::class -> arguments.getBoolean(key)
Byte::class -> arguments.getByte(key)
Char::class -> arguments.getChar(key)
Double::class -> arguments.getDouble(key)
Float::class -> arguments.getFloat(key)
Int::class -> arguments.getInt(key)
Long::class -> arguments.getLong(key)
Short::class -> arguments.getShort(key)
// References
Bundle::class -> arguments.getBundle(key)
CharSequence::class -> arguments.getCharSequence(key)
Parcelable::class -> arguments.getParcelable(key)
// Scalar arrays
BooleanArray::class -> arguments.getBooleanArray(key)
ByteArray::class -> arguments.getByteArray(key)
CharArray::class -> arguments.getCharArray(key)
DoubleArray::class -> arguments.getDoubleArray(key)
FloatArray::class -> arguments.getFloatArray(key)
IntArray::class -> arguments.getIntArray(key)
LongArray::class -> arguments.getLongArray(key)
ShortArray::class -> arguments.getShortArray(key)
Array::class -> {
val componentType = property.returnType.classifier ?.javaClass ?.componentType!!
@Suppress("UNCHECKED_CAST") // Checked by reflection.
when {
Parcelable::class.java.isAssignableFrom(componentType) -> {
arguments.getParcelableArray(key)
}
String::class.java.isAssignableFrom(componentType) -> {
arguments.getStringArray(key)
}
CharSequence::class.java.isAssignableFrom(componentType) -> {
arguments.getCharSequenceArray(key)
}
Serializable::class.java.isAssignableFrom(componentType) -> {
arguments.getSerializable(key)
}
else -> {
val valueType = componentType.canonicalName
throw IllegalArgumentException(
"Illegal value array type $valueType for key \"$key\""
)
}
}
}
Serializable::class -> arguments.getSerializable(key)
else -> null
} as? T
}
}
object ArgumentPropertyNonNullableDelegate {
operator fun <T: Any> getValue(thisRef: Fragment, property: KProperty<*>): T {
return ArgumentPropertyNullableDelegate.getValue<T>(thisRef, property)!!
}
}
fun argumentOrNull() = ArgumentPropertyNullableDelegate
fun argumentOrThrow() = ArgumentPropertyNonNullableDelegate

View File

@@ -0,0 +1,61 @@
package dev.inmo.micro_utils.common
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import androidx.fragment.app.Fragment
fun findViewsByTag(viewGroup: ViewGroup, tag: Any?): List<View> {
return viewGroup.children.flatMap {
findViewsByTag(it, tag)
}.toList()
}
fun findViewsByTag(viewGroup: ViewGroup, key: Int, tag: Any?): List<View> {
return viewGroup.children.flatMap {
findViewsByTag(it, key, tag)
}.toList()
}
fun findViewsByTag(view: View, tag: Any?): List<View> {
val result = mutableListOf<View>()
if (view.tag == tag) {
result.add(view)
}
if (view is ViewGroup) {
result.addAll(findViewsByTag(view, tag))
}
return result.toList()
}
fun findViewsByTag(view: View, key: Int, tag: Any?): List<View> {
val result = mutableListOf<View>()
if (view.getTag(key) == tag) {
result.add(view)
}
if (view is ViewGroup) {
result.addAll(findViewsByTag(view, key, tag))
}
return result.toList()
}
fun Activity.findViewsByTag(tag: Any?) = rootView ?.let {
findViewsByTag(it, tag)
}
fun Activity.findViewsByTag(key: Int, tag: Any?) = rootView ?.let {
findViewsByTag(it, key, tag)
}
fun Fragment.findViewsByTag(tag: Any?) = view ?.let {
findViewsByTag(it, tag)
}
fun Fragment.findViewsByTag(key: Int, tag: Any?) = view ?.let {
findViewsByTag(it, key, tag)
}
fun Fragment.findViewsByTagInActivity(tag: Any?) = activity ?.findViewsByTag(tag)
fun Fragment.findViewsByTagInActivity(key: Int, tag: Any?) = activity ?.findViewsByTag(key, tag)

View File

@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.common
import android.app.Activity
import android.view.View
val Activity.rootView: View?
get() = findViewById<View?>(android.R.id.content) ?.rootView ?: window.decorView.findViewById<View?>(android.R.id.content) ?.rootView

View File

@@ -2,6 +2,8 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.Serializable
private inline fun <T> getObject(
additional: MutableList<T>,
iterator: Iterator<T>
@@ -14,16 +16,33 @@ private inline fun <T> getObject(
/**
* Diff object which contains information about differences between two [Iterable]s
*
* See tests for more info
*
* @param removed The objects which has been presented in the old collection but absent in new one. Index here is the index in the old collection
* @param added The object which appear in new collection only. Indexes here show the index in the new collection
* @param replaced Pair of old-new changes. First object has been presented in the old collection on its
* [IndexedValue.index] place, the second one is the object in new collection. Both have indexes due to the fact that in
* case when some value has been replaced after adds or removes in original collection the object index will be changed
*
* @see calculateDiff
*/
data class Diff<T> internal constructor(
val removed: List<IndexedValue<T>>,
@Serializable
data class Diff<T> @Warning(warning) constructor(
val removed: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>,
/**
* Old-New values pairs
*/
val replaced: List<Pair<IndexedValue<T>, IndexedValue<T>>>,
val added: List<IndexedValue<T>>
)
val replaced: List<Pair<@Serializable(IndexedValueSerializer::class) IndexedValue<T>, @Serializable(IndexedValueSerializer::class) IndexedValue<T>>>,
val added: List<@Serializable(IndexedValueSerializer::class) IndexedValue<T>>
) {
fun isEmpty(): Boolean = removed.isEmpty() && replaced.isEmpty() && added.isEmpty()
companion object {
private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
}
}
fun <T> emptyDiff(): Diff<T> = Diff(emptyList(), emptyList(), emptyList())
private inline fun <T> performChanges(
potentialChanges: MutableList<Pair<IndexedValue<T>?, IndexedValue<T>?>>,
@@ -32,14 +51,14 @@ private inline fun <T> performChanges(
changedList: MutableList<Pair<IndexedValue<T>, IndexedValue<T>>>,
removedList: MutableList<IndexedValue<T>>,
addedList: MutableList<IndexedValue<T>>,
strictComparison: Boolean
comparisonFun: (T?, T?) -> Boolean
) {
var i = -1
val (oldObject, newObject) = potentialChanges.lastOrNull() ?: return
for ((old, new) in potentialChanges.take(potentialChanges.size - 1)) {
i++
val oldOneEqualToNewObject = old ?.value === newObject ?.value || (old ?.value == newObject ?.value && !strictComparison)
val newOneEqualToOldObject = new ?.value === oldObject ?.value || (new ?.value == oldObject ?.value && !strictComparison)
val oldOneEqualToNewObject = comparisonFun(old ?.value, newObject ?.value)
val newOneEqualToOldObject = comparisonFun(new ?.value, oldObject ?.value)
if (oldOneEqualToNewObject || newOneEqualToOldObject) {
changedList.addAll(
potentialChanges.take(i).mapNotNull {
@@ -93,7 +112,7 @@ private inline fun <T> performChanges(
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
comparisonFun: (T?, T?) -> Boolean
): Diff<T> {
var i = -1
var j = -1
@@ -121,7 +140,7 @@ fun <T> Iterable<T>.calculateDiff(
}
when {
oldObject === newObject || (oldObject == newObject && !strictComparison) -> {
comparisonFun(oldObject, newObject) -> {
changedObjects.addAll(potentiallyChangedObjects.map {
@Suppress("UNCHECKED_CAST")
it as Pair<IndexedValue<T>, IndexedValue<T>>
@@ -132,23 +151,49 @@ fun <T> Iterable<T>.calculateDiff(
potentiallyChangedObjects.add(oldObject ?.let { IndexedValue(i, oldObject) } to newObject ?.let { IndexedValue(j, newObject) })
val previousOldsAdditionsSize = additionalInOld.size
val previousNewsAdditionsSize = additionalInNew.size
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
i -= (additionalInOld.size - previousOldsAdditionsSize)
j -= (additionalInNew.size - previousNewsAdditionsSize)
}
}
}
potentiallyChangedObjects.add(null to null)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, strictComparison)
performChanges(potentiallyChangedObjects, additionalInOld, additionalInNew, changedObjects, removedObjects, addedObjects, comparisonFun)
return Diff(removedObjects.toList(), changedObjects.toList(), addedObjects.toList())
}
/**
* Calculating [Diff] object
*
* @param strictComparison If this parameter set to true, objects which are not equal by links will be used as different
* objects. For example, in case of two "Example" string they will be equal by value, but CAN be different by links
*/
fun <T> Iterable<T>.calculateDiff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(
other,
comparisonFun = if (strictComparison) {
{ t1, t2 ->
t1 === t2
}
} else {
{ t1, t2 ->
t1 === t2 || t1 == t2 // small optimization for cases when t1 and t2 are the same - comparison will be faster potentially
}
}
)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
strictComparison: Boolean = false
): Diff<T> = calculateDiff(other, strictComparison)
inline fun <T> Iterable<T>.diff(
other: Iterable<T>,
noinline comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(other, comparisonFun)
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new)
inline fun <T> Diff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, strictComparison = false)
inline fun <T> StrictDiff(old: Iterable<T>, new: Iterable<T>) = old.calculateDiff(new, true)
/**
@@ -158,6 +203,23 @@ inline fun <T> Iterable<T>.calculateStrictDiff(
other: Iterable<T>
) = calculateDiff(other, strictComparison = true)
/**
* Applies [diff] to [this] [MutableList]
*/
fun <T> MutableList<T>.applyDiff(
diff: Diff<T>
) {
for (i in diff.removed.indices.sortedDescending()) {
removeAt(diff.removed[i].index)
}
diff.added.forEach { (i, t) ->
add(i, t)
}
diff.replaced.forEach { (_, new) ->
set(new.index, new.value)
}
}
/**
* This method call [calculateDiff] with strict mode [strictComparison] and then apply differences to [this]
* mutable list
@@ -165,14 +227,27 @@ inline fun <T> Iterable<T>.calculateStrictDiff(
fun <T> MutableList<T>.applyDiff(
source: Iterable<T>,
strictComparison: Boolean = false
) = calculateDiff(source, strictComparison).let {
for (i in it.removed.indices.sortedDescending()) {
removeAt(it.removed[i].index)
}
it.added.forEach { (i, t) ->
add(i, t)
}
it.replaced.forEach { (_, new) ->
set(new.index, new.value)
}
): Diff<T> = calculateDiff(source, strictComparison).also {
applyDiff(it)
}
/**
* This method call [calculateDiff] and then apply differences to [this]
* mutable list
*/
fun <T> MutableList<T>.applyDiff(
source: Iterable<T>,
comparisonFun: (T?, T?) -> Boolean
): Diff<T> = calculateDiff(source, comparisonFun).also {
applyDiff(it)
}
/**
* Reverse [this] [Diff]. Result will contain [Diff.added] on [Diff.removed] (and vice-verse), all the
* [Diff.replaced] values will be reversed too
*/
fun <T> Diff<T>.reversed() = Diff(
removed = added,
replaced = replaced.map { it.second to it.first },
added = removed
)

View File

@@ -1,3 +1,5 @@
@file:Suppress("unused", "NOTHING_TO_INLINE")
package dev.inmo.micro_utils.common
import kotlinx.serialization.*
@@ -21,11 +23,10 @@ import kotlinx.serialization.encoding.*
sealed interface Either<T1, T2> {
val optionalT1: Optional<T1>
val optionalT2: Optional<T2>
@Deprecated("Use optionalT1 instead", ReplaceWith("optionalT1"))
val t1: T1?
val t1OrNull: T1?
get() = optionalT1.dataOrNull()
@Deprecated("Use optionalT2 instead", ReplaceWith("optionalT2"))
val t2: T2?
val t2OrNull: T2?
get() = optionalT2.dataOrNull()
}
@@ -33,7 +34,7 @@ class EitherSerializer<T1, T2>(
t1Serializer: KSerializer<T1>,
t2Serializer: KSerializer<T2>,
) : KSerializer<Either<T1, T2>> {
@OptIn(InternalSerializationApi::class)
@OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class)
override val descriptor: SerialDescriptor = buildSerialDescriptor(
"TypedSerializer",
SerialKind.CONTEXTUAL
@@ -96,7 +97,7 @@ class EitherSerializer<T1, T2>(
*/
@Serializable
data class EitherFirst<T1, T2>(
override val t1: T1
val t1: T1
) : Either<T1, T2> {
override val optionalT1: Optional<T1> = t1.optional
override val optionalT2: Optional<T2> = Optional.absent()
@@ -107,7 +108,7 @@ data class EitherFirst<T1, T2>(
*/
@Serializable
data class EitherSecond<T1, T2>(
override val t2: T2
val t2: T2
) : Either<T1, T2> {
override val optionalT1: Optional<T1> = Optional.absent()
override val optionalT2: Optional<T2> = t2.optional

View File

@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.common
inline fun <T> Boolean.letIfTrue(block: () -> T): T? {
return if (this) {
block()
} else {
null
}
}
inline fun <T> Boolean.letIfFalse(block: () -> T): T? {
return if (this) {
null
} else {
block()
}
}
inline fun Boolean.alsoIfTrue(block: () -> Unit): Boolean {
letIfTrue(block)
return this
}
inline fun Boolean.alsoIfFalse(block: () -> Unit): Boolean {
letIfFalse(block)
return this
}
inline fun <T> Boolean.ifTrue(block: () -> T): T? {
return if (this) {
block()
} else {
null
}
}
inline fun <T> Boolean.ifFalse(block: () -> T): T? {
return if (this) {
null
} else {
block()
}
}

View File

@@ -0,0 +1,30 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class IndexedValueSerializer<T>(private val subSerializer: KSerializer<T>) : KSerializer<IndexedValue<T>> {
private val originalSerializer = PairSerializer(Int.serializer(), subSerializer)
override val descriptor: SerialDescriptor
get() = originalSerializer.descriptor
override fun deserialize(decoder: Decoder): IndexedValue<T> {
val pair = originalSerializer.deserialize(decoder)
return IndexedValue(
pair.first,
pair.second
)
}
override fun serialize(encoder: Encoder, value: IndexedValue<T>) {
originalSerializer.serialize(
encoder,
Pair(value.index, value.value)
)
}
}

View File

@@ -0,0 +1,135 @@
package dev.inmo.micro_utils.common
/**
* Contains diff based on the comparison of objects with the same [K].
*
* @param removed Contains map with keys removed from parent map
* @param changed Contains map with keys values changed new map in comparison with old one
* @param added Contains map with new keys and values
*/
data class MapDiff<K, V> @Warning(warning) constructor(
val removed: Map<K, V>,
val changed: Map<K, Pair<V, V>>,
val added: Map<K, V>
) {
fun isEmpty() = removed.isEmpty() && changed.isEmpty() && added.isEmpty()
inline fun isNotEmpty() = !isEmpty()
companion object {
private const val warning = "This feature can be changed without any warranties. Use with caution and only in case you know what you are doing"
fun <K, V> empty() = MapDiff<K, V>(emptyMap(), emptyMap(), emptyMap())
}
}
private inline fun <K, V> createCompareFun(
strictComparison: Boolean
): (K, V, V) -> Boolean = if (strictComparison) {
{ _, first, second -> first === second }
} else {
{ _, first, second -> first == second }
}
/**
* Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
*
* @param compareFun Will be used to determine changed values
*/
fun <K, V> Map<K, V>.diff(
other: Map<K, V>,
compareFun: (K, V, V) -> Boolean
): MapDiff<K, V> {
val removed: Map<K, V> = (keys - other.keys).associateWith {
getValue(it)
}
val added: Map<K, V> = (other.keys - keys).associateWith {
other.getValue(it)
}
val changed = keys.intersect(other.keys).mapNotNull {
val old = getValue(it)
val new = other.getValue(it)
if (compareFun(it, old, new)) {
return@mapNotNull null
} else {
it to (old to new)
}
}.toMap()
return MapDiff(
removed,
changed,
added
)
}
/**
* Compare [this] [Map] with the [other] one in principle when [other] is newer than [this]
*
* @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
* `equals` will be used
*/
fun <K, V> Map<K, V>.diff(
other: Map<K, V>,
strictComparison: Boolean = false
): MapDiff<K, V> = diff(
other,
compareFun = createCompareFun(strictComparison)
)
/**
* Will apply [mapDiff] to [this] [MutableMap]
*/
fun <K, V> MutableMap<K, V>.applyDiff(
mapDiff: MapDiff<K, V>
) {
mapDiff.apply {
removed.keys.forEach { remove(it) }
changed.forEach { (k, oldNew) ->
put(k, oldNew.second)
}
added.forEach { (k, new) ->
put(k, new)
}
}
}
/**
* Will apply changes with [from] map into [this] one
*
* @param compareFun Will be used to determine changed values
*
* @return [MapDiff] applied to [this] [MutableMap]
*/
fun <K, V> MutableMap<K, V>.applyDiff(
from: Map<K, V>,
compareFun: (K, V, V) -> Boolean
): MapDiff<K, V> {
return diff(from, compareFun).also {
applyDiff(it)
}
}
/**
* Will apply changes with [from] map into [this] one
*
* @param strictComparison If true, will use strict (===) comparison for the values' comparison. Otherwise, standard
* `equals` will be used
*
* @return [MapDiff] applied to [this] [MutableMap]
*/
fun <K, V> MutableMap<K, V>.applyDiff(
from: Map<K, V>,
strictComparison: Boolean = false
): MapDiff<K, V> = applyDiff(
from,
compareFun = createCompareFun(strictComparison)
)
/**
* Reverse [this] [MapDiff]. Result will contain [MapDiff.added] on [MapDiff.removed] (and vice-verse), all the
* [MapDiff.changed] values will be reversed too
*/
fun <K, V> MapDiff<K, V>.reversed(): MapDiff<K, V> = MapDiff(
removed = added,
changed = changed.mapValues { (_, oldNew) -> oldNew.second to oldNew.first },
added = removed
)

View File

@@ -0,0 +1,53 @@
package dev.inmo.micro_utils.common
import kotlin.jvm.JvmName
interface SimpleMapper<T1, T2> {
fun convertToT1(from: T2): T1
fun convertToT2(from: T1): T2
}
@JvmName("convertFromT2")
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T2) = convertToT1(from)
@JvmName("convertFromT1")
fun <T1, T2> SimpleMapper<T1, T2>.convert(from: T1) = convertToT2(from)
class SimpleMapperImpl<T1, T2>(
private val t1: (T2) -> T1,
private val t2: (T1) -> T2,
) : SimpleMapper<T1, T2> {
override fun convertToT1(from: T2): T1 = t1.invoke(from)
override fun convertToT2(from: T1): T2 = t2.invoke(from)
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T1, T2> simpleMapper(
noinline t1: (T2) -> T1,
noinline t2: (T1) -> T2,
) = SimpleMapperImpl(t1, t2)
interface SimpleSuspendableMapper<T1, T2> {
suspend fun convertToT1(from: T2): T1
suspend fun convertToT2(from: T1): T2
}
@JvmName("convertFromT2")
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T2) = convertToT1(from)
@JvmName("convertFromT1")
suspend fun <T1, T2> SimpleSuspendableMapper<T1, T2>.convert(from: T1) = convertToT2(from)
class SimpleSuspendableMapperImpl<T1, T2>(
private val t1: suspend (T2) -> T1,
private val t2: suspend (T1) -> T2,
) : SimpleSuspendableMapper<T1, T2> {
override suspend fun convertToT1(from: T2): T1 = t1.invoke(from)
override suspend fun convertToT2(from: T1): T2 = t2.invoke(from)
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T1, T2> simpleSuspendableMapper(
noinline t1: suspend (T2) -> T1,
noinline t2: suspend (T1) -> T2,
) = SimpleSuspendableMapperImpl(t1, t2)

View File

@@ -41,10 +41,18 @@ data class Optional<T> internal constructor(
inline val <T> T.optional
get() = Optional.presented(this)
inline val <T : Any> T?.optionalOrAbsentIfNull
get() = if (this == null) {
Optional.absent<T>()
} else {
Optional.presented(this)
}
/**
* Will call [block] when data presented ([Optional.dataPresented] == true)
*/
inline fun <T> Optional<T>.onPresented(block: (T) -> Unit): Optional<T> = apply {
@OptIn(Warning::class)
if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) }
}
@@ -52,6 +60,7 @@ inline fun <T> Optional<T>.onPresented(block: (T) -> Unit): Optional<T> = apply
* Will call [block] when data presented ([Optional.dataPresented] == true)
*/
inline fun <T, R> Optional<T>.mapOnPresented(block: (T) -> R): R? = run {
@OptIn(Warning::class)
if (dataPresented) { @Suppress("UNCHECKED_CAST") block(data as T) } else null
}
@@ -59,6 +68,7 @@ inline fun <T, R> Optional<T>.mapOnPresented(block: (T) -> R): R? = run {
* Will call [block] when data absent ([Optional.dataPresented] == false)
*/
inline fun <T> Optional<T>.onAbsent(block: () -> Unit): Optional<T> = apply {
@OptIn(Warning::class)
if (!dataPresented) { block() }
}
@@ -66,27 +76,22 @@ inline fun <T> Optional<T>.onAbsent(block: () -> Unit): Optional<T> = apply {
* Will call [block] when data presented ([Optional.dataPresented] == true)
*/
inline fun <T, R> Optional<T>.mapOnAbsent(block: () -> R): R? = run {
if (!dataPresented) { @Suppress("UNCHECKED_CAST") block() } else null
@OptIn(Warning::class)
if (!dataPresented) { block() } else null
}
/**
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or null otherwise
*/
fun <T> Optional<T>.dataOrNull() = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else null
fun <T> Optional<T>.dataOrNull() = @OptIn(Warning::class) if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else null
/**
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or throw [throwable] otherwise
*/
fun <T> Optional<T>.dataOrThrow(throwable: Throwable) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else throw throwable
fun <T> Optional<T>.dataOrThrow(throwable: Throwable) = @OptIn(Warning::class) if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else throw throwable
/**
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it
*/
inline fun <T> Optional<T>.dataOrElse(block: () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()
/**
* Returns [Optional.data] if [Optional.dataPresented] of [this] is true, or call [block] and returns the result of it
*/
@Deprecated("dataOrElse now is inline", ReplaceWith("dataOrElse", "dev.inmo.micro_utils.common.dataOrElse"))
suspend fun <T> Optional<T>.dataOrElseSuspendable(block: suspend () -> T) = if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()
inline fun <T> Optional<T>.dataOrElse(block: () -> T) = @OptIn(Warning::class) if (dataPresented) @Suppress("UNCHECKED_CAST") (data as T) else block()

View File

@@ -0,0 +1,28 @@
package dev.inmo.micro_utils.common
/**
* Convert [this] [Long] to [Int] with bounds of [Int.MIN_VALUE] and [Int.MAX_VALUE]
*/
fun Long.toCoercedInt(): Int = coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()
/**
* Convert [this] [Long] to [Short] with bounds of [Short.MIN_VALUE] and [Short.MAX_VALUE]
*/
fun Long.toCoercedShort(): Short = coerceIn(Short.MIN_VALUE.toLong(), Short.MAX_VALUE.toLong()).toShort()
/**
* Convert [this] [Long] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
*/
fun Long.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toLong(), Byte.MAX_VALUE.toLong()).toByte()
/**
* Convert [this] [Int] to [Short] with bounds of [Short.MIN_VALUE] and [Short.MAX_VALUE]
*/
fun Int.toCoercedShort(): Short = coerceIn(Short.MIN_VALUE.toInt(), Short.MAX_VALUE.toInt()).toShort()
/**
* Convert [this] [Int] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
*/
fun Int.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toInt(), Byte.MAX_VALUE.toInt()).toByte()
/**
* Convert [this] [Short] to [Byte] with bounds of [Byte.MIN_VALUE] and [Byte.MAX_VALUE]
*/
fun Short.toCoercedByte(): Byte = coerceIn(Byte.MIN_VALUE.toShort(), Byte.MAX_VALUE.toShort()).toByte()

View File

@@ -0,0 +1,37 @@
package dev.inmo.micro_utils.common
import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline
@Serializable
@JvmInline
value class Progress private constructor(
val of1: Double
) {
val of1Float
get() = of1.toFloat()
val of100
get() = of1 * 100
val of100Float
get() = of100.toFloat()
val of100Int
get() = of100.toInt()
init {
require(of1 in rangeOfValues) {
"Progress main value should be in $rangeOfValues, but incoming value is $of1"
}
}
companion object {
val rangeOfValues = 0.0 .. 1.0
val START = Progress(rangeOfValues.start)
val COMPLETED = Progress(rangeOfValues.endInclusive)
operator fun invoke(of1: Double) = Progress(of1.coerceIn(rangeOfValues))
operator fun invoke(part: Number, total: Number) = Progress(
part.toDouble() / total.toDouble()
)
}
}

View File

@@ -0,0 +1,80 @@
@file:Suppress(
"RemoveRedundantCallsOfConversionMethods",
"RedundantVisibilityModifier",
)
package dev.inmo.micro_utils.common
import kotlin.Byte
import kotlin.Double
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.Short
import kotlin.Suppress
public operator fun Progress.plus(other: Progress): Progress = Progress(of1 + other.of1)
public operator fun Progress.minus(other: Progress): Progress = Progress(of1 - other.of1)
public operator fun Progress.plus(i: Byte): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Byte): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Byte): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Byte): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Byte): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Short): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Short): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Short): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Short): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Short): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Int): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Int): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Int): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Int): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Int): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Long): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Long): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Long): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Long): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Long): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Float): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Float): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Float): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Float): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Float): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.plus(i: Double): Progress = Progress((of1 + i).toDouble())
public operator fun Progress.minus(i: Double): Progress = Progress((of1 - i).toDouble())
public operator fun Progress.times(i: Double): Progress = Progress((of1 * i).toDouble())
public operator fun Progress.div(i: Double): Progress = Progress((of1 / i).toDouble())
public operator fun Progress.rem(i: Double): Progress = Progress((of1 % i).toDouble())
public operator fun Progress.compareTo(other: Progress): Int = (of1 - other.of1).toInt()

View File

@@ -1,5 +1,27 @@
package dev.inmo.micro_utils.common
/**
* Executes the given [action] until getting of successful result specified number of [times].
*
* A zero-based index of current iteration is passed as a parameter to [action].
*/
inline fun <R> repeatOnFailure(
onFailure: (Throwable) -> Boolean,
action: () -> R
): Result<R> {
do {
runCatching {
action()
}.onFailure {
if (!onFailure(it)) {
return Result.failure(it)
}
}.onSuccess {
return Result.success(it)
}
} while (true)
}
/**
* Executes the given [action] until getting of successful result specified number of [times].
*
@@ -10,12 +32,23 @@ inline fun <R> repeatOnFailure(
onEachFailure: (Throwable) -> Unit = {},
action: (Int) -> R
): Optional<R> {
repeat(times) {
runCatching {
action(it)
}.onFailure(onEachFailure).onSuccess {
return Optional.presented(it)
var i = 0
val result = repeatOnFailure(
{
onEachFailure(it)
if (i < times) {
i++
true
} else {
false
}
}
) {
action(i)
}
return if (result.isSuccess) {
Optional.presented(result.getOrThrow())
} else {
Optional.absent()
}
return Optional.absent()
}

View File

@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.common
val FixedSignsRange = 0 .. 100
expect fun Float.fixed(signs: Int): Float
expect fun Double.fixed(signs: Int): Double

View File

@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.common
import org.w3c.dom.Element
inline val Element.isOverflowWidth
get() = scrollWidth > clientWidth
inline val Element.isOverflowHeight
get() = scrollHeight > clientHeight
inline val Element.isOverflow
get() = isOverflowHeight || isOverflowWidth

View File

@@ -0,0 +1,4 @@
package dev.inmo.micro_utils.common
actual fun Float.fixed(signs: Int): Float = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toFloat()
actual fun Double.fixed(signs: Int): Double = this.asDynamic().toFixed(signs.coerceIn(FixedSignsRange)).unsafeCast<String>().toDouble()

View File

@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.common
import java.io.File
import java.io.InputStream
import java.util.UUID
fun InputStream.downloadToTempFile(
fileName: String = UUID.randomUUID().toString(),
fileExtension: String? = ".temp",
folder: File? = null
) = File.createTempFile(
fileName,
fileExtension,
folder
).apply {
outputStream().use {
copyTo(it)
}
deleteOnExit()
}

View File

@@ -0,0 +1,12 @@
package dev.inmo.micro_utils.common
import java.math.BigDecimal
import java.math.RoundingMode
actual fun Float.fixed(signs: Int): Float = BigDecimal.valueOf(this.toDouble())
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toFloat();
actual fun Double.fixed(signs: Int): Double = BigDecimal.valueOf(this)
.setScale(signs.coerceIn(FixedSignsRange), RoundingMode.HALF_UP)
.toDouble();

View File

@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.common
import okio.FileSystem
import okio.Path
import okio.use
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.*
import platform.posix.snprintf
import platform.posix.sprintf
@OptIn(ExperimentalForeignApi::class)
actual fun Float.fixed(signs: Int): Float {
return memScoped {
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toFloat()
}
}
@OptIn(ExperimentalForeignApi::class)
actual fun Double.fixed(signs: Int): Double {
return memScoped {
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toDouble()
}
}

View File

@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.common
import okio.FileSystem
import okio.Path
import okio.use
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.*
import platform.posix.snprintf
import platform.posix.sprintf
@OptIn(ExperimentalForeignApi::class)
actual fun Float.fixed(signs: Int): Float {
return memScoped {
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toFloat()
}
}
@OptIn(ExperimentalForeignApi::class)
actual fun Double.fixed(signs: Int): Double {
return memScoped {
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toDouble()
}
}

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.common"/>

View File

@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.common
import okio.FileSystem
import okio.Path
import okio.use
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.*
import platform.posix.snprintf
import platform.posix.sprintf
@OptIn(ExperimentalForeignApi::class)
actual fun Float.fixed(signs: Int): Float {
return memScoped {
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toFloat()
}
}
@OptIn(ExperimentalForeignApi::class)
actual fun Double.fixed(signs: Int): Double {
return memScoped {
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toDouble()
}
}

View File

@@ -4,7 +4,7 @@ plugins {
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
kotlin {
sourceSets {
@@ -22,6 +22,7 @@ kotlin {
dependencies {
api libs.kt.coroutines.android
}
dependsOn(jvmMain)
}
}
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,60 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
import dev.inmo.micro_utils.common.applyDiff
import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
/**
* Each value of [this] [Flow] will trigger [applyDiff] to the result [SnapshotStateList]
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [SnapshotStateList]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T> Flow<List<T>>.asMutableComposeListState(
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<List<T>?> = defaultSafelyWithoutExceptionHandlerWithNull,
): SnapshotStateList<T> {
val state = mutableStateListOf<T>()
val changeBlock: suspend (List<T>) -> Unit = useContextOnChange ?.let {
{
withContext(useContextOnChange) {
state.applyDiff(it)
}
}
} ?: {
state.applyDiff(it)
}
subscribeSafelyWithoutExceptions(scope, onException, changeBlock)
return state
}
/**
* In fact, it is just classcast of [asMutableComposeListState] to [List]
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [List]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*
* @return Changing in time [List] which follow [Flow] values
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <reified T> Flow<List<T>>.asComposeList(
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<List<T>?> = defaultSafelyWithoutExceptionHandlerWithNull,
): List<T> = asMutableComposeListState(scope, useContextOnChange, onException)

View File

@@ -0,0 +1,94 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.*
import dev.inmo.micro_utils.common.compose.asState
import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandlerWithNull
import dev.inmo.micro_utils.coroutines.doInUI
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
/**
* Will map [this] [Flow] as [MutableState]. Returned [MutableState] WILL NOT change source [Flow]
*
* @param initial First value which will be passed to the result [MutableState]
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [MutableState]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
fun <T> Flow<T>.asMutableComposeState(
initial: T,
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): MutableState<T> {
val state = mutableStateOf(initial)
val changeBlock: suspend (T) -> Unit = useContextOnChange ?.let {
{
withContext(useContextOnChange) {
state.value = it
}
}
} ?: {
state.value = it
}
subscribeSafelyWithoutExceptions(scope, onException, block = changeBlock)
return state
}
/**
* Will map [this] [StateFlow] as [MutableState]. Returned [MutableState] WILL NOT change source [StateFlow].
* This conversation will pass its [StateFlow.value] as the first value
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [MutableState]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.asMutableComposeState(
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): MutableState<T> = asMutableComposeState(value, scope, useContextOnChange, onException)
/**
* Will create [MutableState] using [asMutableComposeState] and use [asState] to convert it as immutable state
*
* @param initial First value which will be passed to the result [State]
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [State]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
fun <T> Flow<T>.asComposeState(
initial: T,
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): State<T> {
val state = asMutableComposeState(initial, scope, useContextOnChange, onException)
return state.asState()
}
/**
* Will map [this] [StateFlow] as [State]. This conversation will pass its [StateFlow.value] as the first value
*
* @param scope Will be used to [subscribeSafelyWithoutExceptions] on [this] to update returned [State]
* @param useContextOnChange Will be used to change context inside of [subscribeSafelyWithoutExceptions] to ensure that
* change will happen in the required [CoroutineContext]. [Dispatchers.Main] by default
* @param onException Will be passed to the [subscribeSafelyWithoutExceptions] as uncaught exceptions handler
*/
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.asComposeState(
scope: CoroutineScope,
useContextOnChange: CoroutineContext? = Dispatchers.Main,
noinline onException: ExceptionHandler<T?> = defaultSafelyWithoutExceptionHandlerWithNull,
): State<T> = asComposeState(value, scope, useContextOnChange, onException)

View File

@@ -0,0 +1,46 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.MutableState
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
/**
* This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow].
* Based on [SpecialMutableStateFlow]
*/
@Deprecated("Will be removed soon")
class FlowState<T>(
initial: T,
internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : MutableState<T>,
SpecialMutableStateFlow<T>(initial, internalScope) {
private var internalValue: T = initial
override var value: T
get() = internalValue
set(value) {
internalValue = value
tryEmit(value)
}
override fun onChangeWithoutSync(value: T) {
internalValue = value
super.onChangeWithoutSync(value)
}
override fun component1(): T = value
override fun component2(): (T) -> Unit = { tryEmit(it) }
override fun tryEmit(value: T): Boolean {
internalValue = value
return super.tryEmit(value)
}
override suspend fun emit(value: T) {
internalValue = value
super.emit(value)
}
}
//fun <T> MutableState<T>.asFlowState(scope: CoroutineScope = CoroutineScope(Dispatchers.Main)) = FlowState(this, scope)

View File

@@ -1,23 +0,0 @@
package dev.inmo.micro_utils.coroutines.compose
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
fun <T> Flow<T>.toMutableState(
initial: T,
scope: CoroutineScope
): MutableState<T> {
val state = mutableStateOf(initial)
subscribeSafelyWithoutExceptions(scope) { state.value = it }
return state
}
@Suppress("NOTHING_TO_INLINE")
inline fun <T> StateFlow<T>.toMutableState(
scope: CoroutineScope
): MutableState<T> = toMutableState(value, scope)

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.coroutines.compose"/>

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,50 @@
package dev.inmo.micro_utils.coroutines
import android.view.View
import android.view.ViewGroup
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
/**
* [kotlinx.coroutines.flow.Flow]-based [android.view.ViewGroup.OnHierarchyChangeListener]
*
* @param recursive If set, any call of [onChildViewAdded] will check if child [View] is [ViewGroup] and subscribe to this
* [ViewGroup] too
* @param [_onChildViewAdded] Internal [MutableSharedFlow] which will be used to pass data to [onChildViewAdded] flow
* @param [_onChildViewRemoved] Internal [MutableSharedFlow] which will be used to pass data to [onChildViewRemoved] flow
*/
class FlowOnHierarchyChangeListener(
private val recursive: Boolean = false,
private val _onChildViewAdded: MutableSharedFlow<Pair<View, View>> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE),
private val _onChildViewRemoved: MutableSharedFlow<Pair<View, View>> = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE)
) : ViewGroup.OnHierarchyChangeListener {
val onChildViewAdded = _onChildViewAdded.asSharedFlow()
val onChildViewRemoved = _onChildViewRemoved.asSharedFlow()
/**
* Will emit data into [onChildViewAdded] flow. If [recursive] is true and [child] is [ViewGroup] will also
* subscribe to [child] hierarchy changes.
*
* Due to the fact that this method is not suspendable, [FlowOnHierarchyChangeListener] will use
* [MutableSharedFlow.tryEmit] to send data into [_onChildViewAdded]. That is why its default extraBufferCapacity is
* [Int.MAX_VALUE]
*/
override fun onChildViewAdded(parent: View, child: View) {
_onChildViewAdded.tryEmit(parent to child)
if (recursive && child is ViewGroup) {
child.setOnHierarchyChangeListener(this)
}
}
/**
* Just emit data into [onChildViewRemoved]
*
* Due to the fact that this method is not suspendable, [FlowOnHierarchyChangeListener] will use
* [MutableSharedFlow.tryEmit] to send data into [_onChildViewRemoved]. That is why its default extraBufferCapacity is
* [Int.MAX_VALUE]
*/
override fun onChildViewRemoved(parent: View, child: View) {
_onChildViewRemoved.tryEmit(parent to child)
}
}

View File

@@ -0,0 +1,17 @@
package dev.inmo.micro_utils.coroutines
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
/**
* Use [ViewGroup.setOnHierarchyChangeListener] recursively for all available [ViewGroup]s starting with [this].
* This extension DO NOT guarantee that recursive subscription will happen after this method call
*/
fun ViewGroup.setOnHierarchyChangeListenerRecursively(
listener: OnHierarchyChangeListener
) {
setOnHierarchyChangeListener(listener)
(0 until childCount).forEach {
(getChildAt(it) as? ViewGroup) ?.setOnHierarchyChangeListenerRecursively(listener)
}
}

View File

@@ -1,19 +1,15 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.consumeAsFlow
fun <T> CoroutineScope.actor(
channelCapacity: Int = Channel.UNLIMITED,
block: suspend (T) -> Unit
): Channel<T> {
val channel = Channel<T>(channelCapacity)
launch {
for (data in channel) {
block(data)
}
}
channel.consumeAsFlow().subscribe(this, block)
return channel
}

View File

@@ -0,0 +1,30 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.consumeAsFlow
fun <T> CoroutineScope.actorAsync(
channelCapacity: Int = Channel.UNLIMITED,
markerFactory: suspend (T) -> Any? = { null },
block: suspend (T) -> Unit
): Channel<T> {
val channel = Channel<T>(channelCapacity)
channel.consumeAsFlow().subscribeAsync(this, markerFactory, block)
return channel
}
inline fun <T> CoroutineScope.safeActorAsync(
channelCapacity: Int = Channel.UNLIMITED,
noinline onException: ExceptionHandler<Unit> = defaultSafelyExceptionHandler,
noinline markerFactory: suspend (T) -> Any? = { null },
crossinline block: suspend (T) -> Unit
): Channel<T> = actorAsync(
channelCapacity,
markerFactory
) {
safely(onException) {
block(it)
}
}

View File

@@ -6,23 +6,19 @@ import kotlin.coroutines.*
suspend fun <T> Iterable<Deferred<T>>.awaitFirstWithDeferred(
scope: CoroutineScope,
cancelOnResult: Boolean = true
): Pair<Deferred<T>, T> = suspendCoroutine<Pair<Deferred<T>, T>> { continuation ->
scope.launch(SupervisorJob()) {
val scope = this
forEach {
scope.launch {
continuation.resume(it to it.await())
scope.cancel()
}
): Pair<Deferred<T>, T> {
val resultDeferred = CompletableDeferred<Pair<Deferred<T>, T>>()
val scope = scope.LinkedSupervisorScope()
forEach {
scope.launch {
resultDeferred.complete(it to it.await())
scope.cancel()
}
}
}.also {
if (cancelOnResult) {
forEach {
try {
it.cancel()
} catch (e: IllegalStateException) {
e.printStackTrace()
return resultDeferred.await().also {
if (cancelOnResult) {
forEach {
runCatchingSafely { it.cancel() }
}
}
}

View File

@@ -0,0 +1,39 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.*
import kotlin.js.JsName
import kotlin.jvm.JvmName
inline fun <T, R> Flow<Flow<T>>.flatMap(
crossinline mapper: suspend (T) -> R
) = flow {
collect {
it.collect {
emit(mapper(it))
}
}
}
@JsName("flatMapIterable")
@JvmName("flatMapIterable")
inline fun <T, R> Flow<Iterable<T>>.flatMap(
crossinline mapper: suspend (T) -> R
) = map {
it.asFlow()
}.flatMap(mapper)
inline fun <T, R> Flow<Flow<T>>.flatMapNotNull(
crossinline mapper: suspend (T) -> R
) = flatMap(mapper).takeNotNull()
@JsName("flatMapNotNullIterable")
@JvmName("flatMapNotNullIterable")
inline fun <T, R> Flow<Iterable<T>>.flatMapNotNull(
crossinline mapper: suspend (T) -> R
) = flatMap(mapper).takeNotNull()
fun <T> Flow<Flow<T>>.flatten() = flatMap { it }
@JsName("flattenIterable")
@JvmName("flattenIterable")
fun <T> Flow<Iterable<T>>.flatten() = flatMap { it }

View File

@@ -0,0 +1,6 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.flow.*
fun <T> Flow<T>.takeNotNull() = mapNotNull { it }
fun <T> Flow<T>.filterNotNull() = takeNotNull()

View File

@@ -115,10 +115,21 @@ suspend inline fun <T> runCatchingSafely(
safely(onException, block)
}
suspend inline fun <T, R> T.runCatchingSafely(
noinline onException: ExceptionHandler<R> = defaultSafelyExceptionHandler,
noinline block: suspend T.() -> R
): Result<R> = runCatching {
safely(onException) { block() }
}
suspend inline fun <T> safelyWithResult(
noinline block: suspend CoroutineScope.() -> T
): Result<T> = runCatchingSafely(defaultSafelyExceptionHandler, block)
suspend inline fun <T, R> T.safelyWithResult(
noinline block: suspend T.() -> R
): Result<R> = runCatchingSafely(defaultSafelyExceptionHandler, block)
/**
* Use this handler in cases you wish to include handling of exceptions by [defaultSafelyWithoutExceptionHandler] and
* returning null at one time

View File

@@ -0,0 +1,136 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.isActive
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* It is interface which will work like classic [Mutex], but in difference have [lockStateFlow] for listening of the
* [SmartMutex] state.
*
* There is [Mutable] and [Immutable] realizations. In case you are owner and manager current state of lock, you need
* [Mutable] [SmartMutex]. Otherwise, [Immutable].
*
* Any [Mutable] [SmartMutex] may produce its [Immutable] variant which will contains [lockStateFlow] equal to its
* [Mutable] creator
*/
sealed interface SmartMutex {
val lockStateFlow: StateFlow<Boolean>
/**
* * True - locked
* * False - unlocked
*/
val isLocked: Boolean
get() = lockStateFlow.value
/**
* Immutable variant of [SmartMutex]. In fact will depend on the owner of [lockStateFlow]
*/
class Immutable(override val lockStateFlow: StateFlow<Boolean>) : SmartMutex
/**
* Mutable variant of [SmartMutex]. With that variant you may [lock] and [unlock]. Besides, you may create
* [Immutable] variant of [this] instance with [immutable] factory
*
* @param locked Preset state of [isLocked] and its internal [_lockStateFlow]
*/
class Mutable(locked: Boolean = false) : SmartMutex {
private val _lockStateFlow = MutableStateFlow<Boolean>(locked)
override val lockStateFlow: StateFlow<Boolean> = _lockStateFlow.asStateFlow()
private val internalChangesMutex = Mutex()
fun immutable() = Immutable(lockStateFlow)
/**
* Holds call until this [SmartMutex] will be re-locked. That means that while [isLocked] == true, [holds] will
* wait for [isLocked] == false and then try to lock
*/
suspend fun lock() {
do {
waitUnlock()
val shouldContinue = internalChangesMutex.withLock {
if (_lockStateFlow.value) {
true
} else {
_lockStateFlow.value = true
false
}
}
} while (shouldContinue && currentCoroutineContext().isActive)
}
/**
* Will try to lock this [SmartMutex] immediataly
*
* @return True if lock was successful. False otherwise
*/
suspend fun tryLock(): Boolean {
return if (!_lockStateFlow.value) {
internalChangesMutex.withLock {
if (!_lockStateFlow.value) {
_lockStateFlow.value = true
true
} else {
false
}
}
} else {
false
}
}
/**
* If [isLocked] == true - will change it to false and return true. If current call will not unlock this
* [SmartMutex] - false
*/
suspend fun unlock(): Boolean {
return if (_lockStateFlow.value) {
internalChangesMutex.withLock {
if (_lockStateFlow.value) {
_lockStateFlow.value = false
true
} else {
false
}
}
} else {
false
}
}
}
}
/**
* Will call [SmartMutex.Mutable.lock], then execute [action] and return the result after [SmartMutex.Mutable.unlock]
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun <T> SmartMutex.Mutable.withLock(action: () -> T): T {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
}
lock()
try {
return action()
} finally {
unlock()
}
}
/**
* Will wait until the [SmartMutex.lockStateFlow] of [this] instance will be false.
*
* Anyway, after the end of this block there are no any guaranties that [SmartMutex.isLocked] == false due to the fact
* that some other parties may lock it again
*/
suspend fun SmartMutex.waitUnlock() = lockStateFlow.first { !it }

View File

@@ -0,0 +1,105 @@
package dev.inmo.micro_utils.coroutines
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Composite mutex which works with next rules:
*
* * [acquireRead] require to [writeMutex] be free. Then it will take one lock from [readSemaphore]
* * [releaseRead] will just free up one permit in [readSemaphore]
* * [lockWrite] will lock [writeMutex] and then await while all [readSemaphore] will be freed
* * [unlockWrite] will just unlock [writeMutex]
*/
class SmartRWLocker(private val readPermits: Int = Int.MAX_VALUE, writeIsLocked: Boolean = false) {
private val _readSemaphore = SmartSemaphore.Mutable(permits = readPermits, acquiredPermits = 0)
private val _writeMutex = SmartMutex.Mutable(locked = writeIsLocked)
val readSemaphore: SmartSemaphore.Immutable = _readSemaphore.immutable()
val writeMutex: SmartMutex.Immutable = _writeMutex.immutable()
/**
* Do lock in [readSemaphore] inside of [writeMutex] locking
*/
suspend fun acquireRead() {
_writeMutex.waitUnlock()
_readSemaphore.acquire()
}
/**
* Release one read permit in [readSemaphore]
*/
suspend fun releaseRead(): Boolean {
return _readSemaphore.release()
}
/**
* Locking [writeMutex] and wait while all [readSemaphore] permits will be freed
*/
suspend fun lockWrite() {
_writeMutex.lock()
_readSemaphore.acquire(readPermits)
}
/**
* Unlock [writeMutex]
*/
suspend fun unlockWrite(): Boolean {
return _writeMutex.unlock().also {
if (it) {
_readSemaphore.release(readPermits)
}
}
}
}
/**
* Will call [SmartSemaphore.Mutable.lock], then execute [action] and return the result after [SmartSemaphore.Mutable.unlock]
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun <T> SmartRWLocker.withReadAcquire(action: () -> T): T {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
}
acquireRead()
try {
return action()
} finally {
releaseRead()
}
}
/**
* Will wait until the [SmartSemaphore.permitsStateFlow] of [this] instance will have [permits] count free permits.
*
* Anyway, after the end of this block there are no any guaranties that [SmartSemaphore.freePermits] >= [permits] due to
* the fact that some other parties may lock it again
*/
suspend fun SmartRWLocker.waitReadRelease(permits: Int = 1) = readSemaphore.waitRelease(permits)
/**
* Will call [SmartMutex.Mutable.lock], then execute [action] and return the result after [SmartMutex.Mutable.unlock]
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun <T> SmartRWLocker.withWriteLock(action: () -> T): T {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
}
lockWrite()
try {
return action()
} finally {
unlockWrite()
}
}
/**
* Will wait until the [SmartMutex.lockStateFlow] of [this] instance will be false.
*
* Anyway, after the end of this block there are no any guaranties that [SmartMutex.isLocked] == false due to the fact
* that some other parties may lock it again
*/
suspend fun SmartRWLocker.waitWriteUnlock() = writeMutex.waitUnlock()

View File

@@ -0,0 +1,168 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.isActive
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* It is interface which will work like classic [Semaphore], but in difference have [permitsStateFlow] for listening of the
* [SmartSemaphore] state.
*
* There is [Mutable] and [Immutable] realizations. In case you are owner and manager current state of lock, you need
* [Mutable] [SmartSemaphore]. Otherwise, [Immutable].
*
* Any [Mutable] [SmartSemaphore] may produce its [Immutable] variant which will contains [permitsStateFlow] equal to its
* [Mutable] creator
*/
sealed interface SmartSemaphore {
val permitsStateFlow: StateFlow<Int>
/**
* * True - locked
* * False - unlocked
*/
val freePermits: Int
get() = permitsStateFlow.value
/**
* Immutable variant of [SmartSemaphore]. In fact will depend on the owner of [permitsStateFlow]
*/
class Immutable(override val permitsStateFlow: StateFlow<Int>) : SmartSemaphore
/**
* Mutable variant of [SmartSemaphore]. With that variant you may [lock] and [unlock]. Besides, you may create
* [Immutable] variant of [this] instance with [immutable] factory
*
* @param locked Preset state of [freePermits] and its internal [_freePermitsStateFlow]
*/
class Mutable(private val permits: Int, acquiredPermits: Int = 0) : SmartSemaphore {
private val _freePermitsStateFlow = MutableStateFlow<Int>(permits - acquiredPermits)
override val permitsStateFlow: StateFlow<Int> = _freePermitsStateFlow.asStateFlow()
private val internalChangesMutex = Mutex(false)
fun immutable() = Immutable(permitsStateFlow)
private fun checkedPermits(permits: Int) = permits.coerceIn(1 .. this.permits)
/**
* Holds call until this [SmartSemaphore] will be re-locked. That means that current method will
*/
suspend fun acquire(permits: Int = 1) {
var acquiredPermits = 0
val checkedPermits = checkedPermits(permits)
try {
do {
val shouldContinue = internalChangesMutex.withLock {
val requiredPermits = checkedPermits - acquiredPermits
val acquiring = minOf(freePermits, requiredPermits).takeIf { it > 0 } ?: return@withLock true
acquiredPermits += acquiring
_freePermitsStateFlow.value -= acquiring
acquiredPermits != checkedPermits
}
if (shouldContinue) {
waitRelease()
}
} while (shouldContinue && currentCoroutineContext().isActive)
} catch (e: Throwable) {
release(acquiredPermits)
throw e
}
}
/**
* Holds call until this [SmartSemaphore] will be re-locked. That means that while [freePermits] == true, [holds] will
* wait for [freePermits] == false and then try to lock
*/
suspend fun acquireByOne(permits: Int = 1) {
val checkedPermits = checkedPermits(permits)
do {
waitRelease(checkedPermits)
val shouldContinue = internalChangesMutex.withLock {
if (_freePermitsStateFlow.value < checkedPermits) {
true
} else {
_freePermitsStateFlow.value -= checkedPermits
false
}
}
} while (shouldContinue && currentCoroutineContext().isActive)
}
/**
* Will try to lock this [SmartSemaphore] immediataly
*
* @return True if lock was successful. False otherwise
*/
suspend fun tryAcquire(permits: Int = 1): Boolean {
val checkedPermits = checkedPermits(permits)
return if (_freePermitsStateFlow.value < checkedPermits) {
internalChangesMutex.withLock {
if (_freePermitsStateFlow.value < checkedPermits) {
_freePermitsStateFlow.value -= checkedPermits
true
} else {
false
}
}
} else {
false
}
}
/**
* If [freePermits] == true - will change it to false and return true. If current call will not unlock this
* [SmartSemaphore] - false
*/
suspend fun release(permits: Int = 1): Boolean {
val checkedPermits = checkedPermits(permits)
return if (_freePermitsStateFlow.value < this.permits) {
internalChangesMutex.withLock {
if (_freePermitsStateFlow.value < this.permits) {
_freePermitsStateFlow.value = minOf(_freePermitsStateFlow.value + checkedPermits, this.permits)
true
} else {
false
}
}
} else {
false
}
}
}
}
/**
* Will call [SmartSemaphore.Mutable.lock], then execute [action] and return the result after [SmartSemaphore.Mutable.unlock]
*/
@OptIn(ExperimentalContracts::class)
suspend inline fun <T> SmartSemaphore.Mutable.withAcquire(permits: Int = 1, action: () -> T): T {
contract {
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
}
acquire(permits)
try {
return action()
} finally {
release(permits)
}
}
/**
* Will wait until the [SmartSemaphore.permitsStateFlow] of [this] instance will have [permits] count free permits.
*
* Anyway, after the end of this block there are no any guaranties that [SmartSemaphore.freePermits] >= [permits] due to
* the fact that some other parties may lock it again
*/
suspend fun SmartSemaphore.waitRelease(permits: Int = 1) = permitsStateFlow.first { it >= permits }

View File

@@ -0,0 +1,86 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.internal.SynchronizedObject
import kotlinx.coroutines.internal.synchronized
/**
* Works like [StateFlow], but guarantee that latest value update will always be delivered to
* each active subscriber
*/
open class SpecialMutableStateFlow<T>(
initialValue: T,
internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) : MutableStateFlow<T>, FlowCollector<T>, MutableSharedFlow<T> {
@OptIn(InternalCoroutinesApi::class)
private val syncObject = SynchronizedObject()
protected val internalSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
replay = 0,
extraBufferCapacity = 2,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
protected val publicSharedFlow: MutableSharedFlow<T> = MutableSharedFlow(
replay = 1,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
protected var _value: T = initialValue
override var value: T
get() = _value
set(value) {
doOnChangeAction(value)
}
protected val job = internalSharedFlow.subscribe(internalScope) {
doOnChangeAction(it)
}
override val replayCache: List<T>
get() = publicSharedFlow.replayCache
override val subscriptionCount: StateFlow<Int>
get() = publicSharedFlow.subscriptionCount
@OptIn(InternalCoroutinesApi::class)
override fun compareAndSet(expect: T, update: T): Boolean {
return synchronized(syncObject) {
if (expect == _value && update != _value) {
doOnChangeAction(update)
}
expect == _value
}
}
protected open fun onChangeWithoutSync(value: T) {
_value = value
publicSharedFlow.tryEmit(value)
}
@OptIn(InternalCoroutinesApi::class)
protected open fun doOnChangeAction(value: T) {
synchronized(syncObject) {
if (_value != value) {
onChangeWithoutSync(value)
}
}
}
@ExperimentalCoroutinesApi
override fun resetReplayCache() = publicSharedFlow.resetReplayCache()
override fun tryEmit(value: T): Boolean {
return internalSharedFlow.tryEmit(value)
}
override suspend fun emit(value: T) {
internalSharedFlow.emit(value)
}
override suspend fun collect(collector: FlowCollector<T>) = publicSharedFlow.collect(collector)
}

View File

@@ -0,0 +1,151 @@
import dev.inmo.micro_utils.coroutines.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class SmartRWLockerTests {
@Test
fun compositeTest() {
val locker = SmartRWLocker()
val readAndWriteWorkers = 10
runTest {
var started = 0
var done = 0
val doneMutex = Mutex()
val readWorkers = (0 until readAndWriteWorkers).map {
launch(start = CoroutineStart.LAZY) {
locker.withReadAcquire {
doneMutex.withLock {
started++
}
delay(100L)
doneMutex.withLock {
done++
}
}
}
}
var doneWrites = 0
val writeWorkers = (0 until readAndWriteWorkers).map {
launch(start = CoroutineStart.LAZY) {
locker.withWriteLock {
assertTrue(done == readAndWriteWorkers || started == 0)
delay(10L)
doneWrites++
}
}
}
readWorkers.forEach { it.start() }
writeWorkers.forEach { it.start() }
readWorkers.joinAll()
writeWorkers.joinAll()
assertEquals(expected = readAndWriteWorkers, actual = done)
assertEquals(expected = readAndWriteWorkers, actual = doneWrites)
}
}
@Test
fun simpleWithWriteLockTest() {
val locker = SmartRWLocker()
runTest {
locker.withWriteLock {
assertEquals(0, locker.readSemaphore.freePermits)
assertEquals(true, locker.writeMutex.isLocked)
}
assertEquals(Int.MAX_VALUE, locker.readSemaphore.freePermits)
assertEquals(false, locker.writeMutex.isLocked)
}
}
@Test
fun failureWithWriteLockTest() {
val locker = SmartRWLocker()
val exception = IllegalArgumentException()
try {
runTest {
val subscope = kotlinx.coroutines.CoroutineScope(this.coroutineContext)
var happenException: Throwable? = null
try {
locker.withWriteLock {
val checkFunction = fun (): Deferred<Unit> {
return subscope.async {
assertEquals(0, locker.readSemaphore.freePermits)
assertEquals(true, locker.writeMutex.isLocked)
throw exception
}
}
doInDefault {
assertEquals(0, locker.readSemaphore.freePermits)
assertEquals(true, locker.writeMutex.isLocked)
checkFunction().await()
}
}
} catch (e: Exception) {
happenException = e
}
if (exception != happenException) {
assertEquals(exception, happenException ?.cause)
}
assertEquals(Int.MAX_VALUE, locker.readSemaphore.freePermits)
assertEquals(false, locker.writeMutex.isLocked)
}
} catch (e: Exception) {
assertEquals(exception, e)
}
}
@Test
fun simpleWithReadAcquireTest() {
val locker = SmartRWLocker()
runTest {
locker.withReadAcquire {
assertEquals(Int.MAX_VALUE - 1, locker.readSemaphore.freePermits)
assertEquals(false, locker.writeMutex.isLocked)
locker.withReadAcquire {
assertEquals(Int.MAX_VALUE - 2, locker.readSemaphore.freePermits)
assertEquals(false, locker.writeMutex.isLocked)
}
}
assertEquals(Int.MAX_VALUE, locker.readSemaphore.freePermits)
assertEquals(false, locker.writeMutex.isLocked)
}
}
@Test
fun simple2WithWriteLockTest() {
val locker = SmartRWLocker()
val unlockDelay = 1000L // 1 sec
var unlocked: Boolean = false
runTest {
launch {
locker.withReadAcquire {
delay(unlockDelay)
}
unlocked = true
}
locker.readSemaphore.permitsStateFlow.first { it == Int.MAX_VALUE - 1 }
assertEquals(false, unlocked)
locker.withWriteLock {
assertEquals(true, unlocked)
assertEquals(0, locker.readSemaphore.freePermits)
assertEquals(true, locker.writeMutex.isLocked)
}
assertEquals(Int.MAX_VALUE, locker.readSemaphore.freePermits)
assertEquals(false, locker.writeMutex.isLocked)
}
}
}

View File

@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import org.w3c.dom.Image
suspend fun preloadImage(src: String): Image {
val image = Image()
image.src = src
val job = Job()
image.addEventListener("load", {
runCatching { job.complete() }
})
runCatchingSafely {
job.join()
}.onFailure {
if (it is CancellationException) {
image.src = ""
}
}.getOrThrow()
return image
}

View File

@@ -0,0 +1,9 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
fun <T> launchInCurrentThread(block: suspend CoroutineScope.() -> T): T {
val scope = CoroutineScope(Dispatchers.Unconfined)
return scope.launchSynchronously(block)
}

View File

@@ -6,7 +6,7 @@ fun <T> CoroutineScope.launchSynchronously(block: suspend CoroutineScope.() -> T
var result: Result<T>? = null
val objectToSynchronize = Object()
synchronized(objectToSynchronize) {
launch {
launch(start = CoroutineStart.UNDISPATCHED) {
result = safelyWithResult(block)
}.invokeOnCompletion {
synchronized(objectToSynchronize) {

View File

@@ -0,0 +1,47 @@
package dev.inmo.micro_utils.coroutines
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import kotlin.test.Test
import kotlin.test.assertEquals
class LaunchInCurrentThreadTests {
@Test
fun simpleTestThatLaunchInCurrentThreadWorks() {
val expectedResult = 10
val result = launchInCurrentThread {
expectedResult
}
assertEquals(expectedResult, result)
}
@Test
fun simpleTestThatSeveralLaunchInCurrentThreadWorks() {
val testData = 0 until 100
testData.forEach {
val result = launchInCurrentThread {
it
}
assertEquals(it, result)
}
}
@Test
fun simpleTestThatLaunchInCurrentThreadWillCorrectlyHandleSuspensionsWorks() {
val testData = 0 until 100
suspend fun test(data: Any): Any {
return withContext(Dispatchers.Default) {
delay(1)
data
}
}
testData.forEach {
val result = launchInCurrentThread {
test(it)
}
assertEquals(it, result)
}
}
}

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.micro_utils.coroutines"/>

View File

@@ -4,13 +4,14 @@ plugins {
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64ProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":micro_utils.common")
api libs.krypto
}
}
jsMain {

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -1,6 +1,8 @@
package dev.inmo.micro_utils.crypto
import korlibs.crypto.md5
typealias MD5 = String
expect fun SourceBytes.md5(): MD5
fun SourceString.md5(): MD5 = encodeToByteArray().md5()
fun SourceBytes.md5(): MD5 = md5().hexLower
fun SourceString.md5(): MD5 = encodeToByteArray().md5().hexLower

View File

@@ -1,3 +0,0 @@
package dev.inmo.micro_utils.crypto
expect fun SourceString.hmacSha256(key: String): String

Some files were not shown because too many files have changed in this diff Show More