Compare commits

...
134 Commits
Author SHA1 Message Date
Harsh Shandilya 2eb0a6a31c feat(release): bump version 2022-11-10 22:12:04 +05:30
Harsh Shandilya 2d44ad2055 feat(github): make release workflow self-contained 2022-11-10 22:09:16 +05:30
Harsh Shandilya 8af4bddfe1 feat(release): add changelog entry for Compose bump 2022-11-10 21:20:17 +05:30
renovate[bot]andGitHub 0be0952e04 fix(deps): update dependency androidx.work:work-runtime-ktx to v2.8.0-beta02 2022-11-10 10:06:12 +00:00
renovate[bot]andGitHub 8b2ba790bc fix(deps): update dependency androidx.test:runner to v1.5.1 2022-11-10 08:07:26 +00:00
Harsh Shandilya 6ed377af94 fix(github): adjust when cache is written to 2022-11-10 13:30:49 +05:30
renovate[bot]andGitHub cfc479a9ee fix(deps): update dependency androidx.compose:compose-bom to v2022.11.00 2022-11-10 05:31:33 +00:00
renovate[bot]andGitHub 7552fb1c36 fix(deps): update dependency androidx.profileinstaller:profileinstaller to v1.3.0-alpha02 2022-11-10 02:24:42 +00:00
renovate[bot]andGitHub bcd8a412b5 fix(deps): update dependency androidx.navigation:navigation-compose to v2.6.0-alpha04 2022-11-09 23:38:19 +00:00
renovate[bot]andGitHub defe4914df fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha07 2022-11-09 20:43:05 +00:00
renovate[bot]andGitHub 621e93939b chore(deps): update plugin napt to v1.18 2022-11-09 17:50:41 +00:00
renovate[bot]andGitHub 247949f92e chore(deps): update dependency com.twitter.compose.rules:detekt to v0.0.23 2022-11-09 14:36:56 +00:00
renovate[bot]andGitHub 5700b2150e fix(deps): update dependency androidx.test.ext:junit to v1.1.4 2022-11-09 03:13:19 +00:00
renovate[bot]andGitHub 530ad44b27 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.5.0 2022-11-08 23:46:03 +00:00
renovate[bot]andGitHub 51c7306cfa fix(deps): update dependency androidx.test:runner to v1.5.0 2022-11-08 20:15:58 +00:00
renovate[bot]andGitHub 79778466b9 chore(deps): update agp to v8.0.0-alpha08 2022-11-07 19:09:52 +00:00
renovate[bot]andHarsh Shandilya 7d7989d5c0 chore(deps): pin dependencies 2022-11-05 16:45:25 +05:30
Harsh Shandilya 923361ee1b fix(renovate): pin actions via SHA 2022-11-05 16:45:14 +05:30
renovate[bot]andGitHub 6adf339e08 chore(deps): update agp to v8.0.0-alpha07 2022-11-02 17:55:42 +00:00
Harsh Shandilya 9e37246810 fix(release): add status parameter explicitly 2022-11-02 14:45:27 +05:30
Harsh Shandilya 7be1157f22 Revert "fix(release): rework Play Store release workflow"
This reverts commit cd7d82d378.
2022-11-02 14:44:48 +05:30
Harsh Shandilya cd7d82d378 fix(release): rework Play Store release workflow 2022-11-02 14:37:26 +05:30
Harsh Shandilya b30425d97d feat(release): start next development iteration 2022-11-02 14:17:58 +05:30
Harsh Shandilya c89589adcb feat(release): bump version
Release to Google Play / publish-google-play-release (push) Canceled after 0s
2022-11-02 14:17:05 +05:30
Harsh Shandilya 6523b85f07 fix(deps): reformat catalog 2022-11-02 12:57:56 +05:30
Harsh Shandilya 33f8b73938 chore: fill out changelog 2022-11-02 12:25:13 +05:30
Harsh Shandilya bb47119c84 fix: remove inaccurate copyright header
Same reasoning as this change https://github.com/rust-lang/rust/commit/2a8807e889a43c6b89eb6f2736907afa87ae592f
2022-11-02 12:23:09 +05:30
Harsh Shandilya 6adb45ff7c fix(metadata-extractor): migrate away from removed APIs 2022-11-01 19:06:27 +05:30
renovate[bot]andGitHub a87b1cf38c fix(deps): update dependency com.chimbori.crux:crux to v3.11.0 2022-11-01 10:50:53 +00:00
renovate[bot]andGitHub b112585533 fix(deps): update material_motion to v0.10.3 2022-10-30 09:52:46 +00:00
renovate[bot]andGitHub 5750bbebd3 fix(deps): update dependency androidx.test.ext:junit to v1.1.4-rc01 2022-10-27 05:20:30 +00:00
renovate[bot]andGitHub 067b762a31 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.5.0-rc01 2022-10-27 02:48:26 +00:00
renovate[bot]andGitHub 658a2f36fd fix(deps): update androidx.test to v1.5.0-rc01 2022-10-26 23:46:41 +00:00
Harsh Shandilya 6a4d0c2d3a refactor: add license headers across the board 2022-10-26 01:55:50 +05:30
Harsh Shandilya 5319669a8e feat: configure Spotless license headers 2022-10-26 01:54:38 +05:30
Harsh Shandilya 720f946f41 fix(gradle): only allow Spotless plugin to be applied to the root project 2022-10-26 01:30:33 +05:30
renovate[bot]andGitHub a1390a0f05 fix(deps): update accompanist to v0.27.0 2022-10-25 03:32:37 +00:00
Harsh Shandilya 655b20e496 fix(github): bump r0adkll/upload-google-play
The new maintainer has not been bumping the v1 tag
2022-10-24 23:20:39 +05:30
Harsh Shandilya 8508a8646f feat(release): start next development iteration 2022-10-24 23:11:30 +05:30
Harsh Shandilya 790874e1a7 feat(release): bump version
Release to Google Play / publish-google-play-release (push) Canceled after 0s
2022-10-24 23:09:41 +05:30
renovate[bot]andGitHub 0ac7c9876e fix(deps): update dependency androidx.test:runner to v1.5.0-beta02 2022-10-24 17:37:20 +00:00
renovate[bot]andGitHub daba852e6d fix(deps): update dependency androidx.test.espresso:espresso-core to v3.5.0-beta02 2022-10-24 17:32:03 +00:00
Harsh Shandilya 47f7f97aa2 fix(renovate): use exact file matches for regex managers 2022-10-24 23:01:22 +05:30
Harsh Shandilya 80bfb34e5b refactor(deps): switch Compose dependencies to BOM 2022-10-24 22:49:58 +05:30
Harsh Shandilya 42310a6a33 fix(ci): ensure signing is setup for release workflow 2022-10-24 22:22:30 +05:30
Harsh Shandilya c554459191 fix(scripts): make GitHub release non-interactive 2022-10-24 22:20:13 +05:30
Harsh Shandilya 1560574e15 fix: delete artifact directories before copying to them 2022-10-24 22:04:51 +05:30
Harsh Shandilya a1f3950042 chore(deps): realign androidx.compose.ui:ui version 2022-10-24 21:21:17 +05:30
Harsh Shandilya d7bafbddbc fix(scripts): automatically put release notes as tag message 2022-10-24 21:17:39 +05:30
renovate[bot]andGitHub ef5e4a952e fix(deps): update dependency androidx.compose.ui:ui to v1.3.0 2022-10-24 15:02:52 +00:00
Harsh Shandilya 5711bab007 fix(scripts): clear changelog in make-release 2022-10-24 20:27:22 +05:30
Harsh Shandilya b1fca935a8 chore(deps): update AndroidX dependencies 2022-10-24 20:24:45 +05:30
Harsh Shandilya 2780f7b204 fix(scripts): fix snapshot deployment 2022-10-24 19:23:22 +05:30
Harsh Shandilya 22231606cb feat(ci): add workflow to publish releases to Google Play 2022-10-24 19:17:54 +05:30
Harsh Shandilya c797853055 feat: init release notes 2022-10-24 19:17:54 +05:30
Harsh Shandilya b003f0c283 fix(scripts): bring make-release up-to-date 2022-10-24 19:17:54 +05:30
Harsh Shandilya cc55687292 feat(rename-artifacts): add support for obfuscation mapping files 2022-10-24 19:17:54 +05:30
Harsh Shandilya 531234e036 fix(scripts): add executable bit to make-release.sh 2022-10-24 18:09:48 +05:30
renovate[bot]andGitHub ed5bbdd571 chore(deps): update gradle/gradle-build-action action to v2.3.3 2022-10-22 16:27:13 +00:00
renovate[bot]andGitHub 23cd91a97a fix(deps): update dependency com.chimbori.crux:crux to v3.10.0 2022-10-22 02:33:32 +00:00
renovate[bot]andGitHub 6761659c7f chore(deps): update actions/upload-artifact action to v3.1.1 2022-10-21 20:52:43 +00:00
renovate[bot]andGitHub 6bd3dd81f0 fix(deps): update richtext to v0.15.0 2022-10-21 00:24:14 +00:00
renovate[bot]andGitHub 765a7f6f0b chore(deps): update dependency com.twitter.compose.rules:detekt to v0.0.22 2022-10-19 20:33:12 +00:00
renovate[bot]andGitHub 46f4d9a397 chore(deps): update dependency com.twitter.compose.rules:detekt to v0.0.21 2022-10-19 12:59:44 +00:00
renovate[bot]andGitHub 2cc9e38e77 chore(deps): update agp to v8.0.0-alpha05 2022-10-18 15:35:44 +00:00
renovate[bot]andGitHub dc954c7ba5 chore(deps): update actions/setup-java action to v3.6.0 2022-10-18 12:58:55 +00:00
Harsh Shandilya 15dc9c3dc7 fix(common): prevent link metadata resolution from creating fatal errors 2022-10-18 12:08:49 +05:30
Harsh Shandilya a4be2d4bf2 fix(deps): update dependency org.jetbrains.kotlin:kotlin-serialization to v1.4.1 2022-10-17 18:05:55 +05:30
Harsh Shandilya 5329b6e3aa feat(release): add script to make a release 2022-10-17 12:27:15 +05:30
Harsh Shandilya 204a3c0bb5 feat(release): start next development iteration 2022-10-17 11:25:46 +05:30
Harsh Shandilya 69eed8a23a feat(release): bump version 2022-10-17 11:25:21 +05:30
renovate[bot]andGitHub 3c741a7fe1 fix(deps): update dependency com.github.ben-manes:gradle-versions-plugin to v0.43.0 2022-10-17 05:52:56 +00:00
Harsh Shandilya ccfcd48438 refactor: simplify argument passing 2022-10-16 12:44:07 +05:30
Harsh Shandilya 79aba9a187 feat: load link metadata lazily 2022-10-16 12:21:19 +05:30
renovate[bot]andGitHub 4337a19abc fix(deps): update dependency org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin to v2.6.0 2022-10-15 03:40:04 +00:00
renovate[bot]andGitHub a53e9f0065 chore(deps): update agp to v8.0.0-alpha04 2022-10-15 00:23:38 +00:00
renovate[bot]andGitHub e6ca8365ca fix(deps): update dependency nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin to v0.7.0 2022-10-14 12:12:01 +00:00
renovate[bot]andGitHub f1805396e5 chore(deps): update dependency com.twitter.compose.rules:detekt to v0.0.20 2022-10-11 17:04:16 +00:00
renovate[bot]andGitHub 6d70d997a3 chore(deps): update dependency com.twitter.compose.rules:detekt to v0.0.19 2022-10-10 13:01:32 +00:00
Harsh Shandilya 4e872bfa4a fix(gradle): re-enable Test state tracking 2022-10-10 01:29:32 +05:30
Harsh Shandilya fdc9ae8968 fix(gradle): raise Kotlin language version to 1.7 2022-10-10 01:29:32 +05:30
Harsh Shandilya f31debc685 fix(gradle): enable allWarningsAsErrors on CI 2022-10-10 01:29:32 +05:30
Harsh Shandilya 74c265538d fix(api): cleanup dependencies 2022-10-10 01:29:32 +05:30
Harsh Shandilya 4607a68de0 refactor(api): update tests for EitherNet 2022-10-10 01:29:32 +05:30
Harsh Shandilya 8652d4ceaa refactor(api): adopt EitherNet 2022-10-10 01:29:32 +05:30
Harsh Shandilya 484fac5779 Revert "Revert "android: enable support for back navigation animation""
This reverts commit f0a7ffb4b4.
2022-10-09 13:11:54 +05:30
Harsh Shandilya 0d7a7a0e22 fix(common): add shadow to ClawAppBar 2022-10-07 21:13:29 +05:30
renovate[bot]andGitHub 8470e95f6b fix(deps): update dependency androidx.test.ext:junit to v1.1.4-beta01 2022-10-07 11:54:17 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ad72c80130 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.5.0-beta01 (#340)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-07 11:53:41 +00:00
renovate[bot]andGitHub be7840125f fix(deps): update androidx.test 2022-10-07 06:21:40 +00:00
renovate[bot]andGitHub cf3549f09a chore(deps): update agp to v8.0.0-alpha03 2022-10-06 22:52:03 +00:00
Harsh Shandilya 6ee00f6a27 refactor(build-logic): cleanup Detekt issues 2022-10-06 11:43:54 +05:30
Harsh Shandilya d963fd8ae4 chore(deps): commonize compose.ui version 2022-10-06 10:33:27 +05:30
renovate[bot]andGitHub 32ca4f2115 fix(deps): update dependency androidx.compose.ui:ui to v1.3.0-rc01 2022-10-05 18:18:11 +00:00
Harsh Shandilya 95cbe8dfe4 chore(deps): bump AndroidX dependencies 2022-10-05 23:23:43 +05:30
Harsh Shandilya 7e7e00f06e fix(database): remove unnecessary experimental annotation 2022-10-05 12:38:01 +05:30
Harsh Shandilya 8eee42ae0f feat(metadata-extractor): allow de-AMP plugin to change our target URL 2022-10-05 12:38:01 +05:30
Harsh Shandilya 8060c347cc refactor(android): resolve Detekt-reported issues 2022-10-05 12:23:30 +05:30
Harsh Shandilya 8851071d58 refactor(common): resolve Detekt-reported issues 2022-10-05 12:23:30 +05:30
Harsh Shandilya 54c4b01411 chore(deps): upgrade to Compose Compiler 1.3.2 2022-10-05 00:08:50 +05:30
renovate[bot]andGitHub 176bc9b022 chore(deps): update actions/checkout action to v3.1.0 2022-10-04 11:26:24 +00:00
Harsh Shandilya 245130ef20 chore(deps): bump sqldelight to 2.0.0-alpha04 2022-10-04 12:36:43 +05:30
Harsh Shandilya 394558d634 fix(ci): run Detekt separately 2022-10-04 00:14:13 +05:30
Harsh Shandilya 0904740980 feat(detekt): enable PreviewNaming lint and re-baseline 2022-10-04 00:02:15 +05:30
renovate[bot]andGitHub 4212ca41cf chore(deps): update dependency com.twitter.compose.rules:detekt to v0.0.18 2022-10-03 18:01:00 +00:00
Harsh Shandilya 33b8503f19 feat(detekt): add a flag to turn on debug input 2022-10-03 22:48:23 +05:30
Harsh Shandilya eb8be2f909 feat(detekt): try running type resolution enabled task if available 2022-10-03 22:45:21 +05:30
Harsh Shandilya e1d16e9f36 refactor(coroutine-utils): suppress false-positive InjectDispatcher lint 2022-10-03 22:40:59 +05:30
Harsh Shandilya dd76c7bed7 refactor(android): remove unnecessary non-null assertion 2022-10-03 22:40:33 +05:30
Harsh Shandilya a1ab32c43e refactor(common): fix Modifier re-use 2022-10-03 22:39:44 +05:30
Harsh Shandilya 069d7e5e05 refactor(common): workaround false-positive MagicNumber lint 2022-10-03 12:57:39 +05:30
Harsh Shandilya 8f927613f3 refactor(common): correctly use modifier in TagText 2022-10-03 12:57:39 +05:30
Harsh Shandilya f5de95af03 refactor(common): remove unused code 2022-10-03 12:57:39 +05:30
Harsh Shandilya 535c8a5808 refactor(common): pass HTMLConverter down as a parameter 2022-10-03 12:57:39 +05:30
Harsh Shandilya 02416a3606 feat(common): delete unused Paparazzi tests 2022-10-03 12:57:39 +05:30
Harsh Shandilya 293d12454f refactor(common): add a message to preview throwable 2022-10-03 12:57:39 +05:30
Harsh Shandilya d178bebebd refactor(common): ensure exceptions in UrlLauncher are logged 2022-10-03 12:57:38 +05:30
Harsh Shandilya 38f1fdb6a3 refactor(common): add missing Modifier parameters 2022-10-03 12:57:38 +05:30
Harsh Shandilya 46bfafd681 refactor(common): pass Modifier to UserProfileInternal 2022-10-03 11:33:47 +05:30
Harsh Shandilya 999f2dfb01 refactor(android): simplify Destinations 2022-10-03 11:32:45 +05:30
Harsh Shandilya 52ac8346b3 refactor: resolve Detekt issues found in metadata-extractor 2022-10-02 16:38:12 +05:30
Harsh Shandilya 1d761b1f6e refactor: suppress LongParameterList in model module 2022-10-02 16:38:12 +05:30
Harsh Shandilya 16404e6654 feat(detekt): sync baseline 2022-10-02 16:38:12 +05:30
Harsh Shandilya 0f684a3728 feat(detekt): add Twitter's Compose rules 2022-10-02 16:38:12 +05:30
Harsh Shandilya 2685111c70 refactor(build): extract detekt configuration 2022-10-02 16:38:12 +05:30
Harsh Shandilya b867804bdb refactor(detekt): adjust for Compose 2022-10-02 16:38:12 +05:30
Harsh Shandilya 37bb6e1d36 feat(detekt): add default config 2022-10-02 16:38:12 +05:30
Harsh Shandilya 78c321d269 chore(common): suppress warnings in generated code 2022-10-02 16:38:12 +05:30
Harsh Shandilya cab3f74ce0 feat(detekt): add baselines 2022-10-02 16:38:12 +05:30
Harsh Shandilya f0cff549b4 feat(build): add Detekt 2022-10-02 16:38:12 +05:30
Harsh Shandilya 5ab0774689 refactor(android): extract annotations to their own file 2022-10-02 16:38:12 +05:30
Harsh Shandilya 8835e07ac6 chore: reformat proguard file 2022-10-02 15:17:39 +05:30
renovate[bot]andGitHub 85de847692 fix(deps): update coil to v2.2.2 2022-10-02 03:54:47 +00:00
Harsh Shandilya ccc14dee4f feat(release): prepare next development revision 2022-09-30 15:19:00 +05:30
137 changed files with 2078 additions and 446 deletions
+12 -1
View File
@@ -2,6 +2,7 @@
"extends": [
"config:base",
":dependencyDashboard",
"helpers:pinGitHubActionDigests",
"github>msfjarvis/shared-workflows//renovate/automerge"
],
"packageRules": [
@@ -19,13 +20,23 @@
"regexManagers": [
{
"fileMatch": [
".kt$"
"build-logic/kotlin-plugins/src/main/kotlin/dev/msfjarvis/aps/gradle/SpotlessPlugin.kt"
],
"matchStrings": [
"KTFMT_VERSION = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "com.facebook:ktfmt"
},
{
"fileMatch": [
"build-logic/kotlin-plugins/src/main/kotlin/dev/msfjarvis/aps/gradle/Detekt.kt"
],
"matchStrings": [
"TWITTER_RULES_VERSION = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "com.twitter.compose.rules:detekt"
}
]
}
+16 -7
View File
@@ -15,23 +15,31 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3.0.2
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
- name: Set up JDK
uses: actions/setup-java@v3.5.1
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
with:
distribution: temurin
java-version: 18
- name: Run unit tests
uses: gradle/gradle-build-action@v2.3.2
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
with:
arguments: check -PslimTests
gradle-home-cache-cleanup: true
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
- name: Run Detekt
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
with:
arguments: detektMain
gradle-home-cache-cleanup: true
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
- name: (Fail-only) Upload test report
if: failure()
uses: actions/upload-artifact@v3.1.0
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
with:
name: Test report
path: android/build/reports
@@ -42,10 +50,10 @@ jobs:
needs: [ "check" ]
steps:
- name: Checkout repository
uses: actions/checkout@v3.0.2
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
- name: Set up JDK
uses: actions/setup-java@v3.5.1
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
with:
distribution: temurin
java-version: 18
@@ -56,10 +64,11 @@ jobs:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
- name: Build release app
uses: gradle/gradle-build-action@v2.3.2
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
with:
arguments: collectReleaseApks
gradle-home-cache-cleanup: true
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
- name: Clean secrets
run: scripts/signing-cleanup.sh
+78
View File
@@ -0,0 +1,78 @@
name: Release to Google Play
on:
workflow_dispatch:
jobs:
publish-google-play-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
with:
depth: 0
- name: Set up JDK
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
with:
distribution: temurin
java-version: 18
- name: Set up Git author
shell: bash
run: |
git config user.name "GitHub Actions"
git config user.email noreply@github.com
- name: Setup Gradle caching
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
with:
gradle-home-cache-cleanup: true
cache-read-only: true
- name: Decrypt secrets
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
env:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
- name: Prepare release assets
shell: bash
run: |
# Remove the `-SNAPSHOT` suffix
./gradlew -q clearPreRelease
git commit -am 'feat(release): bump version'
# Build the release assets
./gradlew collectReleaseApks collectReleaseBundle
- name: Clean secrets
run: scripts/signing-cleanup.sh
- name: Publish bundle to Google Play
uses: r0adkll/upload-google-play@78c9e796b1035c94169c101d8e46cb880194bfc3 # tag=v1.0.19
with:
mappingFile: android/bundle/mapping.txt
packageName: dev.msfjarvis.claw.android
releaseFiles: android/bundle/*.aab
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
status: completed
track: production
whatsNewDirectory: distribution/whatsnew
- name: Post-release work
shell: bash
run: |
# Tag the current version
VERSION="$(tail -n1 android/version.properties | cut -d = -f 2)"
git tag "v${VERSION}" -F distribution/whatsnew/whatsnew-en-GB
# Push everything to GitHub
git push origin --all
# Create a GitHub release
gh release create "v${VERSION}" -F distribution/whatsnew/whatsnew-en-GB --title "v${VERSION}" ./android/apk/*.apk ./android/bundle/*.aab
# Start the next development iteration
./gradlew -q bumpSnapshot
truncate -s 0 distribution/whatsnew/whatsnew-en-GB # Clear changelog
git commit -am 'feat(release): start next development iteration'
git push origin main
+23
View File
@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+1 -27
View File
@@ -16,30 +16,4 @@ Snapshots from the development branch can be obtained [here](https://github.com/
## License
```
Copyright (c) 2021 Harsh Shandilya
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
```
See [LICENSE](LICENSE)
+2
View File
@@ -0,0 +1,2 @@
/apk/
/bundle/
+7
View File
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
import dagger.hilt.android.plugin.HiltExtension
@@ -39,6 +45,7 @@ android {
}
dependencies {
implementation(platform(libs.androidx.compose.bom))
annotationProcessor(libs.androidx.hilt.compiler)
annotationProcessor(libs.dagger.hilt.compiler)
implementation(projects.api)
+1 -1
View File
@@ -16,7 +16,7 @@
}
-keepclassmembers class * implements android.os.Parcelable {
public static final ** CREATOR;
public static final ** CREATOR;
}
-keep class androidx.annotation.Keep
+1
View File
@@ -12,6 +12,7 @@
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.Claw.SplashScreen"
android:enableOnBackInvokedCallback="true"
tools:targetApi="s">
<profileable
android:shell="true"
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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
import android.app.Application
@@ -34,15 +40,20 @@ class ClawApplication : Application(), Configuration.Provider, ImageLoaderFactor
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
.memoryCache { MemoryCache.Builder(this).maxSizePercent(0.25).build() }
.memoryCache { MemoryCache.Builder(this).maxSizePercent(MEMORY_CACHE_RATIO).build() }
.diskCache {
DiskCache.Builder()
.directory(cacheDir.resolve("image_cache"))
.maxSizeBytes(25L * 1024 * 1024) // 25 MB
.maxSizeBytes(DISK_CACHE_MAX_SIZE)
.build()
}
// Show a short crossfade when loading images asynchronously.
.crossfade(true)
.build()
}
private companion object {
private const val MEMORY_CACHE_RATIO = 0.25
private const val DISK_CACHE_MAX_SIZE = 25L * 1024 * 1024 // 25 MB
}
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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
import android.app.assist.AssistContent
@@ -30,10 +36,14 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
installSplashScreen()
setContent {
LobstersApp(urlLauncher = urlLauncher, htmlConverter = htmlConverter) { url -> webUri = url }
LobstersApp(
urlLauncher = urlLauncher,
htmlConverter = htmlConverter,
setWebUri = { url -> webUri = url },
)
}
val postUpdateWorkRequest =
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(24, TimeUnit.HOURS)
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiresCharging(false)
@@ -61,4 +71,8 @@ class MainActivity : ComponentActivity() {
}
}
}
private companion object {
private const val POST_REFRESH_PERIOD = 24L // 24 hours
}
}
@@ -1,6 +1,14 @@
/*
* Copyright © 2021-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 com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.slack.eithernet.ApiResultCallAdapterFactory
import com.slack.eithernet.ApiResultConverterFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -26,7 +34,9 @@ object ApiModule {
return Retrofit.Builder()
.client(client)
.baseUrl(LobstersApi.BASE_URL)
.addConverterFactory(ApiResultConverterFactory)
.addConverterFactory(json.asConverterFactory(contentType))
.addCallAdapterFactory(ApiResultCallAdapterFactory)
.build()
}
@@ -1,3 +1,9 @@
/*
* 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 dagger.Binds
@@ -7,14 +13,13 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.msfjarvis.claw.util.coroutines.DefaultDispatcherProvider
import dev.msfjarvis.claw.util.coroutines.DispatcherProvider
import javax.inject.Qualifier
import kotlinx.coroutines.CoroutineDispatcher
@Module
@InstallIn(SingletonComponent::class)
abstract class CoroutineDispatcherModule {
interface CoroutineDispatcherModule {
@Binds abstract fun DefaultDispatcherProvider.bind(): DispatcherProvider
@Binds fun DefaultDispatcherProvider.bind(): DispatcherProvider
companion object {
@[Provides IODispatcher]
@@ -33,9 +38,3 @@ abstract class CoroutineDispatcherModule {
}
}
}
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class MainDispatcher
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class IODispatcher
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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 android.content.Context
@@ -1,3 +1,9 @@
/*
* Copyright © 2021 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 dagger.Module
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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 com.chimbori.crux.Crux
@@ -1,3 +1,9 @@
/*
* 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 android.content.Context
@@ -21,24 +27,26 @@ import okhttp3.logging.HttpLoggingInterceptor
@Module
@InstallIn(SingletonComponent::class)
abstract class OkHttpModule {
interface OkHttpModule {
@Binds abstract fun NapierLogger.bindLogger(): HttpLoggingInterceptor.Logger
@Binds fun NapierLogger.bindLogger(): HttpLoggingInterceptor.Logger
@Binds @IntoSet abstract fun UserAgentInterceptor.bindUAInterceptor(): Interceptor
@Binds @IntoSet fun UserAgentInterceptor.bindUAInterceptor(): Interceptor
companion object {
private const val CACHE_SIZE_MB = 10L * 1024 * 1024
private const val THREAD_STATS_TAG = 0x000090000
@Provides
fun provideCache(@ApplicationContext context: Context): Cache {
return Cache(context.cacheDir, 10 * 1024 * 1024)
return Cache(context.cacheDir, CACHE_SIZE_MB)
}
@Provides
fun provideSocketFactory(): SocketFactory {
return object : DelegatingSocketFactory(getDefault()) {
override fun configureSocket(socket: Socket): Socket {
TrafficStats.setThreadStatsTag(0x000090000)
TrafficStats.setThreadStatsTag(THREAD_STATS_TAG)
return super.configureSocket(socket)
}
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021 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
@@ -0,0 +1,15 @@
/*
* 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,3 +1,9 @@
/*
* 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.network
import java.net.InetAddress
@@ -1,3 +1,9 @@
/*
* 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.network
import io.github.aakira.napier.Napier
@@ -1,3 +1,9 @@
/*
* 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.network
import dev.msfjarvis.claw.android.BuildConfig
@@ -1,12 +1,21 @@
/*
* Copyright © 2021-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.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.slack.eithernet.ApiResult.Failure
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.model.LobstersPost
import java.io.IOException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -18,17 +27,18 @@ constructor(
) : PagingSource<Int, LobstersPost>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LobstersPost> {
return try {
val page = params.key ?: 1
val posts = withContext(ioDispatcher) { remoteFetcher.getItemsAtPage(page) }
LoadResult.Page(
data = posts,
prevKey = if (page == 1) null else page - 1,
nextKey = page.plus(1)
)
} catch (e: Exception) {
LoadResult.Error(e)
val page = params.key ?: 1
return when (val result = withContext(ioDispatcher) { remoteFetcher.getItemsAtPage(page) }) {
is Success ->
LoadResult.Page(
data = result.value,
prevKey = if (page == 1) null else page - 1,
nextKey = page.plus(1)
)
is Failure.NetworkFailure -> LoadResult.Error(result.error)
is Failure.UnknownFailure -> LoadResult.Error(result.error)
is Failure.HttpFailure,
is Failure.ApiFailure -> LoadResult.Error(IOException("API returned an invalid response"))
}
}
@@ -1,6 +1,14 @@
/*
* 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.paging
import com.slack.eithernet.ApiResult
/** SAM interface to abstract over a remote API that fetches paginated content. */
fun interface RemoteFetcher<T> {
suspend fun getItemsAtPage(page: Int): List<T>
suspend fun getItemsAtPage(page: Int): ApiResult<List<T>, Unit>
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.ui
import androidx.compose.animation.ExperimentalAnimationApi
@@ -38,7 +44,6 @@ 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.comments.LocalHTMLConverter
import dev.msfjarvis.claw.common.res.ClawIcons
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.decorations.ClawAppBar
@@ -50,13 +55,14 @@ import soup.compose.material.motion.navigation.MaterialMotionNavHost
import soup.compose.material.motion.navigation.composable
import soup.compose.material.motion.navigation.rememberMaterialMotionNavController
@Suppress("ModifierMissing") // Top-level composable, will never have a modifier supplied.
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
fun LobstersApp(
viewModel: ClawViewModel = viewModel(),
urlLauncher: UrlLauncher,
htmlConverter: HTMLConverter,
setWebUri: (String?) -> Unit,
viewModel: ClawViewModel = viewModel(),
) {
val systemUiController = rememberSystemUiController()
val hottestListState = rememberLazyListState()
@@ -75,11 +81,7 @@ fun LobstersApp(
LobstersTheme(
dynamicColor = true,
providedValues =
arrayOf(
LocalUriHandler provides urlLauncher,
LocalHTMLConverter provides htmlConverter,
),
providedValues = arrayOf(LocalUriHandler provides urlLauncher),
) {
val currentUiMode = LocalConfiguration.current.uiMode
val systemBarsColor = MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation()
@@ -88,7 +90,7 @@ fun LobstersApp(
listOf(
NavigationItem(
label = "Hottest",
route = Destinations.Hottest.getRoute(),
route = Destinations.Hottest.route,
icon = ClawIcons.Flame,
selectedIcon = ClawIcons.FlameFilled,
) {
@@ -96,7 +98,7 @@ fun LobstersApp(
},
NavigationItem(
label = "Newest",
route = Destinations.Newest.getRoute(),
route = Destinations.Newest.route,
icon = ClawIcons.New,
selectedIcon = ClawIcons.NewFilled,
) {
@@ -104,7 +106,7 @@ fun LobstersApp(
},
NavigationItem(
label = "Saved",
route = Destinations.Saved.getRoute(),
route = Destinations.Saved.route,
icon = ClawIcons.HeartBorder,
selectedIcon = ClawIcons.Heart,
) {
@@ -158,12 +160,12 @@ fun LobstersApp(
) { paddingValues ->
MaterialMotionNavHost(
navController = navController,
startDestination = Destinations.startDestination.getRoute(),
startDestination = Destinations.startDestination.route,
modifier = Modifier.padding(paddingValues),
) {
val uri = LobstersApi.BASE_URL
composable(
route = Destinations.Hottest.getRoute(),
route = Destinations.Hottest.route,
deepLinks =
listOf(navDeepLink { uriPattern = uri }, navDeepLink { uriPattern = "$uri/" }),
) {
@@ -177,7 +179,7 @@ fun LobstersApp(
)
}
composable(
route = Destinations.Newest.getRoute(),
route = Destinations.Newest.route,
) {
setWebUri("https://lobste.rs/")
NetworkPosts(
@@ -188,7 +190,7 @@ fun LobstersApp(
postActions = postActions,
)
}
composable(Destinations.Saved.getRoute()) {
composable(Destinations.Saved.route) {
setWebUri(null)
DatabasePosts(
items = savedPosts,
@@ -197,26 +199,27 @@ fun LobstersApp(
)
}
composable(
route = Destinations.Comments.getRoute("{postId}"),
route = Destinations.Comments.route,
arguments = listOf(navArgument("postId") { type = NavType.StringType }),
deepLinks =
listOf(
navDeepLink { uriPattern = "$uri/s/{postId}/.*" },
navDeepLink { uriPattern = "$uri/s/{postId}" },
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,
getDetails = viewModel::getPostComments,
postActions = postActions,
htmlConverter = htmlConverter,
)
}
composable(
route = Destinations.User.getRoute("{username}"),
route = Destinations.User.route,
arguments = listOf(navArgument("username") { type = NavType.StringType }),
deepLinks = listOf(navDeepLink { uriPattern = "$uri/u/{username}" }),
deepLinks =
listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.placeholder}" }),
) { backStackEntry ->
val username = requireNotNull(backStackEntry.arguments?.getString("username"))
UserProfile(
@@ -1,3 +1,9 @@
/*
* 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.ui.decorations
import androidx.compose.animation.AnimatedVisibility
@@ -61,8 +67,10 @@ fun ClawNavigationBar(
navItem.listStateResetCallback()
return@NavigationBarItem
}
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
if (navItem.route != Destinations.startDestination.getRoute()) {
navController.graph.startDestinationRoute?.let { startDestination ->
navController.popBackStack(startDestination, false)
}
if (navItem.route != Destinations.startDestination.route) {
navController.navigate(navItem.route)
}
}
@@ -1,3 +1,9 @@
/*
* 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.ui
import android.content.Context
@@ -11,6 +17,8 @@ import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
import dev.msfjarvis.claw.common.posts.PostActions
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
@@ -43,7 +51,9 @@ fun rememberPostActions(
}
override fun viewComments(postId: String) {
navController.navigate(Destinations.Comments.getRoute(postId))
navController.navigate(
Destinations.Comments.route.replace(Destinations.Comments.placeholder, postId)
)
}
override fun viewCommentsPage(commentsUrl: String) {
@@ -59,6 +69,14 @@ fun rememberPostActions(
override fun toggleSave(post: SavedPost) {
viewModel.toggleSave(post)
}
override suspend fun getComments(postId: String): LobstersPostDetails {
return viewModel.getPostComments(postId)
}
override suspend fun getLinkMetadata(url: String): LinkMetadata {
return viewModel.getLinkMetadata(url)
}
}
}
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.ui.lists
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.ui.lists
import androidx.compose.runtime.Composable
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.ui.lists
import androidx.compose.foundation.layout.Box
@@ -1,24 +1,34 @@
/*
* Copyright © 2021-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.ui.navigation
sealed class Destinations {
abstract val route: String
object Hottest : Destinations() {
fun getRoute() = "hottest"
override val route = "hottest"
}
object Newest : Destinations() {
fun getRoute() = "newest"
override val route = "newest"
}
object Saved : Destinations() {
fun getRoute() = "saved"
override val route = "saved"
}
object Comments : Destinations() {
fun getRoute(postId: String) = "comments/$postId"
const val placeholder = "{postId}"
override val route = "comments/$placeholder"
}
object User : Destinations() {
fun getRoute(username: String) = "user/$username"
const val placeholder = "{username}"
override val route = "user/$placeholder"
}
companion object {
@@ -1,15 +1,24 @@
/*
* Copyright © 2021-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.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import com.slack.eithernet.ApiResult.Failure
import com.slack.eithernet.ApiResult.Success
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.msfjarvis.claw.android.injection.IODispatcher
import dev.msfjarvis.claw.android.paging.LobstersPagingSource
import dev.msfjarvis.claw.android.ui.toLocalDateTime
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.database.local.SavedPost
import java.io.IOException
import java.time.Month
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -24,7 +33,7 @@ class ClawViewModel
constructor(
private val api: LobstersApi,
private val savedPostsRepository: SavedPostsRepository,
private val postDetailsRepository: PostDetailsRepository,
private val linkMetadataRepository: LinkMetadataRepository,
private val pagingSourceFactory: LobstersPagingSource.Factory,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
@@ -73,12 +82,28 @@ constructor(
suspend fun getPostComments(postId: String) =
withContext(ioDispatcher) {
val details = api.getPostDetails(postId)
val extendedDetails = postDetailsRepository.getExtendedDetails(details)
extendedDetails
when (val result = api.getPostDetails(postId)) {
is Success -> result.value
is Failure.NetworkFailure -> throw result.error
is Failure.UnknownFailure -> throw result.error
is Failure.HttpFailure,
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
}
}
suspend fun getUserProfile(username: String) = withContext(ioDispatcher) { api.getUser(username) }
suspend fun getLinkMetadata(url: String) =
withContext(ioDispatcher) { linkMetadataRepository.getLinkMetadata(url) }
suspend fun getUserProfile(username: String) =
withContext(ioDispatcher) {
when (val result = api.getUser(username)) {
is Success -> result.value
is Failure.NetworkFailure -> throw result.error
is Failure.UnknownFailure -> throw result.error
is Failure.HttpFailure,
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
}
}
fun refreshHottestPosts() {
hottestPostsPagingSource?.invalidate()
@@ -0,0 +1,21 @@
/*
* 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.viewmodel
import dev.msfjarvis.claw.metadata.MetadataExtractor
import dev.msfjarvis.claw.model.LinkMetadata
import javax.inject.Inject
class LinkMetadataRepository
@Inject
constructor(
private val metadataExtractor: MetadataExtractor,
) {
suspend fun getLinkMetadata(url: String): LinkMetadata {
return metadataExtractor.getExtractedMetadata(url)
}
}
@@ -1,25 +0,0 @@
package dev.msfjarvis.claw.android.viewmodel
import dev.msfjarvis.claw.metadata.MetadataExtractor
import dev.msfjarvis.claw.model.ExtendedPostDetails
import dev.msfjarvis.claw.model.LobstersPostDetails
import javax.inject.Inject
class PostDetailsRepository
@Inject
constructor(
private val metadataExtractor: MetadataExtractor,
) {
suspend fun getExtendedDetails(details: LobstersPostDetails): ExtendedPostDetails {
val metadata = metadataExtractor.getExtractedMetadata(details.url)
return ExtendedPostDetails(
title = details.title,
linkMetadata = metadata,
description = details.description,
submitter = details.submitter,
tags = details.tags,
comments = details.comments,
commentsUrl = details.commentsUrl,
)
}
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.viewmodel
import app.cash.sqldelight.coroutines.asFlow
@@ -1,16 +1,22 @@
/*
* 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.work
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.slack.eithernet.ApiResult
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.common.posts.toDbModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@@ -23,7 +29,6 @@ import kotlinx.coroutines.flow.first
* and for new-enough posts that are still getting comments to have an accurate one.
*/
@Suppress("DEPRECATION") // We're being nasty
@OptIn(ExperimentalCoroutinesApi::class)
@HiltWorker
class SavedPostUpdaterWorker
@AssistedInject
@@ -39,8 +44,8 @@ constructor(
.map { post ->
CoroutineScope(coroutineContext + Job()).async {
val details = runCatching { lobstersApi.getPostDetails(post.shortId) }.getOrNull()
if (details != null) {
savedPostsRepository.savePost(details.toDbModel())
if (details is ApiResult.Success) {
savedPostsRepository.savePost(details.value.toDbModel())
}
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
#
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
#
versioning-plugin.versionCode=10100
versioning-plugin.versionName=1.1.0
versioning-plugin.versionCode=10500
versioning-plugin.versionName=1.5.0
+10 -4
View File
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
@@ -7,11 +13,11 @@ plugins {
dependencies {
api(projects.model)
api(libs.retrofit.lib)
implementation(libs.kotlinx.serialization.core)
testImplementation(libs.kotlinx.coroutines.core)
api(libs.retrofit)
api(libs.eithernet)
testImplementation(testFixtures(libs.eithernet))
testImplementation(kotlin("test-junit"))
testImplementation(libs.kotlinx.coroutines.core)
testImplementation(libs.kotlinx.serialization.json)
testImplementation(libs.retrofit.kotlinxSerializationConverter)
testImplementation(libs.retrofit.mock)
}
@@ -1,5 +1,12 @@
/*
* Copyright © 2021-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.api
import com.slack.eithernet.ApiResult
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.LobstersPostDetails
import dev.msfjarvis.claw.model.User
@@ -10,14 +17,17 @@ import retrofit2.http.Query
/** Simple interface defining an API for lobste.rs */
interface LobstersApi {
@GET("hottest.json") suspend fun getHottestPosts(@Query("page") page: Int): List<LobstersPost>
@GET("hottest.json")
suspend fun getHottestPosts(@Query("page") page: Int): ApiResult<List<LobstersPost>, Unit>
@GET("newest.json") suspend fun getNewestPosts(@Query("page") page: Int): List<LobstersPost>
@GET("newest.json")
suspend fun getNewestPosts(@Query("page") page: Int): ApiResult<List<LobstersPost>, Unit>
@GET("s/{postId}.json")
suspend fun getPostDetails(@Path("postId") postId: String): LobstersPostDetails
suspend fun getPostDetails(@Path("postId") postId: String): ApiResult<LobstersPostDetails, Unit>
@GET("u/{username}.json") suspend fun getUser(@Path("username") username: String): User
@GET("u/{username}.json")
suspend fun getUser(@Path("username") username: String): ApiResult<User, Unit>
companion object {
const val BASE_URL = "https://lobste.rs"
@@ -1,52 +1,52 @@
/*
* 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.api
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.User
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.runBlocking
import retrofit2.Retrofit
import retrofit2.mock.MockRetrofit
import retrofit2.mock.NetworkBehavior
import retrofit2.mock.create
class ApiTest {
private val retrofit = Retrofit.Builder().baseUrl(LobstersApi.BASE_URL).build()
private val networkBehaviour = createNetworkBehaviour()
private val api =
MockRetrofit.Builder(retrofit)
.networkBehavior(networkBehaviour)
.build()
.create<LobstersApi>()
.let(::FakeApi)
private fun createNetworkBehaviour(): NetworkBehavior {
val behaviour = NetworkBehavior.create()
behaviour.setFailurePercent(0)
behaviour.setErrorPercent(0)
return behaviour
}
private val wrapper = ApiWrapper(newEitherNetController())
private val api
get() = wrapper.api
@Test
fun `api gets correct number of items`() = runBlocking {
val posts = api.getHottestPosts(1)
assertEquals(25, posts.size)
assertIs<Success<List<LobstersPost>>>(posts)
assertEquals(25, posts.value.size)
}
@Test
fun `posts with no urls`() = runBlocking {
val posts = api.getHottestPosts(1)
val commentsOnlyPosts = posts.asSequence().filter { it.url.isEmpty() }.toSet()
assertIs<Success<List<LobstersPost>>>(posts)
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
assertEquals(2, commentsOnlyPosts.size)
}
@Test
fun `post details with comments`() = runBlocking {
val postDetails = api.getPostDetails("tdfoqh")
assertEquals(7, postDetails.comments.size)
assertIs<Success<LobstersPostDetails>>(postDetails)
assertEquals(7, postDetails.value.comments.size)
}
@Test
fun `get user details`() = runBlocking {
val user = api.getUser("msfjarvis")
assertEquals("msfjarvis", user.username)
assertIs<Success<User>>(user)
assertEquals("msfjarvis", user.value.username)
}
}
@@ -0,0 +1,34 @@
/*
* 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.api
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.User
import dev.msfjarvis.claw.util.TestUtils.getJson
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
class ApiWrapper(controller: EitherNetController<LobstersApi>) {
private val json = Json { ignoreUnknownKeys = true }
private val hottest: List<LobstersPost> = json.decodeFromString(getJson("hottest.json"))
private val postDetails: LobstersPostDetails =
json.decodeFromString(getJson("post_details_tdfoqh.json"))
private val user: User = json.decodeFromString(getJson("msfjarvis.json"))
val api = controller.api
init {
controller.enqueue(LobstersApi::getHottestPosts) { success(hottest) }
controller.enqueue(LobstersApi::getNewestPosts) { success(hottest) }
controller.enqueue(LobstersApi::getPostDetails) { success(postDetails) }
controller.enqueue(LobstersApi::getUser) { success(user) }
}
}
@@ -1,33 +0,0 @@
package dev.msfjarvis.claw.api
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.LobstersPostDetails
import dev.msfjarvis.claw.model.User
import dev.msfjarvis.claw.util.TestUtils.getJson
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import retrofit2.mock.BehaviorDelegate
class FakeApi(private val delegate: BehaviorDelegate<LobstersApi>) : LobstersApi {
private val json = Json { ignoreUnknownKeys = true }
private val hottest: List<LobstersPost> = json.decodeFromString(getJson("hottest.json"))
private val postDetails: LobstersPostDetails =
json.decodeFromString(getJson("post_details_tdfoqh.json"))
private val user: User = json.decodeFromString(getJson("msfjarvis.json"))
override suspend fun getHottestPosts(page: Int): List<LobstersPost> {
return delegate.returningResponse(hottest).getHottestPosts(page)
}
override suspend fun getNewestPosts(page: Int): List<LobstersPost> {
TODO("Not yet implemented")
}
override suspend fun getPostDetails(postId: String): LobstersPostDetails {
return delegate.returningResponse(postDetails).getPostDetails(postId)
}
override suspend fun getUser(username: String): User {
return delegate.returningResponse(user).getUser(username)
}
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021 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 java.io.File
+6
View File
@@ -1,3 +1,9 @@
/*
* 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("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
@@ -1,3 +1,9 @@
/*
* 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.benchmark
import androidx.benchmark.macro.BaselineProfileMode
@@ -1,3 +1,9 @@
/*
* 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.benchmark
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
@@ -1,3 +1,9 @@
/*
* 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.benchmark
import androidx.benchmark.macro.MacrobenchmarkScope
+4 -3
View File
@@ -1,8 +1,9 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.
*/
import org.gradle.api.JavaVersion
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.withType
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle
import com.android.build.api.dsl.TestExtension
@@ -1,3 +1,9 @@
/*
* 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("UnstableApiUsage")
package dev.msfjarvis.aps.gradle
@@ -50,12 +56,4 @@ class ApplicationPlugin : Plugin<Project> {
project.configureBuildSigning()
}
}
private fun Project.isSnapshot(): Boolean {
with(project.providers) {
val workflow = environmentVariable("GITHUB_WORKFLOW")
val snapshot = environmentVariable("SNAPSHOT")
return workflow.isPresent || snapshot.isPresent
}
}
}
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle
import com.android.build.gradle.LibraryPlugin
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle
import com.android.build.api.artifact.SingleArtifact
@@ -5,6 +11,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.VariantOutputConfiguration
import dev.msfjarvis.aps.gradle.artifacts.CollectApksTask
import dev.msfjarvis.aps.gradle.artifacts.CollectBundleTask
import java.util.Locale
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
@@ -17,21 +24,24 @@ class RenameArtifactsPlugin : Plugin<Project> {
project.pluginManager.withPlugin("com.android.application") {
project.extensions.getByType<ApplicationAndroidComponentsExtension>().run {
onVariants { variant ->
project.tasks.register<CollectApksTask>("collect${variant.name.capitalize()}Apks") {
val taskPrefix = "collect${variant.name.capitalize(Locale.ROOT)}"
project.tasks.register<CollectApksTask>("${taskPrefix}Apks") {
variantName.set(variant.name)
apkFolder.set(variant.artifacts.get(SingleArtifact.APK))
mappingFile.set(variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE))
builtArtifactsLoader.set(variant.artifacts.getBuiltArtifactsLoader())
outputDirectory.set(project.layout.projectDirectory.dir("outputs"))
outputDirectory.set(project.layout.projectDirectory.dir("apk"))
}
project.tasks.register<CollectBundleTask>("collect${variant.name.capitalize()}Bundle") {
project.tasks.register<CollectBundleTask>("${taskPrefix}Bundle") {
val mainOutput =
variant.outputs.single {
it.outputType == VariantOutputConfiguration.OutputType.SINGLE
}
variantName.set(variant.name)
versionName.set(mainOutput.versionName)
mappingFile.set(variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE))
bundleFile.set(variant.artifacts.get(SingleArtifact.BUNDLE))
outputDirectory.set(project.layout.projectDirectory.dir("outputs"))
outputDirectory.set(project.layout.projectDirectory.dir("bundle"))
}
}
}
@@ -1,14 +1,24 @@
/*
* 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.aps.gradle.artifacts
import com.android.build.api.variant.BuiltArtifactsLoader
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
@@ -21,6 +31,10 @@ import org.gradle.api.tasks.TaskAction
abstract class CollectApksTask : DefaultTask() {
@get:InputFiles @get:PathSensitive(PathSensitivity.NONE) abstract val apkFolder: DirectoryProperty
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val mappingFile: RegularFileProperty
@get:Input abstract val variantName: Property<String>
@get:Internal abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader>
@@ -30,9 +44,13 @@ abstract class CollectApksTask : DefaultTask() {
@TaskAction
fun run() {
val outputDir = outputDirectory.asFile.get()
val outputDirStream =
Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile)
outputDirStream.forEach(File::delete)
outputDirStream.close()
outputDir.mkdirs()
val builtArtifacts =
builtArtifactsLoader.get().load(apkFolder.get()) ?: throw RuntimeException("Cannot load APKs")
builtArtifactsLoader.get().load(apkFolder.get()) ?: error("Cannot load APKs")
builtArtifacts.elements.forEach { artifact ->
Files.copy(
Paths.get(artifact.outputFile),
@@ -40,5 +58,10 @@ abstract class CollectApksTask : DefaultTask() {
StandardCopyOption.REPLACE_EXISTING,
)
}
Files.copy(
mappingFile.get().asFile.toPath(),
outputDir.resolve("mapping.txt").toPath(),
StandardCopyOption.REPLACE_EXISTING,
)
}
}
@@ -1,6 +1,14 @@
/*
* 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.aps.gradle.artifacts
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
@@ -14,6 +22,8 @@ import org.gradle.api.tasks.TaskAction
abstract class CollectBundleTask : DefaultTask() {
@get:InputFile abstract val bundleFile: RegularFileProperty
@get:InputFile abstract val mappingFile: RegularFileProperty
@get:Input abstract val variantName: Property<String>
@get:Input abstract val versionName: Property<String>
@@ -23,11 +33,20 @@ abstract class CollectBundleTask : DefaultTask() {
@TaskAction
fun taskAction() {
val outputDir = outputDirectory.asFile.get()
val outputDirStream =
Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile)
outputDirStream.forEach(File::delete)
outputDirStream.close()
outputDir.mkdirs()
Files.copy(
bundleFile.get().asFile.toPath(),
outputDir.resolve("Claw-${variantName.get()}-${versionName.get()}.aab").toPath(),
StandardCopyOption.REPLACE_EXISTING,
)
Files.copy(
mappingFile.get().asFile.toPath(),
outputDir.resolve("mapping.txt").toPath(),
StandardCopyOption.REPLACE_EXISTING,
)
}
}
@@ -1,8 +1,9 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.aps.gradle.signing
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle.versioning
const val VERSIONING_PROP_FILE = "version.properties"
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle.versioning
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle.versioning
import com.vdurmont.semver4j.Semver
+5 -3
View File
@@ -1,8 +1,9 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.
*/
import org.gradle.api.JavaVersion
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.withType
@@ -51,6 +52,7 @@ gradlePlugin {
dependencies {
implementation(libs.build.agp)
implementation(libs.build.detekt)
implementation(libs.build.kotlin.gradle)
implementation(libs.build.kotlin.serialization)
implementation(libs.build.spotless)
@@ -1,3 +1,9 @@
/*
* 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.aps.gradle
import com.github.benmanes.gradle.versions.VersionsPlugin
@@ -0,0 +1,35 @@
/*
* 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.aps.gradle
import io.gitlab.arturbosch.detekt.DetektPlugin
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
object Detekt {
private const val TWITTER_RULES_VERSION = "0.0.23"
fun apply(project: Project) {
project.pluginManager.apply(DetektPlugin::class.java)
project.extensions.configure<DetektExtension> {
debug = project.providers.gradleProperty("debugDetekt").isPresent
parallel = true
ignoredBuildTypes = listOf("benchmark", "release")
basePath = project.layout.projectDirectory.toString()
baseline =
project.rootProject.layout.projectDirectory
.dir("detekt-baselines")
.file("${project.name}.xml")
.asFile
}
project.dependencies.add(
"detektPlugins",
"com.twitter.compose.rules:detekt:$TWITTER_RULES_VERSION",
)
}
}
@@ -1,8 +1,9 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.aps.gradle
import org.gradle.api.Plugin
@@ -1,8 +1,9 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.aps.gradle
import org.gradle.api.JavaVersion
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
class KotlinCommonPlugin : Plugin<Project> {
override fun apply(project: Project) {
Detekt.apply(project)
project.tasks.run {
withType<JavaCompile>().configureEach {
sourceCompatibility = JavaVersion.VERSION_11.toString()
@@ -25,16 +27,15 @@ class KotlinCommonPlugin : Plugin<Project> {
}
withType<KotlinCompile>().configureEach {
kotlinOptions {
allWarningsAsErrors = false
allWarningsAsErrors = project.providers.environmentVariable("GITHUB_WORKFLOW").isPresent
jvmTarget = JavaVersion.VERSION_11.toString()
freeCompilerArgs = freeCompilerArgs + ADDITIONAL_COMPILER_ARGS
languageVersion = "1.5"
languageVersion = "1.7"
}
}
withType<Test>().configureEach {
maxParallelForks = Runtime.getRuntime().availableProcessors() * 2
testLogging { events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) }
doNotTrackState("We want tests to always run even if Gradle thinks otherwise")
}
}
}
@@ -43,8 +44,6 @@ class KotlinCommonPlugin : Plugin<Project> {
private val ADDITIONAL_COMPILER_ARGS =
listOf(
"-opt-in=kotlin.RequiresOptIn",
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=1.7.20",
)
}
}
@@ -1,8 +1,9 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.aps.gradle
import org.gradle.api.Plugin
@@ -1,12 +1,14 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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.aps.gradle
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.gradle.spotless.SpotlessPlugin
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
@@ -16,17 +18,22 @@ import org.gradle.kotlin.dsl.getByType
class SpotlessPlugin : Plugin<Project> {
override fun apply(project: Project) {
if (project.rootProject != project) {
throw GradleException("Spotless plugin must only be applied to the root project.")
}
project.pluginManager.apply(SpotlessPlugin::class)
project.extensions.getByType<SpotlessExtension>().run {
kotlin {
ktfmt(KTFMT_VERSION).googleStyle()
target("**/*.kt")
targetExclude("**/build/", "**/gen/")
targetExclude("**/build/", "/spotless/")
licenseHeaderFile(project.file("spotless/license.kt"))
}
kotlinGradle {
ktfmt(KTFMT_VERSION).googleStyle()
target("**/*.kts")
targetExclude("**/build/")
licenseHeaderFile(project.file("spotless/license.kt"), "import|plugins|@file")
}
format("xml") {
target("**/*.xml")
+4 -2
View File
@@ -1,6 +1,8 @@
/*
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
* 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("UnstableApiUsage")
+6
View File
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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("DSL_SCOPE_VIOLATION")
plugins {
+7
View File
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
@@ -9,6 +15,7 @@ plugins {
androidComponents { beforeVariants { it.enableUnitTest = false } }
dependencies {
implementation(platform(libs.androidx.compose.bom))
api(libs.napier)
implementation(projects.database)
implementation(projects.model)
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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
sealed class NetworkState {
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.comments
import android.text.format.DateUtils
@@ -23,6 +29,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@@ -40,20 +47,28 @@ 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.Comment
import dev.msfjarvis.claw.model.ExtendedPostDetails
import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.LobstersPostDetails
import java.time.Instant
import java.time.temporal.TemporalAccessor
@Composable
fun CommentsHeader(
postDetails: ExtendedPostDetails,
postDetails: LobstersPostDetails,
postActions: PostActions,
htmlConverter: HTMLConverter,
modifier: Modifier = Modifier,
) {
val htmlConverter = LocalHTMLConverter.current
val uriHandler = LocalUriHandler.current
val linkMetadata by
produceState(
initialValue = LinkMetadata(postDetails.url, null, null),
) {
runCatching { postActions.getLinkMetadata(postDetails.url) }
.onSuccess { metadata -> value = metadata }
}
Surface(color = MaterialTheme.colorScheme.background) {
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
Column(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp),
@@ -62,13 +77,11 @@ fun CommentsHeader(
TagRow(tags = postDetails.tags)
Spacer(Modifier.height(4.dp))
if (postDetails.linkMetadata.url.isNotBlank()) {
if (linkMetadata.url.isNotBlank()) {
PostLink(
linkMetadata = postDetails.linkMetadata,
linkMetadata = linkMetadata,
modifier =
Modifier.clickable {
postActions.viewPost(postDetails.linkMetadata.url, postDetails.commentsUrl)
},
Modifier.clickable { postActions.viewPost(linkMetadata.url, postDetails.commentsUrl) },
)
Spacer(Modifier.height(4.dp))
}
@@ -128,20 +141,29 @@ fun PostLink(
}
}
private val CommentEntryPadding = 16f.dp
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CommentEntry(
comment: Comment,
htmlConverter: HTMLConverter,
modifier: Modifier = Modifier,
) {
var expanded by remember(comment) { mutableStateOf(true) }
val htmlConverter = LocalHTMLConverter.current
val uriHandler = LocalUriHandler.current
Box(
modifier =
Modifier.fillMaxWidth()
modifier
.fillMaxWidth()
.clickable { expanded = !expanded }
.background(MaterialTheme.colorScheme.background)
.padding(start = (comment.indentLevel * 16).dp, end = 16.dp, top = 16.dp, bottom = 16.dp),
.padding(
start = CommentEntryPadding * comment.indentLevel,
end = CommentEntryPadding,
top = CommentEntryPadding,
bottom = CommentEntryPadding,
),
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Submitter(
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.comments
import androidx.compose.foundation.layout.PaddingValues
@@ -24,17 +30,24 @@ import dev.msfjarvis.claw.common.NetworkState.Success
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.ui.NetworkError
import dev.msfjarvis.claw.common.ui.ProgressBar
import dev.msfjarvis.claw.model.ExtendedPostDetails
import dev.msfjarvis.claw.model.LobstersPostDetails
@Composable
private fun CommentsPageInternal(
details: ExtendedPostDetails,
details: LobstersPostDetails,
postActions: PostActions,
htmlConverter: HTMLConverter,
modifier: Modifier = Modifier,
) {
Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
item { CommentsHeader(postDetails = details, postActions = postActions) }
item {
CommentsHeader(
postDetails = details,
postActions = postActions,
htmlConverter = htmlConverter,
)
}
if (details.comments.isNotEmpty()) {
item {
@@ -49,7 +62,7 @@ private fun CommentsPageInternal(
if (index != 0) {
Divider()
}
CommentEntry(item)
CommentEntry(comment = item, htmlConverter = htmlConverter)
}
} else {
item {
@@ -70,13 +83,13 @@ private fun CommentsPageInternal(
@Composable
fun CommentsPage(
postId: String,
getDetails: suspend (String) -> ExtendedPostDetails,
postActions: PostActions,
htmlConverter: HTMLConverter,
modifier: Modifier = Modifier,
) {
val postDetails by
produceState<NetworkState>(Loading) {
runCatching { getDetails(postId) }
runCatching { postActions.getComments(postId) }
.fold(
onSuccess = { details -> value = Success(details) },
onFailure = { value = Error(error = it, description = "Failed to load comments") }
@@ -86,9 +99,10 @@ fun CommentsPage(
when (postDetails) {
is Success<*> -> {
CommentsPageInternal(
(postDetails as Success<ExtendedPostDetails>).data,
postActions,
modifier.fillMaxSize(),
details = (postDetails as Success<LobstersPostDetails>).data,
postActions = postActions,
htmlConverter = htmlConverter,
modifier = modifier.fillMaxSize(),
)
}
is Error -> {
@@ -1,10 +1,12 @@
/*
* Copyright © 2021-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.comments
import androidx.compose.runtime.staticCompositionLocalOf
/** Defines a contract to convert strings of HTML to Markdown. */
fun interface HTMLConverter {
fun convertHTMLToMarkdown(html: String): String
}
val LocalHTMLConverter = staticCompositionLocalOf<HTMLConverter> { error("To be provided") }
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.posts
import androidx.compose.animation.Crossfade
@@ -19,7 +25,6 @@ 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.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
@@ -146,10 +151,10 @@ fun Submitter(
url = avatarUrl,
placeholder = ClawIcons.Account,
contentDescription = contentDescription,
modifier = modifier.requiredSize(24.dp).clip(CircleShape),
modifier = Modifier.requiredSize(24.dp).clip(CircleShape),
)
Text(text = text, modifier = modifier, style = MaterialTheme.typography.bodyMedium)
Text(text = text, style = MaterialTheme.typography.bodyMedium)
}
}
@@ -181,7 +186,7 @@ fun CommentsButton(
badge = {
if (commentCount != null) {
Badge(
modifier = Modifier.absoluteOffset(x = (-8).dp),
modifier = Modifier.absoluteOffset(x = -(8.dp)),
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
) {
Text(
@@ -224,9 +229,9 @@ fun TagText(
Text(
text = tag,
modifier =
Modifier.background(MaterialTheme.colorScheme.tertiaryContainer, RoundedCornerShape(50))
.padding(vertical = 4.dp, horizontal = 12.dp)
.then(modifier),
modifier
.background(MaterialTheme.colorScheme.tertiaryContainer, CircleShape)
.padding(vertical = 4.dp, horizontal = 12.dp),
color = MaterialTheme.colorScheme.onTertiaryContainer,
style = MaterialTheme.typography.labelLarge,
)
@@ -1,10 +1,20 @@
/*
* Copyright © 2021-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.posts
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.LobstersPostDetails
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,3 +1,9 @@
/*
* Copyright © 2021-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.posts
import dev.msfjarvis.claw.database.local.SavedPost
@@ -1,3 +1,9 @@
/*
* 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
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,18 @@
/*
* 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.*
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.theme
import androidx.compose.ui.graphics.Color
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.theme
import android.os.Build
@@ -71,6 +77,7 @@ internal val DarkThemeColors =
inverseSurface = md_theme_dark_inverseSurface,
)
@Suppress("SpreadOperator")
@Composable
fun LobstersTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
@@ -80,7 +87,7 @@ fun LobstersTheme(
) {
val colorScheme =
when {
dynamicColor && Build.VERSION.SDK_INT >= 31 -> {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) {
dynamicDarkColorScheme(context)
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.theme
import androidx.compose.material3.Typography
@@ -1,3 +1,9 @@
/*
* 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.ui
import androidx.compose.material3.LocalContentColor
@@ -1,3 +1,9 @@
/*
* 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.ui
import androidx.compose.foundation.clickable
@@ -67,5 +73,5 @@ fun NetworkError(
@ThemePreviews
@Composable
fun NetworkErrorPreview() {
LobstersTheme { NetworkError(label = "Failed to load posts", error = Throwable()) }
LobstersTheme { NetworkError(label = "Failed to load posts", error = Throwable("Preview")) }
}
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.ui
import androidx.compose.runtime.Composable
@@ -1,3 +1,9 @@
/*
* 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.ui
import androidx.compose.foundation.layout.Box
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.ui.decorations
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -7,8 +13,10 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.preview.DevicePreviews
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
@@ -24,7 +32,7 @@ fun ClawAppBar(
) {
TopAppBar(
title = title,
modifier = modifier,
modifier = modifier.shadow(8.dp),
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = backgroundColor),
navigationIcon = navigationIcon,
)
@@ -1,3 +1,9 @@
/*
* 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.ui.decorations
import androidx.compose.foundation.background
@@ -19,9 +25,13 @@ import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation
import java.time.Month
@Composable
fun MonthHeader(month: Month) {
fun MonthHeader(
month: Month,
modifier: Modifier = Modifier,
) {
Box(
Modifier.fillMaxWidth()
modifier
.fillMaxWidth()
.wrapContentHeight()
.background(MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation())
) {
@@ -1,3 +1,9 @@
/*
* 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.ui
import androidx.compose.material3.ColorScheme
@@ -1,3 +1,9 @@
/*
* 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("NON_SOURCE_REPEATED_ANNOTATION") // IDE false-positive
package dev.msfjarvis.claw.common.ui.preview
@@ -1,3 +1,9 @@
/*
* Copyright © 2021-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.urllauncher
import android.content.ActivityNotFoundException
@@ -20,7 +26,7 @@ class UrlLauncher(private val context: Context) : UriHandler {
customTabsIntent.launchUrl(context, Uri.parse(uri))
} catch (e: ActivityNotFoundException) {
val error = "Failed to open URL: $uri"
Napier.d(tag = "UrlLauncher") { error }
Napier.d(throwable = e, tag = "UrlLauncher") { error }
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
}
}
@@ -1,3 +1,9 @@
/*
* 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.user
import androidx.compose.foundation.layout.Arrangement
@@ -50,7 +56,7 @@ fun UserProfile(
}
when (user) {
is Success<*> -> {
UserProfileInternal((user as Success<User>).data)
UserProfileInternal(user = (user as Success<User>).data, modifier = modifier)
}
is Error -> {
val error = user as Error
@@ -1,3 +0,0 @@
package dev.msfjarvis.claw.common
fun <T> unsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer)

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