Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc70931c2c | ||
|
|
af4a6c4672 | ||
|
|
3347224018 | ||
|
|
558062a915 | ||
|
|
a7ab941543 | ||
|
|
57b5f33720 | ||
|
|
f36d0ef5e6 | ||
|
|
0d9b0fec6b | ||
|
|
d1b83410d0 | ||
|
|
990bb26276 | ||
|
|
4f4b480a13 | ||
|
|
b2490cd014 | ||
|
|
9d85563cf2 | ||
|
|
ac573d9311 | ||
|
|
1e5ae4048c | ||
|
|
3db05fb3b1 | ||
|
|
5a098e125a | ||
|
|
78b77fd4c1 | ||
|
|
b2470ca89b | ||
|
|
4764698583 | ||
|
|
c91a34aae5 | ||
|
|
8dc76f6ce5 | ||
|
|
0ebe949da6 | ||
|
|
6c1a40d614 | ||
|
|
9a81d06e2d | ||
|
|
687cd210af | ||
|
|
87135c0e1b | ||
|
|
48cfa6b7f1 | ||
|
|
25c78eed91 | ||
|
|
6a50387c39 | ||
|
|
bac6f65dd0 | ||
|
|
5ec34fea3b | ||
|
|
539e53c6b1 | ||
|
|
dfe108d13e | ||
|
|
37e6c87046 | ||
|
|
e92929910d | ||
|
|
7488afdbb3 | ||
|
|
2dbc99a27a | ||
|
|
1dd26feebb | ||
|
|
203847dcd7 | ||
|
|
a4a3620f71 | ||
|
|
5fba9bfe7b | ||
|
|
c05aa75f1e | ||
|
|
2f378aaeec | ||
|
|
e92279634d | ||
|
|
1bc15b7b7c | ||
|
|
7d8083b268 | ||
|
|
7035159fa6 | ||
|
|
a2a9446e3b | ||
|
|
5c40fedde2 | ||
|
|
a44ca13e3a | ||
|
|
67f3595d33 | ||
|
|
ae10d433e6 | ||
|
|
e33768d9ca | ||
|
|
7624fa204d | ||
|
|
4738674f7f | ||
|
|
7cbc96161e | ||
|
|
d940ba252e | ||
|
|
60507cc817 | ||
|
|
3a99c28233 | ||
|
|
7e91628339 | ||
|
|
eb053eb5bf | ||
|
|
7462b1a2f7 | ||
|
|
8d557c3898 | ||
|
|
6b1c53c31a | ||
|
|
5e27937358 | ||
|
|
a8aad809ba | ||
|
|
d94ca0e9c6 | ||
|
|
aa51535e65 | ||
|
|
7737c82db9 | ||
|
|
7ee64e0ead | ||
|
|
987990704c | ||
|
|
e8975e70f5 | ||
|
|
72409a0b73 | ||
|
|
3dfd2d7862 | ||
|
|
c93d2f73a9 | ||
|
|
66d638f952 | ||
|
|
815a8f186b | ||
|
|
ae745b917e | ||
|
|
bd759c53cd | ||
|
|
8dca324f71 | ||
|
|
dd7a7d9977 | ||
|
|
a7c81a77f2 | ||
|
|
15b1e68611 | ||
|
|
a5e25e2eb5 | ||
|
|
c10ec27b51 | ||
|
|
9c86a9124d | ||
|
|
069c5a4389 | ||
|
|
b498cf4b62 | ||
|
|
70ee848a41 | ||
|
|
60fd6a4af4 | ||
|
|
04ed687dbb | ||
|
|
d694415a4a | ||
|
|
3f2a9c999b | ||
|
|
ea40650214 | ||
|
|
ae66aced83 | ||
|
|
5d2eebf093 | ||
|
|
5c8057d71f | ||
|
|
4d78a73602 | ||
|
|
7c5a9eb683 | ||
|
|
df423d28d6 | ||
|
|
a92616c5a3 | ||
|
|
2fc29f65ca | ||
|
|
aab712e680 | ||
|
|
4bca86d91e | ||
|
|
63cca3eba2 | ||
|
|
cba2ed5944 | ||
|
|
ee5886c9b9 | ||
|
|
e96dadae34 | ||
|
|
a23ad82b60 | ||
|
|
f816580886 | ||
|
|
a5f8f1a4c2 | ||
|
|
0d47f06be3 | ||
|
|
c4eb2d2116 | ||
|
|
8c1dc62211 | ||
|
|
b1a66bb8dd | ||
|
|
61a2cefcc3 | ||
|
|
ad775bf8e1 | ||
|
|
a80e8bb298 | ||
|
|
2db51746ac | ||
|
|
3d7e6d7f4c | ||
|
|
6918cf5f76 | ||
|
|
cd05ed4da9 | ||
|
|
b669900c75 | ||
|
|
3a9bf04a4d | ||
|
|
0cd4257e73 | ||
|
|
6f55da754c | ||
|
|
31adb45152 | ||
|
|
68a20e0d11 | ||
|
|
52657b0c08 | ||
|
|
a2123b7a5d | ||
|
|
207f98ca17 | ||
|
|
58170b6072 | ||
|
|
744955bf11 | ||
|
|
cd6d53d19e | ||
|
|
50e03fbd50 | ||
|
|
6dd219ee05 | ||
|
|
2a6cd96c8c | ||
|
|
1323668fe6 | ||
|
|
24b747eb38 | ||
|
|
ed31d1fefb | ||
|
|
237a06f1cf | ||
|
|
06528e7504 | ||
|
|
387f9a2601 | ||
|
|
4bcfd083d6 | ||
|
|
0e922176fe | ||
|
|
250e7c20e5 | ||
|
|
c3de960966 | ||
|
|
866995b2d3 | ||
|
|
825ee23afe | ||
|
|
66ce35927a | ||
|
|
468d363c65 | ||
|
|
009744cb47 | ||
|
|
2d63a1e79f | ||
|
|
5452c55e86 | ||
|
|
6b164f84d9 | ||
|
|
bf104a1d85 | ||
|
|
1d32ec810f | ||
|
|
1f7b9dbbc4 | ||
|
|
8c0bfc2143 | ||
|
|
279ddd9974 | ||
|
|
71b29c0b56 | ||
|
|
b331a85a2a | ||
|
|
e8037e8d60 | ||
|
|
ea0dd4cb78 | ||
|
|
4734ab4a22 | ||
|
|
4df97acd73 | ||
|
|
8ec233d761 | ||
|
|
135a1fc5ed | ||
|
|
4cb5b09001 | ||
|
|
3bb8b51cb5 | ||
|
|
1ee31873ab | ||
|
|
79fac06a4c | ||
|
|
553bbedc95 | ||
|
|
d31fe53f78 | ||
|
|
be74e52156 | ||
|
|
a2157ae618 | ||
|
|
8b7bcaa92f | ||
|
|
157998ddf5 | ||
|
|
cd302bb1bb | ||
|
|
52341757e4 | ||
|
|
a74633df54 | ||
|
|
b936b54132 | ||
|
|
cf111f7852 | ||
|
|
f23f9498cd | ||
|
|
7c019353df | ||
|
|
3954e63141 | ||
|
|
d04d638b27 | ||
|
|
3ad414dadd | ||
|
|
8bc9bd4dbb | ||
|
|
b0c3066adc | ||
|
|
c5dfd4fffe | ||
|
|
7de7b4f843 | ||
|
|
7079c5a6f1 | ||
|
|
46ebda2673 | ||
|
|
eee9df7d8a | ||
|
|
f8be557e6e | ||
|
|
d6f776e12e | ||
|
|
6fd3f32f7e | ||
|
|
adbb400728 | ||
|
|
8f6a46aa2e | ||
|
|
0ec52b84fe | ||
|
|
b436f10e18 | ||
|
|
75ebf57c68 | ||
|
|
b3cea28687 | ||
|
|
878b51fa30 | ||
|
|
81f024d197 | ||
|
|
a21d665f5d | ||
|
|
954bc9710b | ||
|
|
5faeef190d | ||
|
|
43c5f93b60 | ||
|
|
adfac94094 | ||
|
|
bc60929fe9 | ||
|
|
fdf2564f15 | ||
|
|
8b6d422d96 | ||
|
|
d2c0fea75f | ||
|
|
efa5afb441 | ||
|
|
830144d5b1 | ||
|
|
f6b684ceb0 | ||
|
|
cd3dfc065b | ||
|
|
72c1529bb9 | ||
|
|
1b17da668d | ||
|
|
3e7f50f4bf | ||
|
|
12bc6a06fb | ||
|
|
c3f146723a | ||
|
|
f64254ccef | ||
|
|
0a43d9c9f6 | ||
|
|
dcb9bfeb18 | ||
|
|
d6f5ea444d | ||
|
|
3a3e02a937 | ||
|
|
b5109fc872 | ||
|
|
d9ed25478f | ||
|
|
f5c9da6aa0 | ||
|
|
293c04b2ad | ||
|
|
e91b344bea | ||
|
|
491d8a6e71 | ||
|
|
04054914f5 | ||
|
|
d273c6f7b5 | ||
|
|
9cf134e916 | ||
|
|
abd1e73d82 | ||
|
|
5625aebb0c | ||
|
|
b84393c0d6 | ||
|
|
c0021ff5dc | ||
|
|
9e1893612c | ||
|
|
3c2c577628 | ||
|
|
190b7b4026 | ||
|
|
6fa03bad66 | ||
|
|
0a84659b70 | ||
|
|
dec5873682 | ||
|
|
8fc18f8324 | ||
|
|
590240b283 | ||
|
|
4b90ff49fd | ||
|
|
2223cea395 | ||
|
|
ad692b73ba | ||
|
|
de57d089eb | ||
|
|
303a706d8a | ||
|
|
108bbbda87 | ||
|
|
65f060a8d2 | ||
|
|
f35f0af6d8 | ||
|
|
6c30892def | ||
|
|
1016c85e3c | ||
|
|
887c05c2b9 | ||
|
|
98cb50fab7 | ||
|
|
38d75b0e51 | ||
|
|
20a2389eb0 | ||
|
|
9cbc754574 | ||
|
|
b564283419 | ||
|
|
32dd218131 | ||
|
|
09a36aa4c6 | ||
|
|
c1f1d67bfa | ||
|
|
93d2397572 | ||
|
|
4b89dc202e | ||
|
|
0718e18fb0 | ||
|
|
90483889c4 | ||
|
|
b7ef4cb2d6 | ||
|
|
b73261a339 | ||
|
|
0b4fd240cc | ||
|
|
b1af5d2349 | ||
|
|
456205dece | ||
|
|
8d092a761a | ||
|
|
05e9c25742 | ||
|
|
998171af9e | ||
|
|
3c62dd9542 | ||
|
|
55cc397333 | ||
|
|
b969b5f0bf | ||
|
|
d7cc55ed53 | ||
|
|
9ab8a3e149 | ||
|
|
0ae48dee33 | ||
|
|
d612ed44d9 | ||
|
|
cb798cf011 | ||
|
|
8392841554 | ||
|
|
24d4d3279a | ||
|
|
84bdcb340b | ||
|
|
e4a28684ef | ||
|
|
0f5698bb58 | ||
|
|
ca2da81af5 | ||
|
|
a26fd0862f | ||
|
|
ab905d4061 | ||
|
|
7c27e26da7 | ||
|
|
7f49333c42 | ||
|
|
50d2af7be4 | ||
|
|
5746bbebf8 | ||
|
|
94551a8c71 | ||
|
|
50ed66414c | ||
|
|
905c27fe84 | ||
|
|
62dd421cf4 | ||
|
|
b0851ac6f0 | ||
|
|
3c9435dcee | ||
|
|
546b557796 | ||
|
|
24d770e952 | ||
|
|
ef1e6a5d39 | ||
|
|
bd8fccb9e5 | ||
|
|
c7c5f08d23 | ||
|
|
76cfa67682 | ||
|
|
a78ae68024 | ||
|
|
ee2621a487 | ||
|
|
87113455a5 | ||
|
|
7fc98f8b4c | ||
|
|
07a6eaac89 | ||
|
|
357e682a1d | ||
|
|
cd712ef20d | ||
|
|
ac9c054f52 | ||
|
|
8d4cc7fb6f | ||
|
|
5c122f85f4 | ||
|
|
d2db437e1a | ||
|
|
40aea664e4 | ||
|
|
6fd5b120cc | ||
|
|
05e7daacb8 | ||
|
|
f17375e9e1 | ||
|
|
85c11e0e80 | ||
|
|
b00083ba2e | ||
|
|
6cf9b2a335 | ||
|
|
95fd166a98 | ||
|
|
3da0674031 | ||
|
|
e62e257154 | ||
|
|
dcb0b770c4 | ||
|
|
8ce0991a5d | ||
|
|
3268f2fb90 | ||
|
|
bf43a973ab | ||
|
|
1486220b4a | ||
|
|
f5262f964e | ||
|
|
55eef3b5ba | ||
|
|
960943ec35 | ||
|
|
d491c1b77c | ||
|
|
d402d55718 | ||
|
|
9f7a3aca14 | ||
|
|
b8e13ae272 | ||
|
|
01a31ad54b | ||
|
|
2e9f874691 | ||
|
|
e5de08792a | ||
|
|
293bcf4575 | ||
|
|
23410dfcdc | ||
|
|
10da5929e9 | ||
|
|
21a51ff907 | ||
|
|
5333358b53 | ||
|
|
9638c8bab5 | ||
|
|
4c44df3136 | ||
|
|
318214b119 | ||
|
|
498b0b85d2 | ||
|
|
38d07285b2 | ||
|
|
cffdfbeafb | ||
|
|
54bcdab2b8 | ||
|
|
dd4a4f9235 | ||
|
|
a2e84f63be | ||
|
|
1c213dff03 | ||
|
|
5d1b187518 | ||
|
|
24a5b13029 | ||
|
|
bd4691c421 | ||
|
|
a633c1272a | ||
|
|
332a1c7789 | ||
|
|
7dc1429f1f | ||
|
|
f4dc74c795 | ||
|
|
15d5913637 | ||
|
|
b0a10b37e6 | ||
|
|
f572e1255a | ||
|
|
5a491447fe | ||
|
|
81457f6c5b | ||
|
|
cda5c2d2d8 | ||
|
|
f9f11e51c8 | ||
|
|
19638658ec | ||
|
|
f8d220083a | ||
|
|
648f4502e6 | ||
|
|
5e2c2cbdde | ||
|
|
52035cde13 | ||
|
|
ea57889959 | ||
|
|
038ded5abd | ||
|
|
1b5d9a87e6 | ||
|
|
6f625a0f6f | ||
|
|
da1948c0e6 | ||
|
|
177d0b7162 | ||
|
|
5c350628ff | ||
|
|
cd014f2ba6 | ||
|
|
08ec5c267c | ||
|
|
d930450a6f | ||
|
|
d09b5cb306 | ||
|
|
89dde6e050 | ||
|
|
4b99abbfa4 | ||
|
|
edc3a017be | ||
|
|
ff69ccb384 | ||
|
|
09b3bd4e74 | ||
|
|
de8dd8a3cf | ||
|
|
928878928a | ||
|
|
58c5cfa5b6 | ||
|
|
3bf0849f1c | ||
|
|
85e65fed92 | ||
|
|
499fd23342 | ||
|
|
595dbd96e9 | ||
|
|
b62bc8dc89 | ||
|
|
a909f1274d | ||
|
|
94a7ad788b | ||
|
|
cfa8bc4971 | ||
|
|
a4ad701aff | ||
|
|
00e94eb1cb | ||
|
|
ff478b3365 | ||
|
|
7bf599ce19 | ||
|
|
a4b9e227bf | ||
|
|
cb9039e927 | ||
|
|
bf65027105 | ||
|
|
28d655efc2 | ||
|
|
e711781eb0 | ||
|
|
8576bad580 | ||
|
|
56e089ffa9 | ||
|
|
6625ebcfc7 | ||
|
|
540ea7ba43 | ||
|
|
dea2d8b468 | ||
|
|
ba292bf709 | ||
|
|
2ce4d1de2e | ||
|
|
9d6712d94b | ||
|
|
4e920328e1 | ||
|
|
1865c62db8 | ||
|
|
d61206a7d2 | ||
|
|
a94c48028d | ||
|
|
909c615ae6 | ||
|
|
5d87bb077f | ||
|
|
72b5b2c795 | ||
|
|
ce91650788 | ||
|
|
1b2534eca6 | ||
|
|
5e3356e1d4 | ||
|
|
ddc7a7598d | ||
|
|
2cfe9d73a2 | ||
|
|
dbed41bdbd | ||
|
|
a0296f5d5a | ||
|
|
39cca12708 | ||
|
|
0c4e3db59c | ||
|
|
8dea17cbf2 | ||
|
|
b428c73602 | ||
|
|
8c9ce29a7a | ||
|
|
f304304bc5 | ||
|
|
f14941cf27 | ||
|
|
c83afb3cd7 | ||
|
|
dfc6d80127 | ||
|
|
3ad1b07ff8 | ||
|
|
d3c2861939 | ||
|
|
8632f8fd7a | ||
|
|
83585b4a8a | ||
|
|
e2e3186bb4 | ||
|
|
29765f0024 | ||
|
|
43edeaff04 | ||
|
|
2b7cf7d3cb | ||
|
|
fdf13fd201 | ||
|
|
92ba77a838 | ||
|
|
d910810dd4 | ||
|
|
5f8853c69d | ||
|
|
0f9b35efb1 | ||
|
|
7096e62948 | ||
|
|
acb7c0e670 | ||
|
|
bb91e6e083 | ||
|
|
67a922362d | ||
|
|
56be44b918 | ||
|
|
15493aec1a | ||
|
|
9aef7582bd | ||
|
|
5378ec6854 | ||
|
|
79dbb64275 | ||
|
|
b8912e747a | ||
|
|
a9dcd0157a | ||
|
|
45e8993d75 | ||
|
|
39bd1fa2eb | ||
|
|
5b33fbc20b | ||
|
|
196195d867 | ||
|
|
20b8076235 | ||
|
|
5f49c8149a | ||
|
|
fb4beb08ee | ||
|
|
6750eff9fa | ||
|
|
692cc8ce67 | ||
|
|
a59b6411b1 | ||
|
|
1345709450 | ||
|
|
8433f38f6e | ||
|
|
91c3d0f9a6 | ||
|
|
579a2ffe2e | ||
|
|
918f28feab | ||
|
|
7b13d40797 | ||
|
|
002588a45a | ||
|
|
1ae0cbb95d | ||
|
|
3e2d32e4c9 | ||
|
|
403f7d47b1 | ||
|
|
d1fcebdf4c | ||
|
|
a983bd729c | ||
|
|
8d311a1193 | ||
|
|
6bbd2c73b5 | ||
|
|
f851e2d626 | ||
|
|
97aafc764f | ||
|
|
183e10dba8 | ||
|
|
f5deaaa8ff | ||
|
|
55537ea191 | ||
|
|
98818023e9 | ||
|
|
e67df253de | ||
|
|
53524c8ef8 | ||
|
|
bc90a8941a | ||
|
|
5c413d1f12 | ||
|
|
8b5ab33730 | ||
|
|
d4d1288900 | ||
|
|
f4f07d0ce3 | ||
|
|
6282f4d6e0 | ||
|
|
19f5f8a0ca | ||
|
|
a296d2b827 | ||
|
|
45872f1653 | ||
|
|
074420056b | ||
|
|
9f7e05d79a | ||
|
|
955299bd89 | ||
|
|
6520758722 | ||
|
|
583137aa25 | ||
|
|
8afce7706c | ||
|
|
7ae0a0bae6 | ||
|
|
f34d6c5222 | ||
|
|
002f4f9309 | ||
|
|
bf77a9aaed | ||
|
|
d17c178eaa | ||
|
|
2c0ba0fae9 | ||
|
|
0396a488cd | ||
|
|
f5ef483cd9 | ||
|
|
78960f6d4e | ||
|
|
2be9408560 | ||
|
|
46dc6fbe91 | ||
|
|
e411cb3d7f | ||
|
|
00535dc859 | ||
|
|
465e6931ad | ||
|
|
7e64100491 | ||
|
|
e199f00ca0 | ||
|
|
8722636b13 | ||
|
|
5db4a1ee8d | ||
|
|
b0bae12e6b |
@@ -3,3 +3,6 @@
|
||||
e7f4c8a7ebe4e907f65af46e6dddfd3a542415c0
|
||||
ab2713154f846b3db6d44625d1022bf421b275d0
|
||||
cbdfa67d075ec1f8ceec0f3559498cbca01783a0
|
||||
|
||||
# Renamed Renovate config file
|
||||
f304304bc5b64edf189aaa473b05df912067a8bd
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
* text=auto eol=lf
|
||||
**/dependencies/** linguist-generated
|
||||
**/clawicons/** linguist-generated
|
||||
android/src/main/baseline-prof.txt linguist-generated
|
||||
android/src/main/baseline-prof.txt linguist-generated -diff
|
||||
|
||||
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 161 KiB |
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":dependencyDashboard",
|
||||
"helpers:pinGitHubActionDigests",
|
||||
"github>msfjarvis/shared-workflows//renovate/automerge"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^com.android.tools.build",
|
||||
"^com.android.test",
|
||||
"^gradle"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^kotlin",
|
||||
"^org.jetbrains.kotlin",
|
||||
"^androidx.compose.compiler"
|
||||
],
|
||||
"groupName": "kotlin"
|
||||
},
|
||||
{
|
||||
"managers": [
|
||||
"gradle"
|
||||
],
|
||||
"packagePatterns": [
|
||||
"^com.squareup.okhttp3"
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchDatasources": [
|
||||
"maven"
|
||||
],
|
||||
"matchPackageNames": [
|
||||
"androidx.compose.compiler:compiler"
|
||||
],
|
||||
"registryUrls": [
|
||||
"https://maven.google.com/"
|
||||
]
|
||||
}
|
||||
],
|
||||
"regexManagers": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"build-logic/kotlin-plugins/src/main/kotlin/dev/msfjarvis/aps/gradle/SpotlessPlugin.kt"
|
||||
],
|
||||
"matchStrings": [
|
||||
"KTFMT_VERSION = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "com.facebook:ktfmt"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"build-logic/kotlin-plugins/src/main/kotlin/dev/msfjarvis/aps/gradle/Detekt.kt"
|
||||
],
|
||||
"matchStrings": [
|
||||
"TWITTER_RULES_VERSION = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "com.twitter.compose.rules:detekt"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"gradle/libs.versions.toml"
|
||||
],
|
||||
"matchStrings": [
|
||||
"composeCompiler = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "androidx.compose.compiler:compiler"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"qodana.yaml"
|
||||
],
|
||||
"matchStrings": [
|
||||
"jetbrains/qodana-jvm-community:(?<currentValue>.*)"
|
||||
],
|
||||
"datasourceTemplate": "docker",
|
||||
"depNameTemplate": "jetbrains/qodana-jvm-community",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base",
|
||||
":dependencyDashboard",
|
||||
"helpers:pinGitHubActionDigests",
|
||||
"github>msfjarvis/shared-workflows//renovate/automerge"
|
||||
],
|
||||
"branchConcurrentLimit": 15,
|
||||
"packageRules": [
|
||||
{
|
||||
matchDatasources: [
|
||||
"maven"
|
||||
],
|
||||
registryUrls: [
|
||||
"https://maven.google.com/",
|
||||
"https://repo1.maven.org/maven2",
|
||||
"https://plugins.gradle.org/m2/",
|
||||
],
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^androidx.*",
|
||||
],
|
||||
"groupName": "androidx"
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^kotlin",
|
||||
"^org.jetbrains.kotlin",
|
||||
"^androidx.compose.compiler",
|
||||
"^anvil",
|
||||
"^coil",
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^io.sentry",
|
||||
"^sentry",
|
||||
],
|
||||
"groupName": "sentry"
|
||||
},
|
||||
{
|
||||
"managers": [
|
||||
"gradle"
|
||||
],
|
||||
"packagePatterns": [
|
||||
// Renovate refuses to stop resolving this from the snapshots repo
|
||||
"^app.cash.sqldelight",
|
||||
// Currently on a pre-release version for bugfixes
|
||||
"com.android.tools:r8",
|
||||
],
|
||||
"enabled": false
|
||||
},
|
||||
],
|
||||
"regexManagers": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"SpotlessPlugin\.kt$"
|
||||
],
|
||||
"matchStrings": [
|
||||
"KTFMT_VERSION = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "com.facebook:ktfmt",
|
||||
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"gradle/libs.versions.toml"
|
||||
],
|
||||
"matchStrings": [
|
||||
"composeCompiler = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "androidx.compose.compiler:compiler",
|
||||
"registryUrlTemplate": "https://maven.google.com",
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"gradle/libs.versions.toml"
|
||||
],
|
||||
"matchStrings": [
|
||||
"sentry-sdk = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "io.sentry:sentry-android",
|
||||
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,10 +6,6 @@ on:
|
||||
- cron: '43 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
AVD_API_LEVEL: 31
|
||||
AVD_ARCH: x86_64
|
||||
|
||||
jobs:
|
||||
baseline-profile:
|
||||
runs-on: macos-latest
|
||||
@@ -19,13 +15,28 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
||||
with:
|
||||
token: ${{ secrets.POST_RELEASE_GH_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Accept all SDK licenses
|
||||
shell: bash
|
||||
run: printf 'y\ny\ny\ny\ny\ny\n' | $ANDROID_HOME/tools/bin/sdkmanager --licenses
|
||||
|
||||
- name: Get build-tools directory
|
||||
id: build-tools-path
|
||||
shell: bash
|
||||
run: echo "dir=${ANDROID_HOME}/build-tools/34.0.0-rc3" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Cache build-tools
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3
|
||||
with:
|
||||
path: ${{ steps.build-tools-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-34.0.0-rc3
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
|
||||
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
@@ -37,19 +48,10 @@ jobs:
|
||||
git config user.email noreply@github.com
|
||||
|
||||
- name: Setup Gradle caching
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 # v2.4.2
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
# Restore AVD from cache
|
||||
- name: Cache AVD
|
||||
uses: actions/cache@c1a5de879eb890d062a85ee0252d6036480b1fe2 # v3.2.1
|
||||
id: avd-cache
|
||||
with:
|
||||
path: |
|
||||
~/.android/avd/*
|
||||
~/.android/adb*
|
||||
key: ${{ runner.os }}-avd-${{ env.AVD_API_LEVEL }}-${{ env.AVD_ARCH }}
|
||||
cache-read-only: true
|
||||
|
||||
- name: Decrypt secrets
|
||||
run: |
|
||||
@@ -63,39 +65,15 @@ jobs:
|
||||
- name: Build app and benchmark
|
||||
run: ./gradlew :benchmark:assembleBenchmark :android:assembleBenchmark
|
||||
|
||||
# Now use reactivecircus/android-emulator-runner to spin up an emulator. We're gonna use it again
|
||||
# to actually run the test once the emulator has booted.
|
||||
- name: Create AVD snapshot for caching
|
||||
if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
uses: reactivecircus/android-emulator-runner@50986b1464923454c95e261820bc626f38490ec0 # v2
|
||||
with:
|
||||
api-level: ${{ env.AVD_API_LEVEL }}
|
||||
arch: ${{ env.AVD_ARCH }}
|
||||
channel: canary
|
||||
target: aosp_atd
|
||||
force-avd-creation: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: false
|
||||
disk-size: 8G
|
||||
script: echo "Generated AVD snapshot for caching."
|
||||
- name: Clear unused Gradle Managed Devices
|
||||
run: ./gradlew cleanManagedDevices --unused-only
|
||||
|
||||
# Run our baseline profile generator. We need to manually pull the baseline profiles off the
|
||||
# emulator when using the GA runner
|
||||
- name: Run benchmark
|
||||
uses: reactivecircus/android-emulator-runner@50986b1464923454c95e261820bc626f38490ec0 # v2
|
||||
with:
|
||||
api-level: ${{ env.AVD_API_LEVEL }}
|
||||
arch: ${{ env.AVD_ARCH }}
|
||||
channel: canary
|
||||
target: aosp_atd
|
||||
disable-animations: true
|
||||
disable-spellchecker: true
|
||||
disk-size: 8G
|
||||
script: |
|
||||
# Run our benchmark, enabling only tests using BaselineProfile
|
||||
./gradlew connectedBenchmarkAndroidTest -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile --stacktrace
|
||||
# Need to manually pull the generated profiles from the emulator
|
||||
adb pull /sdcard/Android/media/dev.msfjarvis.claw.benchmark benchmark/build/outputs/baseline-prof/
|
||||
- name: Run benchmark on Gradle Managed Device
|
||||
run: |
|
||||
./gradlew api31BenchmarkAndroidTest \
|
||||
-Dorg.gradle.workers.max=1 \
|
||||
-Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile \
|
||||
-Pandroid.testoptions.manageddevices.emulator.gpu="swiftshader_indirect"
|
||||
|
||||
# If we're on main branch, copy over the baseline profile and
|
||||
# commit it to the repository (if changed)
|
||||
@@ -104,20 +82,20 @@ jobs:
|
||||
run: |
|
||||
# Pull down any changes which may have been committed while this workflow has been running
|
||||
git pull
|
||||
# Sort the baseline profile, saving it to app/
|
||||
sort -o android/src/main/baseline-prof.txt benchmark/build/outputs/baseline-prof/BaselineProfileGenerator_startup-baseline-prof.txt
|
||||
# Sort the baseline profile, output to android/
|
||||
sort -o android/src/main/baseline-prof.txt benchmark/build/outputs/managed_device_android_test_additional_output/benchmark/api31/BaselineProfileGenerator_generateBaselineProfile-baseline-prof.txt
|
||||
# If the baseline profile has changed, commit it
|
||||
if [[ $(git diff --stat android/src/main/baseline-prof.txt) != '' ]]; then
|
||||
git add android/src/main/baseline-prof.txt
|
||||
git commit -m "chore(android): refresh baseline profile" && git push
|
||||
fi
|
||||
|
||||
# Upload the entire generated folder and attach it to the CI run
|
||||
# Upload the entire output folder and attach it to the CI run
|
||||
- name: Attach baseline profile
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: Baseline profile output
|
||||
path: benchmark/build/outputs/baseline-prof
|
||||
path: benchmark/build/outputs/managed_device_android_test_additional_output
|
||||
|
||||
- name: Clean secrets
|
||||
if: always()
|
||||
|
||||
@@ -11,38 +11,48 @@ on:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Accept all SDK licenses
|
||||
shell: bash
|
||||
run: printf 'y\ny\ny\ny\ny\ny\n' | $ANDROID_HOME/tools/bin/sdkmanager --licenses
|
||||
|
||||
- name: Get build-tools directory
|
||||
id: build-tools-path
|
||||
shell: bash
|
||||
run: echo "dir=${ANDROID_HOME}/build-tools/34.0.0-rc3" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Cache build-tools
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3
|
||||
with:
|
||||
path: ${{ steps.build-tools-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-34.0.0-rc3
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
|
||||
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
|
||||
- name: Run unit tests
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 # v2.4.2
|
||||
with:
|
||||
arguments: --no-configuration-cache --stacktrace check -PslimTests
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
- name: Run Detekt
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
with:
|
||||
arguments: --no-configuration-cache --stacktrace detektMain
|
||||
arguments: --no-configuration-cache --stacktrace check
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
- name: (Fail-only) Upload test report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
|
||||
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
|
||||
with:
|
||||
name: Test report
|
||||
path: android/build/reports
|
||||
@@ -53,10 +63,25 @@ jobs:
|
||||
needs: [ "check" ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
||||
|
||||
- name: Accept all SDK licenses
|
||||
shell: bash
|
||||
run: printf 'y\ny\ny\ny\ny\ny\n' | $ANDROID_HOME/tools/bin/sdkmanager --licenses
|
||||
|
||||
- name: Get build-tools directory
|
||||
id: build-tools-path
|
||||
shell: bash
|
||||
run: echo "dir=${ANDROID_HOME}/build-tools/34.0.0-rc3" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Cache build-tools
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3
|
||||
with:
|
||||
path: ${{ steps.build-tools-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-34.0.0-rc3
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
|
||||
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
@@ -69,7 +94,9 @@ jobs:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
||||
- name: Build release app
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 # v2.4.2
|
||||
env:
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
with:
|
||||
arguments: --no-configuration-cache --stacktrace collectReleaseApks
|
||||
gradle-home-cache-cleanup: true
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
name: CodeQL
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
||||
|
||||
- name: Accept all SDK licenses
|
||||
shell: bash
|
||||
run: printf 'y\ny\ny\ny\ny\ny\n' | $ANDROID_HOME/tools/bin/sdkmanager --licenses
|
||||
|
||||
- name: Get build-tools directory
|
||||
id: build-tools-path
|
||||
shell: bash
|
||||
run: echo "dir=${ANDROID_HOME}/build-tools/34.0.0-rc3" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Cache build-tools
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3
|
||||
with:
|
||||
path: ${{ steps.build-tools-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-34.0.0-rc3
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
|
||||
- name: Setup Gradle caching
|
||||
uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 # v2.4.2
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: true
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
|
||||
with:
|
||||
languages: java
|
||||
tools: latest
|
||||
queries: +security-extended
|
||||
|
||||
- name: Build project
|
||||
shell: bash
|
||||
run: |
|
||||
./gradlew assemble
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@0225834cc549ee0ca93cb085b92954821a145866 # v2.3.5
|
||||
with:
|
||||
category: "/language:java"
|
||||
@@ -1,30 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: "20 3 * * 0"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
|
||||
with:
|
||||
languages: java
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
|
||||
with:
|
||||
category: "/language:java"
|
||||
@@ -1,19 +0,0 @@
|
||||
name: Qodana
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
qodana:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Qodana Scan'
|
||||
uses: JetBrains/qodana-action@3fdeefa830a7634a5c04f1cd70fcfb69956137a2 # v2022.3.0
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
||||
@@ -7,13 +7,28 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # v3.2.0
|
||||
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
|
||||
with:
|
||||
token: ${{ secrets.POST_RELEASE_GH_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Accept all SDK licenses
|
||||
shell: bash
|
||||
run: printf 'y\ny\ny\ny\ny\ny\n' | $ANDROID_HOME/tools/bin/sdkmanager --licenses
|
||||
|
||||
- name: Get build-tools directory
|
||||
id: build-tools-path
|
||||
shell: bash
|
||||
run: echo "dir=${ANDROID_HOME}/build-tools/34.0.0-rc3" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Cache build-tools
|
||||
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3
|
||||
with:
|
||||
path: ${{ steps.build-tools-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-34.0.0-rc3
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
|
||||
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
@@ -25,7 +40,7 @@ jobs:
|
||||
git config user.email noreply@github.com
|
||||
|
||||
- name: Setup Gradle caching
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 # v2.4.2
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: true
|
||||
@@ -45,7 +60,7 @@ jobs:
|
||||
echo VERSION="${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Update changelog
|
||||
uses: thomaseizinger/keep-a-changelog-new-release@5bc232893483441c5d6cd6c9dcb1e48abf9a2bae # 1.3.0
|
||||
uses: msfjarvis/keep-a-changelog-new-release@ce8769801992253adb0209055c236a0cd192500b # 2.0.0
|
||||
with:
|
||||
tag: v${{ env.VERSION }}
|
||||
version: ${{ env.VERSION }}
|
||||
@@ -57,8 +72,11 @@ jobs:
|
||||
|
||||
- name: Build release assets
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
run: |
|
||||
./gradlew --no-configuration-cache collectReleaseApks collectReleaseBundle
|
||||
./gradlew --no-configuration-cache collectReleaseApks collectReleaseBundle -PsentryUploadMappings
|
||||
|
||||
- name: Clean secrets
|
||||
run: scripts/signing-cleanup.sh
|
||||
@@ -80,7 +98,7 @@ jobs:
|
||||
printf '%s' "${CHANGELOG}" >> distribution/whatsnew/whatsnew-en-GB
|
||||
|
||||
- name: Publish bundle to Google Play
|
||||
uses: r0adkll/upload-google-play@78c9e796b1035c94169c101d8e46cb880194bfc3 # tag=v1.0.19
|
||||
uses: r0adkll/upload-google-play@70c7397e868b00eccd390222f492ab3e30520d09 # v1.1.1
|
||||
with:
|
||||
mappingFile: android/bundle/mapping.txt
|
||||
packageName: dev.msfjarvis.claw.android
|
||||
|
||||
@@ -245,3 +245,4 @@ hs_err_pid*
|
||||
|
||||
keystore.properties
|
||||
distribution/
|
||||
screenshots/
|
||||
|
||||
@@ -7,132 +7,244 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.27.0] - 2023-05-31
|
||||
|
||||
### Changed
|
||||
|
||||
* Small accessibility improvements
|
||||
* Slightly tweak the layout for story items to take less vertical space
|
||||
* Upgrade to Compose `1.5.0-beta01` release
|
||||
* Set accessibility web URI for profile screen
|
||||
* Directly use Material Icons from upstream artifacts
|
||||
* Upgrade dependencies
|
||||
* Add adaptive navigation support (thanks @Yash-Garg)
|
||||
* Use latest SQLite for backing databases
|
||||
|
||||
## [1.26.0] - 2023-05-03
|
||||
|
||||
### Changed
|
||||
|
||||
* Upgrade to Compose May release
|
||||
* Disable Sentry performance reporting
|
||||
* Migrate to Compose Foundation `FlowRow` in `LobstersCard`
|
||||
|
||||
## [1.25.0] - 2023-04-18
|
||||
|
||||
### Changed
|
||||
|
||||
* Make the app draw edge to edge
|
||||
* Simplify Top App Bar color scheme
|
||||
* Rework how baseline profiles are generated
|
||||
* Upgrade to Compose April release
|
||||
|
||||
## [1.24.0] - 2023-03-24
|
||||
|
||||
### Changed
|
||||
|
||||
* Rebuild app icon assets to align better with Material guidelines
|
||||
* Adopt Slack's Compose lints and address issues found by it
|
||||
* Upgrade to OkHttp 4.x
|
||||
* Upgrade to Compose March release
|
||||
* Start work on support for logging in with lobste.rs
|
||||
|
||||
## [1.23.0] - 2023-03-02
|
||||
|
||||
### Added
|
||||
|
||||
* Introduce [Sentry](https://sentry.io) for error reporting and performance monitoring
|
||||
|
||||
## [1.22.0] - 2023-03-02
|
||||
|
||||
### Changed
|
||||
|
||||
* In-app browser now respects the user's choice for dark mode
|
||||
* Upgrade dependencies
|
||||
|
||||
## [1.21.0] - 2023-02-09
|
||||
|
||||
### Changed
|
||||
|
||||
* Fix bug that caused the app to crash on launch
|
||||
|
||||
## [1.20.0] - 2023-02-09
|
||||
|
||||
### Changed
|
||||
|
||||
* Upgrade dependencies
|
||||
|
||||
## [1.19.0] - 2023-02-01
|
||||
|
||||
### Changed
|
||||
|
||||
* Upgrade dependencies
|
||||
* Optimize packaged baseline profiles
|
||||
|
||||
## [1.18.0] - 2023-01-21
|
||||
|
||||
### Changed
|
||||
|
||||
* Special-case deleted stories in comments API
|
||||
* Fix regression where save button had the incorrect visual state
|
||||
|
||||
## [1.17.0] - 2023-01-12
|
||||
|
||||
### Added
|
||||
|
||||
* The app will now mark new comments as unread when returning to posts
|
||||
|
||||
### Changed
|
||||
|
||||
* Upgrade to Kotlin 1.8.0
|
||||
* Revert questionable app bar scrolling behaviour change
|
||||
|
||||
## [1.16.0] - 2022-12-24
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix bug where newest posts tab actually showed hottest posts instead
|
||||
* Fix bug where newest posts tab actually showed hottest posts instead
|
||||
|
||||
## [1.15.0] - 2022-12-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix a case where collapsing the parent of an already collapsed comment caused it to become expanded
|
||||
- Tweak progress bar UI to use a linear indicator
|
||||
- Allow app bar to hide when scrolling down
|
||||
- Fix bug where refreshing a list of posts caused it to jump around multiple times
|
||||
* Fix a case where collapsing the parent of an already collapsed comment caused it to become expanded
|
||||
* Tweak progress bar UI to use a linear indicator
|
||||
* Allow app bar to hide when scrolling down
|
||||
* Fix bug where refreshing a list of posts caused it to jump around multiple times
|
||||
|
||||
## [1.14.0] - 2022-12-12
|
||||
|
||||
### Changed
|
||||
|
||||
- Rework how often saved posts are updated
|
||||
- Collapsing a comment now collapses all comments under it similar to how it works on the website
|
||||
* Rework how often saved posts are updated
|
||||
* Collapsing a comment now collapses all comments under it similar to how it works on the website
|
||||
|
||||
## [1.13.0] - 2022-12-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix crash when viewing [jcs](https://lobste.rs/u/jcs)' comments
|
||||
* Fix crash when viewing [jcs](https://lobste.rs/u/jcs)' comments
|
||||
|
||||
## [1.12.0] - 2022-12-09
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not schedule post update job every time the app starts
|
||||
- Improve favicon loading to reduce unnecessary redraws
|
||||
- Fix post lists being reloaded unnecessarily
|
||||
* Do not schedule post update job every time the app starts
|
||||
* Improve favicon loading to reduce unnecessary redraws
|
||||
* Fix post lists being reloaded unnecessarily
|
||||
|
||||
## [1.11.0] - 2022-12-07
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to Compose 2022.12.00 release
|
||||
- Refresh packaged baseline profile
|
||||
- Improve how saved state of posts is queried
|
||||
* Upgrade to Compose 2022.12.00 release
|
||||
* Refresh packaged baseline profile
|
||||
* Improve how saved state of posts is queried
|
||||
|
||||
## [1.10.0] - 2022-11-30
|
||||
|
||||
### Changed
|
||||
|
||||
- Configure Coil image loader with better caching settings
|
||||
- Refresh packaged baseline profile
|
||||
* Configure Coil image loader with better caching settings
|
||||
* Refresh packaged baseline profile
|
||||
|
||||
## [1.9.0] - 2022-11-28
|
||||
|
||||
### Changed
|
||||
|
||||
- A loading indicator is added to the bottom of the post list to identify when new posts are being fetched
|
||||
* A loading indicator is added to the bottom of the post list to identify when new posts are being fetched
|
||||
|
||||
## [1.8.0] - 2022-11-21
|
||||
|
||||
### Changed
|
||||
|
||||
- The release pipeline now automatically generates Play Store release notes from the changelog file
|
||||
* The release pipeline now automatically generates Play Store release notes from the changelog file
|
||||
|
||||
### Fixed
|
||||
|
||||
- Downgrade AGP to 8.0.0-alpha07 to fix Baseline Profiles not being packaged
|
||||
* Downgrade AGP to 8.0.0-alpha07 to fix Baseline Profiles not being packaged
|
||||
|
||||
## [1.7.0] - 2022-11-19
|
||||
|
||||
### Changed
|
||||
|
||||
- Switch to Compose Material's swipe refresh feature
|
||||
- Change metadata extractor logic to no longer prefer canonical URLs. The submitted URL will be retained as-is now.
|
||||
- Remove navigation transition animations
|
||||
* Switch to Compose Material's swipe refresh feature
|
||||
* Change metadata extractor logic to no longer prefer canonical URLs. The submitted URL will be retained as-is now.
|
||||
* Remove navigation transition animations
|
||||
|
||||
## [1.6.0] - 2022-11-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Migrate dependency injection from Hilt to Anvil + Whetstone
|
||||
* Migrate dependency injection from Hilt to Anvil + Whetstone
|
||||
|
||||
## [1.5.0] - 2022-11-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to Compose 2022.11.00 release
|
||||
* Upgrade to Compose 2022.11.00 release
|
||||
|
||||
## [1.4.0] - 2022-11-02
|
||||
|
||||
### Changed
|
||||
|
||||
- Misc fixes to link metadata extractor
|
||||
* Misc fixes to link metadata extractor
|
||||
|
||||
## [1.3.0] - 2022-10-24
|
||||
|
||||
### Changed
|
||||
|
||||
- Prevent errors during link metadata retrieval from crashing the app
|
||||
- Update Jetpack libraries to 2022-10-24 release
|
||||
* Prevent errors during link metadata retrieval from crashing the app
|
||||
* Update Jetpack libraries to 2022-10-24 release
|
||||
|
||||
## [1.2.0] - 2022-10-17
|
||||
|
||||
### Changed
|
||||
|
||||
- Lazily load link metadata to improve comment page loading speed
|
||||
- Upgrade to latest Compose release
|
||||
- Refactor code to align with Twitter's public Compose guidelines
|
||||
* Lazily load link metadata to improve comment page loading speed
|
||||
* Upgrade to latest Compose release
|
||||
* Refactor code to align with Twitter's public Compose guidelines
|
||||
|
||||
## [1.1.0] - 2022-09-30
|
||||
|
||||
### Added
|
||||
|
||||
- Add score and relative time to comments
|
||||
* Add score and relative time to comments
|
||||
|
||||
### Changed
|
||||
|
||||
- Update Jetpack Compose
|
||||
- Declare data backup and transfer rules
|
||||
- Improve legibility of links in comments and user profiles
|
||||
- Commonize and improve how network errors are displayed
|
||||
* Update Jetpack Compose
|
||||
* Declare data backup and transfer rules
|
||||
* Improve legibility of links in comments and user profiles
|
||||
* Commonize and improve how network errors are displayed
|
||||
|
||||
## [1.0.0] - 2022-09-20
|
||||
|
||||
- Initial Play Store release
|
||||
* Initial Play Store release
|
||||
|
||||
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.16.0...HEAD
|
||||
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.27.0...HEAD
|
||||
|
||||
[1.27.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.26.0...v1.27.0
|
||||
|
||||
[1.26.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.25.0...v1.26.0
|
||||
|
||||
[1.25.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.24.0...v1.25.0
|
||||
|
||||
[1.24.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.23.0...v1.24.0
|
||||
|
||||
[1.23.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.22.0...v1.23.0
|
||||
|
||||
[1.22.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.21.0...v1.22.0
|
||||
|
||||
[1.21.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.20.0...v1.21.0
|
||||
|
||||
[1.20.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.19.0...v1.20.0
|
||||
|
||||
[1.19.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.18.0...v1.19.0
|
||||
|
||||
[1.18.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.17.0...v1.18.0
|
||||
|
||||
[1.17.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.16.0...v1.17.0
|
||||
|
||||
[1.16.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.15.0...v1.16.0
|
||||
|
||||
|
||||
@@ -2,20 +2,44 @@
|
||||
|
||||
Unofficial Android app for read-only access to [lobste.rs](https://lobste.rs), built with [Jetpack Compose](https://developer.android.com/jetpack/compose).
|
||||
|
||||
<img src="https://github.com/msfjarvis/compose-lobsters/blob/main/.github/readme_feature.webp"
|
||||
alt="Get it on Google Play"
|
||||
height="550" />
|
||||
|
||||
## Download
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=dev.msfjarvis.claw.android">
|
||||
<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"
|
||||
alt="Get it on Google Play"
|
||||
height="80" />
|
||||
</a>
|
||||
|
||||
Snapshots from the development branch can be obtained [here](https://github.com/msfjarvis/compose-lobsters/releases/tag/nightly).
|
||||
> Snapshots from the development branch can be obtained [here](https://github.com/msfjarvis/compose-lobsters/releases/tag/nightly).
|
||||
|
||||
<img src="https://github.com/msfjarvis/compose-lobsters/blob/main/.github/readme_feature.webp"
|
||||
alt="Side by side screenshots of the app's main page in dark and light UI modes"
|
||||
height="550" />
|
||||
|
||||
## Dependency Diagram
|
||||
```mermaid
|
||||
%%{
|
||||
init: {
|
||||
'theme': 'neutral'
|
||||
}
|
||||
}%%
|
||||
|
||||
graph LR
|
||||
|
||||
benchmark --> android
|
||||
android --> api
|
||||
android --> common
|
||||
android --> core
|
||||
android --> coroutine-utils
|
||||
android --> database
|
||||
android --> metadata-extractor
|
||||
android --> model
|
||||
api --> model
|
||||
common --> core
|
||||
common --> database
|
||||
common --> model
|
||||
metadata-extractor --> model
|
||||
database --> core
|
||||
|
||||
```
|
||||
## License
|
||||
|
||||
See [LICENSE](LICENSE)
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import dev.msfjarvis.claw.gradle.addTestDependencies
|
||||
|
||||
plugins {
|
||||
id("dev.msfjarvis.claw.android-application")
|
||||
id("dev.msfjarvis.claw.rename-artifacts")
|
||||
id("dev.msfjarvis.claw.kotlin-android")
|
||||
id("dev.msfjarvis.claw.kotlin-kapt")
|
||||
id("dev.msfjarvis.claw.sentry")
|
||||
id("dev.msfjarvis.claw.versioning-plugin")
|
||||
alias(libs.plugins.anvil)
|
||||
alias(libs.plugins.whetstone)
|
||||
}
|
||||
|
||||
whetstone {
|
||||
addOns {
|
||||
compose.set(true)
|
||||
workManager.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.msfjarvis.claw.android"
|
||||
defaultConfig { applicationId = "dev.msfjarvis.claw.android" }
|
||||
@@ -37,26 +33,29 @@ android {
|
||||
matchingFallbacks += "release"
|
||||
isDebuggable = false
|
||||
proguardFile("benchmark-rules.pro")
|
||||
applicationIdSuffix = ".benchmark"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
whetstone {
|
||||
addOns {
|
||||
compose.set(true)
|
||||
workManager.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kapt(libs.dagger.compiler)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.dagger)
|
||||
implementation(projects.api)
|
||||
implementation(projects.common)
|
||||
implementation(projects.core)
|
||||
implementation(projects.coroutineUtils)
|
||||
implementation(projects.database)
|
||||
implementation(projects.metadataExtractor)
|
||||
implementation(projects.model)
|
||||
implementation(platform(libs.okhttp.bom))
|
||||
implementation(libs.accompanist.sysuicontroller)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.compose.material)
|
||||
implementation(libs.androidx.compose.material.icons.extended)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.material3.window.size)
|
||||
implementation(libs.androidx.core.splashscreen)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.androidx.lifecycle.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.paging.compose)
|
||||
@@ -64,6 +63,23 @@ dependencies {
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
implementation(libs.coil)
|
||||
implementation(libs.copydown)
|
||||
implementation(libs.dagger)
|
||||
implementation(libs.jsoup)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.napier)
|
||||
implementation(libs.sqldelight.extensions.coroutines)
|
||||
implementation(projects.api)
|
||||
implementation(projects.common)
|
||||
implementation(projects.core)
|
||||
implementation(projects.coroutineUtils)
|
||||
implementation(projects.database)
|
||||
implementation(projects.metadataExtractor)
|
||||
implementation(projects.model)
|
||||
|
||||
kapt(libs.dagger.compiler)
|
||||
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testImplementation(libs.okhttp.mockwebserver)
|
||||
addTestDependencies(project)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 7.4.0-alpha09" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha09)" variant="all" version="7.4.0-alpha09">
|
||||
<issues format="6" by="lint 8.0.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.1)" variant="all" version="8.0.1">
|
||||
|
||||
</issues>
|
||||
|
||||
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 42 KiB |
@@ -1,22 +1,19 @@
|
||||
-allowaccessmodification
|
||||
-dontpreverify
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-verbose
|
||||
|
||||
-keepattributes *Annotation*
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keepclassmembers class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
|
||||
-keep class androidx.annotation.Keep
|
||||
@@ -24,13 +21,13 @@
|
||||
-keep @androidx.annotation.Keep class * {*;}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <methods>;
|
||||
@androidx.annotation.Keep <methods>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <fields>;
|
||||
@androidx.annotation.Keep <fields>;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class * {
|
||||
@androidx.annotation.Keep <init>(...);
|
||||
@androidx.annotation.Keep <init>(...);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
# Keep `Companion` object fields of serializable classes.
|
||||
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||
-if @kotlinx.serialization.Serializable class **
|
||||
-keepclassmembers class <1> {
|
||||
static <1>$Companion Companion;
|
||||
}
|
||||
|
||||
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
|
||||
-if @kotlinx.serialization.Serializable class ** {
|
||||
static **$* *;
|
||||
}
|
||||
-keepclassmembers class <2>$<3> {
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# Keep `INSTANCE.serializer()` of serializable objects.
|
||||
-if @kotlinx.serialization.Serializable class ** {
|
||||
public static ** INSTANCE;
|
||||
}
|
||||
-keepclassmembers class <1> {
|
||||
public static <1> INSTANCE;
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||
|
||||
-dontobfuscate
|
||||
-keepattributes SourceFile, LineNumberTable
|
||||
|
||||
# From https://github.com/square/retrofit/blob/master/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro
|
||||
|
||||
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
|
||||
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
|
||||
-if interface * { @retrofit2.http.* <methods>; }
|
||||
-keep,allowobfuscation interface <1>
|
||||
|
||||
# Keep inherited services.
|
||||
-if interface * { @retrofit2.http.* <methods>; }
|
||||
-keep,allowobfuscation interface * extends <1>
|
||||
|
||||
# With R8 full mode generic signatures are stripped for classes that are not
|
||||
# kept. Suspend functions are wrapped in continuations where the type argument
|
||||
# is used.
|
||||
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
|
||||
|
||||
# R8 full mode strips generic signatures from return types if not kept.
|
||||
-if interface * { @retrofit2.http.* public *** *(...); }
|
||||
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Claw.SplashScreen"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
tools:targetApi="s">
|
||||
tools:targetApi="tiramisu">
|
||||
<profileable
|
||||
android:shell="true"
|
||||
tools:targetApi="29" />
|
||||
@@ -25,19 +25,6 @@
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
<data android:ssp="//lobste.rs" />
|
||||
|
||||
<data android:host="lobste.rs" />
|
||||
<data android:path="/" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -163,5 +150,14 @@
|
||||
<action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<!-- Required: set your sentry.io project identifier (DSN) -->
|
||||
<meta-data android:name="io.sentry.dsn" android:value="${sentryDsn}" />
|
||||
<!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
|
||||
<meta-data android:name="io.sentry.traces.user-interaction.enable" android:value="${enableSentry}" />
|
||||
<meta-data android:name="io.sentry.breadcrumbs.user-interaction" android:value="${enableSentry}" />
|
||||
<!-- enable screenshot for crashes -->
|
||||
<meta-data android:name="io.sentry.attach-screenshot" android:value="${enableSentry}" />
|
||||
<!-- enable view hierarchy for crashes -->
|
||||
<meta-data android:name="io.sentry.attach-view-hierarchy" android:value="${enableSentry}" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -11,7 +11,10 @@ import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
||||
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.deliveryhero.whetstone.Whetstone
|
||||
import com.deliveryhero.whetstone.activity.ContributesActivityInjector
|
||||
import dev.msfjarvis.claw.android.ui.LobstersApp
|
||||
@@ -26,14 +29,19 @@ class MainActivity : ComponentActivity() {
|
||||
@Inject lateinit var htmlConverter: HTMLConverter
|
||||
private var webUri: String? = null
|
||||
|
||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
installSplashScreen()
|
||||
Whetstone.inject(this)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setContent {
|
||||
val windowSizeClass = calculateWindowSizeClass(this)
|
||||
|
||||
LobstersApp(
|
||||
urlLauncher = urlLauncher,
|
||||
htmlConverter = htmlConverter,
|
||||
windowSizeClass = windowSizeClass,
|
||||
setWebUri = { url -> webUri = url },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.preferencesDataStoreFile
|
||||
import com.deliveryhero.whetstone.ForScope
|
||||
import com.deliveryhero.whetstone.app.ApplicationScope
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dev.msfjarvis.claw.util.coroutines.DispatcherProvider
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
||||
@Module
|
||||
@ContributesTo(ApplicationScope::class)
|
||||
class DataStoreModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideLoginDatastore(
|
||||
@ForScope(ApplicationScope::class) context: Context,
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
): DataStore<Preferences> {
|
||||
return PreferenceDataStoreFactory.create(
|
||||
corruptionHandler = null,
|
||||
migrations = emptyList(),
|
||||
scope = CoroutineScope(dispatcherProvider.io() + SupervisorJob()),
|
||||
) {
|
||||
context.preferencesDataStoreFile("login")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.settings
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoginSettings
|
||||
@Inject
|
||||
constructor(
|
||||
val dataStore: DataStore<Preferences>,
|
||||
) {
|
||||
companion object {
|
||||
val CSRF_TOKEN = stringPreferencesKey("csrf_token")
|
||||
val LOGIN_COOKIE = stringPreferencesKey("login_cookie")
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.filled.NewReleases
|
||||
import androidx.compose.material.icons.filled.Whatshot
|
||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||
import androidx.compose.material.icons.outlined.NavigateBefore
|
||||
import androidx.compose.material.icons.outlined.NewReleases
|
||||
import androidx.compose.material.icons.outlined.Whatshot
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -40,39 +45,37 @@ import androidx.navigation.navArgument
|
||||
import androidx.navigation.navDeepLink
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.deliveryhero.whetstone.compose.injectedViewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import dev.msfjarvis.claw.android.R
|
||||
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
|
||||
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationRail
|
||||
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
|
||||
import dev.msfjarvis.claw.android.ui.decorations.TransparentSystemBars
|
||||
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
|
||||
import dev.msfjarvis.claw.android.ui.lists.NetworkPosts
|
||||
import dev.msfjarvis.claw.android.ui.navigation.ClawNavigationType
|
||||
import dev.msfjarvis.claw.android.ui.navigation.Destinations
|
||||
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.common.comments.CommentsPage
|
||||
import dev.msfjarvis.claw.common.comments.HTMLConverter
|
||||
import dev.msfjarvis.claw.common.res.ClawIcons
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.decorations.ClawAppBar
|
||||
import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation
|
||||
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
||||
import dev.msfjarvis.claw.common.user.UserProfile
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(
|
||||
ExperimentalComposeUiApi::class,
|
||||
ExperimentalMaterial3Api::class,
|
||||
)
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun LobstersApp(
|
||||
urlLauncher: UrlLauncher,
|
||||
htmlConverter: HTMLConverter,
|
||||
windowSizeClass: WindowSizeClass,
|
||||
setWebUri: (String?) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: ClawViewModel = injectedViewModel(),
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val hottestListState = rememberLazyListState()
|
||||
val newestListState = rememberLazyListState()
|
||||
val savedListState = rememberLazyListState()
|
||||
@@ -85,67 +88,54 @@ fun LobstersApp(
|
||||
|
||||
val hottestPosts = viewModel.hottestPosts.collectAsLazyPagingItems()
|
||||
val newestPosts = viewModel.newestPosts.collectAsLazyPagingItems()
|
||||
val savedPosts by viewModel.savedPosts.collectAsState(emptyMap())
|
||||
val savedPosts by viewModel.savedPosts.collectAsState(persistentMapOf())
|
||||
|
||||
val navigationType = ClawNavigationType.fromSize(windowSizeClass.widthSizeClass)
|
||||
|
||||
LobstersTheme(
|
||||
dynamicColor = true,
|
||||
providedValues = arrayOf(LocalUriHandler provides urlLauncher),
|
||||
) {
|
||||
val currentUiMode = LocalConfiguration.current.uiMode
|
||||
val systemBarsColor = MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation()
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
val navItems =
|
||||
listOf(
|
||||
persistentListOf(
|
||||
NavigationItem(
|
||||
label = "Hottest",
|
||||
route = Destinations.Hottest.route,
|
||||
icon = ClawIcons.Flame,
|
||||
selectedIcon = ClawIcons.FlameFilled,
|
||||
icon = rememberVectorPainter(Icons.Outlined.Whatshot),
|
||||
selectedIcon = rememberVectorPainter(Icons.Filled.Whatshot),
|
||||
) {
|
||||
coroutineScope.launch { hottestListState.animateScrollToItem(index = 0) }
|
||||
},
|
||||
NavigationItem(
|
||||
label = "Newest",
|
||||
route = Destinations.Newest.route,
|
||||
icon = ClawIcons.New,
|
||||
selectedIcon = ClawIcons.NewFilled,
|
||||
icon = rememberVectorPainter(Icons.Outlined.NewReleases),
|
||||
selectedIcon = rememberVectorPainter(Icons.Filled.NewReleases),
|
||||
) {
|
||||
coroutineScope.launch { newestListState.animateScrollToItem(index = 0) }
|
||||
},
|
||||
NavigationItem(
|
||||
label = "Saved",
|
||||
route = Destinations.Saved.route,
|
||||
icon = ClawIcons.HeartBorder,
|
||||
selectedIcon = ClawIcons.Heart,
|
||||
icon = rememberVectorPainter(Icons.Outlined.FavoriteBorder),
|
||||
selectedIcon = rememberVectorPainter(Icons.Filled.Favorite),
|
||||
) {
|
||||
coroutineScope.launch { savedListState.animateScrollToItem(index = 0) }
|
||||
},
|
||||
)
|
||||
|
||||
SideEffect { systemUiController.setStatusBarColor(color = systemBarsColor) }
|
||||
|
||||
// Track UI mode as a key to force a navbar color update when dark theme is toggled
|
||||
LaunchedEffect(currentDestination, currentUiMode) {
|
||||
val color =
|
||||
if (navItems.none { item -> item.route == currentDestination }) {
|
||||
backgroundColor
|
||||
} else {
|
||||
systemBarsColor
|
||||
}
|
||||
systemUiController.setNavigationBarColor(color = color)
|
||||
}
|
||||
TransparentSystemBars()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
ClawAppBar(
|
||||
backgroundColor = systemBarsColor,
|
||||
navigationIcon = {
|
||||
if (navItems.none { it.route == currentDestination }) {
|
||||
IconButton(
|
||||
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() }
|
||||
) {
|
||||
Icon(
|
||||
painter = ClawIcons.ArrowBack,
|
||||
painter = rememberVectorPainter(Icons.Outlined.NavigateBefore),
|
||||
contentDescription = "Go back to previous screen",
|
||||
)
|
||||
}
|
||||
@@ -156,87 +146,91 @@ fun LobstersApp(
|
||||
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
ClawNavigationBar(
|
||||
navController = navController,
|
||||
items = navItems,
|
||||
isVisible = navItems.any { it.route == currentDestination },
|
||||
)
|
||||
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
|
||||
ClawNavigationBar(
|
||||
navController = navController,
|
||||
items = navItems,
|
||||
isVisible = navItems.any { it.route == currentDestination },
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier =
|
||||
modifier
|
||||
.semantics { testTagsAsResourceId = true }
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
modifier = modifier.semantics { testTagsAsResourceId = true },
|
||||
) { paddingValues ->
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Destinations.startDestination.route,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
) {
|
||||
val uri = LobstersApi.BASE_URL
|
||||
composable(
|
||||
route = Destinations.Hottest.route,
|
||||
deepLinks =
|
||||
listOf(navDeepLink { uriPattern = uri }, navDeepLink { uriPattern = "$uri/" }),
|
||||
Row(modifier = Modifier.padding(paddingValues)) {
|
||||
AnimatedVisibility(visible = navigationType == ClawNavigationType.NAVIGATION_RAIL) {
|
||||
ClawNavigationRail(
|
||||
navController = navController,
|
||||
items = navItems,
|
||||
isVisible = navItems.any { it.route == currentDestination },
|
||||
)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Destinations.startDestination.route,
|
||||
) {
|
||||
setWebUri("https://lobste.rs/")
|
||||
NetworkPosts(
|
||||
lazyPagingItems = hottestPosts,
|
||||
listState = hottestListState,
|
||||
isPostSaved = viewModel::isPostSaved,
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.Newest.route,
|
||||
) {
|
||||
setWebUri("https://lobste.rs/")
|
||||
NetworkPosts(
|
||||
lazyPagingItems = newestPosts,
|
||||
listState = newestListState,
|
||||
isPostSaved = viewModel::isPostSaved,
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(Destinations.Saved.route) {
|
||||
setWebUri(null)
|
||||
DatabasePosts(
|
||||
items = savedPosts,
|
||||
listState = savedListState,
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.Comments.route,
|
||||
arguments = listOf(navArgument("postId") { type = NavType.StringType }),
|
||||
deepLinks =
|
||||
listOf(
|
||||
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}/.*" },
|
||||
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}" },
|
||||
),
|
||||
) { backStackEntry ->
|
||||
val postId = requireNotNull(backStackEntry.arguments?.getString("postId"))
|
||||
setWebUri("https://lobste.rs/s/$postId")
|
||||
CommentsPage(
|
||||
postId = postId,
|
||||
postActions = postActions,
|
||||
htmlConverter = htmlConverter,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.User.route,
|
||||
arguments = listOf(navArgument("username") { type = NavType.StringType }),
|
||||
deepLinks =
|
||||
listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.placeholder}" }),
|
||||
) { backStackEntry ->
|
||||
val username = requireNotNull(backStackEntry.arguments?.getString("username"))
|
||||
UserProfile(
|
||||
username = username,
|
||||
getProfile = viewModel::getUserProfile,
|
||||
)
|
||||
val uri = LobstersApi.BASE_URL
|
||||
composable(route = Destinations.Hottest.route) {
|
||||
setWebUri("https://lobste.rs/")
|
||||
NetworkPosts(
|
||||
lazyPagingItems = hottestPosts,
|
||||
listState = hottestListState,
|
||||
isPostSaved = viewModel::isPostSaved,
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(route = Destinations.Newest.route) {
|
||||
setWebUri("https://lobste.rs/")
|
||||
NetworkPosts(
|
||||
lazyPagingItems = newestPosts,
|
||||
listState = newestListState,
|
||||
isPostSaved = viewModel::isPostSaved,
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(route = Destinations.Saved.route) {
|
||||
setWebUri(null)
|
||||
DatabasePosts(
|
||||
items = savedPosts,
|
||||
listState = savedListState,
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.Comments.route,
|
||||
arguments = listOf(navArgument("postId") { type = NavType.StringType }),
|
||||
deepLinks =
|
||||
listOf(
|
||||
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}/.*" },
|
||||
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}" },
|
||||
),
|
||||
) { backStackEntry ->
|
||||
val postId = requireNotNull(backStackEntry.arguments?.getString("postId"))
|
||||
setWebUri("https://lobste.rs/s/$postId")
|
||||
CommentsPage(
|
||||
postId = postId,
|
||||
postActions = postActions,
|
||||
htmlConverter = htmlConverter,
|
||||
getSeenComments = viewModel::getSeenComments,
|
||||
markSeenComments = viewModel::markSeenComments,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.User.route,
|
||||
arguments = listOf(navArgument("username") { type = NavType.StringType }),
|
||||
deepLinks =
|
||||
listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.placeholder}" }),
|
||||
) { backStackEntry ->
|
||||
val username = requireNotNull(backStackEntry.arguments?.getString("username"))
|
||||
setWebUri("https://lobste.rs/u/$username")
|
||||
UserProfile(
|
||||
username = username,
|
||||
getProfile = viewModel::getUserProfile,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -23,13 +23,14 @@ import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.navigation.NavController
|
||||
import dev.msfjarvis.claw.android.ui.navigation.Destinations
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val AnimationDuration = 100
|
||||
const val AnimationDuration = 100
|
||||
|
||||
@Composable
|
||||
fun ClawNavigationBar(
|
||||
navController: NavController,
|
||||
items: List<NavigationItem>,
|
||||
items: ImmutableList<NavigationItem>,
|
||||
isVisible: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -54,10 +55,10 @@ fun ClawNavigationBar(
|
||||
val isCurrentDestination = navController.currentDestination?.route == navItem.route
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
Crossfade(isCurrentDestination) {
|
||||
Crossfade(isCurrentDestination, label = "nav-label") {
|
||||
Icon(
|
||||
painter = if (it) navItem.selectedIcon else navItem.icon,
|
||||
contentDescription = navItem.label.uppercase()
|
||||
contentDescription = navItem.label.replaceFirstChar(Char::uppercase),
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -66,13 +67,13 @@ fun ClawNavigationBar(
|
||||
onClick = {
|
||||
if (isCurrentDestination) {
|
||||
navItem.listStateResetCallback()
|
||||
return@NavigationBarItem
|
||||
}
|
||||
navController.graph.startDestinationRoute?.let { startDestination ->
|
||||
navController.popBackStack(startDestination, false)
|
||||
}
|
||||
if (navItem.route != Destinations.startDestination.route) {
|
||||
navController.navigate(navItem.route)
|
||||
} else {
|
||||
navController.graph.startDestinationRoute?.let { startDestination ->
|
||||
navController.popBackStack(startDestination, false)
|
||||
}
|
||||
if (navItem.route != Destinations.startDestination.route) {
|
||||
navController.navigate(navItem.route)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.testTag(navItem.label.uppercase()),
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.decorations
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationRail
|
||||
import androidx.compose.material3.NavigationRailItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.navigation.NavController
|
||||
import dev.msfjarvis.claw.android.ui.navigation.Destinations
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun ClawNavigationRail(
|
||||
navController: NavController,
|
||||
items: ImmutableList<NavigationItem>,
|
||||
isVisible: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = isVisible,
|
||||
enter =
|
||||
slideInHorizontally(
|
||||
// Enters by sliding in from offset -fullWidth to 0.
|
||||
initialOffsetX = { fullWidth -> -fullWidth },
|
||||
animationSpec = tween(durationMillis = AnimationDuration, easing = LinearOutSlowInEasing),
|
||||
),
|
||||
exit =
|
||||
slideOutHorizontally(
|
||||
// Exits by sliding out from offset 0 to -fullWidth.
|
||||
targetOffsetX = { fullWidth -> -fullWidth },
|
||||
animationSpec = tween(durationMillis = AnimationDuration, easing = FastOutLinearInEasing),
|
||||
),
|
||||
modifier = Modifier,
|
||||
) {
|
||||
NavigationRail(modifier = modifier) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
items.forEach { navItem ->
|
||||
val isCurrentDestination = navController.currentDestination?.route == navItem.route
|
||||
NavigationRailItem(
|
||||
icon = {
|
||||
Crossfade(isCurrentDestination, label = "nav-label") {
|
||||
Icon(
|
||||
painter = if (it) navItem.selectedIcon else navItem.icon,
|
||||
contentDescription = navItem.label.replaceFirstChar(Char::uppercase),
|
||||
)
|
||||
}
|
||||
},
|
||||
label = { Text(text = navItem.label) },
|
||||
selected = isCurrentDestination,
|
||||
onClick = {
|
||||
if (isCurrentDestination) {
|
||||
navItem.listStateResetCallback()
|
||||
} else {
|
||||
navController.graph.startDestinationRoute?.let { startDestination ->
|
||||
navController.popBackStack(startDestination, false)
|
||||
}
|
||||
if (navItem.route != Destinations.startDestination.route) {
|
||||
navController.navigate(navItem.route)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.testTag(navItem.label.uppercase()),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.decorations
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
@Composable
|
||||
fun TransparentSystemBars() {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val useDarkIcons = !isSystemInDarkTheme()
|
||||
|
||||
DisposableEffect(systemUiController, useDarkIcons) {
|
||||
systemUiController.setSystemBarsColor(color = Color.Transparent, darkIcons = useDarkIcons)
|
||||
|
||||
onDispose {}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -7,6 +7,8 @@
|
||||
package dev.msfjarvis.claw.android.ui.lists
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
@@ -17,29 +19,34 @@ import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.ui.decorations.MonthHeader
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import java.time.Month
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun DatabasePosts(
|
||||
items: Map<Month, List<SavedPost>>,
|
||||
items: ImmutableMap<Month, List<SavedPost>>,
|
||||
listState: LazyListState,
|
||||
postActions: PostActions,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = modifier,
|
||||
) {
|
||||
items.forEach { (month, posts) ->
|
||||
stickyHeader { MonthHeader(month = month) }
|
||||
items(posts) { item ->
|
||||
ListItem(
|
||||
item = item,
|
||||
isSaved = { true },
|
||||
postActions = postActions,
|
||||
)
|
||||
|
||||
Divider()
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
) {
|
||||
items.forEach { (month, posts) ->
|
||||
stickyHeader(contentType = "month-header") { MonthHeader(month = month) }
|
||||
items(
|
||||
items = posts,
|
||||
key = { it.shortId },
|
||||
contentType = { "LobstersItem" },
|
||||
) { item ->
|
||||
ListItem(
|
||||
item = item,
|
||||
isSaved = { true },
|
||||
postActions = postActions,
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -26,7 +26,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.items
|
||||
import androidx.paging.compose.itemContentType
|
||||
import androidx.paging.compose.itemKey
|
||||
import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.posts.toDbModel
|
||||
import dev.msfjarvis.claw.common.ui.NetworkError
|
||||
@@ -57,7 +58,12 @@ fun NetworkPosts(
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
) {
|
||||
items(items = lazyPagingItems, key = { it.shortId }) { item ->
|
||||
items(
|
||||
count = lazyPagingItems.itemCount,
|
||||
key = lazyPagingItems.itemKey { it.shortId },
|
||||
contentType = lazyPagingItems.itemContentType { "LobstersItem" },
|
||||
) { index ->
|
||||
val item = lazyPagingItems[index]
|
||||
if (item != null) {
|
||||
val dbModel = item.toDbModel()
|
||||
ListItem(
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.navigation
|
||||
|
||||
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
|
||||
|
||||
enum class ClawNavigationType {
|
||||
BOTTOM_NAVIGATION,
|
||||
NAVIGATION_RAIL;
|
||||
|
||||
companion object {
|
||||
fun fromSize(windowSize: WindowWidthSizeClass): ClawNavigationType {
|
||||
return when (windowSize) {
|
||||
WindowWidthSizeClass.Medium,
|
||||
WindowWidthSizeClass.Expanded -> NAVIGATION_RAIL
|
||||
else -> BOTTOM_NAVIGATION
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import dev.msfjarvis.claw.android.injection.IODispatcher
|
||||
import dev.msfjarvis.claw.api.injection.BaseUrl
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
/** Helper for extracting CSRF token for authenticated requests to https://lobste.rs. */
|
||||
class CSRFRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
@IODispatcher private val dispatcher: CoroutineDispatcher,
|
||||
@BaseUrl private val url: String,
|
||||
) {
|
||||
suspend fun extractToken(): String? {
|
||||
val request = Request.Builder().url(url).get().build()
|
||||
return withContext(dispatcher) {
|
||||
okHttpClient.newCall(request).execute().use { response ->
|
||||
val doc = Jsoup.parse(response.body?.string() ?: return@use null)
|
||||
val element = doc.select("meta[name=\"csrf-token\"]").first() ?: return@use null
|
||||
return@use element.attr("content")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -20,9 +20,13 @@ import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.STARTING
|
||||
import dev.msfjarvis.claw.android.ui.toLocalDateTime
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.time.Month
|
||||
import javax.inject.Inject
|
||||
import kotlinx.collections.immutable.ImmutableMap
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -60,9 +64,9 @@ constructor(
|
||||
val savedPosts
|
||||
get() = savedPostsFlow.map(::mapSavedPosts)
|
||||
|
||||
private fun mapSavedPosts(items: List<SavedPost>): Map<Month, List<SavedPost>> {
|
||||
private fun mapSavedPosts(items: List<SavedPost>): ImmutableMap<Month, List<SavedPost>> {
|
||||
val sorted = items.sortedByDescending { post -> post.createdAt.toLocalDateTime() }
|
||||
return sorted.groupBy { post -> post.createdAt.toLocalDateTime().month }
|
||||
return sorted.groupBy { post -> post.createdAt.toLocalDateTime().month }.toImmutableMap()
|
||||
}
|
||||
|
||||
suspend fun isPostSaved(post: SavedPost): Boolean {
|
||||
@@ -86,11 +90,23 @@ constructor(
|
||||
is Success -> result.value
|
||||
is Failure.NetworkFailure -> throw result.error
|
||||
is Failure.UnknownFailure -> throw result.error
|
||||
is Failure.HttpFailure,
|
||||
is Failure.HttpFailure -> {
|
||||
if (result.code == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
throw IOException("Story was removed by moderator")
|
||||
} else {
|
||||
throw IOException("API returned an invalid response")
|
||||
}
|
||||
}
|
||||
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSeenComments(postId: String) = savedPostsRepository.getSeenComments(postId)
|
||||
|
||||
fun markSeenComments(postId: String, comments: List<Comment>) {
|
||||
viewModelScope.launch { savedPostsRepository.markSeenComments(postId, comments) }
|
||||
}
|
||||
|
||||
suspend fun getLinkMetadata(url: String) =
|
||||
withContext(ioDispatcher) { linkMetadataRepository.getLinkMetadata(url) }
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -10,7 +10,9 @@ import app.cash.sqldelight.coroutines.asFlow
|
||||
import app.cash.sqldelight.coroutines.mapToList
|
||||
import dev.msfjarvis.claw.android.injection.DatabaseDispatcher
|
||||
import dev.msfjarvis.claw.database.LobstersDatabase
|
||||
import dev.msfjarvis.claw.database.local.PostComments
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
import io.github.aakira.napier.Napier
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -23,6 +25,7 @@ constructor(
|
||||
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
|
||||
) {
|
||||
private val savedPostQueries = database.savedPostQueries
|
||||
private val postCommentsQueries = database.postCommentsQueries
|
||||
val savedPosts = savedPostQueries.selectAllPosts().asFlow().mapToList(dbDispatcher)
|
||||
|
||||
suspend fun savePost(post: SavedPost) {
|
||||
@@ -44,6 +47,15 @@ constructor(
|
||||
withContext(dbDispatcher) { savedPostQueries.deletePost(post.shortId) }
|
||||
}
|
||||
|
||||
suspend fun getSeenComments(postId: String) =
|
||||
withContext(dbDispatcher) { postCommentsQueries.getCommentIds(postId).executeAsOneOrNull() }
|
||||
|
||||
suspend fun markSeenComments(postId: String, comments: List<Comment>) {
|
||||
withContext(dbDispatcher) {
|
||||
postCommentsQueries.rememberComments(PostComments(postId, comments.map { it.shortId }))
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val TAG = "SavedPostsRepository"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 38 B After Width: | Height: | Size: 44 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 38 B After Width: | Height: | Size: 44 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 38 B After Width: | Height: | Size: 46 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 38 B After Width: | Height: | Size: 52 B |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.mockwebserver.Dispatcher
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import okhttp3.mockwebserver.RecordedRequest
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class CSRFRepositoryTest {
|
||||
@Test
|
||||
fun `correctly extracts CSRF token`() = runTest {
|
||||
val repo =
|
||||
CSRFRepository(
|
||||
OkHttpClient.Builder().build(),
|
||||
Dispatchers.Default,
|
||||
server.url("/").toString(),
|
||||
)
|
||||
assertThat(repo.extractToken())
|
||||
.isEqualTo(
|
||||
"OZWykgFemPVeOSNmB53-ccKXe458X7xCInO1-qzFU6nk_9RCSrSQqS9OPmA5_pyy8qD3IYAIZ7XfAM3gdhJpkQ"
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val server = MockWebServer()
|
||||
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
fun setup() {
|
||||
val dispatcher =
|
||||
object : Dispatcher() {
|
||||
override fun dispatch(request: RecordedRequest): MockResponse {
|
||||
return when (val path = request.path) {
|
||||
"/" ->
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody(
|
||||
javaClass.classLoader!!
|
||||
.getResourceAsStream("csrf_page.html")
|
||||
.readAllBytes()
|
||||
.decodeToString(),
|
||||
)
|
||||
else -> error("Invalid path: $path")
|
||||
}
|
||||
}
|
||||
}
|
||||
server.dispatcher = dispatcher
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
|
||||
#
|
||||
versioning-plugin.versionCode=11600
|
||||
versioning-plugin.versionName=1.16.0
|
||||
versioning-plugin.versionCode=12700
|
||||
versioning-plugin.versionName=1.27.0
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
import dev.msfjarvis.claw.gradle.addTestDependencies
|
||||
|
||||
plugins {
|
||||
id("dev.msfjarvis.claw.kotlin-android")
|
||||
id("dev.msfjarvis.claw.android-library")
|
||||
id("dev.msfjarvis.claw.kotlin-android")
|
||||
alias(libs.plugins.anvil)
|
||||
alias(libs.plugins.whetstone)
|
||||
}
|
||||
@@ -18,14 +18,15 @@ android { namespace = "dev.msfjarvis.claw.api" }
|
||||
anvil { generateDaggerFactories.set(true) }
|
||||
|
||||
dependencies {
|
||||
api(projects.model)
|
||||
api(libs.retrofit)
|
||||
api(libs.eithernet)
|
||||
api(libs.retrofit)
|
||||
api(projects.model)
|
||||
|
||||
implementation(libs.dagger)
|
||||
implementation(libs.javax.inject)
|
||||
|
||||
testImplementation(testFixtures(libs.eithernet))
|
||||
testImplementation(libs.kotest.assertions.core)
|
||||
testImplementation(libs.kotest.runner.junit5)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
testImplementation(libs.kotlinx.serialization.json)
|
||||
testImplementation(libs.retrofit.kotlinxSerializationConverter)
|
||||
addTestDependencies(project)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.0.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-alpha08)" variant="all" version="8.0.0-alpha08">
|
||||
<issues format="6" by="lint 8.0.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.1)" variant="all" version="8.0.1">
|
||||
|
||||
</issues>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -9,6 +9,7 @@ package dev.msfjarvis.claw.api
|
||||
import com.slack.eithernet.ApiResult
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.Tags
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
@@ -29,6 +30,12 @@ interface LobstersApi {
|
||||
@GET("u/{username}.json")
|
||||
suspend fun getUser(@Path("username") username: String): ApiResult<User, Unit>
|
||||
|
||||
@GET("t/{tags}.json")
|
||||
suspend fun getPostsByTags(
|
||||
@Path("tags") tag: Tags,
|
||||
@Query("page") page: Int,
|
||||
): ApiResult<List<LobstersPost>, Unit>
|
||||
|
||||
companion object {
|
||||
const val BASE_URL = "https://lobste.rs"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -13,6 +13,7 @@ import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import javax.inject.Qualifier
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Converter
|
||||
import retrofit2.Retrofit
|
||||
@@ -25,10 +26,11 @@ object ApiModule {
|
||||
fun provideRetrofit(
|
||||
client: OkHttpClient,
|
||||
converterFactories: Set<@JvmSuppressWildcards Converter.Factory>,
|
||||
@BaseUrl baseUrl: String,
|
||||
): Retrofit {
|
||||
return Retrofit.Builder()
|
||||
.client(client)
|
||||
.baseUrl(LobstersApi.BASE_URL)
|
||||
.baseUrl(baseUrl)
|
||||
.addConverterFactory(ApiResultConverterFactory)
|
||||
.addCallAdapterFactory(ApiResultCallAdapterFactory)
|
||||
.apply { converterFactories.forEach(this::addConverterFactory) }
|
||||
@@ -39,4 +41,8 @@ object ApiModule {
|
||||
fun provideApi(retrofit: Retrofit): LobstersApi {
|
||||
return retrofit.create()
|
||||
}
|
||||
|
||||
@Provides @BaseUrl fun provideBaseUrl(): String = LobstersApi.BASE_URL
|
||||
}
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class BaseUrl
|
||||
|
||||
@@ -1,50 +1,76 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.api
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.slack.eithernet.ApiResult.Success
|
||||
import com.slack.eithernet.test.newEitherNetController
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.Tags
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.shouldBeTypeOf
|
||||
import dev.msfjarvis.claw.util.TestUtils.assertIs
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ApiTest : FunSpec() {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ApiTest {
|
||||
private val wrapper = ApiWrapper(newEitherNetController())
|
||||
private val api
|
||||
get() = wrapper.api
|
||||
|
||||
init {
|
||||
test("api gets correct number of items") {
|
||||
val posts = api.getHottestPosts(1)
|
||||
posts.shouldBeTypeOf<Success<List<LobstersPost>>>()
|
||||
posts.value shouldHaveSize 25
|
||||
}
|
||||
@Test
|
||||
fun `api gets correct number of items`() = runTest {
|
||||
val posts = api.getHottestPosts(1)
|
||||
assertIs<Success<List<LobstersPost>>>(posts)
|
||||
assertThat(posts.value).hasSize(25)
|
||||
}
|
||||
|
||||
test("posts with no urls") {
|
||||
val posts = api.getHottestPosts(1)
|
||||
posts.shouldBeTypeOf<Success<List<LobstersPost>>>()
|
||||
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
|
||||
commentsOnlyPosts shouldHaveSize 2
|
||||
}
|
||||
@Test
|
||||
fun `posts with no urls`() = runTest {
|
||||
val posts = api.getHottestPosts(1)
|
||||
assertIs<Success<List<LobstersPost>>>(posts)
|
||||
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
|
||||
assertThat(commentsOnlyPosts).hasSize(2)
|
||||
}
|
||||
|
||||
test("post details with comments") {
|
||||
val postDetails = api.getPostDetails("tdfoqh")
|
||||
postDetails.shouldBeTypeOf<Success<LobstersPostDetails>>()
|
||||
postDetails.value.comments shouldHaveSize 7
|
||||
}
|
||||
@Test
|
||||
fun `post details with comments`() = runTest {
|
||||
val postDetails = api.getPostDetails("tdfoqh")
|
||||
assertIs<Success<LobstersPostDetails>>(postDetails)
|
||||
assertThat(postDetails.value.comments).hasSize(7)
|
||||
}
|
||||
|
||||
test("get user details") {
|
||||
val user = api.getUser("msfjarvis")
|
||||
user.shouldBeTypeOf<Success<User>>()
|
||||
user.value.username shouldBe "msfjarvis"
|
||||
}
|
||||
@Test
|
||||
fun `get user details`() = runTest {
|
||||
val user = api.getUser("msfjarvis")
|
||||
assertIs<Success<User>>(user)
|
||||
assertThat(user.value.username).isEqualTo("msfjarvis")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get posts by single tag`() = runTest {
|
||||
val tags = Tags()
|
||||
tags.addTag("meta")
|
||||
val posts = api.getPostsByTags(tags, 1)
|
||||
assertIs<Success<List<LobstersPost>>>(posts)
|
||||
assertThat(posts.value).hasSize(25)
|
||||
assertThat(posts.value[0].tags).contains("meta")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get posts by multiple tags`() = runTest {
|
||||
val tags = Tags()
|
||||
tags.addTag("programming")
|
||||
tags.addTag("rust")
|
||||
val posts = api.getPostsByTags(tags, 1)
|
||||
assertIs<Success<List<LobstersPost>>>(posts)
|
||||
assertThat(posts.value).hasSize(25)
|
||||
assertThat(posts.value[0].tags).containsAnyOf("programming", "rust")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.api
|
||||
|
||||
import com.slack.eithernet.ApiResult.Companion.httpFailure
|
||||
import com.slack.eithernet.ApiResult.Companion.success
|
||||
import com.slack.eithernet.test.EitherNetController
|
||||
import com.slack.eithernet.test.enqueue
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.Tags
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import dev.msfjarvis.claw.util.TestUtils.getJson
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonNamingStrategy
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
class ApiWrapper(controller: EitherNetController<LobstersApi>) {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
namingStrategy = JsonNamingStrategy.SnakeCase
|
||||
}
|
||||
private val hottest: List<LobstersPost> = json.decodeFromString(getJson("hottest.json"))
|
||||
private val postDetails: LobstersPostDetails =
|
||||
json.decodeFromString(getJson("post_details_tdfoqh.json"))
|
||||
private val user: User = json.decodeFromString(getJson("msfjarvis.json"))
|
||||
private val metaPosts: List<LobstersPost> = json.decodeFromString(getJson("meta.json"))
|
||||
private val programmingRustPosts: List<LobstersPost> =
|
||||
json.decodeFromString(getJson("programming_rust.json"))
|
||||
private val getPostsBody = { args: Array<Any> ->
|
||||
val tags = args[0] as Tags
|
||||
if ("meta" in tags) {
|
||||
success(metaPosts)
|
||||
} else if ("programming" in tags && "rust" in tags) {
|
||||
success(programmingRustPosts)
|
||||
} else {
|
||||
httpFailure(400)
|
||||
}
|
||||
}
|
||||
|
||||
val api = controller.api
|
||||
|
||||
@@ -30,5 +51,7 @@ class ApiWrapper(controller: EitherNetController<LobstersApi>) {
|
||||
controller.enqueue(LobstersApi::getHottestPosts) { success(hottest) }
|
||||
controller.enqueue(LobstersApi::getPostDetails) { success(postDetails) }
|
||||
controller.enqueue(LobstersApi::getUser) { success(user) }
|
||||
controller.enqueue(LobstersApi::getPostsByTags, getPostsBody)
|
||||
controller.enqueue(LobstersApi::getPostsByTags, getPostsBody)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.util
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.io.File
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
object TestUtils {
|
||||
fun getJson(path: String): String {
|
||||
// Load the JSON response
|
||||
val uri = javaClass.classLoader.getResource(path)
|
||||
val uri = javaClass.classLoader!!.getResource(path)
|
||||
val file = File(uri.path)
|
||||
return String(file.readBytes())
|
||||
}
|
||||
|
||||
inline fun <reified T> assertIs(value: Any?): T {
|
||||
contract { returns() implies (value is T) }
|
||||
assertThat(value).isInstanceOf(T::class.java)
|
||||
return value as T
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.api.dsl.ManagedVirtualDevice
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.test)
|
||||
@@ -15,7 +17,10 @@ plugins {
|
||||
android {
|
||||
namespace = "dev.msfjarvis.claw.benchmark"
|
||||
|
||||
defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }
|
||||
defaultConfig {
|
||||
minSdk = 28
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
create("benchmark") {
|
||||
@@ -25,10 +30,24 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
testOptions {
|
||||
managedDevices {
|
||||
devices {
|
||||
create<ManagedVirtualDevice>("api31") {
|
||||
device = "Pixel 6"
|
||||
apiLevel = 31
|
||||
systemImageSource = "aosp"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetProjectPath = ":android"
|
||||
experimentalProperties["android.experimental.self-instrumenting"] = true
|
||||
}
|
||||
|
||||
androidComponents { beforeVariants(selector().all()) { it.enable = it.buildType == "benchmark" } }
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.benchmark.macro.junit4)
|
||||
implementation(libs.androidx.profileinstaller)
|
||||
@@ -39,5 +58,3 @@ dependencies {
|
||||
implementation(libs.androidx.test.runner)
|
||||
implementation(libs.androidx.test.uiautomator)
|
||||
}
|
||||
|
||||
androidComponents { beforeVariants(selector().all()) { it.enable = it.buildType == "benchmark" } }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -26,10 +26,11 @@ class BaselineProfileGenerator {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startup() =
|
||||
fun generateBaselineProfile() =
|
||||
baselineProfileRule.collectStableBaselineProfile(
|
||||
packageName = PACKAGE_NAME,
|
||||
maxIterations = 5,
|
||||
stableIterations = 2,
|
||||
maxIterations = 8,
|
||||
) {
|
||||
startActivityAndWait()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -9,9 +9,10 @@ package dev.msfjarvis.claw.benchmark
|
||||
import androidx.benchmark.macro.MacrobenchmarkScope
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import androidx.test.uiautomator.UiObject2
|
||||
import androidx.test.uiautomator.Until
|
||||
|
||||
const val PACKAGE_NAME = "dev.msfjarvis.claw.android"
|
||||
const val PACKAGE_NAME = "dev.msfjarvis.claw.android.benchmark"
|
||||
private const val AWAIT_TIMEOUT = 10_000L
|
||||
private const val SAVE_BUTTON_DESC = "Add to saved posts"
|
||||
private const val NAV_ID_HOTTEST = "HOTTEST"
|
||||
@@ -39,7 +40,7 @@ private fun UiDevice.waitForSubmitterName() {
|
||||
|
||||
private fun UiDevice.savePosts() {
|
||||
waitForSubmitterName()
|
||||
findObjects(By.desc(SAVE_BUTTON_DESC)).forEach { btn -> btn.click() }
|
||||
findObjects(By.desc(SAVE_BUTTON_DESC)).forEach(UiObject2::click)
|
||||
}
|
||||
|
||||
private fun UiDevice.exploreScreens() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -7,22 +7,18 @@
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins { `kotlin-dsl` }
|
||||
|
||||
afterEvaluate {
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_11.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_17.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
freeCompilerArgs = freeCompilerArgs + "-Xsam-conversions=class"
|
||||
}
|
||||
}
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
compilerOptions { jvmTarget.set(JvmTarget.JVM_17) }
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
@@ -59,6 +55,10 @@ gradlePlugin {
|
||||
id = "dev.msfjarvis.claw.rename-artifacts"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.RenameArtifactsPlugin"
|
||||
}
|
||||
register("sentry") {
|
||||
id = "dev.msfjarvis.claw.sentry"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.SentryPlugin"
|
||||
}
|
||||
register("spotless") {
|
||||
id = "dev.msfjarvis.claw.spotless"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.SpotlessPlugin"
|
||||
@@ -77,9 +77,10 @@ gradlePlugin {
|
||||
dependencies {
|
||||
implementation(libs.build.agp)
|
||||
implementation(libs.build.cachefix)
|
||||
implementation(libs.build.detekt)
|
||||
implementation(libs.build.kotlin.gradle)
|
||||
implementation(libs.build.r8)
|
||||
implementation(libs.build.semver)
|
||||
implementation(libs.build.sentry)
|
||||
implementation(libs.build.spotless)
|
||||
implementation(libs.build.vcu)
|
||||
implementation(libs.build.versions)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -15,7 +15,6 @@ dependencyResolutionManagement {
|
||||
filter {
|
||||
includeGroup("androidx.databinding")
|
||||
includeGroup("com.android")
|
||||
includeGroup("com.android.tools")
|
||||
includeGroup("com.android.tools.analytics-library")
|
||||
includeGroup("com.android.tools.build")
|
||||
includeGroup("com.android.tools.build.jetifier")
|
||||
@@ -25,6 +24,15 @@ dependencyResolutionManagement {
|
||||
includeGroup("com.android.tools.lint")
|
||||
includeGroup("com.android.tools.utp")
|
||||
includeGroup("com.google.testing.platform")
|
||||
includeModule("com.android.tools", "annotations")
|
||||
includeModule("com.android.tools", "common")
|
||||
includeModule("com.android.tools", "desugar_jdk_libs")
|
||||
includeModule("com.android.tools", "desugar_jdk_libs_configuration")
|
||||
includeModule("com.android.tools", "dvlib")
|
||||
includeModule("com.android.tools", "play-sdk-proto")
|
||||
includeModule("com.android.tools", "repository")
|
||||
includeModule("com.android.tools", "sdklib")
|
||||
includeModule("com.android.tools", "sdk-common")
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
@@ -35,6 +43,10 @@ dependencyResolutionManagement {
|
||||
includeModule("gradle.plugin.org.gradle.android", "android-cache-fix-gradle-plugin")
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository { maven("https://storage.googleapis.com/r8-releases/raw/main") }
|
||||
filter { includeModule("com.android.tools", "r8") }
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -10,19 +10,20 @@ package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.android.build.api.dsl.Lint
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.HasUnitTestBuilder
|
||||
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import dev.msfjarvis.claw.gradle.LintConfig.configureLint
|
||||
import org.gradle.android.AndroidCacheFixPlugin
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.findByType
|
||||
|
||||
private const val SLIM_TESTS_PROPERTY = "slimTests"
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
class AndroidCommonPlugin : Plugin<Project> {
|
||||
|
||||
@@ -52,8 +53,8 @@ class AndroidCommonPlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
testOptions {
|
||||
@@ -61,43 +62,28 @@ class AndroidCommonPlugin : Plugin<Project> {
|
||||
unitTests.isReturnDefaultValues = true
|
||||
}
|
||||
}
|
||||
project.extensions.findByType<ApplicationExtension>()?.run { lint.configureLint(project) }
|
||||
project.extensions.findByType<LibraryExtension>()?.run { lint.configureLint(project) }
|
||||
project.extensions.findByType<ApplicationExtension>()?.lint?.configureLint(project)
|
||||
project.extensions.findByType<LibraryExtension>()?.lint?.configureLint(project)
|
||||
val catalog = project.extensions.getByType<VersionCatalogsExtension>()
|
||||
val libs = catalog.named("libs")
|
||||
project.dependencies.addProvider("lintChecks", libs.findLibrary("slack-compose-lints").get())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Lint.configureLint(project: Project) {
|
||||
abortOnError = false
|
||||
checkReleaseBuilds = false
|
||||
warningsAsErrors = false
|
||||
disable.add("DialogFragmentCallbacksDetector")
|
||||
baseline = project.file("lint-baseline.xml")
|
||||
}
|
||||
|
||||
/**
|
||||
* When the "slimTests" project property is provided, disable the unit test tasks on `release` build
|
||||
* type and `nonFree` product flavor to avoid running the same tests repeatedly in different build
|
||||
* variants.
|
||||
*
|
||||
* Examples: `./gradlew test -PslimTests` will run unit tests for `nonFreeDebug` and `debug` build
|
||||
* variants in Android App and Library projects, and all tests in JVM projects.
|
||||
*/
|
||||
private fun Project.configureSlimTests() {
|
||||
if (providers.gradleProperty(SLIM_TESTS_PROPERTY).isPresent) {
|
||||
// Disable unit test tasks on the release build type for Android Library projects
|
||||
extensions.findByType<LibraryAndroidComponentsExtension>()?.run {
|
||||
beforeVariants(selector().withBuildType("release")) {
|
||||
it.enableUnitTest = false
|
||||
it.enableAndroidTest = false
|
||||
}
|
||||
// Disable unit test tasks on the release build type for Android Library projects
|
||||
extensions.findByType<LibraryAndroidComponentsExtension>()?.run {
|
||||
beforeVariants(selector().withBuildType("release")) {
|
||||
(it as HasUnitTestBuilder).enableUnitTest = false
|
||||
it.enableAndroidTest = false
|
||||
}
|
||||
}
|
||||
|
||||
// Disable unit test tasks on the release build type for Android Application projects.
|
||||
extensions.findByType<ApplicationAndroidComponentsExtension>()?.run {
|
||||
beforeVariants(selector().withBuildType("release")) {
|
||||
it.enableUnitTest = false
|
||||
it.enableAndroidTest = false
|
||||
}
|
||||
// Disable unit test tasks on the release build type for Android Application projects.
|
||||
extensions.findByType<ApplicationAndroidComponentsExtension>()?.run {
|
||||
beforeVariants(selector().withBuildType("release")) {
|
||||
(it as HasUnitTestBuilder).enableUnitTest = false
|
||||
it.enableAndroidTest = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -23,8 +23,6 @@ class ApplicationPlugin : Plugin<Project> {
|
||||
project.pluginManager.apply(AppPlugin::class)
|
||||
project.pluginManager.apply(AndroidCommonPlugin::class)
|
||||
project.extensions.configure<ApplicationExtension> {
|
||||
installation.installOptions("--user 0")
|
||||
|
||||
dependenciesInfo {
|
||||
includeInBundle = false
|
||||
includeInApk = false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -22,13 +22,7 @@ class DependencyUpdatesPlugin : Plugin<Project> {
|
||||
project.pluginManager.apply(VersionsPlugin::class)
|
||||
project.pluginManager.apply(VersionCatalogUpdatePlugin::class)
|
||||
project.tasks.withType<DependencyUpdatesTask>().configureEach {
|
||||
rejectVersionIf {
|
||||
when (candidate.group) {
|
||||
"com.squareup.okhttp3",
|
||||
"org.jetbrains.kotlin" -> true
|
||||
else -> isNonStable(candidate.version) && !isNonStable(currentVersion)
|
||||
}
|
||||
}
|
||||
rejectVersionIf { isNonStable(candidate.version) && !isNonStable(currentVersion) }
|
||||
checkConstraints = true
|
||||
checkBuildEnvironmentConstraints = true
|
||||
checkForGradleUpdate = true
|
||||
@@ -39,7 +33,7 @@ class DependencyUpdatesPlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
private fun isNonStable(version: String): Boolean {
|
||||
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
|
||||
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
|
||||
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
|
||||
val isStable = stableKeyword || regex.matches(version)
|
||||
return isStable.not()
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import io.gitlab.arturbosch.detekt.DetektPlugin
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
object Detekt {
|
||||
private const val TWITTER_RULES_VERSION = "0.0.26"
|
||||
|
||||
fun apply(project: Project) {
|
||||
project.pluginManager.apply(DetektPlugin::class.java)
|
||||
project.extensions.configure<DetektExtension> {
|
||||
debug = project.providers.gradleProperty("debugDetekt").isPresent
|
||||
parallel = true
|
||||
ignoredBuildTypes = listOf("benchmark", "release")
|
||||
basePath = project.layout.projectDirectory.toString()
|
||||
baseline =
|
||||
project.rootProject.layout.projectDirectory
|
||||
.dir("detekt-baselines")
|
||||
.file("${project.name}.xml")
|
||||
.asFile
|
||||
}
|
||||
project.dependencies.add(
|
||||
"detektPlugins",
|
||||
"com.twitter.compose.rules:detekt:$TWITTER_RULES_VERSION",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -13,25 +13,32 @@ import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
|
||||
@Suppress("Unused")
|
||||
class KotlinCommonPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
Detekt.apply(project)
|
||||
if (project.rootProject == project) {
|
||||
LintConfig.configureRootProject(project)
|
||||
} else if (project.name != "benchmark") {
|
||||
LintConfig.configureSubProject(project)
|
||||
}
|
||||
project.tasks.run {
|
||||
withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_11.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_11.toString()
|
||||
sourceCompatibility = JavaVersion.VERSION_17.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_17.toString()
|
||||
}
|
||||
withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors =
|
||||
false // project.providers.environmentVariable("GITHUB_WORKFLOW").isPresent
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
freeCompilerArgs = freeCompilerArgs + ADDITIONAL_COMPILER_ARGS
|
||||
languageVersion = "1.7"
|
||||
withType<KotlinJvmCompile>().configureEach {
|
||||
compilerOptions {
|
||||
allWarningsAsErrors.set(
|
||||
project.providers.environmentVariable("GITHUB_WORKFLOW").isPresent
|
||||
)
|
||||
jvmTarget.set(JvmTarget.JVM_17)
|
||||
freeCompilerArgs.addAll(ADDITIONAL_COMPILER_ARGS)
|
||||
languageVersion.set(KotlinVersion.KOTLIN_1_7)
|
||||
}
|
||||
}
|
||||
withType<Test>().configureEach {
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.dsl.Lint
|
||||
import com.android.build.gradle.LintPlugin
|
||||
import dev.msfjarvis.claw.gradle.LintConfig.configureLint
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.findByType
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
|
||||
|
||||
@Suppress("Unused")
|
||||
class KotlinJvmPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.pluginManager.apply(KotlinPluginWrapper::class)
|
||||
project.pluginManager.apply(LintPlugin::class)
|
||||
project.extensions.findByType<Lint>()?.configureLint(project, isJVM = true)
|
||||
project.pluginManager.apply(KotlinCommonPlugin::class)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.dsl.Lint
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
object LintConfig {
|
||||
fun Lint.configureLint(project: Project, isJVM: Boolean = false) {
|
||||
quiet = project.providers.environmentVariable("CI").isPresent
|
||||
abortOnError = true
|
||||
checkReleaseBuilds = true
|
||||
warningsAsErrors = true
|
||||
ignoreWarnings = false
|
||||
checkAllWarnings = true
|
||||
noLines = false
|
||||
showAll = true
|
||||
explainIssues = true
|
||||
textReport = false
|
||||
xmlReport = false
|
||||
htmlReport = true
|
||||
sarifReport = true
|
||||
if (!isJVM) {
|
||||
enable += "ComposeM2Api"
|
||||
error += "ComposeM2Api"
|
||||
}
|
||||
baseline = project.file("lint-baseline.xml")
|
||||
}
|
||||
|
||||
fun configureRootProject(project: Project) {
|
||||
project.tasks.register<Copy>("collectLintReports") {
|
||||
into(project.layout.buildDirectory.dir("lint-reports"))
|
||||
}
|
||||
}
|
||||
|
||||
fun configureSubProject(project: Project) {
|
||||
val collectorTask = project.rootProject.tasks.named<Copy>("collectLintReports")
|
||||
val lintTask = project.tasks.named("lint")
|
||||
val name = project.name
|
||||
|
||||
collectorTask.configure {
|
||||
from(project.layout.buildDirectory.file("reports")) {
|
||||
include("*.sarif")
|
||||
rename { it.replace("-results", "-results-$name") }
|
||||
}
|
||||
dependsOn(":$name:lint")
|
||||
}
|
||||
lintTask.configure { finalizedBy(collectorTask) }
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.VariantOutputConfiguration
|
||||
import dev.msfjarvis.claw.gradle.artifacts.CollectApksTask
|
||||
import dev.msfjarvis.claw.gradle.artifacts.CollectBundleTask
|
||||
import java.util.Locale
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
@@ -24,7 +23,7 @@ class RenameArtifactsPlugin : Plugin<Project> {
|
||||
project.pluginManager.withPlugin("com.android.application") {
|
||||
project.extensions.configure<ApplicationAndroidComponentsExtension> {
|
||||
onVariants { variant ->
|
||||
val taskPrefix = "collect${variant.name.capitalize(Locale.ROOT)}"
|
||||
val taskPrefix = "collect${variant.name.replaceFirstChar { it.uppercase() }}"
|
||||
project.tasks.register<CollectApksTask>("${taskPrefix}Apks") {
|
||||
variantName.set(variant.name)
|
||||
apkFolder.set(variant.artifacts.get(SingleArtifact.APK))
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import io.sentry.android.gradle.extensions.InstrumentationFeature
|
||||
import io.sentry.android.gradle.extensions.SentryPluginExtension
|
||||
import java.util.EnumSet
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
@Suppress("Unused")
|
||||
class SentryPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
project.pluginManager.withPlugin("com.android.application") {
|
||||
val catalog = project.extensions.getByType<VersionCatalogsExtension>()
|
||||
val libs = catalog.named("libs")
|
||||
project.extensions.configure<ApplicationAndroidComponentsExtension> {
|
||||
onVariants(selector().all()) { variant ->
|
||||
val sentryDsn = project.providers.environmentVariable(SENTRY_DSN_PROPERTY)
|
||||
variant.manifestPlaceholders.put("sentryDsn", sentryDsn.getOrElse(""))
|
||||
}
|
||||
onVariants(selector().withBuildType("benchmark")) { variant ->
|
||||
variant.manifestPlaceholders.put("enableSentry", "false")
|
||||
}
|
||||
onVariants(selector().withBuildType("debug")) { variant ->
|
||||
variant.manifestPlaceholders.put("enableSentry", "false")
|
||||
}
|
||||
onVariants(selector().withBuildType("release")) { variant ->
|
||||
variant.manifestPlaceholders.put("enableSentry", "true")
|
||||
}
|
||||
}
|
||||
project.plugins.apply(io.sentry.android.gradle.SentryPlugin::class)
|
||||
project.extensions.configure<SentryPluginExtension> {
|
||||
val enableMappings =
|
||||
project.providers.gradleProperty(SENTRY_UPLOAD_MAPPINGS_PROPERTY).isPresent
|
||||
includeProguardMapping.set(enableMappings)
|
||||
autoUploadProguardMapping.set(enableMappings)
|
||||
uploadNativeSymbols.set(false)
|
||||
autoUploadNativeSymbols.set(false)
|
||||
includeNativeSources.set(false)
|
||||
ignoredVariants.set(emptySet())
|
||||
ignoredBuildTypes.set(setOf("benchmark", "debug"))
|
||||
ignoredFlavors.set(emptySet())
|
||||
tracingInstrumentation {
|
||||
enabled.set(true)
|
||||
debug.set(false)
|
||||
forceInstrumentDependencies.set(false)
|
||||
features.set(EnumSet.allOf(InstrumentationFeature::class.java))
|
||||
}
|
||||
experimentalGuardsquareSupport.set(false)
|
||||
autoInstallation {
|
||||
enabled.set(true)
|
||||
sentryVersion.set(libs.findVersion("sentry-sdk").get().requiredVersion)
|
||||
}
|
||||
includeDependenciesReport.set(true)
|
||||
}
|
||||
with(project.dependencies) {
|
||||
addProvider("implementation", platform(libs.findLibrary("sentry-bom").get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
private const val SENTRY_DSN_PROPERTY = "SENTRY_DSN"
|
||||
private const val SENTRY_UPLOAD_MAPPINGS_PROPERTY = "sentryUploadMappings"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -46,6 +46,6 @@ class SpotlessPlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val KTFMT_VERSION = "0.41"
|
||||
private const val KTFMT_VERSION = "0.44"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.kotlin.dsl.DependencyHandlerScope
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
|
||||
/** Extension function to configure JUnit5 dependencies with the Truth assertion library. */
|
||||
fun DependencyHandlerScope.addTestDependencies(project: Project) {
|
||||
val catalog = project.extensions.getByType<VersionCatalogsExtension>()
|
||||
val libs = catalog.named("libs")
|
||||
addProvider("testImplementation", libs.findLibrary("junit-jupiter-api").get())
|
||||
addProvider<MinimalExternalModuleDependency, ExternalModuleDependency>(
|
||||
"testImplementation",
|
||||
libs.findLibrary("truth").get(),
|
||||
) {
|
||||
exclude(group = "junit", module = "junit")
|
||||
}
|
||||
addProvider("testRuntimeOnly", libs.findLibrary("junit-jupiter-engine").get())
|
||||
addProvider<MinimalExternalModuleDependency, ExternalModuleDependency>(
|
||||
"testRuntimeOnly",
|
||||
libs.findLibrary("junit-legacy").get(),
|
||||
) {
|
||||
// See https://github.com/google/truth/issues/333
|
||||
because("Truth needs it")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -21,7 +21,7 @@ import org.gradle.kotlin.dsl.withType
|
||||
/**
|
||||
* A Gradle [Plugin] that takes a [Project] with the [AppPlugin] applied and dynamically sets the
|
||||
* versionCode and versionName properties based on values read from a [VERSIONING_PROP_FILE] file in
|
||||
* the [Project.getBuildDir] directory. It also adds Gradle tasks to bump the major, minor, and
|
||||
* the [Project.getProjectDir] directory. It also adds Gradle tasks to bump the major, minor, and
|
||||
* patch versions along with one to prepare the next snapshot.
|
||||
*/
|
||||
@Suppress("Unused")
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION")
|
||||
|
||||
plugins {
|
||||
id("dev.msfjarvis.claw.spotless")
|
||||
id("dev.msfjarvis.claw.versions")
|
||||
id("dev.msfjarvis.claw.kotlin-common")
|
||||
alias(libs.plugins.android.test) apply false
|
||||
alias(libs.plugins.modulegraph)
|
||||
}
|
||||
|
||||
moduleGraphConfig {
|
||||
readmePath.set(project.layout.projectDirectory.file("README.md").asFile.absolutePath)
|
||||
heading.set("## Dependency Diagram")
|
||||
}
|
||||
|
||||
@@ -1,46 +1,20 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.api.variant.HasUnitTestBuilder
|
||||
|
||||
plugins {
|
||||
id("dev.msfjarvis.claw.kotlin-android")
|
||||
id("dev.msfjarvis.claw.android-library")
|
||||
id("dev.msfjarvis.claw.kotlin-android")
|
||||
alias(libs.plugins.anvil)
|
||||
alias(libs.plugins.whetstone)
|
||||
}
|
||||
|
||||
anvil { generateDaggerFactories.set(true) }
|
||||
|
||||
androidComponents { beforeVariants { it.enableUnitTest = false } }
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
api(libs.napier)
|
||||
implementation(projects.core)
|
||||
implementation(projects.database)
|
||||
implementation(projects.model)
|
||||
implementation(libs.accompanist.flowlayout)
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.androidx.compose.animation)
|
||||
implementation(libs.androidx.compose.foundation)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.runtime)
|
||||
implementation(libs.androidx.compose.ui.text)
|
||||
implementation(libs.androidx.compose.ui.tooling)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.compose.richtext.markdown)
|
||||
implementation(libs.compose.richtext.material3)
|
||||
implementation(libs.compose.richtext.ui)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(libs.testparameterinjector)
|
||||
}
|
||||
|
||||
android {
|
||||
buildFeatures {
|
||||
androidResources = true
|
||||
@@ -52,3 +26,33 @@ android {
|
||||
}
|
||||
namespace = "dev.msfjarvis.claw.common"
|
||||
}
|
||||
|
||||
androidComponents { beforeVariants { (it as HasUnitTestBuilder).enableUnitTest = false } }
|
||||
|
||||
anvil { generateDaggerFactories.set(true) }
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
api(projects.database)
|
||||
api(projects.model)
|
||||
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.browser)
|
||||
implementation(libs.androidx.compose.animation)
|
||||
implementation(libs.androidx.compose.foundation)
|
||||
implementation(libs.androidx.compose.material.icons.extended)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.runtime)
|
||||
implementation(libs.androidx.compose.ui.text)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.compose.richtext.markdown)
|
||||
implementation(libs.compose.richtext.material3)
|
||||
implementation(libs.compose.richtext.ui)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.napier)
|
||||
|
||||
compileOnly(libs.androidx.compose.ui.tooling.preview)
|
||||
|
||||
runtimeOnly(libs.androidx.compose.ui.tooling)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.0.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.0-alpha08)" variant="all" version="8.0.0-alpha08">
|
||||
<issues format="6" by="lint 8.0.1" type="baseline" client="gradle" dependencies="false" name="AGP (8.0.1)" variant="all" version="8.0.1">
|
||||
|
||||
</issues>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -8,6 +8,8 @@ package dev.msfjarvis.claw.common
|
||||
|
||||
internal sealed class NetworkState {
|
||||
class Success<T>(val data: T) : NetworkState()
|
||||
|
||||
class Error(val error: Throwable, val description: String) : NetworkState()
|
||||
|
||||
object Loading : NetworkState()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -8,7 +8,6 @@ package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -21,6 +20,8 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Public
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
@@ -29,22 +30,26 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.posts.PostTitle
|
||||
import dev.msfjarvis.claw.common.posts.Submitter
|
||||
import dev.msfjarvis.claw.common.posts.TagRow
|
||||
import dev.msfjarvis.claw.common.res.ClawIcons
|
||||
import dev.msfjarvis.claw.common.ui.NetworkImage
|
||||
import dev.msfjarvis.claw.common.ui.ThemedRichText
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import java.time.Instant
|
||||
import java.time.temporal.TemporalAccessor
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
internal fun CommentsHeader(
|
||||
@@ -68,7 +73,7 @@ internal fun CommentsHeader(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
PostTitle(title = postDetails.title)
|
||||
TagRow(tags = postDetails.tags)
|
||||
TagRow(tags = postDetails.tags.toImmutableList())
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
if (linkMetadata.url.isNotBlank()) {
|
||||
@@ -111,7 +116,7 @@ private fun PostLink(
|
||||
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
NetworkImage(
|
||||
url = linkMetadata.faviconUrl,
|
||||
placeholder = ClawIcons.Web,
|
||||
placeholder = rememberVectorPainter(Icons.Filled.Public),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
@@ -128,7 +133,6 @@ private fun PostLink(
|
||||
|
||||
private val CommentEntryPadding = 16f.dp
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
internal fun CommentEntry(
|
||||
commentNode: CommentNode,
|
||||
@@ -157,6 +161,7 @@ internal fun CommentEntry(
|
||||
buildCommenterString(
|
||||
commenterName = comment.user.username,
|
||||
score = comment.score,
|
||||
isUnread = commentNode.isUnread,
|
||||
createdAt = comment.createdAt,
|
||||
updatedAt = comment.updatedAt,
|
||||
),
|
||||
@@ -165,7 +170,10 @@ internal fun CommentEntry(
|
||||
modifier =
|
||||
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") },
|
||||
)
|
||||
AnimatedContent(targetState = commentNode.isExpanded) { expandedState ->
|
||||
AnimatedContent(
|
||||
label = "comment_body",
|
||||
targetState = commentNode.isExpanded,
|
||||
) { expandedState ->
|
||||
if (expandedState) {
|
||||
ThemedRichText(
|
||||
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
|
||||
@@ -181,6 +189,7 @@ internal fun CommentEntry(
|
||||
fun buildCommenterString(
|
||||
commenterName: String,
|
||||
score: Int,
|
||||
isUnread: Boolean,
|
||||
createdAt: TemporalAccessor,
|
||||
updatedAt: TemporalAccessor,
|
||||
): AnnotatedString {
|
||||
@@ -219,5 +228,17 @@ fun buildCommenterString(
|
||||
append(updatedRelative.toString())
|
||||
append(')')
|
||||
}
|
||||
if (isUnread) {
|
||||
append(' ')
|
||||
withStyle(
|
||||
style =
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
) {
|
||||
append("(unread)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -8,13 +8,15 @@ package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.Divider
|
||||
import dev.msfjarvis.claw.database.local.PostComments
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
|
||||
internal data class CommentNode(
|
||||
val comment: Comment,
|
||||
var parent: CommentNode? = null,
|
||||
val children: MutableList<CommentNode> = mutableListOf(),
|
||||
var isExpanded: Boolean = true
|
||||
val isUnread: Boolean = false,
|
||||
var isExpanded: Boolean = true,
|
||||
) {
|
||||
fun addChild(child: CommentNode) {
|
||||
if (comment.indentLevel + 1 == child.comment.indentLevel) {
|
||||
@@ -26,14 +28,20 @@ internal data class CommentNode(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createListNode(comments: List<Comment>): MutableList<CommentNode> {
|
||||
internal fun createListNode(
|
||||
comments: List<Comment>,
|
||||
commentState: PostComments?
|
||||
): MutableList<CommentNode> {
|
||||
val commentNodes = mutableListOf<CommentNode>()
|
||||
val isUnread = { id: String -> commentState?.commentIds?.contains(id) == false }
|
||||
|
||||
for (i in comments.indices) {
|
||||
if (comments[i].indentLevel == 1) {
|
||||
commentNodes.add(CommentNode(comment = comments[i]))
|
||||
commentNodes.add(CommentNode(comment = comments[i], isUnread = isUnread(comments[i].shortId)))
|
||||
} else {
|
||||
commentNodes.last().addChild(CommentNode(comment = comments[i]))
|
||||
commentNodes
|
||||
.last()
|
||||
.addChild(CommentNode(comment = comments[i], isUnread = isUnread(comments[i].shortId)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
@@ -31,16 +32,22 @@ import dev.msfjarvis.claw.common.NetworkState.Success
|
||||
import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.ui.NetworkError
|
||||
import dev.msfjarvis.claw.common.ui.ProgressBar
|
||||
import dev.msfjarvis.claw.database.local.PostComments
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun CommentsPageInternal(
|
||||
details: LobstersPostDetails,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
commentState: PostComments?,
|
||||
markSeenComments: (String, List<Comment>) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val commentNodes = createListNode(details.comments).toMutableStateList()
|
||||
val commentNodes = createListNode(details.comments, commentState).toMutableStateList()
|
||||
LaunchedEffect(key1 = commentNodes) { markSeenComments(details.shortId, details.comments) }
|
||||
|
||||
Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
|
||||
LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
|
||||
@@ -90,12 +97,14 @@ private fun CommentsPageInternal(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Suppress("UNCHECKED_CAST", "LongParameterList")
|
||||
@Composable
|
||||
fun CommentsPage(
|
||||
postId: String,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
getSeenComments: suspend (String) -> PostComments?,
|
||||
markSeenComments: (String, List<Comment>) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val postDetails by
|
||||
@@ -106,6 +115,8 @@ fun CommentsPage(
|
||||
onFailure = { value = Error(error = it, description = "Failed to load comments") }
|
||||
)
|
||||
}
|
||||
val commentState by
|
||||
produceState<PostComments?>(initialValue = null) { value = getSeenComments(postId) }
|
||||
|
||||
when (postDetails) {
|
||||
is Success<*> -> {
|
||||
@@ -113,6 +124,8 @@ fun CommentsPage(
|
||||
details = (postDetails as Success<LobstersPostDetails>).data,
|
||||
postActions = postActions,
|
||||
htmlConverter = htmlConverter,
|
||||
commentState = commentState,
|
||||
markSeenComments = markSeenComments,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -14,6 +14,8 @@ import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
@@ -24,6 +26,11 @@ import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material.icons.outlined.Comment
|
||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.Divider
|
||||
@@ -39,14 +46,20 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.flowlayout.FlowRow
|
||||
import dev.msfjarvis.claw.common.res.ClawIcons
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.NetworkImage
|
||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@@ -104,7 +117,7 @@ fun LobstersCard(
|
||||
fun PostDetails(post: SavedPost, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
PostTitle(title = post.title)
|
||||
TagRow(tags = post.tags)
|
||||
TagRow(tags = post.tags.toImmutableList())
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Submitter(
|
||||
text = AnnotatedString("Submitted by ${post.submitterName}"),
|
||||
@@ -141,7 +154,7 @@ internal fun Submitter(
|
||||
) {
|
||||
NetworkImage(
|
||||
url = avatarUrl,
|
||||
placeholder = ClawIcons.Account,
|
||||
placeholder = rememberVectorPainter(Icons.Filled.AccountCircle),
|
||||
contentDescription = contentDescription,
|
||||
modifier = Modifier.requiredSize(24.dp).clip(CircleShape),
|
||||
)
|
||||
@@ -155,13 +168,16 @@ private fun SaveButton(
|
||||
isSaved: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Crossfade(targetState = isSaved) { saved ->
|
||||
Crossfade(targetState = isSaved, label = "save-button") { saved ->
|
||||
Box(modifier = modifier.padding(12.dp)) {
|
||||
Icon(
|
||||
painter = if (saved) ClawIcons.Heart else ClawIcons.HeartBorder,
|
||||
painter =
|
||||
rememberVectorPainter(
|
||||
if (saved) Icons.Filled.Favorite else Icons.Outlined.FavoriteBorder
|
||||
),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
contentDescription = if (saved) "Remove from saved posts" else "Add to saved posts",
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -191,7 +207,7 @@ private fun CommentsButton(
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
painter = ClawIcons.Comment,
|
||||
painter = rememberVectorPainter(Icons.Outlined.Comment),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
contentDescription = "Open comments",
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
@@ -200,16 +216,18 @@ private fun CommentsButton(
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
internal fun TagRow(
|
||||
tags: List<String>,
|
||||
tags: ImmutableList<String>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
FlowRow(
|
||||
modifier = modifier,
|
||||
mainAxisSpacing = 8.dp,
|
||||
crossAxisSpacing = 8.dp,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
tags.forEach { tag -> TagText(tag) }
|
||||
tags.forEach { tag ->
|
||||
TagText(tag = tag, modifier = Modifier.padding(top = 4.dp, bottom = 4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,3 +246,55 @@ private fun TagText(
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
}
|
||||
|
||||
@ThemePreviews
|
||||
@Composable
|
||||
fun LobstersCardPreview() {
|
||||
LobstersTheme {
|
||||
LobstersCard(
|
||||
post =
|
||||
SavedPost(
|
||||
shortId = "ooga",
|
||||
title = "Simple Anomaly Detection Using Plain SQL",
|
||||
url = "https://hakibenita.com/sql-anomaly-detection",
|
||||
createdAt = "2020-09-21T08:04:24.000-05:00",
|
||||
commentCount = 1,
|
||||
commentsUrl = "https://lobste.rs/s/q1hh1g/simple_anomaly_detection_using_plain_sql",
|
||||
submitterName = "Haki",
|
||||
submitterAvatarUrl = "/avatars/Haki-100.png",
|
||||
tags = listOf("databases", "apis"),
|
||||
description = "",
|
||||
),
|
||||
isSaved = true,
|
||||
postActions =
|
||||
object : PostActions {
|
||||
override fun viewPost(postUrl: String, commentsUrl: String) {}
|
||||
|
||||
override fun viewComments(postId: String) {}
|
||||
|
||||
override fun viewCommentsPage(commentsUrl: String) {}
|
||||
|
||||
override fun toggleSave(post: SavedPost) {}
|
||||
|
||||
override suspend fun getComments(postId: String): LobstersPostDetails {
|
||||
return LobstersPostDetails(
|
||||
shortId = "ooga",
|
||||
title = "Simple Anomaly Detection Using Plain SQL",
|
||||
url = "https://hakibenita.com/sql-anomaly-detection",
|
||||
createdAt = "2020-09-21T08:04:24.000-05:00",
|
||||
commentCount = 1,
|
||||
commentsUrl = "https://lobste.rs/s/q1hh1g/simple_anomaly_detection_using_plain_sql",
|
||||
tags = listOf("databases", "apis"),
|
||||
description = "",
|
||||
submitter = User("Haki", "", "", "", ""),
|
||||
comments = emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getLinkMetadata(url: String): LinkMetadata {
|
||||
return LinkMetadata("", "", "")
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -14,9 +14,14 @@ import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
@Stable
|
||||
interface PostActions {
|
||||
fun viewPost(postUrl: String, commentsUrl: String)
|
||||
|
||||
fun viewComments(postId: String)
|
||||
|
||||
fun viewCommentsPage(commentsUrl: String)
|
||||
|
||||
fun toggleSave(post: SavedPost)
|
||||
|
||||
suspend fun getComments(postId: String): LobstersPostDetails
|
||||
|
||||
suspend fun getLinkMetadata(url: String): LinkMetadata
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.res
|
||||
|
||||
import dev.msfjarvis.claw.common.res.clawicons.account_circle_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.arrow_back_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.comment_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.favorite_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.favorite_border_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.new_releases_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.new_releases_filled_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.public_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.whatshot_black_24dp
|
||||
import dev.msfjarvis.claw.common.res.clawicons.whatshot_filled_black_24dp
|
||||
|
||||
object ClawIcons {
|
||||
|
||||
val Account = account_circle_black_24dp()
|
||||
|
||||
val ArrowBack = arrow_back_black_24dp()
|
||||
|
||||
val Comment = comment_black_24dp()
|
||||
|
||||
val Flame = whatshot_black_24dp()
|
||||
|
||||
val FlameFilled = whatshot_filled_black_24dp()
|
||||
|
||||
val Heart = favorite_black_24dp()
|
||||
|
||||
val HeartBorder = favorite_border_black_24dp()
|
||||
|
||||
val New = new_releases_black_24dp()
|
||||
|
||||
val NewFilled = new_releases_filled_black_24dp()
|
||||
|
||||
val Web = public_black_24dp()
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class account_circle_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1_0
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(12.0f, 2.0f)
|
||||
cubicTo(6.48f, 2.0f, 2.0f, 6.48f, 2.0f, 12.0f)
|
||||
cubicTo(2.0f, 17.52f, 6.48f, 22.0f, 12.0f, 22.0f)
|
||||
cubicTo(17.52f, 22.0f, 22.0f, 17.52f, 22.0f, 12.0f)
|
||||
cubicTo(22.0f, 6.4799995f, 17.52f, 2.0f, 12.0f, 2.0f)
|
||||
close()
|
||||
moveTo(12.0f, 6.0f)
|
||||
cubicTo(13.93f, 6.0f, 15.5f, 7.57f, 15.5f, 9.5f)
|
||||
cubicTo(15.5f, 11.43f, 13.93f, 13.0f, 12.0f, 13.0f)
|
||||
cubicTo(10.07f, 13.0f, 8.5f, 11.43f, 8.5f, 9.5f)
|
||||
cubicTo(8.5f, 7.5699997f, 10.07f, 6.0f, 12.0f, 6.0f)
|
||||
close()
|
||||
moveTo(12.0f, 20.0f)
|
||||
cubicTo(9.97f, 20.0f, 7.57f, 19.18f, 5.86f, 17.119999f)
|
||||
cubicTo(7.55f, 15.8f, 9.68f, 15.0f, 12.0f, 15.0f)
|
||||
cubicTo(14.32f, 15.0f, 16.45f, 15.8f, 18.14f, 17.119999f)
|
||||
cubicTo(16.43f, 19.18f, 14.03f, 20.0f, 12.0f, 20.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 20.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 20.0
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class arrow_back_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(20.0f, 11.0f)
|
||||
lineTo(7.83f, 11.0f)
|
||||
lineTo(13.42f, 5.41f)
|
||||
lineTo(12.0f, 4.0f)
|
||||
lineTo(4.0f, 12.0f)
|
||||
lineTo(12.0f, 20.0f)
|
||||
lineTo(13.41f, 18.59f)
|
||||
lineTo(7.83f, 13.0f)
|
||||
lineTo(20.0f, 13.0f)
|
||||
lineTo(20.0f, 11.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 4.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 4.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 16.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 16.0
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class comment_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(21.99f, 4.0f)
|
||||
cubicTo(21.99f, 2.9f, 21.1f, 2.0f, 20.0f, 2.0f)
|
||||
lineTo(4.0f, 2.0f)
|
||||
cubicTo(2.9f, 2.0f, 2.0f, 2.9f, 2.0f, 4.0f)
|
||||
lineTo(2.0f, 16.0f)
|
||||
cubicTo(2.0f, 17.1f, 2.9f, 18.0f, 4.0f, 18.0f)
|
||||
lineTo(18.0f, 18.0f)
|
||||
lineTo(22.0f, 22.0f)
|
||||
lineTo(21.99f, 4.0f)
|
||||
close()
|
||||
moveTo(20.0f, 4.0f)
|
||||
lineTo(20.0f, 17.17f)
|
||||
lineTo(18.83f, 16.0f)
|
||||
lineTo(4.0f, 16.0f)
|
||||
lineTo(4.0f, 4.0f)
|
||||
lineTo(20.0f, 4.0f)
|
||||
close()
|
||||
moveTo(6.0f, 12.0f)
|
||||
lineTo(18.0f, 12.0f)
|
||||
lineTo(18.0f, 14.0f)
|
||||
lineTo(6.0f, 14.0f)
|
||||
close()
|
||||
moveTo(6.0f, 9.0f)
|
||||
lineTo(18.0f, 9.0f)
|
||||
lineTo(18.0f, 11.0f)
|
||||
lineTo(6.0f, 11.0f)
|
||||
close()
|
||||
moveTo(6.0f, 6.0f)
|
||||
lineTo(18.0f, 6.0f)
|
||||
lineTo(18.0f, 8.0f)
|
||||
lineTo(6.0f, 8.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 20.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 20.0
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class favorite_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(12.0f, 21.35f)
|
||||
lineTo(10.55f, 20.03f)
|
||||
cubicTo(5.4f, 15.36f, 2.0f, 12.28f, 2.0f, 8.5f)
|
||||
cubicTo(2.0f, 5.42f, 4.42f, 3.0f, 7.5f, 3.0f)
|
||||
cubicTo(9.24f, 3.0f, 10.91f, 3.81f, 12.0f, 5.09f)
|
||||
cubicTo(13.09f, 3.81f, 14.76f, 3.0f, 16.5f, 3.0f)
|
||||
cubicTo(19.58f, 3.0f, 22.0f, 5.42f, 22.0f, 8.5f)
|
||||
cubicTo(22.0f, 12.28f, 18.6f, 15.360001f, 13.45f, 20.04f)
|
||||
lineTo(12.0f, 21.35f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 3.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 20.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 18.350000381469727
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class favorite_border_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(16.5f, 3.0f)
|
||||
cubicTo(14.76f, 3.0f, 13.09f, 3.81f, 12.0f, 5.09f)
|
||||
cubicTo(10.91f, 3.81f, 9.24f, 3.0f, 7.5f, 3.0f)
|
||||
cubicTo(4.42f, 3.0f, 2.0f, 5.42f, 2.0f, 8.5f)
|
||||
cubicTo(2.0f, 12.28f, 5.4f, 15.360001f, 10.55f, 20.04f)
|
||||
lineTo(12.0f, 21.35f)
|
||||
lineTo(13.45f, 20.03f)
|
||||
cubicTo(18.6f, 15.36f, 22.0f, 12.28f, 22.0f, 8.5f)
|
||||
cubicTo(22.0f, 5.42f, 19.58f, 3.0f, 16.5f, 3.0f)
|
||||
close()
|
||||
moveTo(12.1f, 18.55f)
|
||||
lineTo(12.0f, 18.65f)
|
||||
lineTo(11.9f, 18.55f)
|
||||
cubicTo(7.14f, 14.24f, 4.0f, 11.39f, 4.0f, 8.5f)
|
||||
cubicTo(4.0f, 6.5f, 5.5f, 5.0f, 7.5f, 5.0f)
|
||||
cubicTo(9.04f, 5.0f, 10.54f, 5.99f, 11.07f, 7.3599997f)
|
||||
lineTo(12.94f, 7.3599997f)
|
||||
cubicTo(13.46f, 5.99f, 14.96f, 5.0f, 16.5f, 5.0f)
|
||||
cubicTo(18.5f, 5.0f, 20.0f, 6.5f, 20.0f, 8.5f)
|
||||
cubicTo(20.0f, 11.39f, 16.86f, 14.24f, 12.1f, 18.55f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 3.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 20.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 18.350000381469727
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class new_releases_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(23.0f, 12.0f)
|
||||
lineTo(20.56f, 9.22f)
|
||||
lineTo(20.9f, 5.54f)
|
||||
lineTo(17.289999f, 4.72f)
|
||||
lineTo(15.399999f, 1.5399997f)
|
||||
lineTo(12.0f, 3.0f)
|
||||
lineTo(8.6f, 1.54f)
|
||||
lineTo(6.71f, 4.72f)
|
||||
lineTo(3.1000001f, 5.5299997f)
|
||||
lineTo(3.44f, 9.21f)
|
||||
lineTo(1.0f, 12.0f)
|
||||
lineTo(3.44f, 14.78f)
|
||||
lineTo(3.1000001f, 18.47f)
|
||||
lineTo(6.71f, 19.289999f)
|
||||
lineTo(8.6f, 22.47f)
|
||||
lineTo(12.0f, 21.0f)
|
||||
lineTo(15.4f, 22.46f)
|
||||
lineTo(17.289999f, 19.279999f)
|
||||
lineTo(20.9f, 18.46f)
|
||||
lineTo(20.56f, 14.779999f)
|
||||
lineTo(23.0f, 12.0f)
|
||||
close()
|
||||
moveTo(18.49f, 14.11f)
|
||||
lineTo(18.75f, 16.9f)
|
||||
lineTo(16.01f, 17.52f)
|
||||
lineTo(14.58f, 19.93f)
|
||||
lineTo(12.0f, 18.82f)
|
||||
lineTo(9.42f, 19.93f)
|
||||
lineTo(7.9900002f, 17.52f)
|
||||
lineTo(5.25f, 16.9f)
|
||||
lineTo(5.51f, 14.099999f)
|
||||
lineTo(3.66f, 12.0f)
|
||||
lineTo(5.51f, 9.88f)
|
||||
lineTo(5.25f, 7.1000004f)
|
||||
lineTo(7.99f, 6.4900002f)
|
||||
lineTo(9.42f, 4.08f)
|
||||
lineTo(12.0f, 5.18f)
|
||||
lineTo(14.58f, 4.0699997f)
|
||||
lineTo(16.01f, 6.4799995f)
|
||||
lineTo(18.75f, 7.0999994f)
|
||||
lineTo(18.49f, 9.889999f)
|
||||
lineTo(20.34f, 12.0f)
|
||||
lineTo(18.49f, 14.11f)
|
||||
close()
|
||||
moveTo(11.0f, 15.0f)
|
||||
lineTo(13.0f, 15.0f)
|
||||
lineTo(13.0f, 17.0f)
|
||||
lineTo(11.0f, 17.0f)
|
||||
close()
|
||||
moveTo(11.0f, 7.0f)
|
||||
lineTo(13.0f, 7.0f)
|
||||
lineTo(13.0f, 13.0f)
|
||||
lineTo(11.0f, 13.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 1.5399997234344482
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 22.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 20.93000030517578
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class new_releases_filled_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(23.0f, 12.0f)
|
||||
lineTo(20.56f, 9.22f)
|
||||
lineTo(20.9f, 5.54f)
|
||||
lineTo(17.289999f, 4.72f)
|
||||
lineTo(15.399999f, 1.5399997f)
|
||||
lineTo(12.0f, 3.0f)
|
||||
lineTo(8.6f, 1.54f)
|
||||
lineTo(6.71f, 4.72f)
|
||||
lineTo(3.1000001f, 5.5299997f)
|
||||
lineTo(3.44f, 9.21f)
|
||||
lineTo(1.0f, 12.0f)
|
||||
lineTo(3.44f, 14.78f)
|
||||
lineTo(3.1000001f, 18.47f)
|
||||
lineTo(6.71f, 19.289999f)
|
||||
lineTo(8.6f, 22.47f)
|
||||
lineTo(12.0f, 21.0f)
|
||||
lineTo(15.4f, 22.46f)
|
||||
lineTo(17.289999f, 19.279999f)
|
||||
lineTo(20.9f, 18.46f)
|
||||
lineTo(20.56f, 14.779999f)
|
||||
lineTo(23.0f, 12.0f)
|
||||
close()
|
||||
moveTo(13.0f, 17.0f)
|
||||
lineTo(11.0f, 17.0f)
|
||||
lineTo(11.0f, 15.0f)
|
||||
lineTo(13.0f, 15.0f)
|
||||
lineTo(13.0f, 17.0f)
|
||||
close()
|
||||
moveTo(13.0f, 13.0f)
|
||||
lineTo(11.0f, 13.0f)
|
||||
lineTo(11.0f, 7.0f)
|
||||
lineTo(13.0f, 7.0f)
|
||||
lineTo(13.0f, 13.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 1.5399997234344482
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 22.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 20.93000030517578
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class public_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(12.0f, 2.0f)
|
||||
cubicTo(6.48f, 2.0f, 2.0f, 6.48f, 2.0f, 12.0f)
|
||||
cubicTo(2.0f, 17.52f, 6.48f, 22.0f, 12.0f, 22.0f)
|
||||
cubicTo(17.52f, 22.0f, 22.0f, 17.52f, 22.0f, 12.0f)
|
||||
cubicTo(22.0f, 6.4799995f, 17.52f, 2.0f, 12.0f, 2.0f)
|
||||
close()
|
||||
moveTo(4.0f, 12.0f)
|
||||
cubicTo(4.0f, 11.39f, 4.08f, 10.79f, 4.21f, 10.22f)
|
||||
lineTo(8.99f, 15.0f)
|
||||
lineTo(8.99f, 16.0f)
|
||||
cubicTo(8.99f, 17.1f, 9.889999f, 18.0f, 10.99f, 18.0f)
|
||||
lineTo(10.99f, 19.93f)
|
||||
cubicTo(7.06f, 19.43f, 4.0f, 16.07f, 4.0f, 12.0f)
|
||||
close()
|
||||
moveTo(17.89f, 17.4f)
|
||||
cubicTo(17.63f, 16.59f, 16.89f, 16.0f, 15.99f, 16.0f)
|
||||
lineTo(14.99f, 16.0f)
|
||||
lineTo(14.99f, 13.0f)
|
||||
cubicTo(14.99f, 12.45f, 14.54f, 12.0f, 13.99f, 12.0f)
|
||||
lineTo(7.99f, 12.0f)
|
||||
lineTo(7.99f, 10.0f)
|
||||
lineTo(9.99f, 10.0f)
|
||||
cubicTo(10.54f, 10.0f, 10.99f, 9.55f, 10.99f, 9.0f)
|
||||
lineTo(10.99f, 7.0f)
|
||||
lineTo(12.99f, 7.0f)
|
||||
cubicTo(14.09f, 7.0f, 14.99f, 6.1f, 14.99f, 5.0f)
|
||||
lineTo(14.99f, 4.59f)
|
||||
cubicTo(17.92f, 5.77f, 20.0f, 8.65f, 20.0f, 12.0f)
|
||||
cubicTo(20.0f, 14.08f, 19.19f, 15.98f, 17.89f, 17.4f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 2.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 20.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 20.0
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class whatshot_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(11.57f, 13.16f)
|
||||
cubicTo(10.21f, 13.44f, 9.4f, 14.32f, 9.4f, 15.57f)
|
||||
cubicTo(9.4f, 16.91f, 10.509999f, 17.99f, 11.889999f, 17.99f)
|
||||
cubicTo(13.94f, 17.99f, 15.599999f, 16.33f, 15.599999f, 14.28f)
|
||||
cubicTo(15.599999f, 13.21f, 15.45f, 12.16f, 15.139999f, 11.16f)
|
||||
cubicTo(14.349999f, 12.23f, 12.94f, 12.88f, 11.57f, 13.16f)
|
||||
close()
|
||||
moveTo(13.5f, 0.67f)
|
||||
cubicTo(13.5f, 0.67f, 14.24f, 3.3200002f, 14.24f, 5.4700003f)
|
||||
cubicTo(14.24f, 7.53f, 12.889999f, 9.200001f, 10.83f, 9.200001f)
|
||||
cubicTo(8.76f, 9.200001f, 7.2f, 7.5300007f, 7.2f, 5.4700007f)
|
||||
lineTo(7.23f, 5.1100006f)
|
||||
cubicTo(5.21f, 7.51f, 4.0f, 10.62f, 4.0f, 14.0f)
|
||||
cubicTo(4.0f, 18.42f, 7.58f, 22.0f, 12.0f, 22.0f)
|
||||
cubicTo(16.42f, 22.0f, 20.0f, 18.42f, 20.0f, 14.0f)
|
||||
cubicTo(20.0f, 8.61f, 17.41f, 3.8f, 13.5f, 0.67f)
|
||||
close()
|
||||
moveTo(12.0f, 20.0f)
|
||||
cubicTo(8.690001f, 20.0f, 6.0f, 17.31f, 6.0f, 14.0f)
|
||||
cubicTo(6.0f, 12.47f, 6.3f, 10.96f, 6.86f, 9.57f)
|
||||
cubicTo(7.87f, 10.58f, 9.27f, 11.2f, 10.83f, 11.2f)
|
||||
cubicTo(13.49f, 11.2f, 15.58f, 9.37f, 16.11f, 6.77f)
|
||||
cubicTo(17.34f, 8.97f, 18.0f, 11.44f, 18.0f, 14.0f)
|
||||
cubicTo(18.0f, 17.31f, 15.309999f, 20.0f, 12.0f, 20.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 4.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 0.6700000166893005
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 16.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 21.329999923706055
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||
import androidx.compose.ui.graphics.drawscope.translate
|
||||
import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* This class has been automatically generated using <a
|
||||
* href="https://github.com/kirill-grouchnikov/aurora">Aurora SVG transcoder</a>.
|
||||
*/
|
||||
class whatshot_filled_black_24dp : Painter() {
|
||||
@Suppress("UNUSED_VARIABLE") private var shape: Outline? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var generalPath: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var brush: Brush? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var stroke: Stroke? = null
|
||||
@Suppress("UNUSED_VARIABLE") private var clip: Shape? = null
|
||||
private var alpha = 1.0f
|
||||
private var blendMode = DrawScope.DefaultBlendMode
|
||||
private var alphaStack = mutableListOf(1.0f)
|
||||
private var blendModeStack = mutableListOf(DrawScope.DefaultBlendMode)
|
||||
|
||||
private fun _paint0(drawScope: DrawScope) {
|
||||
@Suppress("UNUSED_VARIABLE") var shapeText: Outline?
|
||||
@Suppress("UNUSED_VARIABLE") var generalPathText: Path? = null
|
||||
@Suppress("UNUSED_VARIABLE") var alphaText = 0.0f
|
||||
@Suppress("UNUSED_VARIABLE") var blendModeText = DrawScope.DefaultBlendMode
|
||||
with(drawScope) {
|
||||
//
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_0
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alphaStack.add(0, alpha)
|
||||
alpha *= 1.0f
|
||||
blendModeStack.add(0, BlendMode.SrcOver)
|
||||
blendMode = BlendMode.SrcOver
|
||||
// _0_1
|
||||
if (generalPath == null) {
|
||||
generalPath = Path()
|
||||
} else {
|
||||
generalPath!!.reset()
|
||||
}
|
||||
generalPath?.run {
|
||||
moveTo(13.5f, 0.67f)
|
||||
cubicTo(13.5f, 0.67f, 14.24f, 3.3200002f, 14.24f, 5.4700003f)
|
||||
cubicTo(14.24f, 7.53f, 12.889999f, 9.200001f, 10.83f, 9.200001f)
|
||||
cubicTo(8.76f, 9.200001f, 7.2f, 7.5300007f, 7.2f, 5.4700007f)
|
||||
lineTo(7.23f, 5.1100006f)
|
||||
cubicTo(5.21f, 7.51f, 4.0f, 10.62f, 4.0f, 14.0f)
|
||||
cubicTo(4.0f, 18.42f, 7.58f, 22.0f, 12.0f, 22.0f)
|
||||
cubicTo(16.42f, 22.0f, 20.0f, 18.42f, 20.0f, 14.0f)
|
||||
cubicTo(20.0f, 8.61f, 17.41f, 3.8f, 13.5f, 0.67f)
|
||||
close()
|
||||
moveTo(11.71f, 19.0f)
|
||||
cubicTo(9.93f, 19.0f, 8.49f, 17.6f, 8.49f, 15.86f)
|
||||
cubicTo(8.49f, 14.24f, 9.54f, 13.099999f, 11.299999f, 12.74f)
|
||||
cubicTo(13.07f, 12.38f, 14.9f, 11.53f, 15.919999f, 10.16f)
|
||||
cubicTo(16.31f, 11.45f, 16.509998f, 12.809999f, 16.509998f, 14.2f)
|
||||
cubicTo(16.509998f, 16.85f, 14.359999f, 19.0f, 11.709998f, 19.0f)
|
||||
close()
|
||||
}
|
||||
shape = Outline.Generic(generalPath!!)
|
||||
brush = SolidColor(Color(0, 0, 0, 255))
|
||||
drawOutline(
|
||||
outline = shape!!,
|
||||
style = Fill,
|
||||
brush = brush!!,
|
||||
alpha = alpha,
|
||||
blendMode = blendMode
|
||||
)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
alpha = alphaStack.removeAt(0)
|
||||
blendMode = blendModeStack.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun innerPaint(drawScope: DrawScope) {
|
||||
_paint0(drawScope)
|
||||
|
||||
shape = null
|
||||
generalPath = null
|
||||
brush = null
|
||||
stroke = null
|
||||
clip = null
|
||||
alpha = 1.0f
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Returns the X of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The X of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigX(): Double {
|
||||
return 4.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Y of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The Y of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigY(): Double {
|
||||
return 0.6700000166893005
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The width of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigWidth(): Double {
|
||||
return 16.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the bounding box of the original SVG image.
|
||||
*
|
||||
* @return The height of the bounding box of the original SVG image.
|
||||
*/
|
||||
fun getOrigHeight(): Double {
|
||||
return 21.329999923706055
|
||||
}
|
||||
}
|
||||
|
||||
override val intrinsicSize: Size
|
||||
get() = Size.Unspecified
|
||||
|
||||
override fun DrawScope.onDraw() {
|
||||
clipRect {
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the scaling factor
|
||||
val fullOrigWidth = getOrigX() + getOrigWidth()
|
||||
val fullOrigHeight = getOrigY() + getOrigHeight()
|
||||
val coef1 = size.width / fullOrigWidth
|
||||
val coef2 = size.height / fullOrigHeight
|
||||
val coef = min(coef1, coef2).toFloat()
|
||||
|
||||
// Use the original icon bounding box and the current icon dimension to compute
|
||||
// the offset pivot for the scaling
|
||||
var translateX = -getOrigX()
|
||||
var translateY = -getOrigY()
|
||||
if (coef1 != coef2) {
|
||||
if (coef1 < coef2) {
|
||||
val extraDy = ((fullOrigWidth - fullOrigHeight) / 2.0f).toFloat()
|
||||
translateY += extraDy
|
||||
} else {
|
||||
val extraDx = ((fullOrigHeight - fullOrigWidth) / 2.0f).toFloat()
|
||||
translateX += extraDx
|
||||
}
|
||||
}
|
||||
val translateXDp = translateX.toFloat().toDp().value
|
||||
val translateYDp = translateY.toFloat().toDp().value
|
||||
|
||||
// Create a combined scale + translate + clip transform before calling the transcoded painting
|
||||
// instructions
|
||||
withTransform({
|
||||
scale(scaleX = coef, scaleY = coef, pivot = Offset.Zero)
|
||||
translate(translateXDp, translateYDp)
|
||||
clipRect(
|
||||
left = 0.0f,
|
||||
top = 0.0f,
|
||||
right = fullOrigWidth.toFloat(),
|
||||
bottom = fullOrigHeight.toFloat(),
|
||||
clipOp = ClipOp.Intersect
|
||||
)
|
||||
}) {
|
||||
innerPaint(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,39 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Copyright © 2021-2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
package dev.msfjarvis.claw.common.ui.decorations
|
||||
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.preview.DevicePreviews
|
||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||
import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun ClawAppBar(
|
||||
backgroundColor: Color,
|
||||
modifier: Modifier = Modifier,
|
||||
navigationIcon: @Composable () -> Unit = {},
|
||||
title: @Composable () -> Unit = {},
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = title,
|
||||
modifier = modifier.shadow(8.dp),
|
||||
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = backgroundColor),
|
||||
colors = TopAppBarDefaults.topAppBarColors(),
|
||||
navigationIcon = navigationIcon,
|
||||
actions = actions,
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,7 +43,6 @@ fun ClawAppBar(
|
||||
fun ClawAppBarPreview() {
|
||||
LobstersTheme {
|
||||
ClawAppBar(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation(),
|
||||
title = { Text("Claw", fontWeight = FontWeight.Bold) },
|
||||
)
|
||||
}
|
||||
|
||||