Compare commits

...
430 Commits
Author SHA1 Message Date
GitHub Actions 26b04cce16 feat(release): bump version 2023-07-02 18:39:14 +00:00
GitHub Actions 9ab2400e1f chore(android): refresh baseline profile 2023-07-02 17:01:00 +00:00
GitHub Actions c3b30cc86c chore(android): refresh baseline profile 2023-07-02 01:25:14 +00:00
Harsh Shandilya 2a97383e7d fix(android): remove explicit item keys
Hail mary fix for Sentry issue COMPOSE-LOBSTERS-2
2023-07-01 18:59:17 +05:30
Harsh Shandilya dd20885270 fix(deps): bump coroutines to 1.7.2 2023-07-01 18:57:26 +05:30
Harsh Shandilya fe752f839a feat(build): migrate to Gradle JVM Toolchains 2023-07-01 18:21:10 +05:30
GitHub ActionsandHarsh Shandilya be9a888816 chore(android): refresh baseline profile 2023-07-01 18:21:10 +05:30
renovate[bot]andGitHub 648e7b31cf chore(deps): update dependency gradle to v8.2 2023-06-30 21:58:31 +00:00
GitHub Actions 21b520f786 chore(android): refresh baseline profile 2023-06-30 01:27:40 +00:00
renovate[bot]andGitHub 8bbcabc8f6 chore(deps): update gradle/gradle-build-action action to v2.5.1 2023-06-29 19:44:16 +00:00
Harsh Shandilya 0e4e344a2f fix(deps): upgrade workmanager to 2.9.0-alpha01 2023-06-29 18:12:26 +05:30
GitHub Actions 7b25b6aa85 chore(android): refresh baseline profile 2023-06-29 01:25:13 +00:00
renovate[bot]andGitHub 5343168b8a chore(deps): update gradle/gradle-build-action action to v2.5.0 2023-06-29 00:05:15 +00:00
renovate[bot]andGitHub 9b39fcf1fc fix(deps): update dependency androidx.navigation:navigation-compose to v2.7.0-beta02 2023-06-28 22:59:50 +00:00
renovate[bot]andGitHub 8ce7ac96d0 chore(deps): update dependency gradle to v8.2-rc-3 2023-06-28 10:44:19 +00:00
Harsh Shandilya b5c57500b1 feat: revert back to old text pipeline
Things have been entirely too slow on the lobste.rs front and I don't care much to continue pushing for it.

Fixes #382
Fixes #383
2023-06-28 10:58:39 +05:30
Harsh Shandilya c089e5ae5a fix(build): make slim tests optional 2023-06-28 10:46:59 +05:30
Harsh Shandilya d8a76cf1e7 Revert "fix(build): temporarily revert to AGP 8.0.x"
This reverts commit 1e888494ee.
2023-06-28 10:46:59 +05:30
renovate[bot]andGitHub 92fce55feb fix(deps): update dependency org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin to v2.7.2 2023-06-28 03:22:11 +00:00
GitHub Actions 60a430baa4 chore(android): refresh baseline profile 2023-06-28 01:35:58 +00:00
Harsh Shandilya b0c8de888c chore(deps): bump sqldelight to 2.0.0-rc02 2023-06-28 00:18:54 +05:30
renovate[bot]andGitHub 73ab2dd1d3 fix(deps): update dependency androidx.test:runner to v1.6.0-alpha03 2023-06-27 16:48:14 +00:00
GitHub Actions 7c4f843f42 chore(android): refresh baseline profile 2023-06-27 01:33:28 +00:00
GitHub Actions 123ec904e3 chore(android): refresh baseline profile 2023-06-26 01:32:43 +00:00
GitHub Actions 80c9211d37 chore(android): refresh baseline profile 2023-06-25 01:32:29 +00:00
GitHub Actions b030694f63 chore(android): refresh baseline profile 2023-06-24 01:35:33 +00:00
GitHub Actions e40e196354 chore(android): refresh baseline profile 2023-06-23 01:32:30 +00:00
renovate[bot]andGitHub b7abef3f8f chore(deps): update sentry to v6.24.0 2023-06-22 16:00:38 +00:00
Harsh ShandilyaandGitHub 5bde6cd9cb chore(deps): bump Compose to June alpha release 2023-06-22 21:29:15 +05:30
Harsh Shandilya cb8c90ace9 fix(renovate): include JitPack repository 2023-06-22 12:09:21 +05:30
Harsh Shandilya 9c2c238e1e fix(build): raise target SDK to 34 2023-06-22 11:27:42 +05:30
renovate[bot]andHarsh Shandilya 21a7baa309 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha16 2023-06-22 11:23:58 +05:30
GitHub Actions a9d75148fd chore(android): refresh baseline profile 2023-06-22 01:35:13 +00:00
renovate[bot]andGitHub 3209b5b1fe chore(deps): update github/codeql-action action to v2.20.1 2023-06-21 13:16:25 +00:00
renovate[bot]andGitHub c749a97ded fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.11.1 2023-06-21 09:28:39 +00:00
GitHub Actions f1ab468ceb chore(android): refresh baseline profile 2023-06-21 01:30:06 +00:00
renovate[bot]andGitHub ebd0e5cb5d chore(deps): update plugin modulegraphassert to v2.4.1 2023-06-19 22:42:11 +00:00
GitHub Actions 19bf81ac0e chore(android): refresh baseline profile 2023-06-19 01:35:03 +00:00
renovate[bot]andGitHub 569d5af8fb fix(deps): update dependency com.google.truth:truth to v1.1.5 2023-06-18 16:26:46 +00:00
GitHub Actions dc754dabb3 chore(android): refresh baseline profile 2023-06-18 01:37:26 +00:00
GitHub Actions ed42532416 chore(android): refresh baseline profile 2023-06-17 01:38:56 +00:00
GitHub Actions 211f39da52 chore(android): refresh baseline profile 2023-06-16 01:34:00 +00:00
renovate[bot]andHarsh Shandilya fefac25781 chore(deps): update sentry 2023-06-15 23:27:40 +05:30
Harsh Shandilya e0c9c88ca9 chore(common): prettify HTMLText preview 2023-06-15 23:27:40 +05:30
Harsh Shandilya ea7f7b6306 fix(common): add some visual distinction for blockquotes 2023-06-15 23:27:40 +05:30
GitHub Actions 8ceca6c5ed chore(android): refresh baseline profile 2023-06-15 01:36:06 +00:00
GitHub Actions e9ff85ad44 chore(android): refresh baseline profile 2023-06-14 01:35:35 +00:00
renovate[bot]andGitHub b8e6d4d3fe chore(deps): update github/codeql-action action to v2.20.0 2023-06-13 22:15:55 +00:00
GitHub Actions e7fdfcab69 chore(android): refresh baseline profile 2023-06-13 01:38:12 +00:00
Harsh Shandilya ad8c830b17 fix(common): make HTMLText use M3 Text component 2023-06-13 02:47:17 +05:30
renovate[bot]andHarsh Shandilya 25d2d4ddb7 chore(deps): update actions/checkout action to v3.5.3 2023-06-13 01:44:31 +05:30
Harsh Shandilya 22d1755fd2 fix(android): use SoLoader to load libraries 2023-06-13 01:44:31 +05:30
Harsh Shandilya d397680d33 chore: add changelog entry 2023-06-09 20:09:44 +05:30
Harsh Shandilya 40cd7758dc chore: remove unused Markdown dependencies 2023-06-09 20:09:44 +05:30
Harsh Shandilya 2dc8eae861 refactor: migrate to HTMLText 2023-06-09 20:09:44 +05:30
Harsh Shandilya 2d23ceee83 feat(common): add HTMLText composable
This allows rendering raw HTML text directly without roundtripping it as Markdown
2023-06-09 20:09:44 +05:30
renovate[bot]andGitHub cccc6fd64b fix(deps): update dependency com.github.ben-manes:gradle-versions-plugin to v0.47.0 2023-06-09 11:04:49 +00:00
GitHub Actions aced95c542 chore(android): refresh baseline profile 2023-06-09 01:30:17 +00:00
Harsh Shandilya 90edeefd6e fix: remove useless toList 2023-06-09 02:04:58 +05:30
GitHub Actions a5b90851c8 feat(release): start next development iteration 2023-06-08 15:03:41 +00:00
GitHub Actions 7d0c6972ef feat(release): bump version 2023-06-08 15:02:17 +00:00
Harsh Shandilya 18cb5dc07c chore: refresh feature graphic 2023-06-08 20:18:09 +05:30
Harsh Shandilya 5343e13215 fix(scripts): ensure demo mode is correctly enabled 2023-06-08 19:55:55 +05:30
renovate[bot]andGitHub 4d06a2722c fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-beta02 2023-06-08 12:14:43 +00:00
Harsh Shandilya b9f5bee35e fix(scripts): make setup-age architecture agnostic 2023-06-08 11:53:55 +00:00
Harsh Shandilya 72078b2768 fix(scripts): bump age to v1.1.1 2023-06-08 11:53:55 +00:00
GitHub Actions 465191609b chore(android): refresh baseline profile 2023-06-08 11:48:51 +00:00
Harsh ShandilyaandGitHub 6e1858a55e fix(benchmark): adjust for API changes 2023-06-08 07:03:28 +00:00
renovate[bot]andGitHub 3eeb947914 fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.10.0 2023-06-08 00:02:53 +00:00
renovate[bot]andGitHub d2c99fa8b7 fix(deps): update androidx 2023-06-07 19:06:35 +00:00
renovate[bot]andGitHub dbba96426d chore(deps): update sentry 2023-06-07 15:43:33 +00:00
Harsh Shandilya a5be35a667 feat(core): manually wire in Sentry OkHttp integration 2023-06-07 21:04:24 +05:30
Harsh Shandilya 6e291a8df1 refactor(scripts): rework how Maestro flows are run 2023-06-07 18:07:18 +05:30
Harsh Shandilya 24898cf9fb chore: refresh feature graphic 2023-06-07 17:19:43 +05:30
Harsh Shandilya 66e64ac305 chore: update changelog 2023-06-07 16:24:08 +05:30
Harsh Shandilya ee6bcc864f feat: update iconography for consistency 2023-06-07 16:24:02 +05:30
Harsh Shandilya 8de49d670b fix(android): add visual indication when there are no saved posts 2023-06-07 16:11:18 +05:30
GitHub Actions 13b1c699f6 chore(android): refresh baseline profile 2023-06-07 01:34:08 +00:00
Harsh Shandilya 86a70ffb48 feat(common): remove animation for comment text 2023-06-07 02:42:15 +05:30
Harsh Shandilya 2bb71238bc fix(android): dismiss existing snackbars when showing a new one 2023-06-07 02:37:43 +05:30
Harsh Shandilya 7d491de86c fix(android): prevent comments page from stacking when spammed 2023-06-07 02:19:54 +05:30
Harsh Shandilya 02575a917b fix(android): check for previous back stack entry 2023-06-07 02:05:14 +05:30
renovate[bot]andGitHub 5206851b22 chore(deps): update dependency gradle to v8.2-rc-2 2023-06-06 19:03:10 +00:00
GitHub Actions 74c36f27ae chore(android): refresh baseline profile 2023-06-06 01:36:55 +00:00
Harsh Shandilya 2048a2bb9b chore: add test resources to .rgignore 2023-06-06 00:45:13 +05:30
renovate[bot]andGitHub ea48cdb4f3 chore(deps): update plugin modulegraphassert to v2.4.0 2023-06-05 16:10:44 +00:00
Harsh Shandilya 942b9f3fb4 fix(android): report fully drawn status 2023-06-05 16:50:29 +05:30
Harsh Shandilya 5f6d635c7a fix(android): inline useless function 2023-06-05 14:58:28 +05:30
Harsh Shandilya 47a398f518 refactor: move metadata-extractor to android 2023-06-05 13:24:22 +05:30
Harsh Shandilya 9fdd72220f feat(scripts): add a quick way to refresh the README graphic 2023-06-05 13:24:22 +05:30
Harsh Shandilya 0a1913e056 feat(build): add module-graph-assert plugin 2023-06-05 10:04:41 +05:30
Harsh Shandilya 68db3dcda6 refactor: move coroutine-utils into core 2023-06-05 09:52:12 +05:30
Harsh Shandilya c50c9c8528 chore: refresh module graph 2023-06-05 09:46:09 +05:30
Harsh Shandilya 8a418f6649 refactor: make SavedPostSerializer an object 2023-06-05 09:41:52 +05:30
Harsh Shandilya acdc924047 chore: cleanup redundant @OptIn annotations 2023-06-05 09:34:48 +05:30
GitHub Actions 2fa1d3f06f chore(android): refresh baseline profile 2023-06-05 01:37:40 +00:00
Harsh Shandilya 7ffa225487 feat(database): add test for SavedPostSerializer 2023-06-05 02:51:53 +05:30
Harsh Shandilya c1659eb2dd chore(gitignore): add claw-export.json 2023-06-05 02:18:09 +05:30
Harsh Shandilya 1e888494ee fix(build): temporarily revert to AGP 8.0.x
Android Studio's canary version is going through some trouble recognizing test sources
2023-06-05 02:16:57 +05:30
Harsh Shandilya 054ba13dc3 fix(ci): enforce concurrency for all workflows 2023-06-05 02:00:15 +05:30
Harsh Shandilya 301760c39f refactor(android): make String.toLocalDateTime private 2023-06-05 01:59:17 +05:30
Harsh Shandilya 79fd814c14 fix(android): add Snackbars to data transfer flow 2023-06-05 01:50:23 +05:30
Harsh Shandilya a82d0e7063 fix(android): only show data transfer action on home screen 2023-06-05 00:56:20 +05:30
Harsh Shandilya c6ea1182e0 refactor: remove explicit calls to rememberVectorPainter 2023-06-04 17:14:09 +00:00
Harsh Shandilya 5e7b79eb89 fix(android): hide data transfer action on data transfer screen 2023-06-04 21:19:22 +05:30
Harsh Shandilya 3738d94789 feat(android): wire up import/export settings to UI 2023-06-04 18:45:14 +05:30
Harsh Shandilya fb0e372917 feat(android): add DataTransferRepository 2023-06-04 18:45:14 +05:30
Harsh Shandilya 8945f9e33f feat(database): build a custom serializer for SavedPost 2023-06-04 18:45:14 +05:30
Harsh Shandilya 2895e7ffe7 refactor(android): split out comments functionality 2023-06-04 18:45:14 +05:30
GitHub Actions f102166fae chore(android): refresh baseline profile 2023-06-04 01:28:30 +00:00
Harsh Shandilya c51e28b8e1 fix: touch up version catalog 2023-06-04 01:33:25 +05:30
GitHub Actions a9923eb8c0 feat(release): start next development iteration 2023-06-03 18:50:53 +00:00
GitHub Actions de2f3964e0 feat(release): bump version 2023-06-03 18:46:20 +00:00
Harsh Shandilya 7c5ccbf63b fix: make nav transitions run at 2x of defaults 2023-06-04 00:03:38 +05:30
Harsh Shandilya b4b96222ed chore: reorder manifest attributes 2023-06-04 00:03:38 +05:30
Harsh Shandilya a920f899fb fix(android): automatically extract native libraries 2023-06-04 00:03:38 +05:30
GitHub Actions 31b39a49d6 chore(android): refresh baseline profile 2023-06-03 01:33:42 +00:00
renovate[bot]andGitHub bfb4e9741a chore(deps): update msfjarvis/keep-a-changelog-new-release action to v2.1.0 2023-06-02 22:59:13 +00:00
GitHub Actions bc5c5a13b4 chore(android): refresh baseline profile 2023-06-02 01:22:03 +00:00
renovate[bot]andGitHub 19c50f40cd chore(deps): update github/codeql-action action to v2.3.6 2023-06-01 19:54:14 +00:00
renovate[bot]andGitHub bb0a987d80 fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.8.0 2023-06-01 10:22:25 +00:00
Harsh Shandilya dba380e4c4 fix(gradle): remove default-enabled feature prop 2023-06-01 12:46:32 +05:30
Harsh Shandilya 4a52524212 fix(gradle): correct property name for CC 2023-06-01 12:46:32 +05:30
Harsh Shandilya 1a94ba5a40 fix(gradle): upgrade to 8.2-rc-1 2023-06-01 12:38:05 +05:30
GitHub Actions 2022255200 chore(android): refresh baseline profile 2023-06-01 01:33:44 +00:00
renovate[bot]andGitHub ab1951f9c5 fix(deps): update dependency com.slack.eithernet:eithernet to v1.4.1 2023-05-31 19:31:40 +00:00
renovate[bot]andGitHub a9176b8508 chore(deps): update dependency io.sentry:sentry-android to v6.21.0 2023-05-31 08:54:31 +00:00
GitHub Actions 02ad998b06 chore(android): refresh baseline profile 2023-05-31 01:23:45 +00:00
GitHub Actions 31059abf23 feat(release): start next development iteration 2023-05-31 01:11:15 +00:00
GitHub Actions fc70931c2c feat(release): bump version 2023-05-31 01:07:01 +00:00
renovate[bot]andGitHub af4a6c4672 chore(deps): update agp to v8.2.0-alpha06 2023-05-31 00:40:15 +00:00
Harsh Shandilya 3347224018 feat(database): use Requery's repackaged SQLite for databases 2023-05-31 06:09:44 +05:30
Harsh Shandilya 558062a915 Revert "feat: add some navigation transitions"
This was fun but good lord it is ugly

