Mark L. Murphy's Blog, page 6

November 10, 2024

How androidx.tech Worked

I have received inquiries as to how my former androidx.tech site worked. It offered a catalogof every Jetpack artifact version, including the source code. It was designed to be a workaroundfor the fact that Google did not offer this sort of stuff back in the day. Nowadays,maven.google.com kinda has this, insofar as it offers ���source���links (and you need to inspect for yourself which is the actual artifact source and which is sample code).

So, here is how androidx.tech worked.

Step 1: Find the POMs

Google���s Maven publications have long had links to source JARs for their artifact versionsin their POM files. The POM files also had information about dependencies that I wanted todisplay. So, I needed the POM files for all the artifacts and their versions. And, to saveme having to re-download them, I wanted to store each of those POMs locally.

The simplest solution, given this need plus the need for the source code, would be to mirrormaven.google.com. I could not figure out how to do this five years ago, and I may also havebeen concerned about how much disk space would be needed by all of this.

I created a Kotlin project, reporeader, that would traverse the artifacts in a Mavenrepository and pull their POMs. This involved:

Downloading https://dl.google.com/dl/android/mave... and parsing the XMLto get the list of artifact groups

Filtering that list to only worry about androidx. groups, as Google���s Maven repository hasa lot of other stuff as well, such as Play Services

Generating the URL for the artifact group index, which involves replacing all the .characters in the artifact group with / and using that to get a group index XML URL,such as https://dl.google.com/dl/android/mave... forandroidx.activity

Downloading and parsing that XML to get the artifacts and versions that are in thatgroup

Generating the URL for the POM, which replaces group-index.xml in the group index URLwith /$artifact/$version/$artifact-$version.pom for a given artifact name and artifactversion, giving you something likehttps://dl.google.com/dl/android/mave...

Downloading that POM

This tool also knew to emit a roster of what was new: new artifact groups, new artifact IDswithin a group, and new artifact versions. I still use this tool today, to generate thelist of changes that go into posts like this one.

Step 2: Download the Sources

A POM file is an XML file with lots of interesting info:

xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-inst..." xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.... 4.0.0 androidx.activity activity-compose 1.10.0-alpha03 aar Activity Compose Compose integration with Activity https://developer.android.com/jetpack... 2020 The Android Open Source Project The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENS... repo The Android Open Source Project scm:git:https://android.googlesource.com/plat... https://cs.android.com/androidx/platf... androidx.activity activity 1.10.0-alpha03 androidx.activity activity-ktx 1.10.0-alpha03 androidx.compose.runtime runtime-saveable 1.7.0 compile androidx.activity activity-ktx [1.10.0-alpha03] compile aar androidx.compose.runtime runtime 1.7.0 compile androidx.compose.ui ui 1.0.1 compile androidx.core core-ktx 1.13.0 compile aar org.jetbrains.kotlinx kotlinx-coroutines-core 1.7.3 runtime androidx.lifecycle lifecycle-runtime 2.6.1 runtime androidx.savedstate savedstate 1.2.1 runtime androidx.lifecycle lifecycle-viewmodel 2.6.1 compile org.jetbrains.kotlin kotlin-stdlib 1.8.22 runtime androidx.lifecycle lifecycle-common 2.6.1 runtime

