Mark L. Murphy's Blog

October 12, 2025

Again, Be Wary of Random Gradle Projects

Since Google declined to supply Gradle security advice to developers,here is my periodic reminder:

Only use the Gradle Wrapper scripts and JAR from a project if you completely trust where they came from (e.g., were generated by Android Studio when you created the project).In particular, do not use the Gradle Wrapper from an arbitrary project that yougrab off of GitHub or elsewhere on the Internet. Delete it or replace it with alocally-generated wrapper (gradle wrapper command).

Always check the distributionUrl in the gradle-wrapper.properties file before importing a project into Android Studio or using the Gradle wrapper scripts, to see if the URL looks reasonable (e.g., points to gradle.org).Even better, if it has a distributionSha256Sum value, confirm that it is one that matchesa known-good Gradle version.

Otherwise ��� especially if you decline to use Safe Mode in Android Studio ���you may wind up the victim of an attack, as I wrote about 2.5 years ago.

It may also be worthwhile toexamine the Gradle plugins and compile-time annotation processorsto see if there is anything unusual, though ���unusual��� is difficult to quantify.

 •  0 comments  •  flag
Share on Twitter
Published on October 12, 2025 04:51

October 8, 2025

8 October 2025 Artifact Wave

No new artifacts this week, but we did get updates to 842 of them, including:

A stable 1.1.0 release for androidx.metrics:metrics-performance

A new minor release for heifwriter

New patch releases for Camera, Compose, HealthConnect, Room, and Wear Compose

View all the changes here!

 •  0 comments  •  flag
Share on Twitter
Published on October 08, 2025 13:19

October 4, 2025

Busting drawWithCache() in Compose

There are only two hard things in Computer Science: cache invalidation and naming things.


��� Phil Karton, via Martin Fowler

For a particular project, I need to take a Compose-defined UI and completely shift allof its colors a bit in the blue direction of the color spectrum. This is to compensatefor a red shift being introduced as part of the physical display. This includes allscreens, with whatever those screens are rendering, including content from third-partylibraries that use the classic View system.

After some fussing around, including asking on Stack Overflow,I came up with this solution:

@Composablefun Modifier.scaleTint(redScale: Float = 0.533333f, greenScale: Float = 0.8f, blueScale: Float = 1f, alphaScale: Float = 1f): Modifier = this.drawWithCache { val graphicsLayer = obtainGraphicsLayer() val matrix = ColorMatrix().apply { setToSaturation(0f) setToScale(redScale, greenScale, blueScale, alphaScale) } graphicsLayer.apply { record { drawContent() } this.colorFilter = ColorFilter.colorMatrix(matrix) } onDrawWithContent { drawLayer(graphicsLayer) } }

You can then apply the modifier to something you want tinted, such as:

@Composablefun Something(message: String, modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxWidth().scaleTint()) { BasicText(message) }}

I thought it worked. Indeed, it does work, so long as you never need to change thecomposable being tinted.

In the example shown above, imagine the following series of events:

Something() gets invoked with "foo" passed as the value for message foo gets rendered on the screen, with the tint applied The user does something which causes a state change As a result, Something() gets called again to recompose, this time with "bar" as the value for message

We would expect bar to now be rendered on the screen, with the tint applied. Instead,we still see foo.

����

The cause of the issue stems from drawWithCache(). The KDocs for drawWithCache() leadoff with:

Draw into a DrawScope with content that is persisted across draw calls as long as the size of the drawing area is the same or any state objects that are read have not changed. In the event that the drawing area changes, or the underlying state values that are being read change, this method is invoked again to recreate objects to be used during drawing.


In this case, message is not part of the state that drawWithCache() pays attentionto. It only knows about the parameters to the scaleTint() modifier, and in my example,those are not changing. drawWithCache() is oblivious to the BasicText() having changed,so it continues to use its drawing cache.

So, we need to bust that cache somehow.