This reverts commit 4f4b480a13.
2023-05-31 05:57:25 +05:30
Harsh Shandilya a7ab941543 chore(deps): update sqldelight to v2.0.0-rc01 2023-05-31 05:54:11 +05:30
renovate[bot]andGitHub 57b5f33720 fix(deps): update dependency com.google.truth:truth to v1.1.4 2023-05-30 17:18:20 +00:00
GitHub Actions f36d0ef5e6 chore(android): refresh baseline profile 2023-05-30 01:24:00 +00:00
GitHub Actions 0d9b0fec6b chore(android): refresh baseline profile 2023-05-29 06:19:36 +00:00
Harsh Shandilya d1b83410d0 fix(ci): adjust baseline profile path 2023-05-29 11:12:39 +05:30
Harsh Shandilya 990bb26276 fix(deps): update accompanist to 0.31.3-beta 2023-05-29 11:05:56 +05:30
Harsh Shandilya 4f4b480a13 feat: add some navigation transitions 2023-05-28 18:14:11 +05:30
Harsh Shandilya b2490cd014 fix: wrap DatabasePosts content in a Box 2023-05-28 18:13:53 +05:30
Harsh Shandilya 9d85563cf2 fix: remove deeplink for lobste.rs
It makes logging in to the website from a browser fairly annoying for minimal gain
2023-05-28 17:25:04 +05:30
Harsh Shandilya ac573d9311 chore: add changelog entry for adaptive nav 2023-05-28 17:22:52 +05:30
Harsh Shandilya 1e5ae4048c fix: make nav rail animate horizontally 2023-05-28 14:01:04 +05:30
Harsh Shandilya 3db05fb3b1 fix: remove dubious return 2023-05-28 14:01:04 +05:30
Harsh ShandilyaandGitHub 5a098e125a Apply suggestions from code review 2023-05-28 13:39:39 +05:30
Yash-Garg 78b77fd4c1 feat: add adaptive navigation for different screen sizes 2023-05-28 13:17:55 +05:30
renovate[bot]andGitHub b2470ca89b chore(deps): pin actions/cache action to 88522ab 2023-05-27 23:56:18 +00:00
Harsh Shandilya 4764698583 fix(ci): manually cache Android SDK build-tools
This is necessary to avoid the extremely slow re-download on each run
2023-05-28 03:19:10 +05:30
Harsh Shandilya c91a34aae5 chore: update changelog 2023-05-27 01:36:06 +05:30
Harsh Shandilya 8dc76f6ce5 fix(renovate): avoid R8 updates 2023-05-26 19:21:49 +05:30
Harsh Shandilya 0ebe949da6 Revert "feat(renovate): keep R8 up-to-date with ToT"
This reverts commit 25c78eed91.
2023-05-26 19:19:18 +05:30
Harsh Shandilya 6c1a40d614 fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-beta01.1 2023-05-26 19:18:29 +05:30
renovate[bot]andGitHub 9a81d06e2d chore(deps): update dependency io.sentry:sentry-android to v6.20.0 2023-05-26 12:40:11 +00:00
renovate[bot]andGitHub 687cd210af chore(deps): update r8 digest to c765114 2023-05-26 09:54:10 +00:00
Harsh ShandilyaandGitHub 87135c0e1b fix(renovate): stop treating R8 as a Maven dependency 2023-05-26 15:23:23 +05:30
Harsh Shandilya 48cfa6b7f1 fix(deps): update AndroidX Navigation and kotlinx.serialization 2023-05-26 01:55:49 +05:30
Harsh Shandilya 25c78eed91 feat(renovate): keep R8 up-to-date with ToT 2023-05-26 01:20:08 +05:30
renovate[bot]andGitHub 6a50387c39 chore(deps): update agp to v8.2.0-alpha05 2023-05-25 19:41:42 +00:00
renovate[bot]andHarsh Shandilya bac6f65dd0 chore(deps): update github/codeql-action action to v2.3.5 2023-05-26 01:08:19 +05:30
Harsh Shandilya 5ec34fea3b fix(ci): accept all SDK licenses 2023-05-26 01:08:19 +05:30
Harsh Shandilya 539e53c6b1 fix: add Retrofit R8 rules 2023-05-26 01:08:19 +05:30
Harsh Shandilya dfe108d13e fix(deps): use an unreleased R8 build with verifier crash fix 2023-05-26 01:08:19 +05:30
Harsh Shandilya 37e6c87046 feat: move up to AGP 8.2.x 2023-05-25 22:19:58 +05:30
renovate[bot]andGitHub e92929910d chore(deps): update github/codeql-action action to v2.3.4 2023-05-25 04:40:10 +00:00
GitHub Actions 7488afdbb3 chore(android): refresh baseline profile 2023-05-25 01:27:30 +00:00
renovate[bot]andGitHub 2dbc99a27a chore(deps): update agp to v8.0.2 2023-05-24 21:57:29 +00:00
renovate[bot]andGitHub 1dd26feebb fix(deps): update androidx 2023-05-24 18:55:21 +00:00
renovate[bot]andGitHub 203847dcd7 fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2023.04.00-beta01 2023-05-24 18:06:07 +00:00
renovate[bot]andGitHub a4a3620f71 fix(deps): update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.19.0 2023-05-24 04:02:03 +00:00
GitHub Actions 5fba9bfe7b chore(android): refresh baseline profile 2023-05-24 01:27:21 +00:00
renovate[bot]andGitHub c05aa75f1e fix(deps): update dependency com.google.testparameterinjector:test-parameter-injector to v1.12 2023-05-23 11:44:26 +00:00
GitHub Actions 2f378aaeec chore(android): refresh baseline profile 2023-05-22 01:27:24 +00:00
renovate[bot]andGitHub e92279634d fix(deps): update coil to v2.4.0 2023-05-21 19:07:33 +00:00
Harsh Shandilya 1bc15b7b7c fix(renovate): disable Coil updates
Coil 2.4.0 uses Kotlin 1.8.21 that we can't use yet
2023-05-21 20:41:20 +05:30
GitHub Actions 7d8083b268 chore(android): refresh baseline profile 2023-05-21 01:26:45 +00:00
GitHub Actions 7035159fa6 chore(android): refresh baseline profile 2023-05-20 01:23:53 +00:00
renovate[bot]andGitHub a2a9446e3b fix(deps): update dependency com.slack.eithernet:eithernet to v1.4.0 2023-05-19 19:34:10 +00:00
renovate[bot]andGitHub 5c40fedde2 chore(deps): update sentry to v6.19.1 2023-05-19 12:45:15 +00:00
GitHub Actions a44ca13e3a chore(android): refresh baseline profile 2023-05-19 01:22:06 +00:00
GitHub Actions 67f3595d33 chore(android): refresh baseline profile 2023-05-18 01:27:06 +00:00
Harsh Shandilya ae10d433e6 refactor: switch to Material Icons
Our previous system resulted in leaner code but was way too annoying to add
new icons to.
2023-05-18 02:03:17 +05:30
Harsh Shandilya e33768d9ca fix(android): set assist URI for profile page 2023-05-18 02:03:17 +05:30
renovate[bot]andGitHub 7624fa204d fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.7.0 2023-05-17 12:31:46 +00:00
renovate[bot]andGitHub 4738674f7f chore(deps): update sentry to v6.19.0 2023-05-16 13:05:10 +00:00
Harsh Shandilya 7cbc96161e feat(build): raise target bytecode to Java 17 2023-05-16 16:14:10 +05:30
Harsh Shandilya d940ba252e chore: expand changelog 2023-05-16 13:32:46 +05:30
GitHub Actions 60507cc817 chore(android): refresh baseline profile 2023-05-15 01:20:52 +00:00
GitHub Actions 3a99c28233 chore(android): refresh baseline profile 2023-05-14 01:23:15 +00:00
Harsh Shandilya 7e91628339 feat: switch over to Compose alphas 2023-05-13 19:27:28 +05:30
GitHub Actions eb053eb5bf chore(android): refresh baseline profile 2023-05-13 01:24:08 +00:00
renovate[bot]andGitHub 7462b1a2f7 fix(deps): update dagger to v2.46.1 2023-05-12 22:00:05 +00:00
Harsh Shandilya 8d557c3898 fix(deps): update coroutines to 1.7.1 2023-05-13 02:49:04 +05:30
Harsh Shandilya 6b1c53c31a chore(gradle): sort dependencies 2023-05-12 13:15:36 +05:30
GitHub Actions 5e27937358 chore(android): refresh baseline profile 2023-05-12 01:20:49 +00:00
renovate[bot]andGitHub a8aad809ba fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.6.0 2023-05-11 17:18:55 +00:00
Harsh ShandilyaandGitHub d94ca0e9c6 fix(deps): remove unused kotest dependencies (#375) 2023-05-11 04:19:58 +00:00
GitHub Actions aa51535e65 chore(android): refresh baseline profile 2023-05-11 01:27:50 +00:00
Harsh Shandilya 7737c82db9 fix: switch TopAppBar to non-deprecated APIs 2023-05-11 01:30:36 +05:30
renovate[bot]andGitHub 7ee64e0ead fix(deps): update androidx 2023-05-10 19:51:26 +00:00
Harsh Shandilya 987990704c fix(renovate): group all AndroidX updates 2023-05-11 01:20:53 +05:30
GitHub Actions e8975e70f5 chore(android): refresh baseline profile 2023-05-09 01:23:53 +00:00
GitHub Actions 72409a0b73 chore(android): refresh baseline profile 2023-05-08 01:25:09 +00:00
Harsh Shandilya 3dfd2d7862 chore: rework dependency declaration 2023-05-07 11:16:19 +00:00
Harsh ShandilyaandGitHub c93d2f73a9 fix(maestro): correct tap index
I initially wrote this to tap the 1st and 3rd save button but failed to account for the fact that once the first button was pressed, its text property would no longer be "Add to saved posts" which results in index=2 actually pointing to the 4th absolute position in the list.
2023-05-06 12:25:08 +05:30
Harsh Shandilya 66d638f952 fix(benchmark): raise min SDK to align with API requirements 2023-05-06 02:24:37 +05:30
Harsh Shandilya 815a8f186b chore: rejig README 2023-05-06 02:23:39 +05:30
Harsh Shandilya ae745b917e chore: refresh README feature image 2023-05-06 02:22:04 +05:30
Harsh Shandilya bd759c53cd chore(deps): update coroutines to 1.7.0 2023-05-06 02:02:34 +05:30
renovate[bot]andGitHub 8dca324f71 chore(deps): update plugin modulegraph to v0.4.0 2023-05-05 19:49:51 +00:00
Harsh Shandilya dd7a7d9977 feat: use Maestro to automatically generate Play Store screenshots 2023-05-05 21:01:31 +05:30
Harsh Shandilya a7c81a77f2 fix(android): make nav content description less shouty 2023-05-05 20:42:27 +05:30
Harsh Shandilya 15b1e68611 Revert "feat: add gradle-dependencies-sort plugin"
The lexer/parser on this is very sensitive to custom APIs being used in the
`dependencies {}` DSL.

This reverts commit 069c5a4389.
2023-05-05 20:31:29 +05:30
Harsh Shandilya a5e25e2eb5 refactor(build): simplify test dependency setup 2023-05-05 20:31:29 +05:30
renovate[bot]andGitHub c10ec27b51 chore(deps): update github/codeql-action action to v2.3.3 2023-05-04 20:26:50 +00:00
Harsh Shandilya 9c86a9124d fix: sort dependencies 2023-05-04 15:16:31 +05:30
Harsh Shandilya 069c5a4389 feat: add gradle-dependencies-sort plugin 2023-05-04 15:09:41 +05:30
Harsh Shandilya b498cf4b62 feat: fix Sentry disabling logic again 2023-05-04 14:09:05 +05:30
GitHub Actions 70ee848a41 chore(android): refresh baseline profile 2023-05-04 01:28:37 +00:00
GitHub Actions 60fd6a4af4 feat(release): start next development iteration 2023-05-03 22:38:06 +00:00
GitHub Actions 04ed687dbb feat(release): bump version 2023-05-03 22:37:02 +00:00
Harsh Shandilya d694415a4a chore: re-sync Lint baseline 2023-05-04 03:53:36 +05:30
Harsh Shandilya 3f2a9c999b chore: sync changelog 2023-05-04 03:52:18 +05:30
Harsh Shandilya ea40650214 feat(android): migrate away from kotest 2023-05-04 03:38:23 +05:30
Harsh Shandilya ae66aced83 feat(api): migrate away from kotest 2023-05-04 03:33:47 +05:30
Harsh Shandilya 5d2eebf093 fix(api): add test util for type assertion 2023-05-04 03:33:47 +05:30
Harsh Shandilya 5c8057d71f fix(model): implement contains operator fun for Tags 2023-05-04 03:33:47 +05:30
Harsh Shandilya 4d78a73602 feat(database): migrate away from kotest 2023-05-04 03:33:47 +05:30
Harsh Shandilya 7c5a9eb683 feat(build): add JUnit 5 and Truth deps 2023-05-04 03:33:47 +05:30
renovate[bot]andGitHub df423d28d6 fix(deps): update dependency androidx.profileinstaller:profileinstaller to v1.3.1 2023-05-03 20:29:20 +00:00
renovate[bot]andHarsh Shandilya a92616c5a3 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha14 2023-05-04 01:58:30 +05:30
renovate[bot]andHarsh Shandilya 2fc29f65ca fix(deps): update dependency androidx.compose:compose-bom to v2023.05.00 2023-05-04 01:58:30 +05:30
Harsh Shandilya aab712e680 fix(android): migrate to new paging-compose APIs 2023-05-04 01:58:30 +05:30
renovate[bot]andGitHub 4bca86d91e fix(deps): update dependency androidx.paging:paging-compose to v1.0.0-alpha19 2023-05-03 18:41:29 +00:00
GitHub Actions 63cca3eba2 chore(android): refresh baseline profile 2023-05-03 01:28:04 +00:00
Harsh Shandilya cba2ed5944 fix: strip out invalid R8 rules 2023-05-03 01:58:22 +05:30
Harsh Shandilya ee5886c9b9 chore(benchmark): use method reference 2023-05-02 19:45:28 +05:30
Harsh Shandilya e96dadae34 fix(scripts): adjust emulator bootup routines 2023-05-02 19:45:28 +05:30
Harsh Shandilya a23ad82b60 fix(scripts): migrate away from deprecated ANDROID_SDK_ROOT 2023-05-02 19:45:28 +05:30
GitHub Actions f816580886 chore(android): refresh baseline profile 2023-05-02 01:28:03 +00:00
renovate[bot]andGitHub a5f8f1a4c2 chore(deps): update agp to v8.0.1 2023-05-01 18:30:20 +00:00
GitHub Actions 0d47f06be3 chore(android): refresh baseline profile 2023-05-01 01:31:57 +00:00
GitHub Actions c4eb2d2116 chore(android): refresh baseline profile 2023-04-30 01:32:24 +00:00
renovate[bot]andGitHub 8c1dc62211 fix(deps): update dependency org.jsoup:jsoup to v1.16.1 2023-04-29 08:41:46 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>Harsh Shandilya
b1a66bb8dd chore(deps): update dependency com.facebook:ktfmt to v0.44 (#374)
* chore(deps): update dependency com.facebook:ktfmt to v0.44

* chore: reformat

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2023-04-29 09:48:04 +05:30
GitHub Actions 61a2cefcc3 chore(android): refresh baseline profile 2023-04-29 01:27:42 +00:00
renovate[bot]andGitHub ad775bf8e1 fix(deps): update dagger to v2.46 2023-04-28 22:48:14 +00:00
renovate[bot]andGitHub a80e8bb298 fix(deps): update dependency io.sentry:sentry-bom to v6.18.1 2023-04-28 19:06:41 +00:00
renovate[bot]andGitHub 2db51746ac chore(deps): update github/codeql-action action to v2.3.2 2023-04-28 05:12:08 +00:00
GitHub Actions 3d7e6d7f4c chore(android): refresh baseline profile 2023-04-28 01:20:55 +00:00
Harsh Shandilya 6918cf5f76 fix(core): adjust OkHttp config 2023-04-27 18:52:21 +05:30
renovate[bot]andGitHub cd05ed4da9 chore(deps): update sentry to v6.18.0 2023-04-27 12:49:45 +00:00
renovate[bot]andGitHub b669900c75 chore(deps): update github/codeql-action action to v2.3.1 2023-04-27 03:05:16 +00:00
GitHub Actions 3a9bf04a4d chore(android): refresh baseline profile 2023-04-27 01:23:43 +00:00
Harsh Shandilya 0cd4257e73 Revert "feat(build): upgrade to AGP 8.1.0-beta01"
Newer AGP releases cause incorrect desugaring of the Crux dependency

This reverts commit 68a20e0d11.
2023-04-26 14:37:03 +00:00
Harsh ShandilyaandGitHub 6f55da754c fix(deps): de-commonize AndroidX Test versioning 2023-04-25 21:11:50 +05:30
Harsh ShandilyaandGitHub 31adb45152 fix(deps): rollback AndroidX Test version to 1.6.0-alpha01
alpha02 doesn't exist
2023-04-25 20:03:31 +05:30
Harsh Shandilya 68a20e0d11 feat(build): upgrade to AGP 8.1.0-beta01 2023-04-25 18:42:05 +05:30
renovate[bot]andGitHub 52657b0c08 fix(deps): update dependency androidx.test:runner to v1.6.0-alpha02 2023-04-25 05:06:38 +00:00
Harsh Shandilya a2123b7a5d chore: remove unnecessary suppress 2023-04-25 01:23:06 +05:30
Harsh Shandilya 207f98ca17 feat(common): add a preview for LobstersCard 2023-04-25 01:20:43 +05:30
Harsh Shandilya 58170b6072 feat: disable Sentry performance reporting and profiling
These features are going paid and I do not care enough to foot the bill
2023-04-24 23:36:47 +05:30
GitHub Actions 744955bf11 chore(android): refresh baseline profile 2023-04-24 01:21:34 +00:00
renovate[bot]andGitHub cd6d53d19e fix(deps): update dependency com.squareup.okhttp3:okhttp-bom to v4.11.0 2023-04-23 02:00:02 +00:00
GitHub Actions 50e03fbd50 chore(android): refresh baseline profile 2023-04-22 01:19:16 +00:00
renovate[bot]andGitHub 6dd219ee05 chore(deps): update github/codeql-action action to v2.3.0 2023-04-21 20:06:55 +00:00
renovate[bot]andGitHub 2a6cd96c8c chore(deps): update dependency gradle to v8.1.1 2023-04-21 15:30:29 +00:00
Harsh Shandilya 1323668fe6 feat(build): add a task to collect Lint SARIF reports 2023-04-20 18:00:01 +05:30
Harsh Shandilya 24b747eb38 fix(build): correct plugin application ordering 2023-04-20 18:00:01 +05:30
Harsh Shandilya ed31d1fefb chore(lint): update lint baselines 2023-04-20 18:00:01 +05:30
Harsh Shandilya 237a06f1cf feat(build): apply Android Lint plugin to JVM modules 2023-04-20 18:00:01 +05:30
renovate[bot]andGitHub 06528e7504 fix(deps): update dependency androidx.test.uiautomator:uiautomator to v2.3.0-alpha03 2023-04-20 06:00:10 +00:00
renovate[bot]andGitHub 387f9a2601 fix(deps): update dependency androidx.navigation:navigation-compose to v2.6.0-beta01 2023-04-20 05:17:28 +00:00
renovate[bot]andGitHub 4bcfd083d6 fix(deps): update dependency androidx.core:core-splashscreen to v1.0.1 2023-04-20 01:49:30 +00:00
renovate[bot]andGitHub 0e922176fe fix(deps): update dependency androidx.activity:activity-compose to v1.7.1 2023-04-19 22:03:59 +00:00
Harsh Shandilya 250e7c20e5 feat(common): use FlowRow from Compose Foundation 2023-04-20 01:44:43 +05:30
Harsh Shandilya c3de960966 feat(deps): upgrade to Compose 2023.04.01 2023-04-20 01:19:27 +05:30
renovate[bot]andGitHub 866995b2d3 fix(deps): update dependency com.slack.lint.compose:compose-lint-checks to v1.2.0 2023-04-19 16:53:22 +00:00
Harsh Shandilya 825ee23afe chore: convert launcher assets from PNG to WebP 2023-04-19 17:25:09 +05:30
Harsh Shandilya 66ce35927a refactor(build): extract out Lint config 2023-04-19 17:16:06 +05:30
Harsh Shandilya 468d363c65 feat(build): rework Lint config
- Export SARIF output
- Expand enabled ruleset
- Make HTML reports more detailed
2023-04-19 15:19:15 +05:30
Harsh Shandilya 009744cb47 fix(ci): ensure full history is cloned 2023-04-19 14:51:37 +05:30
Harsh Shandilya 2d63a1e79f chore: sync up copyright headers 2023-04-19 12:38:36 +05:30
Harsh Shandilya 5452c55e86 chore(build): enable Spotless license header sync by default 2023-04-19 12:37:44 +05:30
Harsh Shandilya 6b164f84d9 refactor(build): migrate to Kotlin compilerOptions DSL 2023-04-19 12:36:03 +05:30
Harsh Shandilya bf104a1d85 chore(build): reorder DSL declarations 2023-04-19 12:29:27 +05:30
renovate[bot]andGitHub 1d32ec810f fix(deps): update dependency com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter to v1 2023-04-19 05:18:18 +00:00
GitHub Actions 1f7b9dbbc4 chore(android): refresh baseline profile 2023-04-19 01:22:24 +00:00
renovate[bot]andGitHub 8c0bfc2143 fix(deps): update kotest to v5.6.1 2023-04-18 20:18:21 +00:00
GitHub Actions 279ddd9974 feat(release): start next development iteration 2023-04-18 08:50:27 +00:00
GitHub Actions 71b29c0b56 feat(release): bump version 2023-04-18 08:48:54 +00:00
Harsh Shandilya b331a85a2a chore: remove Qodana configurations 2023-04-18 14:06:49 +05:30
Harsh Shandilya e8037e8d60 chore(codeql): run analysis on pushes to main branch 2023-04-18 13:57:29 +05:30
Harsh Shandilya ea0dd4cb78 feat(core): implement certificate pinning for lobste.rs 2023-04-18 13:44:31 +05:30
GitHub ActionsandHarsh Shandilya 4734ab4a22 chore(android): refresh baseline profile 2023-04-18 13:44:31 +05:30
Harsh Shandilya 4df97acd73 fix(codeql): manually build project 2023-04-18 13:44:31 +05:30
GitHub ActionsandHarsh Shandilya 8ec233d761 chore(android): refresh baseline profile 2023-04-18 13:44:31 +05:30
renovate[bot]andHarsh Shandilya 135a1fc5ed fix(deps): update kotest to v5.6.0 2023-04-18 13:44:31 +05:30
GitHub ActionsandHarsh Shandilya 4cb5b09001 chore(android): refresh baseline profile 2023-04-18 13:44:31 +05:30
Harsh Shandilya 3bb8b51cb5 chore: update changelog 2023-04-18 13:44:31 +05:30
GitHub ActionsandHarsh Shandilya 1ee31873ab chore(android): refresh baseline profile 2023-04-18 13:44:31 +05:30
Harsh Shandilya 79fac06a4c fix(benchmark): adjust baseline profile iterations 2023-04-18 13:44:31 +05:30
Harsh Shandilya 553bbedc95 feat(benchmark): migrate to Gradle Managed Devices 2023-04-18 13:44:31 +05:30
Harsh Shandilya d31fe53f78 chore: change benchmark package name 2023-04-18 13:44:18 +05:30
Harsh Shandilya be74e52156 fix: adjust baseline profile emulator options 2023-04-18 13:44:18 +05:30
Harsh Shandilya a2157ae618 feat: rework Sentry disabling logic for benchmark builds 2023-04-18 13:44:18 +05:30
Harsh Shandilya 8b7bcaa92f fix(renovate): disable Kotlin updates
Renovate can't understand how to stop
2023-04-18 13:44:18 +05:30
renovate[bot]andHarsh Shandilya 157998ddf5 fix(deps): update dependency org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin to v2.7.1 2023-04-18 13:44:18 +05:30
renovate[bot]andHarsh Shandilya cd302bb1bb chore(deps): update gradle/gradle-build-action action to v2.4.2 2023-04-18 13:44:18 +05:30
GitHub ActionsandHarsh Shandilya 52341757e4 chore(android): refresh baseline profile 2023-04-18 13:44:18 +05:30
GitHub ActionsandHarsh Shandilya a74633df54 chore(android): refresh baseline profile 2023-04-18 13:44:18 +05:30
Harsh Shandilya b936b54132 feat: rework system bar colors 2023-04-18 13:44:18 +05:30
Harsh Shandilya cf111f7852 fix: include Anvil updates in Kotlin bundle 2023-04-18 13:44:18 +05:30
renovate[bot]andHarsh Shandilya f23f9498cd chore(deps): update github/codeql-action action to v2.2.12 2023-04-18 13:44:18 +05:30
Harsh Shandilya 7c019353df feat(deps): upgrade AGP to 8.0.0 2023-04-18 13:44:18 +05:30
renovate[bot]andGitHub 3954e63141 chore(deps): update actions/checkout action to v3.5.2 2023-04-13 15:11:26 +00:00
renovate[bot]andGitHub d04d638b27 chore(deps): update gradle/gradle-build-action action to v2.4.1 2023-04-13 08:23:27 +00:00
renovate[bot]andGitHub 3ad414dadd chore(deps): update actions/checkout action to v3.5.1 2023-04-13 02:43:26 +00:00
GitHub Actions 8bc9bd4dbb chore(android): refresh baseline profile 2023-04-13 01:30:13 +00:00
renovate[bot]andGitHub b0c3066adc chore(deps): update dependency gradle to v8.1 2023-04-12 15:06:23 +00:00
GitHub Actions c5dfd4fffe chore(android): refresh baseline profile 2023-04-12 01:30:16 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
7de7b4f843 chore(deps): update dependency gradle to v8.1-rc-4 (#369)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-12 00:30:14 +05:30
GitHub Actions 7079c5a6f1 chore(android): refresh baseline profile 2023-04-09 01:24:44 +00:00
GitHub Actions 46ebda2673 chore(android): refresh baseline profile 2023-04-08 01:20:21 +00:00
GitHub Actions eee9df7d8a chore(android): refresh baseline profile 2023-04-07 01:30:51 +00:00
renovate[bot]andGitHub f8be557e6e chore(deps): update github/codeql-action action to v2.2.11 2023-04-06 17:43:27 +00:00
renovate[bot]andGitHub d6f776e12e fix(deps): update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.18.0 2023-04-06 05:46:57 +00:00
GitHub Actions 6fd3f32f7e chore(android): refresh baseline profile 2023-04-06 01:25:12 +00:00
renovate[bot]andHarsh Shandilya adbb400728 fix(deps): update dependency androidx.navigation:navigation-compose to v2.6.0-alpha09
(cherry picked from commit 4a0fa59c9189eb18eff966c47d2307a5591cfbc5)
2023-04-06 01:33:59 +05:30
renovate[bot]andHarsh Shandilya 8f6a46aa2e fix(deps): update dependency androidx.datastore:datastore-preferences to v1.1.0-alpha04
(cherry picked from commit 8a78f2bd470adfad7130826b7b550d0c20e4a4e7)
2023-04-06 01:33:59 +05:30
renovate[bot]andHarsh Shandilya 0ec52b84fe fix(deps): update dependency androidx.compose:compose-bom to v2023.04.00
(cherry picked from commit c67e4d2923965a671e3043a9f503c3a0ea88013e)
2023-04-06 01:33:59 +05:30
renovate[bot]andHarsh Shandilya b436f10e18 chore(deps): update github/codeql-action action to v2.2.10
(cherry picked from commit 5fed8bd9d309fb18f4b24b09458e859fd939d9f5)
2023-04-06 01:06:50 +05:30
renovate[bot]andHarsh Shandilya 75ebf57c68 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha13
(cherry picked from commit 8293dccef8722344993e3cc55e4e6806e16db804)
2023-04-06 01:06:50 +05:30
GitHub Actions b3cea28687 chore(android): refresh baseline profile 2023-04-05 01:30:38 +00:00
renovate[bot]andGitHub 878b51fa30 fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.5.0 2023-04-04 19:19:14 +00:00
renovate[bot]andGitHub 81f024d197 chore(deps): update dependency gradle to v8.1-rc-3 2023-04-04 12:41:24 +00:00
renovate[bot]andHarsh Shandilya a21d665f5d chore(deps): update plugin modulegraph to v0.3.0
(cherry picked from commit d600239ba264490346e4fcaef321c50d8883119e)
2023-04-04 11:24:46 +05:30
renovate[bot]andGitHub 954bc9710b fix(deps): update accompanist to v0.30.1 2023-04-04 04:05:45 +00:00
GitHub Actions 5faeef190d chore(android): refresh baseline profile 2023-04-04 01:22:37 +00:00
GitHub Actions 43c5f93b60 chore(android): refresh baseline profile 2023-04-03 01:28:56 +00:00
Harsh Shandilya adfac94094 feat: add a module graph to the README 2023-04-02 23:44:54 +05:30
GitHub Actions bc60929fe9 chore(android): refresh baseline profile 2023-04-02 01:25:34 +00:00
GitHub Actions fdf2564f15 chore(android): refresh baseline profile 2023-04-01 01:22:47 +00:00
renovate[bot]andGitHub 8b6d422d96 chore(deps): update sentry to v6.17.0 2023-03-31 11:01:58 +00:00
GitHub Actions d2c0fea75f chore(android): refresh baseline profile 2023-03-31 01:23:47 +00:00
GitHub Actions efa5afb441 chore(android): refresh baseline profile 2023-03-30 01:28:39 +00:00
Harsh Shandilya 830144d5b1 fix(gradle): CC is now stable 2023-03-29 15:18:15 +05:30
Harsh Shandilya f6b684ceb0 chore(build): upgrade to Gradle 8.1 RC2 2023-03-29 15:18:00 +05:30
GitHub Actions cd3dfc065b chore(android): refresh baseline profile 2023-03-29 01:25:25 +00:00
Harsh Shandilya 72c1529bb9 chore: sort dependencies with gradle-dependencies-sorter 2023-03-28 12:15:18 +05:30
GitHub Actions 1b17da668d chore(android): refresh baseline profile 2023-03-28 01:32:21 +00:00
renovate[bot]andGitHub 3e7f50f4bf chore(deps): update actions/setup-java action to v3.11.0 2023-03-27 16:11:28 +00:00
renovate[bot]andGitHub 12bc6a06fb chore(deps): update github/codeql-action action to v2.2.9 2023-03-27 15:47:54 +00:00
GitHub Actions c3f146723a chore(android): refresh baseline profile 2023-03-27 01:26:13 +00:00
renovate[bot]andGitHub f64254ccef fix(deps): update coil to v2.3.0 2023-03-26 03:37:24 +00:00
GitHub Actions 0a43d9c9f6 chore(android): refresh baseline profile 2023-03-26 01:25:07 +00:00
GitHub Actions dcb9bfeb18 chore(android): refresh baseline profile 2023-03-25 01:27:05 +00:00
renovate[bot]andGitHub d6f5ea444d fix(deps): update dependency androidx.datastore:datastore-preferences to v1.1.0-alpha03 2023-03-24 19:06:44 +00:00
renovate[bot]andGitHub 3a3e02a937 fix(deps): update dependency com.google.testparameterinjector:test-parameter-injector to v1.11 2023-03-24 14:03:24 +00:00
GitHub Actions b5109fc872 feat(release): start next development iteration 2023-03-24 09:56:53 +00:00
GitHub Actions d9ed25478f feat(release): bump version 2023-03-24 09:55:40 +00:00
Harsh Shandilya f5c9da6aa0 chore: update changelog 2023-03-24 15:24:23 +05:30
Harsh Shandilya 293c04b2ad fix(build): remove DSL_SCOPE_VIOLATION suppression 2023-03-24 14:59:34 +05:30
Harsh Shandilya e91b344bea fix(build): remove no longer needed SAM conversion flag 2023-03-24 14:59:34 +05:30
Harsh Shandilya 491d8a6e71 fix(build): remove no longer needed afterEvaluate 2023-03-24 14:37:01 +05:30
Harsh Shandilya 04054914f5 chore(build): upgrade to Gradle 8.1 RC1 2023-03-24 14:35:56 +05:30
Harsh Shandilya d273c6f7b5 fix(ci): use exact versions 2023-03-24 14:27:03 +05:30
renovate[bot]andGitHub 9cf134e916 chore(deps): update actions/checkout action to v3.5.0 2023-03-24 06:43:57 +00:00
renovate[bot]andGitHub abd1e73d82 chore(deps): update actions/checkout digest to 8f4b7f8 2023-03-24 06:04:09 +00:00
GitHub Actions 5625aebb0c chore(android): refresh baseline profile 2023-03-24 01:25:53 +00:00
renovate[bot]andGitHub b84393c0d6 fix(deps): update accompanist to v0.30.0 2023-03-23 03:06:25 +00:00
GitHub Actions c0021ff5dc chore(android): refresh baseline profile 2023-03-23 01:28:23 +00:00
renovate[bot]andGitHub 9e1893612c chore(deps): update github/codeql-action digest to 67a35a0 2023-03-22 21:09:02 +00:00
Harsh Shandilya 3c2c577628 fix(deps): upgrade AndroidX dependencies 2023-03-23 01:00:07 +05:30
Harsh Shandilya 190b7b4026 fix(gradle): remove unused version declaration 2023-03-23 01:00:07 +05:30
renovate[bot]andGitHub 6fa03bad66 chore(deps): update dependency androidx.compose.compiler:compiler to v1.4.4 2023-03-22 18:49:52 +00:00
GitHub Actions 0a84659b70 chore(android): refresh baseline profile 2023-03-22 01:21:36 +00:00
GitHub Actions dec5873682 chore(android): refresh baseline profile 2023-03-21 01:30:49 +00:00
renovate[bot]andGitHub 8fc18f8324 fix(deps): update dependency io.sentry.android.gradle:io.sentry.android.gradle.gradle.plugin to v3.4.3 2023-03-20 21:00:07 +00:00
GitHub Actions 590240b283 chore(android): refresh baseline profile 2023-03-20 01:24:09 +00:00
GitHub Actions 4b90ff49fd chore(android): refresh baseline profile 2023-03-19 01:27:20 +00:00
renovate[bot]andGitHub 2223cea395 chore(deps): update reactivecircus/android-emulator-runner digest to d94c3fb 2023-03-18 11:39:49 +00:00
GitHub Actions ad692b73ba chore(android): refresh baseline profile 2023-03-18 01:23:26 +00:00
renovate[bot]andGitHub de57d089eb chore(deps): update sentry to v6.16.0 2023-03-17 10:57:52 +00:00
GitHub Actions 303a706d8a chore(android): refresh baseline profile 2023-03-17 01:23:23 +00:00
GitHub Actions 108bbbda87 chore(android): refresh baseline profile 2023-03-16 01:24:39 +00:00
renovate[bot]andGitHub 65f060a8d2 chore(deps): update actions/checkout action to v3.4.0 2023-03-15 23:04:10 +00:00
renovate[bot]andGitHub f35f0af6d8 chore(deps): update actions/checkout digest to 24cb908 2023-03-15 20:05:42 +00:00
renovate[bot]andGitHub 6c30892def chore(deps): update github/codeql-action digest to 168b99b 2023-03-15 16:58:44 +00:00
GitHub Actions 1016c85e3c chore(android): refresh baseline profile 2023-03-15 01:27:15 +00:00
renovate[bot]andGitHub 887c05c2b9 fix(deps): update dependency nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin to v0.8.0 2023-03-14 10:50:38 +00:00
renovate[bot]andHarsh Shandilya 98cb50fab7 chore(deps): update actions/cache action to v3.3.1
(cherry picked from commit 5d965a3cf41e5ef781451c232c5965800b2d0a7b)
2023-03-14 16:19:03 +05:30
renovate[bot]andHarsh Shandilya 38d75b0e51 fix(deps): update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.17.0
(cherry picked from commit f693de3a5ef4c2c90b6e715d9221b2cc6e7a3e0a)
2023-03-14 16:19:03 +05:30
Harsh Shandilya 20a2389eb0 feat(android): set up DataStore 2023-03-14 16:19:03 +05:30
GitHub Actions 9cbc754574 chore(android): refresh baseline profile 2023-03-12 01:19:11 +00:00
GitHub Actions b564283419 chore(android): refresh baseline profile 2023-03-11 01:21:57 +00:00
renovate[bot]andGitHub 32dd218131 chore(deps): update github/codeql-action digest to 16964e9 2023-03-10 19:28:32 +00:00
GitHub Actions 09a36aa4c6 chore(android): refresh baseline profile 2023-03-10 01:29:02 +00:00
Harsh Shandilya c1f1d67bfa feat(android): add a repository for extracting CSRF token 2023-03-10 01:29:04 +05:30
renovate[bot]andGitHub 93d2397572 chore(deps): update actions/cache action to v3.3.0 2023-03-09 14:07:15 +00:00
renovate[bot]andGitHub 4b89dc202e fix(deps): update dependency androidx.profileinstaller:profileinstaller to v1.3.0-rc01 2023-03-09 08:02:47 +00:00
renovate[bot]andGitHub 0718e18fb0 fix(deps): update dependency androidx.navigation:navigation-compose to v2.6.0-alpha07 2023-03-09 04:31:28 +00:00
renovate[bot]andGitHub 90483889c4 fix(deps): update dependency androidx.lifecycle:lifecycle-viewmodel-compose to v2.6.0 2023-03-09 01:50:37 +00:00
GitHub Actions b7ef4cb2d6 chore(android): refresh baseline profile 2023-03-09 01:23:10 +00:00
renovate[bot]andGitHub b73261a339 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha11 2023-03-08 22:06:44 +00:00
renovate[bot]andGitHub 0b4fd240cc fix(deps): update dependency androidx.activity:activity-compose to v1.7.0-rc01 2023-03-08 19:58:16 +00:00
Harsh Shandilya b1af5d2349 chore: fix OkHttp capitalisation in changelog 2023-03-09 01:09:39 +05:30
Harsh Shandilya 456205dece chore(deps): bump compose-lint-checks 2023-03-09 01:08:21 +05:30
GitHub Actions 8d092a761a chore(android): refresh baseline profile 2023-03-08 01:28:52 +00:00
Harsh Shandilya 05e9c25742 fix: update incorrect comment 2023-03-08 02:50:17 +05:30
Harsh Shandilya 998171af9e chore: update changelog 2023-03-08 02:43:11 +05:30
Harsh Shandilya 3c62dd9542 fix: address Lint warnings in icon resources 2023-03-08 02:27:51 +05:30
Harsh Shandilya 55cc397333 chore: add labels to Crossfade elements 2023-03-08 02:27:51 +05:30
Harsh Shandilya b969b5f0bf refactor: use immutable collections where flagged by Lint 2023-03-08 02:27:51 +05:30
Harsh Shandilya d7cc55ed53 chore(build): make Lint errors fatal 2023-03-08 02:27:51 +05:30
Harsh Shandilya 9ab8a3e149 fix(api): avoid unnecessary build warning 2023-03-08 02:27:51 +05:30
Harsh Shandilya 0ae48dee33 feat(build): remove Detekt and switch to Slack's Compose lints 2023-03-08 02:27:51 +05:30
Harsh ShandilyaandGitHub d612ed44d9 chore: update changelog 2023-03-07 13:00:52 +05:30
Harsh Shandilya cb798cf011 feat: upgrade to OkHttp 4.10.0 2023-03-07 12:53:51 +05:30
GitHub Actions 8392841554 chore(android): refresh baseline profile 2023-03-07 01:35:08 +00:00
Harsh Shandilya 24d4d3279a fix: remove unused version catalog key 2023-03-07 01:00:07 +05:30
Harsh Shandilya 84bdcb340b feat: add Play Store feature graphic 2023-03-06 10:51:16 +05:30
Harsh Shandilya e4a28684ef feat: refresh icon assets 2023-03-06 10:49:58 +05:30
GitHub Actions 0f5698bb58 chore(android): refresh baseline profile 2023-03-06 01:24:49 +00:00
GitHub Actions ca2da81af5 chore(android): refresh baseline profile 2023-03-05 22:05:24 +00:00
Harsh Shandilya a26fd0862f fix(build): skip Sentry setup for CI benchmarks 2023-03-06 03:17:49 +05:30
Harsh Shandilya ab905d4061 fix(build): set a default for sentryDsn 2023-03-06 02:45:26 +05:30
renovate[bot]andGitHub 7c27e26da7 chore(deps): update jetbrains/qodana-jvm-android docker tag to v2023 2023-03-04 09:56:19 +00:00
renovate[bot]andGitHub 7f49333c42 chore(deps): update dependency gradle to v8.0.2 2023-03-04 05:58:54 +00:00
GitHub Actions 50d2af7be4 feat(release): start next development iteration 2023-03-02 21:52:11 +00:00
157 changed files with 13282 additions and 12203 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 198 KiB

+13 -25
View File
@@ -15,15 +15,24 @@
"https://maven.google.com/",
"https://repo1.maven.org/maven2",
"https://plugins.gradle.org/m2/",
"https://jitpack.io",
],
},
{
"matchPackagePatterns": [
"^androidx.*",
],
"groupName": "androidx"
},
{
"matchPackagePatterns": [
"^kotlin",
"^org.jetbrains.kotlin",
"^androidx.compose.compiler"
"^androidx.compose.compiler",
"^anvil",
"^coil",
],
"groupName": "kotlin"
"enabled": false
},
{
"matchPackagePatterns": [
@@ -37,10 +46,10 @@
"gradle"
],
"packagePatterns": [
// We want to stay in sync with Retrofit
"^com.squareup.okhttp3",
// 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
},
@@ -57,17 +66,6 @@
"depNameTemplate": "com.facebook:ktfmt",
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
},
{
"fileMatch": [
"Detekt\.kt$"
],
"matchStrings": [
"COMPOSE_RULES_VERSION = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "io.nlopez.compose.rules:detekt",
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
},
{
"fileMatch": [
"gradle/libs.versions.toml"
@@ -79,16 +77,6 @@
"depNameTemplate": "androidx.compose.compiler:compiler",
"registryUrlTemplate": "https://maven.google.com",
},
{
"fileMatch": [
"qodana.yaml"
],
"matchStrings": [
"jetbrains/qodana-jvm-android:(?<currentValue>.*)"
],
"datasourceTemplate": "docker",
"depNameTemplate": "jetbrains/qodana-jvm-android",
},
{
"fileMatch": [
"gradle/libs.versions.toml"
+33 -54
View File
@@ -6,9 +6,8 @@ on:
- cron: '43 0 * * *'
workflow_dispatch:
env:
AVD_API_LEVEL: 31
AVD_ARCH: x86_64
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
baseline-profile:
@@ -19,13 +18,28 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
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@3f07048e3d294f56e9b90ac5ea2c6f74e9ad0f98 # v3.10.0
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: temurin
java-version: 18
@@ -37,19 +51,10 @@ jobs:
git config user.email noreply@github.com
- name: Setup Gradle caching
uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0
uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # v2.5.1
with:
gradle-home-cache-cleanup: true
# Restore AVD from cache
- name: Cache AVD
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0 # v3.2.6
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: |
@@ -61,43 +66,17 @@ jobs:
# This allows us to build most of what we need without the emulator running
# and using resources
- name: Build app and benchmark
env:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
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)
@@ -106,20 +85,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@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()
+39 -14
View File
@@ -18,25 +18,35 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
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@3f07048e3d294f56e9b90ac5ea2c6f74e9ad0f98 # v3.10.0
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: temurin
java-version: 18
- name: Run unit tests
uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0
uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # v2.5.1
with:
arguments: --no-configuration-cache --stacktrace check
gradle-home-cache-cleanup: true
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
- name: Run Detekt
uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0
with:
arguments: --no-configuration-cache --stacktrace detektMain
arguments: --no-configuration-cache --stacktrace check -PslimTests
gradle-home-cache-cleanup: true
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
@@ -53,10 +63,25 @@ jobs:
needs: [ "check" ]
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- 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@3f07048e3d294f56e9b90ac5ea2c6f74e9ad0f98 # v3.10.0
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: temurin
java-version: 18
@@ -69,7 +94,7 @@ jobs:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
- name: Build release app
uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0
uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # v2.5.1
env:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
with:
+46 -6
View File
@@ -1,7 +1,13 @@
name: "CodeQL"
name: CodeQL
on:
workflow_dispatch:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
analyze:
@@ -13,14 +19,48 @@ jobs:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- 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@40b6781dcdec2762ad36556682ac74e31030cfe2 # v2.5.1
with:
gradle-home-cache-cleanup: true
cache-read-only: true
- name: Initialize CodeQL
uses: github/codeql-action/init@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2
uses: github/codeql-action/init@f6e388ebf0efc915c6c5b165b019ee61a6746a38 # v2.20.1
with:
languages: java
- name: Autobuild
uses: github/codeql-action/autobuild@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2
tools: latest
queries: +security-extended
- name: Build project
shell: bash
run: |
./gradlew assemble -PslimTests
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2
uses: github/codeql-action/analyze@f6e388ebf0efc915c6c5b165b019ee61a6746a38 # v2.20.1
with:
category: "/language:java"
-16
View File
@@ -1,16 +0,0 @@
name: Qodana
on:
workflow_dispatch:
jobs:
qodana:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@7afb26c0c2f325c0d5c21ea1f617c79c7f899337 # v2022.3.4
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
+22 -4
View File
@@ -2,18 +2,36 @@ name: Release to Google Play
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
publish-google-play-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
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@3f07048e3d294f56e9b90ac5ea2c6f74e9ad0f98 # v3.10.0
uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # v3.11.0
with:
distribution: temurin
java-version: 18
@@ -25,7 +43,7 @@ jobs:
git config user.email noreply@github.com
- name: Setup Gradle caching
uses: gradle/gradle-build-action@6095a76664413da4c8c134ee32e8a8ae900f0f1f # v2.4.0
uses: gradle/gradle-build-action@40b6781dcdec2762ad36556682ac74e31030cfe2 # v2.5.1
with:
gradle-home-cache-cleanup: true
cache-read-only: true
@@ -45,7 +63,7 @@ jobs:
echo VERSION="${VERSION}" >> $GITHUB_ENV
- name: Update changelog
uses: msfjarvis/keep-a-changelog-new-release@ce8769801992253adb0209055c236a0cd192500b # 2.0.0
uses: msfjarvis/keep-a-changelog-new-release@bf43ae42f4e42f137572950af426efcf6e777207 # 2.1.0
with:
tag: v${{ env.VERSION }}
version: ${{ env.VERSION }}
+2
View File
@@ -245,3 +245,5 @@ hs_err_pid*
keystore.properties
distribution/
screenshots/
claw-export.json
+1
View File
@@ -1,2 +1,3 @@
*.json
baseline-prof.txt
**/test/resources/**
+83 -1
View File
@@ -7,6 +7,74 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [1.30.0] - 2023-07-02
### Changed
* Added another workaround for native library loading crash
## [1.29.0] - 2023-06-08
### Added
* Backup and restore options for saved posts
### Fixed
* Spamming the comments button no longer causes it to be opened multiple times
* Saved posts screen now has a visual indication when you have nothing saved
* Iconography has been updated across the board to be more consistent
## [1.28.0] - 2023-06-03
### Changed
* Navigation transitions have been slightly sped up
### Fixed
* Add workaround for a native library loading crash observed on some devices
## [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
@@ -182,7 +250,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Initial Play Store release
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.23.0...HEAD
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.30.0...HEAD
[1.30.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.29.0...v1.30.0
[1.29.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.28.0...v1.29.0
[1.28.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.27.0...v1.28.0
[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
+24 -1
View File
@@ -8,12 +8,35 @@ Unofficial Android app for read-only access to [lobste.rs](https://lobste.rs), b
height="80" />
</a>
> 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" />
Snapshots from the development branch can be obtained [here](https://github.com/msfjarvis/compose-lobsters/releases/tag/nightly).
## Dependency Diagram
```mermaid
%%{
init: {
'theme': 'neutral'
}
}%%
graph LR
benchmark --> android
android --> api
android --> common
android --> core
android --> database
android --> model
api --> model
common --> core
common --> database
common --> model
```
## License
See [LICENSE](LICENSE)
+41 -18
View File
@@ -4,7 +4,9 @@
* 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")
@@ -14,16 +16,10 @@ plugins {
id("dev.msfjarvis.claw.sentry")
id("dev.msfjarvis.claw.versioning-plugin")
alias(libs.plugins.anvil)
alias(libs.plugins.modulegraphassert)
alias(libs.plugins.whetstone)
}
whetstone {
addOns {
compose.set(true)
workManager.set(true)
}
}
android {
namespace = "dev.msfjarvis.claw.android"
defaultConfig { applicationId = "dev.msfjarvis.claw.android" }
@@ -38,33 +34,60 @@ android {
matchingFallbacks += "release"
isDebuggable = false
proguardFile("benchmark-rules.pro")
applicationIdSuffix = ".benchmark"
}
}
packaging { jniLibs { useLegacyPackaging = true } }
}
moduleGraphAssert {
assertOnAnyBuild = true
maxHeight = 4
restricted = arrayOf(":core -X> :.*")
}
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)
implementation(libs.androidx.profileinstaller)
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.androidx.work.runtime)
implementation(libs.coil)
implementation(libs.copydown)
implementation(libs.crux)
implementation(libs.dagger)
implementation(libs.jsoup)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.napier)
implementation(libs.soloader)
implementation(libs.sqldelight.extensions.coroutines)
implementation(projects.api)
implementation(projects.common)
implementation(projects.core)
implementation(projects.database)
implementation(projects.model)
kapt(libs.dagger.compiler)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.okhttp.mockwebserver)
addTestDependencies(project)
}
+1 -1
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+7 -10
View File
@@ -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>(...);
}
+20
View File
@@ -1,2 +1,22 @@
-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>
+10 -14
View File
@@ -6,13 +6,13 @@
<application
android:name=".ClawApplication"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/full_backup_content"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.Claw.SplashScreen"
android:enableOnBackInvokedCallback="true"
tools:targetApi="tiramisu">
<profileable
android:shell="true"
@@ -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>
+11047 -8254
View File
File diff suppressed because it is too large Load Diff
@@ -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.
@@ -15,6 +15,7 @@ import androidx.work.WorkManager
import com.deliveryhero.whetstone.Whetstone
import com.deliveryhero.whetstone.app.ApplicationComponentOwner
import com.deliveryhero.whetstone.app.ContributesAppInjector
import com.facebook.soloader.SoLoader
import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
import dev.msfjarvis.claw.core.injection.AppPlugin
import java.util.concurrent.TimeUnit
@@ -31,6 +32,7 @@ class ClawApplication : Application(), ApplicationComponentOwner {
override fun onCreate() {
Whetstone.inject(this)
super.onCreate()
SoLoader.init(this, false)
plugins.forEach { plugin -> plugin.apply(this) }
val postUpdateWorkRequest =
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
@@ -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.core.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")
}
}
}
@@ -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.
@@ -1,15 +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.android.injection
import javax.inject.Qualifier
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class MainDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class IODispatcher
@@ -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,7 +13,7 @@ import com.slack.eithernet.ApiResult.Success
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dev.msfjarvis.claw.android.injection.IODispatcher
import dev.msfjarvis.claw.core.injection.IODispatcher
import dev.msfjarvis.claw.model.LobstersPost
import java.io.IOException
import kotlinx.coroutines.CoroutineDispatcher
@@ -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")
}
}
@@ -6,23 +6,36 @@
*/
package dev.msfjarvis.claw.android.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
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.ArrowBack
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.ImportExport
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.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.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
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.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
@@ -38,43 +51,44 @@ 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.datatransfer.DataTransferScreen
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 systemUiController = rememberSystemUiController()
val hottestListState = rememberLazyListState()
val newestListState = rememberLazyListState()
val savedListState = rememberLazyListState()
val navController = rememberNavController()
val coroutineScope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val postActions = rememberPostActions(urlLauncher, navController, viewModel)
val backStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = backStackEntry?.destination?.route
@@ -82,67 +96,57 @@ 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 = Icons.Outlined.Whatshot,
selectedIcon = Icons.Filled.Whatshot,
) {
coroutineScope.launch { hottestListState.animateScrollToItem(index = 0) }
},
NavigationItem(
label = "Newest",
route = Destinations.Newest.route,
icon = ClawIcons.New,
selectedIcon = ClawIcons.NewFilled,
icon = Icons.Outlined.NewReleases,
selectedIcon = Icons.Filled.NewReleases,
) {
coroutineScope.launch { newestListState.animateScrollToItem(index = 0) }
},
NavigationItem(
label = "Saved",
route = Destinations.Saved.route,
icon = ClawIcons.HeartBorder,
selectedIcon = ClawIcons.Heart,
icon = Icons.Outlined.FavoriteBorder,
selectedIcon = 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 }) {
if (
navController.previousBackStackEntry != null &&
navItems.none { it.route == currentDestination }
) {
IconButton(
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() }
) {
Icon(
painter = ClawIcons.ArrowBack,
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Go back to previous screen",
)
}
@@ -153,85 +157,112 @@ fun LobstersApp(
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
}
},
actions = {
if (navItems.any { it.route == currentDestination }) {
IconButton(onClick = { navController.navigate(Destinations.DataTransfer.route) }) {
Icon(
imageVector = Icons.Filled.ImportExport,
contentDescription = "Data transfer options"
)
}
}
},
)
},
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 },
)
}
},
snackbarHost = { SnackbarHost(snackbarHostState) },
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,
// Make animations 2x faster than default specs
enterTransition = { fadeIn(animationSpec = tween(350)) },
exitTransition = { fadeOut(animationSpec = tween(350)) },
) {
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,
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"))
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,
)
}
composable(route = Destinations.DataTransfer.route) {
DataTransferScreen(
context = context,
dataTransferRepository = viewModel.dataTransferRepository,
snackbarHostState = snackbarHostState,
)
}
}
}
}
@@ -0,0 +1,136 @@
/*
* 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.datatransfer
import android.content.Context
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.activity.result.contract.ActivityResultContracts.GetContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Upload
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.android.viewmodel.DataTransferRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
private const val MIME_TYPE = "application/json"
@Composable
fun DataTransferScreen(
context: Context,
dataTransferRepository: DataTransferRepository,
snackbarHostState: SnackbarHostState,
modifier: Modifier = Modifier,
) {
val coroutineScope = rememberCoroutineScope()
Column(modifier = modifier) {
ImportOption(context, coroutineScope, dataTransferRepository, snackbarHostState)
ExportOption(context, coroutineScope, dataTransferRepository, snackbarHostState)
}
}
@Composable
private fun ImportOption(
context: Context,
coroutineScope: CoroutineScope,
dataTransferRepository: DataTransferRepository,
snackbarHostState: SnackbarHostState,
) {
val importAction =
rememberLauncherForActivityResult(GetContent()) { uri ->
if (uri == null) {
coroutineScope.launch { snackbarHostState.showSnackbarDismissing("No file selected") }
return@rememberLauncherForActivityResult
}
coroutineScope.launch {
context.contentResolver.openInputStream(uri)?.use { stream ->
dataTransferRepository.importPosts(stream)
snackbarHostState.showSnackbarDismissing("Successfully imported posts")
}
}
}
SettingsActionItem(
title = "Import saved posts",
description = "Import saved posts from a previously generated export",
icon = Icons.Filled.Download,
) {
importAction.launch(MIME_TYPE)
}
}
@Composable
private fun ExportOption(
context: Context,
coroutineScope: CoroutineScope,
dataTransferRepository: DataTransferRepository,
snackbarHostState: SnackbarHostState,
) {
val exportAction =
rememberLauncherForActivityResult(CreateDocument(MIME_TYPE)) { uri ->
if (uri == null) {
coroutineScope.launch { snackbarHostState.showSnackbarDismissing("No file selected") }
return@rememberLauncherForActivityResult
}
coroutineScope.launch {
context.contentResolver.openOutputStream(uri)?.use { stream ->
dataTransferRepository.exportPosts(stream)
snackbarHostState.showSnackbarDismissing("Successfully exported posts")
}
}
}
SettingsActionItem(
title = "Export posts to file",
description = "Write all saved posts into a JSON file that can be imported at a later date",
icon = Icons.Filled.Upload,
) {
exportAction.launch("claw-export.json")
}
}
@Composable
private fun SettingsActionItem(
title: String,
modifier: Modifier = Modifier,
description: String? = null,
icon: ImageVector? = null,
onClick: (() -> Unit)? = null,
) {
ListItem(
headlineContent = { Text(title) },
supportingContent = { description?.let { Text(it) } },
leadingContent = {
icon?.let {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
}
},
modifier = modifier.clickable { onClick?.invoke() },
)
}
/** Shows a Snackbar but dismisses any existing ones first. */
private suspend fun SnackbarHostState.showSnackbarDismissing(
text: String,
) {
currentSnackbarData?.dismiss()
showSnackbar(text)
}
@@ -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.
@@ -19,17 +19,18 @@ import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
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()
imageVector = if (it) navItem.selectedIcon else navItem.icon,
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()),
@@ -85,7 +86,7 @@ fun ClawNavigationBar(
class NavigationItem(
val label: String,
val route: String,
val icon: Painter,
val selectedIcon: Painter,
val icon: ImageVector,
val selectedIcon: ImageVector,
val listStateResetCallback: () -> Unit,
)
@@ -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(
imageVector = 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 © 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.
@@ -19,8 +19,6 @@ import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.LobstersPostDetails
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
fun Context.getActivity(): ComponentActivity? {
return when (this) {
@@ -30,14 +28,6 @@ fun Context.getActivity(): ComponentActivity? {
}
}
/**
* Parses a given [String] into a [LocalDateTime]. This method is only intended to be used for dates
* in the format returned by the Lobsters API, and is not a general purpose parsing solution.
*/
fun String.toLocalDateTime(): LocalDateTime {
return LocalDateTime.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(this))
}
@Composable
fun rememberPostActions(
urlLauncher: UrlLauncher,
@@ -51,9 +41,10 @@ fun rememberPostActions(
}
override fun viewComments(postId: String) {
navController.navigate(
val currentRoute = navController.currentDestination?.route
val newRoute =
Destinations.Comments.route.replace(Destinations.Comments.placeholder, postId)
)
if (currentRoute != Destinations.Comments.route) navController.navigate(newRoute)
}
override fun viewCommentsPage(commentsUrl: String) {
@@ -6,40 +6,66 @@
*/
package dev.msfjarvis.claw.android.ui.lists
import androidx.activity.compose.ReportDrawn
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Inbox
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
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, { it.shortId }) { item ->
ListItem(
item = item,
isSaved = { true },
postActions = postActions,
ReportDrawn()
Box(modifier = modifier.fillMaxSize()) {
if (items.isEmpty()) {
Column(modifier = Modifier.align(Alignment.Center)) {
Icon(
imageVector = Icons.Filled.Inbox,
contentDescription = "Empty inbox icon",
modifier = Modifier.align(Alignment.CenterHorizontally).size(36.dp),
)
Divider()
Text(text = "No saved posts", style = MaterialTheme.typography.headlineSmall)
}
} else {
LazyColumn(state = listState) {
items.forEach { (month, posts) ->
stickyHeader(contentType = "month-header") { MonthHeader(month = month) }
items(
items = posts,
contentType = { "LobstersItem" },
) { item ->
ListItem(
item = item,
isSaved = { true },
postActions = postActions,
)
Divider()
}
}
}
}
}
@@ -1,11 +1,12 @@
/*
* 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.lists
import androidx.activity.compose.ReportDrawnWhen
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -26,7 +27,7 @@ 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 dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.posts.toDbModel
import dev.msfjarvis.claw.common.ui.NetworkError
@@ -43,6 +44,7 @@ fun NetworkPosts(
postActions: PostActions,
modifier: Modifier = Modifier,
) {
ReportDrawnWhen { lazyPagingItems.itemCount > 0 }
val refreshLoadState = lazyPagingItems.loadState.refresh
val isRefreshing = refreshLoadState == LoadState.Loading
val pullRefreshState = rememberPullRefreshState(isRefreshing, lazyPagingItems::refresh)
@@ -57,7 +59,11 @@ fun NetworkPosts(
LazyColumn(
state = listState,
) {
items(items = lazyPagingItems, key = { it.shortId }) { item ->
items(
count = lazyPagingItems.itemCount,
contentType = lazyPagingItems.itemContentType { "LobstersItem" },
) { index ->
val item = lazyPagingItems[index]
if (item != null) {
val dbModel = item.toDbModel()
ListItem(
@@ -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.
@@ -31,6 +31,10 @@ sealed class Destinations {
override val route = "user/$placeholder"
}
object DataTransfer : Destinations() {
override val route: String = "datatransfer"
}
companion object {
val startDestination
get() = Hottest
@@ -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
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2022 Harsh Shandilya.
* 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.
@@ -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.api.injection.BaseUrl
import dev.msfjarvis.claw.core.injection.IODispatcher
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")
}
}
}
}
@@ -13,18 +13,21 @@ import androidx.paging.PagingConfig
import com.deliveryhero.whetstone.viewmodel.ContributesViewModel
import com.slack.eithernet.ApiResult.Failure
import com.slack.eithernet.ApiResult.Success
import dev.msfjarvis.claw.android.injection.IODispatcher
import dev.msfjarvis.claw.android.paging.LobstersPagingSource
import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.PAGE_SIZE
import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.STARTING_PAGE_INDEX
import dev.msfjarvis.claw.android.ui.toLocalDateTime
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.core.injection.IODispatcher
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.Comment
import java.io.IOException
import java.net.HttpURLConnection
import java.time.LocalDateTime
import java.time.Month
import java.time.format.DateTimeFormatter
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
@@ -37,7 +40,9 @@ class ClawViewModel
constructor(
private val api: LobstersApi,
private val savedPostsRepository: SavedPostsRepository,
private val commentsRepository: CommentsRepository,
private val linkMetadataRepository: LinkMetadataRepository,
val dataTransferRepository: DataTransferRepository,
private val pagingSourceFactory: LobstersPagingSource.Factory,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
@@ -62,9 +67,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 {
@@ -99,10 +104,10 @@ constructor(
}
}
suspend fun getSeenComments(postId: String) = savedPostsRepository.getSeenComments(postId)
suspend fun getSeenComments(postId: String) = commentsRepository.getSeenComments(postId)
fun markSeenComments(postId: String, comments: List<Comment>) {
viewModelScope.launch { savedPostsRepository.markSeenComments(postId, comments) }
viewModelScope.launch { commentsRepository.markSeenComments(postId, comments) }
}
suspend fun getLinkMetadata(url: String) =
@@ -118,4 +123,13 @@ constructor(
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
}
}
/**
* Parses a given [String] into a [LocalDateTime]. This method is only intended to be used for
* dates in the format returned by the Lobsters API, and is not a general purpose parsing
* solution.
*/
private fun String.toLocalDateTime(): LocalDateTime {
return LocalDateTime.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(this))
}
}
@@ -0,0 +1,33 @@
/*
* 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.viewmodel
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.local.PostComments
import dev.msfjarvis.claw.model.Comment
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
class CommentsRepository
@Inject
constructor(
database: LobstersDatabase,
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
) {
private val postCommentsQueries = database.postCommentsQueries
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 }))
}
}
}
@@ -0,0 +1,54 @@
/*
* 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.core.injection.DatabaseDispatcher
import dev.msfjarvis.claw.core.injection.IODispatcher
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.SavedPostSerializer
import dev.msfjarvis.claw.database.local.SavedPost
import java.io.InputStream
import java.io.OutputStream
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
@OptIn(ExperimentalSerializationApi::class)
class DataTransferRepository
@Inject
constructor(
private val database: LobstersDatabase,
private val json: Json,
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
) {
private val serializer = ListSerializer(SavedPostSerializer)
private val savedPostQueries = database.savedPostQueries
suspend fun importPosts(input: InputStream) {
val posts: List<SavedPost> =
withContext(ioDispatcher) {
json.decodeFromStream(
serializer,
input,
)
}
withContext(dbDispatcher) {
database.transaction { posts.forEach(savedPostQueries::insertOrReplacePost) }
}
}
suspend fun exportPosts(output: OutputStream) {
val posts = withContext(dbDispatcher) { savedPostQueries.selectAllPosts().executeAsList() }
withContext(ioDispatcher) { json.encodeToStream(serializer, posts, output) }
}
}
@@ -1,21 +1,49 @@
/*
* 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.android.viewmodel
import dev.msfjarvis.claw.metadata.MetadataExtractor
import com.chimbori.crux.Crux
import com.chimbori.crux.api.Fields
import dev.msfjarvis.claw.model.LinkMetadata
import javax.inject.Inject
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.Jsoup
class LinkMetadataRepository
@Inject
constructor(
private val metadataExtractor: MetadataExtractor,
private val crux: Crux,
private val okHttpClient: OkHttpClient,
) {
suspend fun getLinkMetadata(url: String): LinkMetadata {
return metadataExtractor.getExtractedMetadata(url)
return run {
val parsedUrl = url.toHttpUrlOrNull() ?: return@run null
if (!parsedUrl.isHttps) return@run null
val request = Request.Builder().url(parsedUrl).build()
val htmlContent =
okHttpClient.newCall(request).execute().use { response ->
val body = response.body ?: return@run null
body.string()
}
val extractedMetadata = crux.extractFrom(parsedUrl, Jsoup.parse(htmlContent, url))
val faviconUrl = extractedMetadata[Fields.FAVICON_URL].toString()
val readingTime = extractedMetadata[Fields.DURATION_MS].toString()
LinkMetadata(
url = url,
faviconUrl = faviconUrl,
readingTime = readingTime,
)
}
?: LinkMetadata(
url = url,
faviconUrl = null,
readingTime = null,
)
}
}
@@ -8,11 +8,9 @@ package dev.msfjarvis.claw.android.viewmodel
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.android.injection.DatabaseDispatcher
import dev.msfjarvis.claw.core.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
@@ -25,7 +23,6 @@ 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) {
@@ -47,15 +44,6 @@ 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,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.
@@ -41,7 +41,6 @@ constructor(
.map { post -> lobstersApi.getPostDetails(post.shortId) }
.filterIsInstance<Success<LobstersPostDetails>>()
.map { result -> result.value.toDbModel() }
.toList()
.let { savedPostsRepository.savePosts(it) }
return Result.success()
}
@@ -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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 B

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 B

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 B

After

Width:  |  Height:  |  Size: 46 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 B

After

Width:  |  Height:  |  Size: 52 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

-20
View File
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- 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="true" />
<meta-data android:name="io.sentry.breadcrumbs.user-interaction" android:value="true" />
<!-- enable screenshot for crashes -->
<meta-data android:name="io.sentry.attach-screenshot" android:value="true" />
<!-- enable view hierarchy for crashes -->
<meta-data android:name="io.sentry.attach-view-hierarchy" android:value="true" />
<!-- enable the performance API by setting a sample-rate, adjust in production env -->
<meta-data android:name="io.sentry.traces.sample-rate" android:value="0.8" />
<!-- enable profiling when starting transactions, adjust in production env -->
<meta-data android:name="io.sentry.traces.profiling.sample-rate" android:value="0.8" />
</application>
</manifest>
@@ -0,0 +1,61 @@
/*
* 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.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
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
}
}
}
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,5 +1,5 @@
#
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
#
versioning-plugin.versionCode=12300
versioning-plugin.versionName=1.23.0
versioning-plugin.versionCode=13000
versioning-plugin.versionName=1.30.0
+9 -8
View File
@@ -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 -1
View File
@@ -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.
@@ -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
@@ -6,65 +6,69 @@
*/
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.test.runTest
import org.junit.jupiter.api.Test
class ApiTest : FunSpec() {
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("get posts by single tag") {
var tags = Tags()
tags.addTag("meta")
val posts = api.getPostsByTags(tags, 1)
posts.shouldBeTypeOf<Success<List<LobstersPost>>>()
posts.value shouldHaveSize 25
posts.value[0].tags.contains("meta")
}
@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("get posts by multiple tags") {
var tags = Tags()
tags.addTag("programming")
tags.addTag("rust")
val posts = api.getPostsByTags(tags, 1)
posts.shouldBeTypeOf<Success<List<LobstersPost>>>()
posts.value shouldHaveSize 25
posts.value[0].tags.contains("programming") or posts.value[0].tags.contains("rust")
}
@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")
}
}
@@ -6,15 +6,16 @@
*/
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
@@ -31,6 +32,16 @@ class ApiWrapper(controller: EitherNetController<LobstersApi>) {
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
@@ -39,7 +50,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) { success(metaPosts) }
controller.enqueue(LobstersApi::getPostsByTags) { success(programmingRustPosts) }
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
}
}
+22 -5
View File
@@ -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" } }
@@ -6,7 +6,6 @@
*/
package dev.msfjarvis.claw.benchmark
import androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
@@ -14,7 +13,6 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
@OptIn(ExperimentalStableBaselineProfilesApi::class)
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
private lateinit var device: UiDevice
@@ -26,10 +24,10 @@ class BaselineProfileGenerator {
}
@Test
fun startup() =
baselineProfileRule.collectStableBaselineProfile(
fun generateBaselineProfile() =
baselineProfileRule.collect(
packageName = PACKAGE_NAME,
maxIterations = 15,
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() {
+2 -19
View File
@@ -4,26 +4,9 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
import org.gradle.api.JavaVersion
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.withType
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<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
freeCompilerArgs = freeCompilerArgs + "-Xsam-conversions=class"
}
}
}
kotlin.jvmToolchain { languageVersion.set(JavaLanguageVersion.of(17)) }
gradlePlugin {
plugins {
@@ -81,8 +64,8 @@ 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)
+14 -2
View File
@@ -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")) } }
@@ -10,24 +10,27 @@ 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
import org.gradle.kotlin.dsl.getByType
class AndroidCommonPlugin : Plugin<Project> {
private companion object {
const val COMPILE_SDK = 33
const val COMPILE_SDK = 34
const val MIN_SDK = 26
const val TARGET_SDK = 33
const val TARGET_SDK = 34
const val SLIM_TESTS_PROPERTY = "slimTests"
}
override fun apply(project: Project) {
@@ -49,43 +52,36 @@ class AndroidCommonPlugin : Plugin<Project> {
resources.excludes.add("**/META-INF/LGPL2.1")
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
testOptions {
animationsDisabled = true
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")
}
private fun Project.configureSlimTests() {
// 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
private fun Project.configureSlimTests() {
if (!providers.gradleProperty(SLIM_TESTS_PROPERTY).isPresent) {
return
}
// 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.
@@ -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
@@ -1,35 +0,0 @@
/*
* 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 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 COMPOSE_RULES_VERSION = "0.1.2"
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",
"io.nlopez.compose.rules:detekt:$COMPOSE_RULES_VERSION",
)
}
}
@@ -1,14 +1,17 @@
/*
* 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 dev.msfjarvis.claw.gradle.KotlinCommonPlugin.Companion.JVM_TOOLCHAIN_ACTION
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
@Suppress("Unused")
@@ -19,5 +22,6 @@ class KotlinAndroidPlugin : Plugin<Project> {
apply(KotlinAndroidPluginWrapper::class)
apply(KotlinCommonPlugin::class)
}
project.extensions.getByType<KotlinProjectExtension>().jvmToolchain(JVM_TOOLCHAIN_ACTION)
}
}
@@ -1,37 +1,37 @@
/*
* 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 org.gradle.api.JavaVersion
import org.gradle.api.Action
import org.gradle.api.Plugin
import org.gradle.api.Project
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.jvm.toolchain.JavaLanguageVersion
import org.gradle.jvm.toolchain.JavaToolchainSpec
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@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()
}
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"
compilerOptions {
allWarningsAsErrors.set(true)
freeCompilerArgs.addAll(ADDITIONAL_COMPILER_ARGS)
languageVersion.set(KotlinVersion.KOTLIN_1_8)
}
}
withType<Test>().configureEach {
@@ -42,10 +42,13 @@ class KotlinCommonPlugin : Plugin<Project> {
}
}
private companion object {
companion object {
private val ADDITIONAL_COMPILER_ARGS =
listOf(
"-opt-in=kotlin.RequiresOptIn",
)
val JVM_TOOLCHAIN_ACTION =
Action<JavaToolchainSpec> { languageVersion.set(JavaLanguageVersion.of(17)) }
}
}
@@ -1,20 +1,32 @@
/*
* 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.KotlinCommonPlugin.Companion.JVM_TOOLCHAIN_ACTION
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.gradle.kotlin.dsl.getByType
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
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(KotlinCommonPlugin::class)
project.pluginManager.run {
apply(KotlinPluginWrapper::class)
apply(LintPlugin::class)
apply(KotlinCommonPlugin::class)
}
project.extensions.findByType<Lint>()?.configureLint(project, isJVM = true)
project.extensions.getByType<KotlinProjectExtension>().jvmToolchain(JVM_TOOLCHAIN_ACTION)
}
}
@@ -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) }
}
}
@@ -25,11 +25,18 @@ class SentryPlugin : Plugin<Project> {
val catalog = project.extensions.getByType<VersionCatalogsExtension>()
val libs = catalog.named("libs")
project.extensions.configure<ApplicationAndroidComponentsExtension> {
onVariants(selector()) { variant ->
onVariants(selector().all()) { variant ->
val sentryDsn = project.providers.environmentVariable(SENTRY_DSN_PROPERTY)
if (sentryDsn.isPresent) {
variant.manifestPlaceholders.put("sentryDsn", sentryDsn.get())
}
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)
@@ -42,7 +49,7 @@ class SentryPlugin : Plugin<Project> {
autoUploadNativeSymbols.set(false)
includeNativeSources.set(false)
ignoredVariants.set(emptySet())
ignoredBuildTypes.set(setOf("debug"))
ignoredBuildTypes.set(setOf("benchmark", "debug"))
ignoredFlavors.set(emptySet())
tracingInstrumentation {
enabled.set(true)
@@ -46,6 +46,6 @@ class SpotlessPlugin : Plugin<Project> {
}
private companion object {
private const val KTFMT_VERSION = "0.43"
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")
+7 -3
View File
@@ -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")
}
+34 -30
View File
@@ -4,43 +4,17 @@
* 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 -1
View File
@@ -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()
}
@@ -7,8 +7,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 +19,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
@@ -41,13 +41,13 @@ 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(
@@ -71,7 +71,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()) {
@@ -114,7 +114,7 @@ private fun PostLink(
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
NetworkImage(
url = linkMetadata.faviconUrl,
placeholder = ClawIcons.Web,
placeholder = Icons.Filled.Public,
contentDescription = "",
modifier = Modifier.size(24.dp),
)
@@ -131,7 +131,6 @@ private fun PostLink(
private val CommentEntryPadding = 16f.dp
@OptIn(ExperimentalAnimationApi::class)
@Composable
internal fun CommentEntry(
commentNode: CommentNode,
@@ -169,13 +168,11 @@ internal fun CommentEntry(
modifier =
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") },
)
AnimatedContent(targetState = commentNode.isExpanded) { expandedState ->
if (expandedState) {
ThemedRichText(
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
modifier = Modifier.padding(top = 8.dp)
)
}
if (commentNode.isExpanded) {
ThemedRichText(
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
modifier = Modifier.padding(top = 8.dp)
)
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* 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.
@@ -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.Comment
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.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.platform.testTag
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 = Icons.Filled.AccountCircle,
contentDescription = contentDescription,
modifier = Modifier.requiredSize(24.dp).clip(CircleShape),
)
@@ -155,13 +168,13 @@ 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,
imageVector = if (saved) Icons.Filled.Favorite else Icons.Filled.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).testTag("save_button"),
)
}
}
@@ -191,7 +204,7 @@ private fun CommentsButton(
},
) {
Icon(
painter = ClawIcons.Comment,
imageVector = Icons.Filled.Comment,
tint = MaterialTheme.colorScheme.secondary,
contentDescription = "Open comments",
modifier = Modifier.align(Alignment.Center),
@@ -200,16 +213,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 +243,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)
}
}
}
}

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