However, it does not contain a link to the sources JAR for the artifact. Fortunately, Googletended to use a consistent naming convention of $artifact-$version-sources.jar. So, if I did notalready have the sources for that artifact version, I generated the URL for it(e.g., https://dl.google.com/dl/android/mave... downloaded it. Since JAR files are ZIP files, I could then use ordinary unZIP code to convertthe JAR into a directory tree of source code.

Step 3: Generate the Site

androidx.tech was a static site, generated by Jekyll. Jekyll is Ruby code and is not designedfor a site of this size, with hundreds of thousands of pages. Still, given a big enough Amazon EC2 instance,I could plow through it.

I had a separate Kotlin project, gen2, that would generate the Markdown source for the site, giventhe POM files and source trees. My publish script would then set Jekyll loose to generate thesite, and from there it was a matter of rsync to push the changes up to the actual host.

 •  0 comments  •  flag
Share on Twitter
Published on November 10, 2024 07:05

November 1, 2024

New Jetpack Artifacts: 1 Nov 2024

While I discontinued the androidx.tech site,I am still keeping tabs on what new Jetpack artifacts are released. By request, I will keep publishingthose, just here on my main blog rather than on the other site. Normally, these notes will comeout on Wednesdays, as that seems to be when Google releases the major artifact waves.

This week, the only completely new artifact seems to be androidx.annotation:annotation-js.

Here is the full list of new artifact versions, including pre-release versions:

androidx.activity:activity:1.10.0-alpha03 androidx.activity:activity-compose:1.10.0-alpha03 androidx.activity:activity-ktx:1.10.0-alpha03 androidx.annotation:annotation:1.9.1 androidx.annotation:annotation-androidnativearm32:1.9.1 androidx.annotation:annotation-androidnativearm64:1.9.1 androidx.annotation:annotation-androidnativex64:1.9.1 androidx.annotation:annotation-androidnativex86:1.9.1 androidx.annotation:annotation-iosarm64:1.9.1 androidx.annotation:annotation-iossimulatorarm64:1.9.1 androidx.annotation:annotation-iosx64:1.9.1 androidx.annotation:annotation-js:1.9.1 androidx.annotation:annotation-jvm:1.9.1 androidx.annotation:annotation-linuxarm64:1.9.1 androidx.annotation:annotation-linuxx64:1.9.1 androidx.annotation:annotation-macosarm64:1.9.1 androidx.annotation:annotation-macosx64:1.9.1 androidx.annotation:annotation-mingwx64:1.9.1 androidx.annotation:annotation-tvosarm64:1.9.1 androidx.annotation:annotation-tvossimulatorarm64:1.9.1 androidx.annotation:annotation-tvosx64:1.9.1 androidx.annotation:annotation-wasm-js:1.9.1 androidx.annotation:annotation-watchosarm32:1.9.1 androidx.annotation:annotation-watchosarm64:1.9.1 androidx.annotation:annotation-watchosdevicearm64:1.9.1 androidx.annotation:annotation-watchossimulatorarm64:1.9.1 androidx.annotation:annotation-watchosx64:1.9.1 androidx.baselineprofile:androidx.baselineprofile.gradle.plugin:1.4.0-alpha04 androidx.baselineprofile.apptarget:androidx.baselineprofile.apptarget.gradle.plugin:1.4.0-alpha04 androidx.baselineprofile.consumer:androidx.baselineprofile.consumer.gradle.plugin:1.4.0-alpha04 androidx.baselineprofile.producer:androidx.baselineprofile.producer.gradle.plugin:1.4.0-alpha04 androidx.benchmark:androidx.benchmark.gradle.plugin:1.4.0-alpha04 androidx.benchmark:benchmark-baseline-profile-gradle-plugin:1.4.0-alpha04 androidx.benchmark:benchmark-common:1.4.0-alpha04 androidx.benchmark:benchmark-gradle-plugin:1.4.0-alpha04 androidx.benchmark:benchmark-junit4:1.4.0-alpha04 androidx.benchmark:benchmark-macro:1.4.0-alpha04 androidx.benchmark:benchmark-macro-junit4:1.4.0-alpha04 androidx.camera:camera-camera2:1.4.0 androidx.camera:camera-camera2:1.5.0-alpha03 androidx.camera:camera-compose:1.5.0-alpha03 androidx.camera:camera-core:1.4.0 androidx.camera:camera-core:1.5.0-alpha03 androidx.camera:camera-effects:1.4.0 androidx.camera:camera-effects:1.5.0-alpha03 androidx.camera:camera-extensions:1.4.0 androidx.camera:camera-extensions:1.5.0-alpha03 androidx.camera:camera-feature-combination-query:1.5.0-alpha03 androidx.camera:camera-feature-combination-query-play-services:1.5.0-alpha03 androidx.camera:camera-lifecycle:1.4.0 androidx.camera:camera-lifecycle:1.5.0-alpha03 androidx.camera:camera-mlkit-vision:1.4.0 androidx.camera:camera-mlkit-vision:1.5.0-alpha03 androidx.camera:camera-video:1.4.0 androidx.camera:camera-video:1.5.0-alpha03 androidx.camera:camera-view:1.4.0 androidx.camera:camera-view:1.5.0-alpha03 androidx.camera.viewfinder:viewfinder-compose:1.4.0-alpha10 androidx.camera.viewfinder:viewfinder-core:1.4.0-alpha10 androidx.camera.viewfinder:viewfinder-view:1.4.0-alpha10 androidx.collection:collection:1.4.5 androidx.collection:collection:1.5.0-alpha05 androidx.collection:collection-iosarm64:1.4.5 androidx.collection:collection-iosarm64:1.5.0-alpha05 androidx.collection:collection-iossimulatorarm64:1.4.5 androidx.collection:collection-iossimulatorarm64:1.5.0-alpha05 androidx.collection:collection-iosx64:1.4.5 androidx.collection:collection-iosx64:1.5.0-alpha05 androidx.collection:collection-jvm:1.4.5 androidx.collection:collection-jvm:1.5.0-alpha05 androidx.collection:collection-ktx:1.4.5 androidx.collection:collection-ktx:1.5.0-alpha05 androidx.collection:collection-linuxarm64:1.4.5 androidx.collection:collection-linuxarm64:1.5.0-alpha05 androidx.collection:collection-linuxx64:1.4.5 androidx.collection:collection-linuxx64:1.5.0-alpha05 androidx.collection:collection-macosarm64:1.4.5 androidx.collection:collection-macosarm64:1.5.0-alpha05 androidx.collection:collection-macosx64:1.4.5 androidx.collection:collection-macosx64:1.5.0-alpha05 androidx.collection:collection-mingwx64:1.5.0-alpha05 androidx.collection:collection-tvosarm64:1.4.5 androidx.collection:collection-tvosarm64:1.5.0-alpha05 androidx.collection:collection-tvossimulatorarm64:1.4.5 androidx.collection:collection-tvossimulatorarm64:1.5.0-alpha05 androidx.collection:collection-tvosx64:1.4.5 androidx.collection:collection-tvosx64:1.5.0-alpha05 androidx.collection:collection-watchosarm32:1.4.5 androidx.collection:collection-watchosarm32:1.5.0-alpha05 androidx.collection:collection-watchosarm64:1.4.5 androidx.collection:collection-watchosarm64:1.5.0-alpha05 androidx.collection:collection-watchosdevicearm64:1.4.5 androidx.collection:collection-watchosdevicearm64:1.5.0-alpha05 androidx.collection:collection-watchossimulatorarm64:1.4.5 androidx.collection:collection-watchossimulatorarm64:1.5.0-alpha05 androidx.collection:collection-watchosx64:1.4.5 androidx.collection:collection-watchosx64:1.5.0-alpha05 androidx.compose:compose-bom:2024.10.01 androidx.compose:compose-bom-alpha:2024.10.01 androidx.compose:compose-bom-beta:2024.10.01 androidx.compose.animation:animation:1.7.5 androidx.compose.animation:animation:1.8.0-alpha05 androidx.compose.animation:animation-android:1.7.5 androidx.compose.animation:animation-android:1.8.0-alpha05 androidx.compose.animation:animation-core:1.7.5 androidx.compose.animation:animation-core:1.8.0-alpha05 androidx.compose.animation:animation-core-android:1.7.5 androidx.compose.animation:animation-core-android:1.8.0-alpha05 androidx.compose.animation:animation-core-jvmstubs:1.7.5 androidx.compose.animation:animation-core-jvmstubs:1.8.0-alpha05 androidx.compose.animation:animation-core-linuxx64stubs:1.7.5 androidx.compose.animation:animation-core-linuxx64stubs:1.8.0-alpha05 androidx.compose.animation:animation-graphics:1.7.5 androidx.compose.animation:animation-graphics:1.8.0-alpha05 androidx.compose.animation:animation-graphics-android:1.7.5 androidx.compose.animation:animation-graphics-android:1.8.0-alpha05 androidx.compose.animation:animation-graphics-jvmstubs:1.7.5 androidx.compose.animation:animation-graphics-jvmstubs:1.8.0-alpha05 androidx.compose.animation:animation-graphics-linuxx64stubs:1.7.5 androidx.compose.animation:animation-graphics-linuxx64stubs:1.8.0-alpha05 androidx.compose.animation:animation-jvmstubs:1.7.5 androidx.compose.animation:animation-jvmstubs:1.8.0-alpha05 androidx.compose.animation:animation-linuxx64stubs:1.7.5 androidx.compose.animation:animation-linuxx64stubs:1.8.0-alpha05 androidx.compose.animation:animation-tooling-internal:1.7.5 androidx.compose.animation:animation-tooling-internal:1.8.0-alpha05 androidx.compose.foundation:foundation:1.7.5 androidx.compose.foundation:foundation:1.8.0-alpha05 androidx.compose.foundation:foundation-android:1.7.5 androidx.compose.foundation:foundation-android:1.8.0-alpha05 androidx.compose.foundation:foundation-jvmstubs:1.7.5 androidx.compose.foundation:foundation-jvmstubs:1.8.0-alpha05 androidx.compose.foundation:foundation-layout:1.7.5 androidx.compose.foundation:foundation-layout:1.8.0-alpha05 androidx.compose.foundation:foundation-layout-android:1.7.5 androidx.compose.foundation:foundation-layout-android:1.8.0-alpha05 androidx.compose.foundation:foundation-layout-jvmstubs:1.7.5 androidx.compose.foundation:foundation-layout-jvmstubs:1.8.0-alpha05 androidx.compose.foundation:foundation-layout-linuxx64stubs:1.7.5 androidx.compose.foundation:foundation-layout-linuxx64stubs:1.8.0-alpha05 androidx.compose.foundation:foundation-linuxx64stubs:1.7.5 androidx.compose.foundation:foundation-linuxx64stubs:1.8.0-alpha05 androidx.compose.material:material:1.7.5 androidx.compose.material:material:1.8.0-alpha05 androidx.compose.material3:material3:1.3.1 androidx.compose.material3:material3:1.4.0-alpha03 androidx.compose.material3:material3-adaptive-navigation-suite:1.3.1 androidx.compose.material3:material3-adaptive-navigation-suite:1.4.0-alpha03 androidx.compose.material3:material3-adaptive-navigation-suite-android:1.3.1 androidx.compose.material3:material3-adaptive-navigation-suite-android:1.4.0-alpha03 androidx.compose.material3:material3-adaptive-navigation-suite-desktop:1.3.1 androidx.compose.material3:material3-adaptive-navigation-suite-jvmstubs:1.4.0-alpha03 androidx.compose.material3:material3-android:1.3.1 androidx.compose.material3:material3-android:1.4.0-alpha03 androidx.compose.material3:material3-desktop:1.3.1 androidx.compose.material3:material3-jvmstubs:1.4.0-alpha03 androidx.compose.material3:material3-linuxx64stubs:1.4.0-alpha03 androidx.compose.material3:material3-window-size-class:1.3.1 androidx.compose.material3:material3-window-size-class:1.4.0-alpha03 androidx.compose.material3:material3-window-size-class-android:1.3.1 androidx.compose.material3:material3-window-size-class-android:1.4.0-alpha03 androidx.compose.material3:material3-window-size-class-desktop:1.3.1 androidx.compose.material3:material3-window-size-class-jvmstubs:1.4.0-alpha03 androidx.compose.material3:material3-window-size-class-linuxx64stubs:1.4.0-alpha03 androidx.compose.material3.adaptive:adaptive:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-android:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-jvmstubs:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-layout:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-layout-android:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-layout-jvmstubs:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-navigation:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-navigation-android:1.1.0-alpha06 androidx.compose.material3.adaptive:adaptive-navigation-jvmstubs:1.1.0-alpha06 androidx.compose.runtime:runtime:1.7.5 androidx.compose.runtime:runtime:1.8.0-alpha05 androidx.compose.runtime:runtime-android:1.7.5 androidx.compose.runtime:runtime-android:1.8.0-alpha05 androidx.compose.runtime:runtime-jvmstubs:1.7.5 androidx.compose.runtime:runtime-jvmstubs:1.8.0-alpha05 androidx.compose.runtime:runtime-linuxx64stubs:1.7.5 androidx.compose.runtime:runtime-linuxx64stubs:1.8.0-alpha05 androidx.compose.runtime:runtime-livedata:1.7.5 androidx.compose.runtime:runtime-livedata:1.8.0-alpha05 androidx.compose.runtime:runtime-rxjava2:1.7.5 androidx.compose.runtime:runtime-rxjava2:1.8.0-alpha05 androidx.compose.runtime:runtime-rxjava3:1.7.5 androidx.compose.runtime:runtime-rxjava3:1.8.0-alpha05 androidx.compose.runtime:runtime-saveable:1.7.5 androidx.compose.runtime:runtime-saveable:1.8.0-alpha05 androidx.compose.runtime:runtime-saveable-android:1.7.5 androidx.compose.runtime:runtime-saveable-android:1.8.0-alpha05 androidx.compose.runtime:runtime-saveable-jvmstubs:1.7.5 androidx.compose.runtime:runtime-saveable-jvmstubs:1.8.0-alpha05 androidx.compose.runtime:runtime-saveable-linuxx64stubs:1.7.5 androidx.compose.runtime:runtime-saveable-linuxx64stubs:1.8.0-alpha05 androidx.compose.runtime:runtime-tracing:1.7.5 androidx.compose.runtime:runtime-tracing:1.8.0-alpha05 androidx.compose.ui:ui:1.7.5 androidx.compose.ui:ui:1.8.0-alpha05 androidx.compose.ui:ui-android:1.7.5 androidx.compose.ui:ui-android:1.8.0-alpha05 androidx.compose.ui:ui-android-stubs:1.7.5 androidx.compose.ui:ui-android-stubs:1.8.0-alpha05 androidx.compose.ui:ui-geometry:1.7.5 androidx.compose.ui:ui-geometry:1.8.0-alpha05 androidx.compose.ui:ui-geometry-android:1.7.5 androidx.compose.ui:ui-geometry-android:1.8.0-alpha05 androidx.compose.ui:ui-geometry-jvmstubs:1.7.5 androidx.compose.ui:ui-geometry-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-geometry-linuxx64stubs:1.7.5 androidx.compose.ui:ui-geometry-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-graphics:1.7.5 androidx.compose.ui:ui-graphics:1.8.0-alpha05 androidx.compose.ui:ui-graphics-android:1.7.5 androidx.compose.ui:ui-graphics-android:1.8.0-alpha05 androidx.compose.ui:ui-graphics-jvmstubs:1.7.5 androidx.compose.ui:ui-graphics-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-graphics-linuxx64stubs:1.7.5 androidx.compose.ui:ui-graphics-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-jvmstubs:1.7.5 androidx.compose.ui:ui-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-linuxx64stubs:1.7.5 androidx.compose.ui:ui-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-test:1.7.5 androidx.compose.ui:ui-test:1.8.0-alpha05 androidx.compose.ui:ui-test-android:1.7.5 androidx.compose.ui:ui-test-android:1.8.0-alpha05 androidx.compose.ui:ui-test-junit4:1.7.5 androidx.compose.ui:ui-test-junit4:1.8.0-alpha05 androidx.compose.ui:ui-test-junit4-android:1.7.5 androidx.compose.ui:ui-test-junit4-android:1.8.0-alpha05 androidx.compose.ui:ui-test-junit4-jvmstubs:1.7.5 androidx.compose.ui:ui-test-junit4-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-test-jvmstubs:1.7.5 androidx.compose.ui:ui-test-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-test-linuxx64stubs:1.7.5 androidx.compose.ui:ui-test-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-test-manifest:1.7.5 androidx.compose.ui:ui-test-manifest:1.8.0-alpha05 androidx.compose.ui:ui-text:1.7.5 androidx.compose.ui:ui-text:1.8.0-alpha05 androidx.compose.ui:ui-text-android:1.7.5 androidx.compose.ui:ui-text-android:1.8.0-alpha05 androidx.compose.ui:ui-text-google-fonts:1.7.5 androidx.compose.ui:ui-text-google-fonts:1.8.0-alpha05 androidx.compose.ui:ui-text-jvmstubs:1.7.5 androidx.compose.ui:ui-text-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-text-linuxx64stubs:1.7.5 androidx.compose.ui:ui-text-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-tooling:1.7.5 androidx.compose.ui:ui-tooling:1.8.0-alpha05 androidx.compose.ui:ui-tooling-android:1.7.5 androidx.compose.ui:ui-tooling-android:1.8.0-alpha05 androidx.compose.ui:ui-tooling-data:1.7.5 androidx.compose.ui:ui-tooling-data:1.8.0-alpha05 androidx.compose.ui:ui-tooling-data-android:1.7.5 androidx.compose.ui:ui-tooling-data-android:1.8.0-alpha05 androidx.compose.ui:ui-tooling-data-jvmstubs:1.7.5 androidx.compose.ui:ui-tooling-data-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-tooling-jvmstubs:1.7.5 androidx.compose.ui:ui-tooling-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-tooling-preview:1.7.5 androidx.compose.ui:ui-tooling-preview:1.8.0-alpha05 androidx.compose.ui:ui-tooling-preview-android:1.7.5 androidx.compose.ui:ui-tooling-preview-android:1.8.0-alpha05 androidx.compose.ui:ui-tooling-preview-jvmstubs:1.7.5 androidx.compose.ui:ui-tooling-preview-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-unit:1.7.5 androidx.compose.ui:ui-unit:1.8.0-alpha05 androidx.compose.ui:ui-unit-android:1.7.5 androidx.compose.ui:ui-unit-android:1.8.0-alpha05 androidx.compose.ui:ui-unit-jvmstubs:1.7.5 androidx.compose.ui:ui-unit-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-unit-linuxx64stubs:1.7.5 androidx.compose.ui:ui-unit-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-util:1.7.5 androidx.compose.ui:ui-util:1.8.0-alpha05 androidx.compose.ui:ui-util-android:1.7.5 androidx.compose.ui:ui-util-android:1.8.0-alpha05 androidx.compose.ui:ui-util-jvmstubs:1.7.5 androidx.compose.ui:ui-util-jvmstubs:1.8.0-alpha05 androidx.compose.ui:ui-util-linuxx64stubs:1.7.5 androidx.compose.ui:ui-util-linuxx64stubs:1.8.0-alpha05 androidx.compose.ui:ui-viewbinding:1.7.5 androidx.compose.ui:ui-viewbinding:1.8.0-alpha05 androidx.constraintlayout:constraintlayout:2.2.0 androidx.constraintlayout:constraintlayout-compose:1.1.0 androidx.constraintlayout:constraintlayout-compose-android:1.1.0 androidx.constraintlayout:constraintlayout-core:1.1.0 androidx.core:core:1.15.0 androidx.core:core-ktx:1.15.0 androidx.core:core-testing:1.15.0 androidx.credentials:credentials:1.5.0-beta01 androidx.credentials:credentials-play-services-auth:1.5.0-beta01 androidx.fragment:fragment:1.8.5 androidx.fragment:fragment-compose:1.8.5 androidx.fragment:fragment-ktx:1.8.5 androidx.fragment:fragment-testing:1.8.5 androidx.fragment:fragment-testing-manifest:1.8.5 androidx.gradle:gradle-version-catalog:2024.10.01 androidx.gradle:gradle-version-catalog-alpha:2024.10.01 androidx.gradle:gradle-version-catalog-beta:2024.10.01 androidx.lifecycle:lifecycle-common:2.8.7 androidx.lifecycle:lifecycle-common:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-iosarm64:2.8.7 androidx.lifecycle:lifecycle-common-iosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-iossimulatorarm64:2.8.7 androidx.lifecycle:lifecycle-common-iossimulatorarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-iosx64:2.8.7 androidx.lifecycle:lifecycle-common-iosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-java8:2.8.7 androidx.lifecycle:lifecycle-common-java8:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-jvm:2.8.7 androidx.lifecycle:lifecycle-common-jvm:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-linuxarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-linuxx64:2.8.7 androidx.lifecycle:lifecycle-common-linuxx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-macosarm64:2.8.7 androidx.lifecycle:lifecycle-common-macosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-common-macosx64:2.8.7 androidx.lifecycle:lifecycle-common-macosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-compiler:2.8.7 androidx.lifecycle:lifecycle-compiler:2.9.0-alpha06 androidx.lifecycle:lifecycle-livedata:2.8.7 androidx.lifecycle:lifecycle-livedata:2.9.0-alpha06 androidx.lifecycle:lifecycle-livedata-core:2.8.7 androidx.lifecycle:lifecycle-livedata-core:2.9.0-alpha06 androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.7 androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.0-alpha06 androidx.lifecycle:lifecycle-livedata-ktx:2.8.7 androidx.lifecycle:lifecycle-livedata-ktx:2.9.0-alpha06 androidx.lifecycle:lifecycle-process:2.8.7 androidx.lifecycle:lifecycle-process:2.9.0-alpha06 androidx.lifecycle:lifecycle-reactivestreams:2.8.7 androidx.lifecycle:lifecycle-reactivestreams:2.9.0-alpha06 androidx.lifecycle:lifecycle-reactivestreams-ktx:2.8.7 androidx.lifecycle:lifecycle-reactivestreams-ktx:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime:2.8.7 androidx.lifecycle:lifecycle-runtime:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-android:2.8.7 androidx.lifecycle:lifecycle-runtime-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-compose:2.8.7 androidx.lifecycle:lifecycle-runtime-compose:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-compose-android:2.8.7 androidx.lifecycle:lifecycle-runtime-compose-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-compose-jvmstubs:2.8.7 androidx.lifecycle:lifecycle-runtime-compose-jvmstubs:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-compose-linuxx64stubs:2.8.7 androidx.lifecycle:lifecycle-runtime-compose-linuxx64stubs:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-desktop:2.8.7 androidx.lifecycle:lifecycle-runtime-desktop:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-iosarm64:2.8.7 androidx.lifecycle:lifecycle-runtime-iosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-iossimulatorarm64:2.8.7 androidx.lifecycle:lifecycle-runtime-iossimulatorarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-iosx64:2.8.7 androidx.lifecycle:lifecycle-runtime-iosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-ktx:2.8.7 androidx.lifecycle:lifecycle-runtime-ktx:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.7 androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-linuxarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-linuxx64:2.8.7 androidx.lifecycle:lifecycle-runtime-linuxx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-macosarm64:2.8.7 androidx.lifecycle:lifecycle-runtime-macosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-macosx64:2.8.7 androidx.lifecycle:lifecycle-runtime-macosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing:2.8.7 androidx.lifecycle:lifecycle-runtime-testing:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-desktop:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-iosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-iossimulatorarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-iosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-linuxarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-linuxx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-macosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-runtime-testing-macosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-service:2.8.7 androidx.lifecycle:lifecycle-service:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel:2.8.7 androidx.lifecycle:lifecycle-viewmodel:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-android:2.8.7 androidx.lifecycle:lifecycle-viewmodel-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7 androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-compose-android:2.8.7 androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-compose-desktop:2.8.7 androidx.lifecycle:lifecycle-viewmodel-compose-desktop:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-desktop:2.8.7 androidx.lifecycle:lifecycle-viewmodel-desktop:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-iosarm64:2.8.7 androidx.lifecycle:lifecycle-viewmodel-iosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-iossimulatorarm64:2.8.7 androidx.lifecycle:lifecycle-viewmodel-iossimulatorarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-iosx64:2.8.7 androidx.lifecycle:lifecycle-viewmodel-iosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7 androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-linuxarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-linuxx64:2.8.7 androidx.lifecycle:lifecycle-viewmodel-linuxx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-macosarm64:2.8.7 androidx.lifecycle:lifecycle-viewmodel-macosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-macosx64:2.8.7 androidx.lifecycle:lifecycle-viewmodel-macosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.7 androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-desktop:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-iosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-iossimulatorarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-iosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-linuxarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-linuxx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-macosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-savedstate-macosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-android:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-desktop:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-iosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-iossimulatorarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-iosx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-linuxarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-linuxx64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-macosarm64:2.9.0-alpha06 androidx.lifecycle:lifecycle-viewmodel-testing-macosx64:2.9.0-alpha06 androidx.media3:media3-cast:1.5.0-beta01 androidx.media3:media3-common:1.5.0-beta01 androidx.media3:media3-common-ktx:1.5.0-beta01 androidx.media3:media3-container:1.5.0-beta01 androidx.media3:media3-database:1.5.0-beta01 androidx.media3:media3-datasource:1.5.0-beta01 androidx.media3:media3-datasource-cronet:1.5.0-beta01 androidx.media3:media3-datasource-okhttp:1.5.0-beta01 androidx.media3:media3-datasource-rtmp:1.5.0-beta01 androidx.media3:media3-decoder:1.5.0-beta01 androidx.media3:media3-effect:1.5.0-beta01 androidx.media3:media3-exoplayer:1.5.0-beta01 androidx.media3:media3-exoplayer-dash:1.5.0-beta01 androidx.media3:media3-exoplayer-hls:1.5.0-beta01 androidx.media3:media3-exoplayer-ima:1.5.0-beta01 androidx.media3:media3-exoplayer-midi:1.5.0-beta01 androidx.media3:media3-exoplayer-rtsp:1.5.0-beta01 androidx.media3:media3-exoplayer-smoothstreaming:1.5.0-beta01 androidx.media3:media3-exoplayer-workmanager:1.5.0-beta01 androidx.media3:media3-extractor:1.5.0-beta01 androidx.media3:media3-muxer:1.5.0-beta01 androidx.media3:media3-session:1.5.0-beta01 androidx.media3:media3-test-utils:1.5.0-beta01 androidx.media3:media3-test-utils-robolectric:1.5.0-beta01 androidx.media3:media3-transformer:1.5.0-beta01 androidx.media3:media3-ui:1.5.0-beta01 androidx.media3:media3-ui-leanback:1.5.0-beta01 androidx.navigation:navigation-common:2.9.0-alpha02 androidx.navigation:navigation-common-ktx:2.9.0-alpha02 androidx.navigation:navigation-compose:2.9.0-alpha02 androidx.navigation:navigation-dynamic-features-fragment:2.9.0-alpha02 androidx.navigation:navigation-dynamic-features-runtime:2.9.0-alpha02 androidx.navigation:navigation-fragment:2.9.0-alpha02 androidx.navigation:navigation-fragment-compose:2.9.0-alpha02 androidx.navigation:navigation-fragment-ktx:2.9.0-alpha02 androidx.navigation:navigation-runtime:2.9.0-alpha02 androidx.navigation:navigation-runtime-ktx:2.9.0-alpha02 androidx.navigation:navigation-safe-args-generator:2.9.0-alpha02 androidx.navigation:navigation-safe-args-gradle-plugin:2.9.0-alpha02 androidx.navigation:navigation-testing:2.9.0-alpha02 androidx.navigation:navigation-ui:2.9.0-alpha02 androidx.navigation:navigation-ui-ktx:2.9.0-alpha02 androidx.navigation.safeargs:androidx.navigation.safeargs.gradle.plugin:2.9.0-alpha02 androidx.navigation.safeargs.kotlin:androidx.navigation.safeargs.kotlin.gradle.plugin:2.9.0-alpha02 androidx.privacysandbox.ads:ads-adservices:1.1.0-beta11 androidx.privacysandbox.ads:ads-adservices-java:1.1.0-beta11 androidx.room:androidx.room.gradle.plugin:2.7.0-alpha11 androidx.room:room-common:2.7.0-alpha11 androidx.room:room-common-iosarm64:2.7.0-alpha11 androidx.room:room-common-iossimulatorarm64:2.7.0-alpha11 androidx.room:room-common-iosx64:2.7.0-alpha11 androidx.room:room-common-jvm:2.7.0-alpha11 androidx.room:room-common-linuxarm64:2.7.0-alpha11 androidx.room:room-common-linuxx64:2.7.0-alpha11 androidx.room:room-common-macosarm64:2.7.0-alpha11 androidx.room:room-common-macosx64:2.7.0-alpha11 androidx.room:room-compiler:2.7.0-alpha11 androidx.room:room-compiler-processing:2.7.0-alpha11 androidx.room:room-compiler-processing-testing:2.7.0-alpha11 androidx.room:room-external-antlr:2.7.0-alpha11 androidx.room:room-gradle-plugin:2.7.0-alpha11 androidx.room:room-guava:2.7.0-alpha11 androidx.room:room-ktx:2.7.0-alpha11 androidx.room:room-migration:2.7.0-alpha11 androidx.room:room-migration-iosarm64:2.7.0-alpha11 androidx.room:room-migration-iossimulatorarm64:2.7.0-alpha11 androidx.room:room-migration-iosx64:2.7.0-alpha11 androidx.room:room-migration-jvm:2.7.0-alpha11 androidx.room:room-migration-linuxarm64:2.7.0-alpha11 androidx.room:room-migration-linuxx64:2.7.0-alpha11 androidx.room:room-migration-macosarm64:2.7.0-alpha11 androidx.room:room-migration-macosx64:2.7.0-alpha11 androidx.room:room-paging:2.7.0-alpha11 androidx.room:room-paging-android:2.7.0-alpha11 androidx.room:room-paging-guava:2.7.0-alpha11 androidx.room:room-paging-iosarm64:2.7.0-alpha11 androidx.room:room-paging-iossimulatorarm64:2.7.0-alpha11 androidx.room:room-paging-iosx64:2.7.0-alpha11 androidx.room:room-paging-jvm:2.7.0-alpha11 androidx.room:room-paging-linuxarm64:2.7.0-alpha11 androidx.room:room-paging-linuxx64:2.7.0-alpha11 androidx.room:room-paging-macosarm64:2.7.0-alpha11 androidx.room:room-paging-macosx64:2.7.0-alpha11 androidx.room:room-paging-rxjava2:2.7.0-alpha11 androidx.room:room-paging-rxjava3:2.7.0-alpha11 androidx.room:room-runtime:2.7.0-alpha11 androidx.room:room-runtime-android:2.7.0-alpha11 androidx.room:room-runtime-iosarm64:2.7.0-alpha11 androidx.room:room-runtime-iossimulatorarm64:2.7.0-alpha11 androidx.room:room-runtime-iosx64:2.7.0-alpha11 androidx.room:room-runtime-jvm:2.7.0-alpha11 androidx.room:room-runtime-linuxarm64:2.7.0-alpha11 androidx.room:room-runtime-linuxx64:2.7.0-alpha11 androidx.room:room-runtime-macosarm64:2.7.0-alpha11 androidx.room:room-runtime-macosx64:2.7.0-alpha11 androidx.room:room-rxjava2:2.7.0-alpha11 androidx.room:room-rxjava3:2.7.0-alpha11 androidx.room:room-testing:2.7.0-alpha11 androidx.room:room-testing-android:2.7.0-alpha11 androidx.room:room-testing-iosarm64:2.7.0-alpha11 androidx.room:room-testing-iossimulatorarm64:2.7.0-alpha11 androidx.room:room-testing-iosx64:2.7.0-alpha11 androidx.room:room-testing-jvm:2.7.0-alpha11 androidx.room:room-testing-linuxarm64:2.7.0-alpha11 androidx.room:room-testing-linuxx64:2.7.0-alpha11 androidx.room:room-testing-macosarm64:2.7.0-alpha11 androidx.room:room-testing-macosx64:2.7.0-alpha11 androidx.savedstate:savedstate:1.3.0-alpha04 androidx.savedstate:savedstate-android:1.3.0-alpha04 androidx.savedstate:savedstate-desktop:1.3.0-alpha04 androidx.savedstate:savedstate-iosarm64:1.3.0-alpha04 androidx.savedstate:savedstate-iossimulatorarm64:1.3.0-alpha04 androidx.savedstate:savedstate-iosx64:1.3.0-alpha04 androidx.savedstate:savedstate-ktx:1.3.0-alpha04 androidx.savedstate:savedstate-linuxarm64:1.3.0-alpha04 androidx.savedstate:savedstate-linuxx64:1.3.0-alpha04 androidx.savedstate:savedstate-macosarm64:1.3.0-alpha04 androidx.savedstate:savedstate-macosx64:1.3.0-alpha04 androidx.sqlite:sqlite:2.5.0-alpha11 androidx.sqlite:sqlite-android:2.5.0-alpha11 androidx.sqlite:sqlite-bundled:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-android:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-iosarm64:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-iossimulatorarm64:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-iosx64:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-jvm:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-linuxarm64:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-linuxx64:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-macosarm64:2.5.0-alpha11 androidx.sqlite:sqlite-bundled-macosx64:2.5.0-alpha11 androidx.sqlite:sqlite-framework:2.5.0-alpha11 androidx.sqlite:sqlite-framework-android:2.5.0-alpha11 androidx.sqlite:sqlite-framework-iosarm64:2.5.0-alpha11 androidx.sqlite:sqlite-framework-iossimulatorarm64:2.5.0-alpha11 androidx.sqlite:sqlite-framework-iosx64:2.5.0-alpha11 androidx.sqlite:sqlite-framework-linuxarm64:2.5.0-alpha11 androidx.sqlite:sqlite-framework-linuxx64:2.5.0-alpha11 androidx.sqlite:sqlite-framework-macosarm64:2.5.0-alpha11 androidx.sqlite:sqlite-framework-macosx64:2.5.0-alpha11 androidx.sqlite:sqlite-iosarm64:2.5.0-alpha11 androidx.sqlite:sqlite-iossimulatorarm64:2.5.0-alpha11 androidx.sqlite:sqlite-iosx64:2.5.0-alpha11 androidx.sqlite:sqlite-jvm:2.5.0-alpha11 androidx.sqlite:sqlite-ktx:2.5.0-alpha11 androidx.sqlite:sqlite-linuxarm64:2.5.0-alpha11 androidx.sqlite:sqlite-linuxx64:2.5.0-alpha11 androidx.sqlite:sqlite-macosarm64:2.5.0-alpha11 androidx.sqlite:sqlite-macosx64:2.5.0-alpha11 androidx.viewpager:viewpager:1.1.0-beta01 androidx.wear.compose:compose-foundation:1.5.0-alpha05 androidx.wear.compose:compose-material:1.5.0-alpha05 androidx.wear.compose:compose-material-core:1.5.0-alpha05 androidx.wear.compose:compose-material3:1.0.0-alpha28 androidx.wear.compose:compose-navigation:1.5.0-alpha05 androidx.wear.compose:compose-ui-tooling:1.5.0-alpha05 androidx.wear.protolayout:protolayout:1.3.0-alpha03 androidx.wear.protolayout:protolayout-expression:1.3.0-alpha03 androidx.wear.protolayout:protolayout-expression-pipeline:1.3.0-alpha03 androidx.wear.protolayout:protolayout-external-protobuf:1.3.0-alpha03 androidx.wear.protolayout:protolayout-material:1.3.0-alpha03 androidx.wear.protolayout:protolayout-material-core:1.3.0-alpha03 androidx.wear.protolayout:protolayout-material3:1.3.0-alpha03 androidx.wear.protolayout:protolayout-proto:1.3.0-alpha03 androidx.wear.protolayout:protolayout-renderer:1.3.0-alpha03 androidx.wear.protolayout:protolayout-testing:1.3.0-alpha03 androidx.wear.tiles:tiles:1.5.0-alpha03 androidx.wear.tiles:tiles-material:1.5.0-alpha03 androidx.wear.tiles:tiles-proto:1.5.0-alpha03 androidx.wear.tiles:tiles-renderer:1.5.0-alpha03 androidx.wear.tiles:tiles-testing:1.5.0-alpha03 androidx.wear.tiles:tiles-tooling:1.5.0-alpha03 androidx.wear.tiles:tiles-tooling-preview:1.5.0-alpha03 androidx.work:work-gcm:2.10.0 androidx.work:work-multiprocess:2.10.0 androidx.work:work-runtime:2.10.0 androidx.work:work-runtime-ktx:2.10.0 androidx.work:work-rxjava2:2.10.0 androidx.work:work-rxjava3:2.10.0 androidx.work:work-testing:2.10.0
 •  0 comments  •  flag
Share on Twitter
Published on November 01, 2024 15:34

October 16, 2024

Hey, Where Did My Artifacts Go?

We are getting a lot of questions like this one,of the form:

My builds are now failing because some of my dependencies cannot be found. Where did they go?


While there can be a few causes for this, the one that is tripping up a lot of developersnow stems from the fact that JCenter���s Maven repository, used by many library publishers,no longer exists.

For actively-published libraries, the library developers would have moved elsewhere by now.For many developers, the best answer would be Maven Central, though some have other options.Google, for example, has the latest version of Volley available through their own Maven repository.

However, there are a lot of not-actively-published libraries. Those are probably gone for good.

The reason the JCenter change is not affecting everyone at the same time comes down to Gradlecaches. Gradle caches artifacts and only tries downloading them when needed, such as:

You add a new library dependency You change the version of a dependency that you want to use Your Gradle artifact cache gets cleared You try building on a new machine than where you had been before

Those latter two probably are the ones that cause the most JCenter-related grief. Some build thatworked before and perhaps works elsewhere (e.g., on a coworker���s machine) does not work for you,because ���elsewhere��� has the artifact cached and you do not. Teams using CI servers that retrieveartifacts on every build might have found out within a day of the JCenter shutdown. Those usingjust local build machines might not find out for years, depending on how long they keep theirGradle artifact cache around.

So, what do you do if you get caught by this?

First, search ���teh interwebs��� for the library to see if there is a site regarding it. Many ofthese libraries were developed in the open in places like GitHub. See if that site has instructionsfor some newer maintained version, and migrate to it. Conversely, if the library has not beenupdate in years, please consider moving to something that is actively maintained.

You could also search MvnRepository. This is an index of severaldifferent artifact repositories, including Maven Central and Google���s Maven repository. It used toindex JCenter, and it also has some other less-common Maven repositories. Perhaps you will findanother source for the particular artifact that you need. However, be careful when dependingon a semi-random library from an even more random repository ��� you are asking to becomethe victim of a supply-chain attack.

If the source is available on GitHub or elsewhere, you could fork the source and maintain your owncopy. Whether you make that available to the public (e.g., on Maven Central) or just share itwith your team is up to you. Maven Local is a quick-and-dirty way to have a Maven repository onyour machine for artifacts. Setting up a private shared repository, such as via Amazon S3, iseminently doable, if slightly arcane.

Going forward, aim to do a clean build on a clean environment periodically. Even if you do notelect to go for a nightly CI server job, do a build once a month on an environment that lacksa Gradle cache. Part of the value in those builds is to more rapidly identify problems like this,so you can take steps before the problem starts affecting the productivity of individual developers.

 •  0 comments  •  flag
Share on Twitter
Published on October 16, 2024 15:02

October 12, 2024

android.tech Shutdown

Five years ago,I rolled out AndroidX Tech (androidx.tech). This site contained an index of all the androidx artifacts frommaven.google.com, including copies of the source code for every version of every artifact.

I have shut it down.

I created it back when CommonsWare was still an operating business, rather than ���a hobby with a logo���.I created it back when Google had very little to offer about the available artifacts and whatthey contained. And I created it back when all the AndroidX artifacts were written in Javaand were targeting Android.

All those things have changed. Google still could do a better job of making the source codefor a specific artifact version available ��� you have to download and unpack a JAR. Butthe code that I wrote to generate that site was failing more and more, not handling Kotlin sourcecorrectly and struggling with Kotlin Multiplatform libraries. If CommonsWare were still inbusiness, perhaps I would invest in trying to address those problems, but there are other thingsthat I would like to do with my time ��� you���ll see some of that shortly.

For those of you who used AndroidX Tech, I apologize for any problems this shutdown causes you.

 •  0 comments  •  flag
Share on Twitter
Published on October 12, 2024 13:25

September 25, 2024

How to Ripple Outside of Compose Material

There is more to life than Material Design.

After all, it is a common complaint, at least here in the US, that designers design aroundiOS. Last I checked, iOS designs are not very Material. So, it stands to reason thatAndroid developers using Compose will need to deviate from Material Design in many cases.

In theory, developers would create custom design systems from the ground up. In practice,I suspect that most developers use Compose Material or Material3 and try to implement the designsthat way. This can work, but sometimes developers wind up having to resort to some fairlysignificant hacks in order to work around cases where Material���s opinions deviate fromthe designers��� opinions.

I am working on a project where I am creating a from-scratch custom design system.Along the way, I will try to point out how I am filling in various Compose gaps, whereMaterial provides a solution but ���you���re on your own��� for a custom design system.

This time, let���s look at indications. Indications are how a clickable composable letsthe user know that, indeed, they clicked the composable. Material Design calls for aripple effect. Compose Material offers the ripple.

Your graphic designer probably is not designing indications for you, but you are goingto need some sort of touch feedback. The ripple isa perfectly cromulentoption, but the ripple is a Compose Material thing and will not be supplied ���out of the box���for non-Material design systems.

The good news is that a lot of the ripple logic resides in a standalone library, onewithout other Material dependencies. This makes it reasonable for use in a non-MaterialCompose app. The bad news is that the documentation on how to actually apply thatlibrary is limited.

So, here is how you can do it.

First, you will need to add that library. That is androidx.compose.material:material-ripple,and the version I wrote this blog post around is 1.7.2:

composeRipple = "1.7.2"compose-ripple = { group = "androidx.compose.material", name = "material-ripple", version.ref = "composeRipple" }dependencies { implementation(libs.compose.ripple) // TODO other cool libraries go here}

The documentation tells you ���oh, do what Material3 does to provide the ripple���. That iscontained mostly in Ripple.kt.So, copy its contents into your own project (be sure to abide by the license!). Thiswill require you to also grab these StateTokens defined elsewhere:

internal object StateTokens { const val DraggedStateLayerOpacity = 0.16f const val FocusStateLayerOpacity = 0.1f const val HoverStateLayerOpacity = 0.08f const val PressedStateLayerOpacity = 0.1f}

For the file version I used, IIRCthere is only one real connection to the rest of Material3 in the file: a referenceto currentValueOf(LocalContentColor). This is only needed if you do not supply a colordirectly when creating the ripple or via a LocalRippleConfiguration composition local.In my case, I was perfectly happy with two options for providing a color and did notneed a third, so I swapped currentValueOf(LocalContentColor) with a RuntimeException:

private fun attachNewRipple() { val calculateColor = ColorProducer { val userDefinedColor = color() if (userDefinedColor.isSpecified) { userDefinedColor } else { // If this is null, the ripple will be removed, so this should always be non-null in // normal use val rippleConfiguration = currentValueOf(LocalRippleConfiguration) if (rippleConfiguration?.color?.isSpecified == true) { rippleConfiguration.color } else { // currentValueOf(LocalContentColor) throw RuntimeException("missing color for ripple") } } }

That should be all the changes that are needed��� at least for the version of Ripple.ktthat I used. It is possible that I forgot something, in which case I apologize.

Then, to actually apply the ripple, set the LocalIndication composition local toa ripple() that you create from the Ripple.kt code that you copied and revised:

CompositionLocalProvider(LocalIndication provides ripple(color = Color.White)) { // TODO cool stuff goes here}

If you want to be able to change the ripple color without replacing the entireripple, rather than provide the color to ripple(), you could leave that parameterout and also define a LocalRippleConfiguration:

CompositionLocalProvider( LocalIndication provides ripple(), LocalRippleConfiguration provides RippleConfiguration(color = Color.White)) { // TODO cool stuff goes here}

RippleConfiguration also lets you control the alpha values used in the rippleeffect, overriding the defaults coming from StateTokens.

No matter how you provide the color, you will need to do that separately if you needdifferent colors for different themes, such as light and dark.

If you are using ComposeThemefor your custom design system, there is an indication property that you can set in yourbuildComposeTheme() builder:

private val AwesomeDarkTheme = buildComposeTheme { name = "AwesomeDarkTheme" indication = ripple(color = Color.White) // TODO define the rest of the theme, which hopefully also is cool}

ComposeTheme will take care of setting LocalIndication for you.

Once LocalIndication is set, clickable() and other modifiers and code will applyit automatically, so your ripple should show up.Alternatively, you can provide a ripple() directly as the indicationto clickable(), perhaps for cases where you want a different implementation than theone from LocalIndication.

If you want an indication, but you want to do something other than a ripple��� lookat the implementation of ripple(), especially the RippleNodeFactory andDelegatingThemeAwareRippleNode, which eventually link into code from that material-ripplelibrary mentioned earlier.

 •  0 comments  •  flag
Share on Twitter
Published on September 25, 2024 16:13

September 13, 2024

When remember() Does Not Remember, Consider if()

One of my concerns when Jetpack Compose was released is its reliance on magic coming fromthings like the Compose Compiler. Magic is wonderful for newcomers, as it reduces cognitive load.Magic is fine for serious experts (magicians), as for them it is not magic, but ratheris sufficiently advanced technology. Magic can be a problem for those of us in betweenthose extremes, to the extent it makes it difficult for us to understand subtle behaviordifferences coming from small code changes.

For example, I have been using Alex Styl���s ComposeThemerecently, to help organize a non-Material design system in Compose UI. The way thatyou build a theme with ComposeTheme is via a buildComposeTheme() top-level function:

val MyTheme = buildComposeTheme { // TODO wonderful theme bits go here}

This returns a composable function, which you can apply akin to MaterialTheme():

@Composablefun MainScreen() { MyTheme { BasicText("Um, hi!") }}

This works well.

I then added support for light and dark themes. Alex���s documentation shows doing thatoutside of the constructed theme function:

@Composablefun MainScreen() { val MyTheme = if (isSystemInDarkTheme()) MyDarkTheme else MyLightTheme MyTheme { // use the theme, where color references get mapped to light or dark }}

Here, MyDarkTheme() and MyLightTheme() are created using buildComposeTheme(), justwith different colors. We choose which one to use, then apply it to our content.

I wanted to hide the decision-making, so I didn���t need it sprinkled throughout thecode (e.g., @Preview functions). So, I wrote my own wrapper:

@Composablefun MyTheme(content: @Composable () -> Unit) { if (isSystemInDarkTheme()) MyDarkTheme(content) else MyLightTheme(content)}

This could be called like MyTheme() was before, routing to MyDarkTheme() or MyLightTheme()as needed.

And it worked��� or so I thought.

The app opts out of all automatic configuration change ���destroy the activity��� behaviorvia android:configChanges. What happens is that Compose UI recomposes, and we updatethe UI based on the new Configuration, not significantly different than updatingthe UI based on the result of some other sort of data change.

What I noticed was that while the app worked, if I changed the theme while the app was running,everything would reset to the beginning. So, if I did some stuff in the app (e.g., navigatedin bottom nav), then used the notification shade tile to turn on/off dark mode, the appwould draw the right theme, but my changes would be undone (e.g., I would be back at thedefault bottom nav location).

Eventually, after some debugging, I discovered that remember() seemed to stop working. ����

@Composablefun MainScreen() { MyTheme { val uuid = remember { UUID.randomUuid() } BasicText("Um, hi! My name is: $uuid") }}

Here, I remember a generated UUID. That should survive recomposition. For most things,it would ��� I could rotate the screen without issue. But if I changed theme, I would geta fresh UUID.

����

Much debugging later, I realized the problem.

Let���s go back to the MyTheme() implementation:

@Composablefun MyTheme(content: @Composable () -> Unit) { if (isSystemInDarkTheme()) MyDarkTheme(content) else MyLightTheme(content)}

When I toggle dark mode,my use of isSystemInDarkTheme() triggers a recomposition. Let���s suppose that isSystemInDarkTheme()originally returned false, then later returns true on the recomposition. The falsemeant that my original composition of MyTheme() went down the MyLightTheme() branch.The later recomposition takes me down the MyDarkTheme() branch. Compose treats thoseas separate compositions. MyTheme() is recomposing, but it is doing so by discardingthe MyLightTheme() composition and creating a new MyDarkTheme() composition. It doesnot matter whether content would generate the same composition nodes or not ���the change in the root from MyLightTheme() to MyDarkTheme() causes the swap incompositions.

My uuid is in the content lambda expression. When we dispose of the MyLightTheme()composition and switch to the MyDarkTheme() composition, we start over with respectto the remember() call, and I wind up with a fresh random UUID.

One workaround is to ���lift the if���, blending Alex���s original approach with mine:

@Composablefun MyTheme(content: @Composable () -> Unit) { val theme = if (isSystemInDarkTheme()) MyDarkTheme else MyLightTheme theme(content)}

This does the same thing, but Compose treats this as a single changed composition, andthe remember() is retained. To be honest, I am not completely clear why this workaroundworks. This is still magic to me, though I am certain that there are others for whom thereasoning is clear.

This is the sort of thing that we have to watch out for when working in Compose. Composeis a principled framework, but the Principle of Least Surprise is not always followed���at least for those among us who are not magicians.

 •  0 comments  •  flag
Share on Twitter
Published on September 13, 2024 12:46

June 11, 2024

Requiem for a Ranch

While elements will still remain, to an extent it appears thatBig Nerd Ranch is locking up the corral for good.

A long long time ago, I was their original Android app development trainer. Working on a contractbasis, I delivered training in Historic Banning Mills(���come for the training, stay for the zipline!���) on many occasions. That included leading somenature walks during breaks, helping folks figure out how to get cell service (tip: walk up a steephill to get out of the valley), and wrangling the occasional piece of luggage. Banning Mills was anunexpectedly delightful spot to deliver training, one of the most entertaining locations that Iever used.

Eventually, Big Nerd Ranch concluded that this Android thing might pan out, so they hired dedicatedstaff and I moved on to deliver training through a series of other firms. Over time, they wrotetheir own set of books, and you���ll probably recognize a few ofthe authors for their contributions to the Android development community.

I remain grateful to Aaron Hillegass for taking a chance on me and giving me the opportunity todeliver Big Nerd Ranch training. That played a significant role in my overall success with CommonsWare.I even wound up doing some more contract work for them in 2018-19, albeit not in the form ofdelivering training.

As their announcement post puts it, ���The landscape of tech education has evolved significantly since our inception���.That���s putting it mildly. Discontinuing their public bootcamps and their series of books is not surprising ���after all, I did much the same thing. Still, it���s tough to see and makes for a somber end to the day.

That said��� maybe I���ll pick up a Stetson and put a propeller on the top, for old times��� sake. ����

 •  0 comments  •  flag
Share on Twitter
Published on June 11, 2024 16:36

May 18, 2024

Random Musings on the Android 15 Beta 2

When Google releases a new beta, I rummage throughthe API differences report,the high-level overviews,and even the release blog post,to see if there are things that warrant more attention from developers. I try to emphasize mainstream features that any developermight reasonably use, along with things that may notget quite as much attention, because they are buried in the JavaDocs.

This update contained a surprising amount of stuff for a second beta release. Usually bynow I have no more random musings, because there is little to review. Technically, though,the first ���platform stability��� release is June���s, so Google just has some late-breaking changes.

What Might Break You

All apps ought to get private spaces,though most should not have an issue. If you deal with work profiles, are a launcher, or are anapp store, you may have additional work to do. You might be interested in the newACTION_PROFILE_AVAILABLEand ACTION_PROFILE_UNAVAILABLEbroadcasts.

If you use NDK code (yourself or via libraries), the 16KB page sizesupport needs to be investigated.

There is a new restriction on activity launches from back in a task���s stack.This appears to be opt-in via android:allowCrossUidActivitySwitchFromBelow. While it isin the ���only affects you if you target Android 15��� documentation, it is unclear if that affectsthe app doing the blocking, the app that might be blocked, both, or none (i.e., the docs are in the wrong spot). There are a variety of other restrictions on semi-background activity startsthat hopefully won���t affect you.

screenWidthDp and screenHeightDp on Configurationnow include the depth of the system bars.

What Might Break You Next Year

The 6-hour-maximum foreground servicestatus for dataSync and mediaProcessing serviceswill only kick in once you target Android 15 or higher.

Similarly, the boot-time restrictions on what foreground services you can startonly appear when you target Android 15 or higher.

Your TextViews may add more end paddingonce you target Android 15, to better accommodate varying fonts and languages.

What Makes Me Go ����

They tightened some unsafe Intent structures, but made it opt in via StrictModeand possibly targeting Android 15.

There is a new contentSensitivity attribute for View,though it is unclear what it controls.

There is a new shouldDefaultToObserveMode attribute,probably for ��� but we are not told what ���observe mode��� is.

There is a new systemUserOnly attributefor all components (activities, services, etc.). This feels like it is tied to private spaces,to have some component be ineligible for use in a private space, but that���s just a guess,because it is poorly documented.

There is a new form of requestPermissions()that takes a device ID, and it is unclear what the ���device��� is in this context.

There is a new registerResourcePaths() method on Resources,which seems like it allows for dynamically adding new resources, such as from some sort oflibrary.

PowerMonitor���represents either an ODPM rail (on-device power rail monitor) or a modeled energy consumer���,which I am certain makes some sense to somebody.

There is a new form of RemoteViews that works off of DrawInstructions,but it is completely non-obvious how you create those instructions.

What Else Helps with Security

You can place an android:requireContentUriPermissionFromCallerattribute on your element to enforce that the activity that starts yours and passes a Urihas certain permissions with respect to that Uri. Similarly, you can usecheckContentUriPermissionFull() on Contextto see if some other app and/or user hasrights to a particular Uri.

What Seems Nice

You can positively state what language your plain res/values/strings.xml file is in viaa defaultLocale attribute.Presumably this goes on given its scope, but as is all too typical, that isundocumented.

You now finally can find out what activity created yours, via getInitialCaller().There is also getCurrentCaller(),which handles both onNewIntent() and onActivityResult() cases. Finally, thereis a getCaller() method,but it is unclear how that differs from getInitialCaller().

You can create customized previews for your app widgets via methods like.

You can opt into custom vibration effects on a NotificationChannel.

You can add up to 32 ���debug tags��� to a JobScheduler job.

What Is Back From the Dead, Only To Die Again

Slices ��� a new UI option added several years ago that nobody every really seemed to use ��� got some API changes! Alas, those changes are deprecation notices. I once again apologize tothose who attended a conference presentation on slices that I delivered years ago.

What Else Caught My Eye

There are a bunch of new permissions related to device policy controllers, such asMANAGE_DEVICE_POLICY_BLOCK_UNINSTALL.

There are new options on WindowManager for ���small cover screen��� UIs.

 •  0 comments  •  flag
Share on Twitter
Published on May 18, 2024 11:01

April 13, 2024

Random Musings on the Android 15 Beta 1

When Google releases a new beta, I rummage throughthe API differences report,the high-level overviews,and even the release blog post,to see if there are things that warrant more attention from developers. I try to emphasize mainstream features that any developermight reasonably use, along with things that may notget quite as much attention, because they are buried in the JavaDocs.

As often happens with the first beta, we had a bunch of stuff deleted, meaningit shipped in a developer preview then was removed. My assumption is that theserepresent things that did not quite ���make the cut��� and are going to be revisited.

That said, we did get a decent bunch of new things as well.

What Might Break You Next Year

Edge-to-edge will be enabled by default on Android 15,for apps that target Android 15. This means that new apps should be built from the ground up tobe edge-to-edge. Ideally, existing apps that do not fully support edge-to-edge shouldtake the next 1.5 years to adopt an edge-to-edge presentation. There is a newandroid:windowOptOutEdgeToEdgeEnforcement attribute,probably for , that you can set to true to perhaps buy more time.However, the docs for that attribute say that it will be deprecated and disabled���in a future SDK level���. If they do that in next year���s Android 16, the attributewill not buy you any meaningful time. Frankly, Google���s penchant for mandating theirparticular preferred aesthetics is annoying ��� as somebody told me on Stack Overflowover a decade ago, if I wanted somebody forcing their designs on me, I���d be programmingfor iOS.

What Makes Me Go ����

They added E2eeContactKeysManager.On the one hand, it provides first-class supportfor end-to-end encryption keys. On the other hand, it is tied to the user���s contactsapp, which seems rather limiting.

They added cover screen supportin an earlier developer preview, and the docs still refer to it. Butthey removed the actual property (COMPAT_SMALL_COVER_SCREEN_OPT_IN) from the SDK.My guess is that the docs are wrong and thisfeature was removed.

WindowManager now has the concept of ���trusted presentations���.Basically, you can find out if your window is only partially shown or is beingshown mostly translucent. For a multi-window environment, I can see the value inknowing these things, as it might impact how often you update your UI, or you mightpause media playback. The ���trusted���, though, makes me wonder if there is a securityaspect.

They re-added FINGERPRINT_SERVICE.

The blog post���s section on ���Secured background activity launches��� doesn���t seem topoint to anything new to Beta 1, so I am uncertain what they are referring to.

What Has Nothing to Do With Police Procedural Dramas

We now have access to a ProfilingManager system servicefor requesting certain types of profiling, including heap dumps and system traces.

What Else Is Interesting

ACTION_CHOOSER, which powers the ���share sheet���, now supportsEXTRA_CHOOSER_CONTENT_TYPE_HINT.The one documented value for this hint isCHOOSER_CONTENT_TYPE_ALBUM,to hint that the content being shared represents an ���album���.

There is a new SecurityStateManager system service,which can return the system patch level and kernel version.

A View can now have an associated ���credential request���tied to CredentialManager.

Wallet apps can now be set as the default recipient of NFC contactless payments viaa new wallet role.

The concept of parent and child activities is being unwound:getParent() and isChild() on Activity are deprecated.There are better solutions for this nowadays, as multi-activity UIs slowly fadefrom existence.

 •  0 comments  •  flag
Share on Twitter
Published on April 13, 2024 14:02

March 23, 2024

Random Musings on the Android 15 Developer Preview 2

When Google releases a new developer preview, I rummage throughthe API differences report(even when Google does not seem to link to them ����), the high-level overviews,and even the release blog post,to see if there are things that warrant more attention from developers. I try to emphasize mainstream features that any developermight reasonably use, along with things that may notget quite as much attention, because they are buried in the JavaDocs.

This release is much larger than was the previous developer preview. It feelslike Developer Preview 1 was ���low-hanging fruit���, to give developers an additional monthor so on the major changes for Android 15.

What Might Break You This Year

If your app is force-stopped, all pending intents are cancelled.This change affects all apps, not just those targeting Android 15. I thought thiswas already the behavior, but apparently it is not. For some devices that shipwith ���task managers��� that apply force-stop logic instead of only terminating appprocesses, this change may really impact your users. Fortunately, at least,you will now get ACTION_BOOT_COMPLETED broadcast to your app so you can re-establishanything that was canceled. However, there also is a newACTION_PACKAGE_UNSTOPPEDbroadcast that you might consider. PackageManager also now has an isPackageStopped()function, so external parties can see if your app was force-stopped.

FINGERPRINT_SERVICE was removed from Context,further impacting the already-deprecated FingerprintManager.

What Might Break You Next Year

Once you target Android 15, you willnot be allowed to start some types of foreground services at boot time.I can see this causing problems for a fair number of apps.

Also, once you target Android 15, if your app supports Arabic, Lao, Myanmar, Tamil, Gujarati, Kannada, Malayalam, Odia, Telugu or Thai, then Android will use a taller font by default.This may cause UI glitches, such as text being cut off due to lack of sufficientvertical space.

What I Like of the Prominent Changes

Google taking PDF rendering seriouslyis a nice improvement. PdfRenderer was designedfor use in print previews, but developers have been trying to use it for arbitrary PDFs,with varying results. And since many developers really do not want to use the user���spreferred PDF reader, we were stuck with various workarounds.The fact that the improved PdfRenderer is being backported and apparently will bewrapped in a Jetpack library also helps a great deal.

Support for deeplink filtering on query parameters and fragmentsis something that developers have been requesting for several years, so it is goodthat we are getting it, even if that is not something that can be backported.

Granular line-break controls,so we can keep titles contiguous, is a long-awaited text rendering improvement.

Similarly, developers have been asking for how to find outwhy the app was started forquite some time.

Screen recording detectionprovides a nice middle ground between being oblivious to screen recording and usingFLAG_SECURE to block it entirely. Note that you needa new normal permissionto enable this capability.

What Makes Me Go ���Hmmmmm������

Resources now has a registerResourcePaths() method.���This will collect the package resources��� paths from its ApplicationInfo and add them to all existing and future contexts while the application is running���.

Getting and setting the system bar colors is now deprecated.

What Else You Might Have Missed

Your manifest components (activities, services, receivers, and providers) can beprotected with android:systemUserOnly="true".This is supposed to limit that component to at most one instance, and that instancecan only be interacted with by the system user. My hope is that we can use this forspecific places where we want to plug into the framework but want to preclude arbitraryother apps from trying to use the component.

Through DevicePolicyManager, eligible apps can mandate ���content protection���or can allow user choice.In this case, ���content protection��� means ���scanning for deceptive apps���. Similarly,a device owner or policy owner can block NFC for certain users.

For app stores, ACTION_UNARCHIVE_PACKAGEmight prove interesting. Also, PackageInstaller now hasa new set of APIs related to archiving apps.

There are new KeyEvent key code valuesfor dedicated emoji picker and screenshot keys.

We can now limit drag-and-drop to be just within our app, even if we have multiple windows.We can also specify an activity IntentSender to use for unhandled drops.

 •  0 comments  •  flag
Share on Twitter
Published on March 23, 2024 07:53