My instinctive reaction was ���OK, there must be a key somewhere���. key is used in manyplaces to say ���please invalidate this composable if the key value changes���. Alas,drawWithCache() does not take a key parameter, only the lambda expression (or otherfunction type) that represents what is to be drawn and cached.

Adding a key parameter to the scaleTint() declaration is easy enough:

@Composablefun Modifier.scaleTint(key: Any, redScale: Float = 0.533333f, greenScale: Float = 0.8f, blueScale: Float = 1f, alphaScale: Float = 1f): Modifier = this.drawWithCache { // rest of previous logic here }

However, that is insufficient. The key needs to be used inside the lambda supplied todrawWithCache().

So, now we need to decide on how to use key in such a way that it is considered ���read���yet has minimum other impact, since we are not using it for anything other than bustingthe cache.

As it turns out, at least right now, just referencing it is sufficient:

@Composablefun Modifier.scaleTint(key: Any, redScale: Float = 0.533333f, greenScale: Float = 0.8f, blueScale: Float = 1f, alphaScale: Float = 1f): Modifier = this.drawWithCache { key // referenced to bust the cache val graphicsLayer = obtainGraphicsLayer() val matrix = ColorMatrix().apply { setToSaturation(0f) setToScale(redScale, greenScale, blueScale, alphaScale) } graphicsLayer.apply { record { drawContent() } this.colorFilter = ColorFilter.colorMatrix(matrix) } onDrawWithContent { drawLayer(graphicsLayer) } }

We then need to supply a value for key that is tied to the state change:

@Composablefun Something(message: String, modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxWidth().scaleTint(message)) { BasicText(message) }}

Now, if message changes, our BasicText() will re-render and we will see the changeon-screen.

I thought that the right answer would be to use the key() function, or perhaps remember()(used by LaunchedEffect). However, both of those functions are composables, and sothey can only be invoked from another composable. The lambda parameter to drawWithCache()is not marked as @Composable, though, so neither key() nor remember() are availableto us.

I am not completely comfortable with this solution, but it is working for now. If youhappen to know of a better way to get drawWithCache() to update in this case,please let me know!

 •  0 comments  •  flag
Share on Twitter
Published on October 04, 2025 10:12

September 24, 2025

24 September 2025 Artifact Wave

Ten brand new artifacts in this wave:

androidx.compose.material3.adaptive:adaptive-navigation3-jvmstubs androidx.compose.material3.adaptive:adaptive-navigation3-linuxx64stubs androidx.compose.runtime:runtime-retain (including mulitplatform artifacts) androidx.credentials.registry:registry-digitalcredentials-openid androidx.credentials.registry:registry-digitalcredentials-sdjwtvc androidx.lifecycle:lifecycle-viewmodel-navigation3-jvmstubs androidx.lifecycle:lifecycle-viewmodel-navigation3-linuxx64stubs androidx.security:security-state-provider androidx.xr.arcore:arcore-openxr androidx.xr.arcore:arcore-testing

Beyond that, you can find the 869 new artifact versions here.

 •  0 comments  •  flag
Share on Twitter
Published on September 24, 2025 14:40

September 17, 2025

17 September Lifecycle/SavedState Mini-Wave

The Lifecycle artifacts got bumped to 2.9.4, SavedState was upgraded to 1.3.3,and the Gradle version catalog BOM is up to 2025.09.01:

androidx.gradle:gradle-version-catalog:2025.09.01 androidx.lifecycle:lifecycle-common:2.9.4 androidx.lifecycle:lifecycle-common-iosarm64:2.9.4 androidx.lifecycle:lifecycle-common-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-common-iosx64:2.9.4 androidx.lifecycle:lifecycle-common-java8:2.9.4 androidx.lifecycle:lifecycle-common-js:2.9.4 androidx.lifecycle:lifecycle-common-jvm:2.9.4 androidx.lifecycle:lifecycle-common-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-common-linuxx64:2.9.4 androidx.lifecycle:lifecycle-common-macosarm64:2.9.4 androidx.lifecycle:lifecycle-common-macosx64:2.9.4 androidx.lifecycle:lifecycle-common-mingwx64:2.9.4 androidx.lifecycle:lifecycle-common-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-common-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-common-tvosx64:2.9.4 androidx.lifecycle:lifecycle-common-wasm-js:2.9.4 androidx.lifecycle:lifecycle-common-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-common-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-common-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-common-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-common-watchosx64:2.9.4 androidx.lifecycle:lifecycle-compiler:2.9.4 androidx.lifecycle:lifecycle-livedata:2.9.4 androidx.lifecycle:lifecycle-livedata-core:2.9.4 androidx.lifecycle:lifecycle-livedata-core-ktx:2.9.4 androidx.lifecycle:lifecycle-livedata-ktx:2.9.4 androidx.lifecycle:lifecycle-process:2.9.4 androidx.lifecycle:lifecycle-reactivestreams:2.9.4 androidx.lifecycle:lifecycle-reactivestreams-ktx:2.9.4 androidx.lifecycle:lifecycle-runtime:2.9.4 androidx.lifecycle:lifecycle-runtime-android:2.9.4 androidx.lifecycle:lifecycle-runtime-compose:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-android:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-desktop:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-iosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-iosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-js:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-linuxx64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-macosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-macosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-mingwx64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-tvosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-wasm-js:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-compose-watchosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-desktop:2.9.4 androidx.lifecycle:lifecycle-runtime-iosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-iosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-js:2.9.4 androidx.lifecycle:lifecycle-runtime-ktx:2.9.4 androidx.lifecycle:lifecycle-runtime-ktx-android:2.9.4 androidx.lifecycle:lifecycle-runtime-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-linuxx64:2.9.4 androidx.lifecycle:lifecycle-runtime-macosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-macosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-mingwx64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-android:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-desktop:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-iosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-iosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-js:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-linuxx64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-macosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-macosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-mingwx64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-tvosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-wasm-js:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-testing-watchosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-tvosx64:2.9.4 androidx.lifecycle:lifecycle-runtime-wasm-js:2.9.4 androidx.lifecycle:lifecycle-runtime-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-runtime-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-runtime-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-runtime-watchosx64:2.9.4 androidx.lifecycle:lifecycle-service:2.9.4 androidx.lifecycle:lifecycle-viewmodel:2.9.4 androidx.lifecycle:lifecycle-viewmodel-android:2.9.4 androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4 androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.4 androidx.lifecycle:lifecycle-viewmodel-compose-jvmstubs:2.9.4 androidx.lifecycle:lifecycle-viewmodel-compose-linuxx64stubs:2.9.4 androidx.lifecycle:lifecycle-viewmodel-desktop:2.9.4 androidx.lifecycle:lifecycle-viewmodel-iosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-iosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-js:2.9.4 androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4 androidx.lifecycle:lifecycle-viewmodel-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-linuxx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-macosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-macosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-mingwx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-desktop:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-iosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-iosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-js:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-linuxx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-macosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-macosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-mingwx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-tvosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-wasm-js:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-savedstate-watchosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-android:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-desktop:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-iosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-iossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-iosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-js:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-linuxarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-linuxx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-macosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-macosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-mingwx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-tvosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-wasm-js:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-testing-watchosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-tvosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-tvossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-tvosx64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-wasm-js:2.9.4 androidx.lifecycle:lifecycle-viewmodel-watchosarm32:2.9.4 androidx.lifecycle:lifecycle-viewmodel-watchosarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-watchosdevicearm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-watchossimulatorarm64:2.9.4 androidx.lifecycle:lifecycle-viewmodel-watchosx64:2.9.4 androidx.savedstate:savedstate:1.3.3 androidx.savedstate:savedstate-android:1.3.3 androidx.savedstate:savedstate-compose:1.3.3 androidx.savedstate:savedstate-compose-android:1.3.3 androidx.savedstate:savedstate-compose-desktop:1.3.3 androidx.savedstate:savedstate-compose-iosarm64:1.3.3 androidx.savedstate:savedstate-compose-iossimulatorarm64:1.3.3 androidx.savedstate:savedstate-compose-iosx64:1.3.3 androidx.savedstate:savedstate-compose-js:1.3.3 androidx.savedstate:savedstate-compose-linuxarm64:1.3.3 androidx.savedstate:savedstate-compose-linuxx64:1.3.3 androidx.savedstate:savedstate-compose-macosarm64:1.3.3 androidx.savedstate:savedstate-compose-macosx64:1.3.3 androidx.savedstate:savedstate-compose-mingwx64:1.3.3 androidx.savedstate:savedstate-compose-tvosarm64:1.3.3 androidx.savedstate:savedstate-compose-tvossimulatorarm64:1.3.3 androidx.savedstate:savedstate-compose-tvosx64:1.3.3 androidx.savedstate:savedstate-compose-wasm-js:1.3.3 androidx.savedstate:savedstate-compose-watchosarm32:1.3.3 androidx.savedstate:savedstate-compose-watchosarm64:1.3.3 androidx.savedstate:savedstate-compose-watchosdevicearm64:1.3.3 androidx.savedstate:savedstate-compose-watchossimulatorarm64:1.3.3 androidx.savedstate:savedstate-compose-watchosx64:1.3.3 androidx.savedstate:savedstate-desktop:1.3.3 androidx.savedstate:savedstate-iosarm64:1.3.3 androidx.savedstate:savedstate-iossimulatorarm64:1.3.3 androidx.savedstate:savedstate-iosx64:1.3.3 androidx.savedstate:savedstate-js:1.3.3 androidx.savedstate:savedstate-ktx:1.3.3 androidx.savedstate:savedstate-linuxarm64:1.3.3 androidx.savedstate:savedstate-linuxx64:1.3.3 androidx.savedstate:savedstate-macosarm64:1.3.3 androidx.savedstate:savedstate-macosx64:1.3.3 androidx.savedstate:savedstate-mingwx64:1.3.3 androidx.savedstate:savedstate-tvosarm64:1.3.3 androidx.savedstate:savedstate-tvossimulatorarm64:1.3.3 androidx.savedstate:savedstate-tvosx64:1.3.3 androidx.savedstate:savedstate-wasm-js:1.3.3 androidx.savedstate:savedstate-watchosarm32:1.3.3 androidx.savedstate:savedstate-watchosarm64:1.3.3 androidx.savedstate:savedstate-watchosdevicearm64:1.3.3 androidx.savedstate:savedstate-watchossimulatorarm64:1.3.3 androidx.savedstate:savedstate-watchosx64:1.3.3
 •  0 comments  •  flag
Share on Twitter
Published on September 17, 2025 13:08

September 10, 2025

10 September Artifact Wave

Only one brand new artifact this time around: androidx.compose.ui:ui-tooling-preview-linuxx64stubs.

Beyond that, you can find the ~750 new artifact versions here.

 •  0 comments  •  flag
Share on Twitter
Published on September 10, 2025 14:06

August 27, 2025

27 August Artifact Wave

This wave���s brand-new artifacts includes a tie between Nav3 and Material3; broader multiplatformsupport for androidx.compose.runtime:runtime-saveable, androidx.navigation3:navigation3-runtime,and androidx.paging:paging-compose; plus a new android.text group:

androidx.compose.material3.adaptive:adaptive-navigation3 androidx.compose.material3.adaptive:adaptive-navigation3-android androidx.compose.runtime:runtime-saveable-iosarm64 androidx.compose.runtime:runtime-saveable-iossimulatorarm64 androidx.compose.runtime:runtime-saveable-iosx64 androidx.compose.runtime:runtime-saveable-js androidx.compose.runtime:runtime-saveable-linuxarm64 androidx.compose.runtime:runtime-saveable-linuxx64 androidx.compose.runtime:runtime-saveable-macosarm64 androidx.compose.runtime:runtime-saveable-macosx64 androidx.compose.runtime:runtime-saveable-mingwx64 androidx.compose.runtime:runtime-saveable-tvosarm64 androidx.compose.runtime:runtime-saveable-tvossimulatorarm64 androidx.compose.runtime:runtime-saveable-tvosx64 androidx.compose.runtime:runtime-saveable-wasm-js androidx.compose.runtime:runtime-saveable-watchosarm32 androidx.compose.runtime:runtime-saveable-watchosarm64 androidx.compose.runtime:runtime-saveable-watchosdevicearm64 androidx.compose.runtime:runtime-saveable-watchossimulatorarm64 androidx.compose.runtime:runtime-saveable-watchosx64 androidx.navigation3:navigation3-runtime-desktop androidx.navigation3:navigation3-runtime-iosarm64 androidx.navigation3:navigation3-runtime-iossimulatorarm64 androidx.navigation3:navigation3-runtime-iosx64 androidx.navigation3:navigation3-runtime-js androidx.navigation3:navigation3-runtime-linuxarm64 androidx.navigation3:navigation3-runtime-linuxx64 androidx.navigation3:navigation3-runtime-macosarm64 androidx.navigation3:navigation3-runtime-macosx64 androidx.navigation3:navigation3-runtime-mingwx64 androidx.navigation3:navigation3-runtime-tvosarm64 androidx.navigation3:navigation3-runtime-tvossimulatorarm64 androidx.navigation3:navigation3-runtime-tvosx64 androidx.navigation3:navigation3-runtime-wasm-js androidx.navigation3:navigation3-runtime-watchosarm32 androidx.navigation3:navigation3-runtime-watchosarm64 androidx.navigation3:navigation3-runtime-watchosdevicearm64 androidx.navigation3:navigation3-runtime-watchossimulatorarm64 androidx.navigation3:navigation3-runtime-watchosx64 androidx.paging:paging-common-desktop androidx.paging:paging-compose-desktop androidx.paging:paging-compose-iosarm64 androidx.paging:paging-compose-iossimulatorarm64 androidx.paging:paging-compose-iosx64 androidx.paging:paging-compose-js androidx.paging:paging-compose-linuxarm64 androidx.paging:paging-compose-linuxx64 androidx.paging:paging-compose-macosarm64 androidx.paging:paging-compose-macosx64 androidx.paging:paging-compose-mingwx64 androidx.paging:paging-compose-tvosarm64 androidx.paging:paging-compose-tvossimulatorarm64 androidx.paging:paging-compose-tvosx64 androidx.paging:paging-compose-wasm-js androidx.paging:paging-compose-watchosarm32 androidx.paging:paging-compose-watchosarm64 androidx.paging:paging-compose-watchosdevicearm64 androidx.paging:paging-compose-watchossimulatorarm64 androidx.paging:paging-compose-watchosx64 androidx.paging:paging-testing-js androidx.paging:paging-testing-mingwx64 androidx.paging:paging-testing-wasm-js androidx.text:text-vertical

The full list of over 1000 updated artifacts can be found here.

 •  0 comments  •  flag
Share on Twitter
Published on August 27, 2025 15:03

August 26, 2025

Uncomfortable Questions About Android Developer Verification

ICEBlock ���is an innovative, completely anonymous crowdsourced platform that allows users to report Immigration and Customs Enforcement (ICE) activity with just two taps on their phone.���

The developer of ICEBlock disclosed his identity. In addition to receiving threats of federal prosecution over the app, the developer has faced other backlash, includinghis wife being fired from a federal government job.

This is one recent example demonstrating that app developer anonymity has impacts and that the lack of such anonymity can cause harm.

With that example in mind, and in the spirit of a previous issue, I have some questions with regards to their proposeddeveloper verification program. To Google, these questions might be uncomfortable.

Question 1. What considerations were taken into account with regards to developers with legitimate reasons for anonymity?

For example, suppose that a developer creates an ICEBlock-like app, but one that supportsAndroid, which ICEBlock does not. Let���s call this workalike app ���ICE Scream���. Given the experiences of ICEBlock���s developer, the developer of ICE Scream may have concerns over their identity being disclosed. How would Google like to address such scenarios?

Question 2. Which civil society organizations (e.g., EFF and AccessNow in the US) did Google engage with to review your plans, and what were the results of those engagements?

Organizations such as these have a long track record of dealing with the balance of equities related to privacy and security. A theoretical developer of ���ICE Scream��� would be well-advised to try reaching out (anonymously) to such organizations for advice. Similarly, their expertise and outside opinions should be of value to Google while drafting programs such as developer verification, and so one hopes that Google availed themselves of such assistance. How did that go?

Question 3. Why does Google���s privacy policy allow Google to share ���personal information��� with any ���businesses or persons���?

Google���s privacy policy states that it provides ���personal information to [Google���s] affiliates and other trusted businesses or persons to process it for us���, with no obvious restrictions on who or what constitutes ���trusted businesses or persons��� and no obvious restrictions on what those parties can do with the personal information. How does Google wish developers, such as the ICE Scream developer, to interpret that policy?

Question 4. How will apps be developed starting in 2027, as app development uses debug keystores, and those keystores do not seem to be part of the Android Developer Verification process? Will it no longer be possible to test apps under development on Google-certified production hardware?

Debug keystores are designed to be transient, as there are few repercussions from replacing those keystores as needed. As such, debug keystores can come and go, especially in environments like classrooms and continuous integration (CI) servers. It seems unrealistic to have these all be registered on a Google-supplied Web site, and it seems hostile to require anyone who wants to learn Android app development to file ���papers, please���. How does Google wish for this to work?

Question 5. Similarly, how will apps be developed starting in 2027, as app development often uses duplicate package names (e.g., in educational settings), and duplicate package names are banned? For example, how will developers be able to build and run Google���s own sample projects?

I have a particular interest here, as I am the author of many books on Android app development. I expected readers to be able to download and run the samples, but under this program, it seems like at most one person on the entire planet can do that. Everybody else will get error messages until they go in and change the package name��� which will be beyond the skills of many people just learning Android app development. How does Google wish for this to work?

If you have your own questions and concerns, in addition to writing blog posts, you can fill out and submit this form.

If any civil society organizations or others are interested in discussing these and related questions, please reach out!.

And, heck, if Google itself would like to talk about this,they too can reach out.

 •  0 comments  •  flag
Share on Twitter
Published on August 26, 2025 16:07

August 23, 2025

Random Musings on the Android 16 QPR2 Beta 1

Apparently, the mid-cycle update will be under the ���QPR��� brand, so we got a beta forit. We did not get a developer preview AFAIK, and it is unclear if there willbe any follow-on betas, despite the ���1��� nomenclature. ��������

So, given the announcement blog postandthe API differences report,let���s see what we are in store for.

What Might Get Google Sued

According to the blog post:

Android 16 QPR2 can automatically generate a themed icon for your app if you don���t provide a dedicated one. The system applies a color filtering algorithm to your existing launcher icon to render it in a monochrome style, allowing it to integrate with the user���s chosen theme.


Given that some firms have a lot invested in particular colors, one imagines that therewill be some disputes over Google���s unilateral decision to replace those colors becauseGooglers feel like it.

What Else is Scary

The new CAPTURE_KEYBOARD permission���Allows an application to be able to capture keys before Android system get chance to process system keys and shortcuts.���However, this is a normal permission, which means apps can request it and users are notinvolved. Depending on the scope of this ���capture��� of keyboard events, a permission like thisshould be dangerous at minimum.

What Might Break Your App

The system-generated dark theme outlined in the blog postmight work some of the time but feels like it is unlikelyto work all of the time. If you do not already implement a dark theme ��� one recognizedby the system ��� you might wish to test this behavior.

The changes to MediaRouter outlined in the blog post might cause you to need to requestmore permissions, such as BLUETOOTH_SCAN, that you have not needed previously.

There is a ���camera compatibility treatment for fixed-orientation apps���that you can opt out of.If you use the camera, pay attention to this.

What Requires Research

Launcher implementations should look into startVisibilityTracking()andstopVisibilityTracking()on AppWidgetHostView. These will help trigger AppWidgetEvents for when the app widgetcan and cannot be seen by the user, for metrics and whatnot. App widget developers canuse queryAppWidgetEvents()on AppWidgetManager to find out about those events.

Launcher implementations ought to consider also researching the newACCESS_LAUNCHER_DATA permission.

Notification now offers isRequestPromotedOngoing() and Notification.Builder hassetRequestPromotedOngoing(),for whatever ���promoted ongoing notifications��� are.

What Points Towards ���Android on the Desktop���

The blog post describes ���Display Topology API���, ���Device-aware ViewConfiguration���, ���Controlled Mouse Scrolling���,���Files Desktop UX���, and a variety of printer API updates. While not completely irrelevantfor phones or tablets, this all points to a bigger push for Android on the desktop, perhapsas part of the Chrome OS changes.

What���s Next?

The timeline in the blog post suggests two more betas, but it is unclear to what extentthey will introduce more changes, revert some of this release���s changes, or otherwiseaffect us.

 •  0 comments  •  flag
Share on Twitter
Published on August 23, 2025 10:40

July 29, 2025

29 July 2025 Artifact Wave, Part One

My guess is that we will get a drop tomorrow. However, a Media3 update is already available,and I will not be in position to pick up tomorrow���s drop until later in the week, sohere is what we have now!

androidx.media3:media3-cast:1.8.0-rc02 androidx.media3:media3-common:1.8.0-rc02 androidx.media3:media3-common-ktx:1.8.0-rc02 androidx.media3:media3-container:1.8.0-rc02 androidx.media3:media3-database:1.8.0-rc02 androidx.media3:media3-datasource:1.8.0-rc02 androidx.media3:media3-datasource-cronet:1.8.0-rc02 androidx.media3:media3-datasource-okhttp:1.8.0-rc02 androidx.media3:media3-datasource-rtmp:1.8.0-rc02 androidx.media3:media3-decoder:1.8.0-rc02 androidx.media3:media3-effect:1.8.0-rc02 androidx.media3:media3-exoplayer:1.8.0-rc02 androidx.media3:media3-exoplayer-dash:1.8.0-rc02 androidx.media3:media3-exoplayer-hls:1.8.0-rc02 androidx.media3:media3-exoplayer-ima:1.8.0-rc02 androidx.media3:media3-exoplayer-midi:1.8.0-rc02 androidx.media3:media3-exoplayer-rtsp:1.8.0-rc02 androidx.media3:media3-exoplayer-smoothstreaming:1.8.0-rc02 androidx.media3:media3-exoplayer-workmanager:1.8.0-rc02 androidx.media3:media3-extractor:1.8.0-rc02 androidx.media3:media3-muxer:1.8.0-rc02 androidx.media3:media3-session:1.8.0-rc02 androidx.media3:media3-test-utils:1.8.0-rc02 androidx.media3:media3-test-utils-robolectric:1.8.0-rc02 androidx.media3:media3-transformer:1.8.0-rc02 androidx.media3:media3-ui:1.8.0-rc02 androidx.media3:media3-ui-compose:1.8.0-rc02 androidx.media3:media3-ui-leanback:1.8.0-rc02
 •  0 comments  •  flag
Share on Twitter
Published on July 29, 2025 14:31