Mark L. Murphy's Blog, page 9
October 16, 2022
Issue Trackers and the ���Can���t Deliver Broadcast��� Bug
There is a bug, unique to Android 13, with this stack trace:
Fatal Exception: android.app.RemoteServiceException$CannotDeliverBroadcastException: can't deliver broadcast at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:1980) at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException() at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2242) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7898) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)Application code does not appear in the stack trace, and it shows up in appsthat do not send or receive broadcasts on their own. The culprit probably is somelibrary (possibly Firebase Cloud Messaging).
So, the community did the right thing and reported this bug, back on September 6. Todate, a bunch of name-brand companies have reported seeing the problem with Android 13devices, including:
Atlassian the BBC Costco RingCentral Target WalmartThose are just the ones identifiable by domain name. My current employeris part of a similarly-sized brand, and we haven���t posted a ���me too!��� comment,so my guess is that lots of others are just watching this issue, hoping for a fix.
And it���s probably going to take ���somebody who knows somebody��� for this issue to get muchattention.
Google���s issue tracker has a so-so track recordof getting conclusive results. Personally, I have filed19 issues in that component,only 8 of which got to a real end status (Fixed, WAI, or Infeasible). 3 arestill open, including one from 2019. And 8 are ���Obsolete���, which basically meansit was ignored and then closed. I havea similar track record overall.
Compounding the problem is that this component is riddled with spam.Only 38 of the most recent 250 tickets are real. The rest are spam.
The vibe that this gives off is that Google has largely given up on this issuetracker. That���s Google���s right, of course, as it is their issue tracker.
But, then, how does Google want us to report problems like the one cited above?
In truth, the viability of this issue tracker seems tied to the particularcomponent. Some Jetpack libraries do a solid job of making use of the issue tracker.Other components, like the one for developer.android.comare where issues go to die, buried under yet more spam.
If you work for Google, and you want the community to report issues, make sure thatwe know where that is. Perhaps it is this issue tracker, in a better-maintainedcomponent (or at least one the spammers aren���t using).Perhaps it is GitHub���s issue tracker for some relevant repo. Perhapsyou find the best approach is by unofficial means, such as Kotlinlang Slack orsocial media. So long as you tell us what we should use, we can use it.
And, um, if somebody could look at that bug,I���d really appreciate it.
April 30, 2022
Random Musings on the Android 13 Developer Beta 1
Each time Google releases a new developer preview, I rummage throughthe API differences reportthe 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.
Once we get to beta releases, changes to the API surface should start to diminish,and Android 13 Beta 1 is no exception. The API differences report is a fractionof what came in the two developer previews, and even those seemed smaller than in pastyears.
What Will Break You, EventuallyREAD_EXTERNAL_STORAGE effectively is deprecated. Once your targetSdkVersion hits32 (for most developers, in 2023), you will need to stop requesting READ_EXTERNAL_STORAGEand start requesting one or more of:
READ_MEDIA_AUDIO READ_MEDIA_IMAGES READ_MEDIA_VIDEOThose will affect your ability to read from the standard shared media collections. For othertypes of content, use the Storage Access Framework.
What May Break You, SoonerMishaal Rahman of Esper wrote this week about predictive back navigation.
(IMHO, ���predictive��� often means ���royally screwed up���)
Mishaal goes into a lot of detail,but the upshot is that it appears that Google wants to use animations to help indicateto a user when a system BACK navigation gesture will send the user to the home screen versusdoing something else. If you manage your own BACK navigation, such as by overridingonBackPressed() somewhere, you may need to migrate to the new OnBackInvokedDispatcherapproach, and you may need to fiddle with android:enableOnBackInvokedCallbackif you find that ���predictive back navigation��� breaks things.
As Mishaal notes, hopefully this Google I|O sessionwill clarify things.
BTW, note that OnBackInvokedDispatcher moved from android.view to android.windowin Beta 1.
What Else Changed?There is an option now to listen to when the keyguard comes and goes.This requires a new SUSBSCRIBE_TO_KEYGUARD_LOCKED_STATEpermission. However, this is designed solely for use by ROLE_ASSISTANT apps, so itwill not be available to many developers.
Some notable things were deprecated:
ACQUIRE_CAUSES_WAKEUP, for use with WakeLock
get() on BaseBundle and subclasses
Several methods on Bundle, whereyou now need to add a Class parameter to the end of the parameter list
The mysterious SPLASH_SCREEN_STYLE_EMPTY value wasrenamed to SPLASH_SCREEN_STYLE_SOLID_COLORand appears to give you a way of opting out of having an icon on the mandatory splash screen.
Finally, if you have been using the force-dark options on WebSettings, those weredeprecated and replaced by ���algorithmic darkening allowed��� methods,because those names just roll off the tongue.
What Comes Next?We are slated to get three more beta releases. I expect there to be few API changes.If that turns out to be true, most likely this will be the last ���random musings���post for the Android 13 cycle.
The final release date is murky, as usual, but probably is in the August/Septembertimeframe. Be sure to budget time in May/June (if not sooner) to start playing with Android 13 andtesting your app���s compatibility with it, perhaps in tandem with raising your targetSdkVersion to 31.
March 19, 2022
Random Musings on the Android 13 Developer Preview 2
Each time Google releases a new developer preview, I rummage throughthe API differences reportthe 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.
What Got Clarified From Last TimeAbout five weeks ago, I wrote about DP1.
This time around, the ���13 DP1 while 12L is in beta?��� answer is ���12L is now foldedinto DP2���.
Also, they are nowdocumenting the POST_NOTIFICATIONS permission.Of particular note is that this permission affects all apps, regardless oftargetSdkVersion. If your targetSdkVersion is below Android 13���s presumedvalue of 33, the system will prompt the user to grant permission when you createyour first notification channel, for a newly-installed app on 13.If you create that channel at an inopportune time��� you willneed to modify your app.
They are also documenting the new option for controlling whether a dynamically-registered receiver is exported.And, they mentioned in the blog post the option for revoking already-grantedpermissions, though the method name changed.Plus, they talk a bit about BODY_SENSORS_BACKGROUND.
But beyond that, the mysteries from DP1 remain mysteries.
What Else Got Announced Of Note?The biggest thing is the Foreground Services (FGS) Task Manager. This allowsusers to stop your app���s process easily from the notification shade, if yourapp has a foreground service running. Of particular note is that the OS willnag users periodically about your app, if your service runs most of the time(20 hours out of the preceding 24, with a maximum of one nag per month).Developers may wind up adding flagsto avoid users getting bothered by those messages, which in turn will causeGoogle to remove the impacts of those flags in some future Android release.
(if you spend enough time in Android development, predicting developer-and-Googleactions in advance becomes simply a matter of pattern matching���)
The War on Background Processingcontinues, beyond the FGS Task Manager.
In a tweak to JobScheduler, ���In Android 13, the system now tries to determine the next time an app will be launched���,which is not at all creepy. Nope, not one bit.
The official blog post mentions a few things that might impact a smallpercentage of developers.
And, that���s pretty much it for official stuff.
What���s Up With All the New Permissions?DP2 adds 15 new permissions over DP1,let alone prior versions of Android.
Some of these, like MANAGE_WIFI_AUTO_JOIN and MANAEG_WIFI_INTERFACES, aredocumented as ���Not for use by third-party applications���, which makes you wonderwhy they bothered to put them in an Android SDK that is explicitly for third-party applications.
They have added three content-specific permissions: READ_MEDIA_AUDIO,READ_MEDIA_IMAGE, and READ_MEDIA_VIDEO. The JavaDocs indicate that theseare replacements for READ_EXTERNAL_STORAGE, but only for those app that targetAndroid 13 or higher. Presumably, holding one of these permissions, and not READ_EXTERNAL_STORAGE,only gives you read access to that media type and not other content.
The other permission that may generate widespread interest is USE_EXACT_ALARM. Android 12 added SCHEDULE_EXACT_ALARM to be able to use exact alarms, but thisis an ���app ops��� permission, one that users have to grant directly in Settings.USE_EXACT_ALARM appears to be a normal permission, but the JavaDocs make it plain that the Play Store (and perhaps elsewhere) will require you to fill out a formto be able to ship an app that requests it. As many developers are discoveringwith MANAGE_EXTERNAL_STORAGE, you need a really good reason to request one of thesesorts of permissions.
What Is Up In the CloudsLast time, I mentioned ACTION_PICK_IMAGES and how it may be backed by CloudMediaProvider objects.It appears that this might extend beyond images someday, as there arenew interfaces tied to CloudMediaProvider rendering previews on a supplied SurfaceIt���s possible that this is just for animated images, but my guess is that theremay be more coming in this area, if not in Android 13 then in future versionsof Android.
What Makes Me Want to Change the ChannelThere are a ton of new and changed classesin android.media.tv,which pertains to actual TV channel playback on an Android TV device.
And, there arechanges to the new android.media.tv.interactive package,including a new TvInteractiveAppService.That is described as ���a service that provides runtime environment and runs TV interactive applications���.It is not completely clear what ���TV interactive applications��� are that are somehowdifferent from ���applications that run on Android TV���.
What Brings to Mind a Beatles SongActivities, fragments, and dialogs all now implement OnBackInvokedDispatcherOwner.From these, you can get an OnBackInvokedDispatcherand use that to register an OnBackInvokedCallback,allowing you to get back.
What Makes Me Wonder If These Supplements Are FDA-ApprovedA process can be supplemental.It is unclear what this means.
There is also a new supplementalapi package,containing an AdServicesVersion. Because of course an operating systemshould be in the business of managing ads.
Oddly, given these changes, SUPPLEMENTAL_PROCESS_SERVICE was removed in DP2 afterhaving been added in DP1.
What Fulfills Some Developer FantasiesI see a fair number of developers wanting to block or the screenshots shown in the overviewscreen. FLAG_SECURE blocks them nicely, but also blocks all other types of screenshots.Some developers have been trying to play games with replacing the activity contentat just the right time, like that���s ever going to be reliable. Now, we finally canopt out of those screenshots.
What Helps Me Stay Awake At NightYou can now call setStateListener() on a WakeLockand register a WakeLockStateListener,letting you know if the WakeLock is enabled or disabled.
Mostly, this appears to be for cases where you want the wakelock to be enabled,but the system decided to disable it.
What Else Caught My EyeThere are a bunch of new feature identifiers,including ones that seem tied to the 12L merger (e.g., FEATURE_EXPANDED_PICTURE_IN_PICTURE).
Android 12L added activity embedding, as one way to take advantage of largerscreen sizes. 13 DP2 appears to let you control which apps can embed youractivities, via android:knownActivityEmbeddingCertsattributes on <application> and <activity>. And, it appears that you canallow arbitrary apps to embed your activity via android:allowUntrustedActivityEmbedding.
There is now an EthernetNetworkSpecifier,suggesting a continued push into less-mobile devices, like perhaps TVs.
There is now a Dumpable and DumpableContainer pair of interfaces,for dumping stuff to a PrintWriter.
showNext() and showPrevious() on RemoteViews are now deprecatedin favor of setDisplayedChild().
There is a new LocaleConfig class,with a broken explanation.
There is a new isLowPowerStandbyEnabled()method on PowerManager, continuing the War on Background Processing. Becausewhen you have 18 power modes, adding a 19th will help. On the other end of the power spectrum, you can now find out if you are plugged into a dock,though I now really want BATTERY_PLUGGED_WHARF and BATTERY_PLUGGED_PIER as well.
And it still seems as though changes are afoot for android:sharedUserId, asthe new EXTRA_NEW_UIDwill tell you about changes to uids.
February 12, 2022
Random Musings on the Android 13 Developer Preview 1
Each time Google releases a new developer preview, I rummage throughthe API differences reportthe 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.
I am not feeling the best today, so I apologize if that impacts the quality of this post.
What Gives Me the ���Time Has No Meaning��� Vibe12L has not shipped in final form yet, and we already have a 13 developer preview?
Even more surprising is the timeline, indicating that a final edition of 13 mightship as early as August.
My initial reaction to 12L was that schedules slipped, so they elected to movetablet-focused items out of the 12 release timeframe. Now, I do not what to think.However, you will want to plan on getting your 13 compatibility testing done a bitearlier than you have had to in previous years.
What Makes Me Want to Pick a Peck of Pickle PhotosACTION_PICK_IMAGES is interesting.I am uncertain what the advantage is of a new Intent action over having ACTION_OPEN_DOCUMENTuse a different UI for image/video MIME types. Still, anything to improve content accessfor developers is a positive thing.
Note that the photo picker seems to be backed by CloudMediaProvider objects.These appear to serve the same role for the photo picker that document providers servefor the Storage Access Framework. If your app is in the business of making photos available,particularly from collections that MediaStore does not index (e.g., cloud), you maywant to pay close attention to CloudMediaProvider.
What Makes Me Want to Speak in TonguesPer-app language preferencesis a very nice improvement. As I wrote about a month ago,developers use hacks to try to get this sort of behavior, and having an official solutionis great! Even better is a statement about Jetpack support for older devices.
Still, most of my questions from that earlier postremain unanswered. For example, if the device language is English and the applanguage is Spanish, and we use ACTION_PICK_IMAGES, what language is used by the photo picker?
From an API standpoint, you can bring up the relevant Settings screen viaACTION_APP_LOCALE_SETTINGS.In theory, you can react to changes via ACTION_APPLICATION_LOCALE_CHANGED,but that apparently requires an undocumented READ_APP_SPECIFIC_LOCALES permission. Hopefully, there is a configurationchange when the app language changes, just as there is a configuration change when thedevice language changes. LocaleManager lets you directly manipulate the user���sselection of language.
What Other High-Profile Things Are Nice to SeeIf you need to talk to local WiFi devices, the NEARBY_WIFI_DEVICES permissionprobably is a big help. This is a common requirement for bootstrapping IoT devices, for example.
JDK 11 support is nice.If it only goes back to Android 12, it will be years before it matters, but it is still nice.
Programmable shaders soundpromising, if you���re into that sort of thing. Similarly, if you have had hyphenationperformance anxiety before, faster hyphenationis nice, except that it will be years before that improvement is something that is outfor a majority of devices.
And, for the ~148 developers writing tiles, helping users add your tilesis a handy thing.
What High-Profile Things Make Me YawnI am somewhat mystified by ���Intent filters block non-matching intents���,in terms of what the actual problem is that is being solved. This does not appearto be a security thing, as external apps can still start your components ��� theyjust cannot do so via a purely explicit Intent.
Themed app iconscontinues Google���s Material You initiative. Color me uninterested.
What Was Rumored But That Google Is HidingThe Android Resource Economy (TARE) is yet another salvo in The War onBackground Processing. Mishaal Rahman reports that it is there,but it appears that Google did not document it.
By contrast, POST_NOTIFICATIONS ��� the permission that you need to holdto raise notifications ��� is in the JavaDocsbut is not mentioned in the required app changes documentation. My guess is thatthis is a documentation gap. Mishaal reports thatit will only be enforced for apps targeting API 33.If true, this gives developers a year to ignore it, only to then scrambleat the last minute to deal with the change.
(not you, of course ��� you are reading this blog post, so clearly youare a forward-thinking developer)
Mishaal also mentions that the clipboard will automatically clear,which is a win for privacy, but really ought to be pointed out to developersbeyond this blog post.
What Makes Me Scratch My Head, But Over There, Not HereThere is a new NEARBY_STREAMING_POLICY permission.The underlying policy ���controls whether to allow the device to stream its notifications and apps to nearby devices���(emphasis added).
There is also canDisplayOnRemoteDevices,which says ���whether the activity can be displayed on a remote device which may or may not be running Android���(emphasis also added).
This makes me wonder what Google is up to.
What���s Old is New AgainAndroid 12 added a mandatory splash screen. Android 13 appears to make that less mandatory:a launcher could try to use setSplashScreenStyle()with SPLASH_SCREEN_STYLE_EMPTYto perhaps inhibit that splash screen. At least, that is how I interpret this API.
What Requires Better Penmanship Than I PossessHandwriting is getting system-level love, such as supportsStylusHandwriting.
This matters little to me, as my handwriting sucks.
What Are Other Nice ChangesThere is a new, non-dangerous READ_BASIC_PHONE_STATE permission.It is unclear what you get access to with that permission, but READ_PHONE_STATEseems overused.
Speaking of permissions, not only can you request runtime permissions, but onAndroid 13, you can revoke ones that you were granted earlier.
One of the long-standing problems with registerReceiver() is that the resultingBroadcastReceiver was always exported. This is not great from a security standpoint.Now, it appears as though we can control this.
A popular request in places like Stack Overflow is for a way to get the current time,from a time source that cannot be modified by the user. Android 13 gives uscurrentNetworkTimeClock(),which reports the time from a network time source (e.g., SNTP). As the docsnote, this time still could be modified, but not easily.
What Will Require Some WorkAll those PackageManager methods that took a ���flags��� int? They are all deprecated andreplaced with ones that take richer objects.
If you work with Parcel directly, there are lots of deprecations and lots of replacements.
What Else Might Break Your AppsThere is a new BODY_SENSORS_BACKGROUNDpermission. Presumably, it is required for background apps that wish to read heartrate or similar data, such as on Wear OS. This permission has scary languageabout being ���a hard restricted permission which cannot be held by an app until theinstaller on record allowlists the permission���. If your app already requestsBODY_SENSORS, pay close attention to what eventually gets documentedabout the need for BODY_SENSORS_BACKGROUND.
There is a new ���light idle mode���, as seen in isDeviceLightIdleMode()and ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED.This is when ���when a device has had its screen off for a short time, switching it into a batching mode where we execute jobs, syncs, networking on a batching schedule���.The ���networking��� aspect of this is particularly disconcerting, and hopefully morewill be explained about this mode.
Some methods were outright removed from the SDK, mostly in android.webkit.
What Else Might Break Your Apps In the Not-Too-Distant Futureandroid:sharedUserId is already deprecated. Google appears to be working on migrationpaths for apps that presently rely upon it, such as sharedUserMaxSdkVersionand EXTRA_UID_CHANGING.My guess is that android:sharedUserId will be ignored in some future Android release.If you are relying upon android:sharedUserId, start work on some alternative mechanism,and watch for documentation on how best to migrate to a non-sharedUserId world.
What Really Needs DocumentationThere is a new system service, advertised under the SUPPLEMENTAL_PROCESS_SERVICEname. It is unclear what this is for.
Mishaal Rahman writes about ���hub mode���,and the docs have things like showClockAndComplicationsthat seem to tie into that. Perhaps this will debut in a later developer preview.
There is a TvIAppManager,described as being the ���system API to the overall TV interactive application framework (TIAF) architecture, which arbitrates interaction between applications and interactive apps���.Right now, that system service has no methods, so the fact that it is undocumented isnot a huge loss. This too might show up in some later developer preview.
There are a bunch of new KeyEvent keycodesthat really could use some explanation (e.g., what is ���Video Application key #1���, exactly?).
January 22, 2022
Navigating in Compose: Criteria
Navigating between screens is a common act in an Android app��� though, asZach Klippenstein noted,���screen��� is a somewhat amorphous term. Naturally, we want to be able to navigateto different ���screens��� when those screens are implemented as composables.
How we do this is a highly contentious topic.
Roughly speaking, there seems to be four major categories of solutions:
Use the official Jetpack Navigation for Compose
Use some sort of wrapper or helper around Navigation for Compose ���Rafael Costa���s compose-destinations libraryis an example
Use Jetpack Navigation, but use the ���classic��� implementation instead of Navigationfor Compose, using fragments to wrap your screen-level composables
Use some separate navigation implementation, such as Adriel Caf�����s Voyager library
I cannot tell you what to use. I can tell you that you should come up with a setof criteria for judging various navigation solutions. Based on a survey of a bunchof navigation solutions, here are some criteria that you may want to consider.
Table StakesIf your navigation solution does not support forward navigation to N destinations,or if it does not support back stacks (e.g., a goBack() function to pop a destinationoff the stack and return to where you had been before), use something else.
Compile-Time Type SafetyOne key point of using Kotlin, and Java before it, is type safety. The more typesafety we get, the more likely it is that we will uncover problems at compile-time,rather than only via testing or by the app going ���� for your users.
���For Routes/DestinationsWhen you tell the navigation solution where to navigate to in forward navigation,you may want to prefer solutions where the identifier is something that is type safe.Some solutions use strings or integers to identify routes or destinations. That makesit very easy to do some really scary things, like compute a destination using math.Generally, primitives-as-identifiers offer little compile-time protection. You might prefersolutions that use enums, sealed class, marker interfaces, or other things thatidentify what are and are not valid options.
(and if you are asking yourself ���what about deeplinks?���, that is covered a bit later)
���For ArgumentsFrequently, our screens need data, whether an identifier (e.g., primary key) orthe actual data itself. So, we want to be able to pass that data from previousscreens. All else being equal, you might want to prefer solutions that offer compile-timetype safety, so you do not wind up in cases where you provide a string and the recipientis expecting an Int instead.
A related criteria is ���content safety���. You might want to prefer solutions where yourcode can just pass the data, without having to worry about whether it complies withany solution-specific limitations. For example, if the solution requires you to URL-encodestrings to be able to pass them safely, that is not great, as you will forget to do this fromtime to time. Ideally, the solution handles those sorts of things for you.
���For Return ValuesAt least for modal destinations, such as dialogs, we often need to pass back somesort of ���result���. For example, we display a dialog to allow the user to pick something,and we need the previous screen to find out what the user selected. Sometimes, thereare ways of accomplishing this outside of a navigation solution, such as the dialogupdating some shared data representation (e.g., shared Jetpack ViewModel) wherethe previous screen finds out about results reactively. But, if the navigation solutionyou are considering offers return values, and you intend to use them, you might wantto prefer ones where those return values are also type-safe and content-safe.
IOW, forward-navigation arguments should not get all the safety love.
Support for Configuration Change and Process DeathLike it or not, configuration changes are real. Birds, perhaps not.
One way or another, your app needs to be able to cope with configuration changes,and your navigation solution should be able cope as well, to support your app.This includes both retaining the navigation data itself across configuration changesand, ideally, having a pattern for app data for your screens to survive as well(e.g., Navigation for Compose���s per-route ViewModel support).
Related is process death:
The user uses your app for a while
The user gets distracted by some not-a-bird for a while, and your app���s UI moves to the background
While in the background, Android terminates your process to free up system RAM
The user returns to your app after your process dies, but within a reasonable period of time(last I knew, the limit was 30 minutes, though that value may have changed over the years)
Android is going to want to not only bring up your app, but pretend that your processhad been around all that time. That is where ���saved instance state��� comes into play,and ideally your navigation solution advertises support for this, so your back-stackand so on get restored along with your UI.
Hooks For Stuff You Might UseOnly you know what your app is going to need to do in terms of its UI. Or perhapsyour designers know, or your product managers. Or, hey, maybe you are just sprayingpixels around like Jackson Pollock sprayed paint.Who am I to judge?
Regardless, there may be some things that you want in your app���s UI or flow thattie into what you will need out of your navigation solution.
Tabs, Bottom Sheets, Pagers, Etc.Many apps use these sorts of UI constructs. It may not be essential that they be handledvia a navigation solution ��� you might be able to model them as being ���internal implementation���of a screen, for example. But, it would be good to get a sense of what patternsare established, if any, for a particular navigation solution to tie into thesekinds of UI constructs. For example, if you need to able to not only navigate to a screen, butto a particular tab or page within that screen, it would be nice if the navigationsolution supported that. Perhaps not essential, but nice.
And, for some of these UI constructs, you might be seeking to have multiple back stacks. For example,you might want to have it so that back navigation within a tab takes you to previous contentwithin that tab, rather than going back to other tabs that the user previously visited.Support for multiple back stacks seems to be a bit of an advanced feature, so if thisis important to you, see what candidate navigation solutions offer.
DeeplinksDeeplinks are popular. Here, by ���deeplink���, I not only mean situations where a destinationis triggered from outside of the app, such as from a link on a Web page. I also meancases where a destination is determined at runtime based on data from an outsidesource, such as a server-driven ���tip of the day��� card that steers users to specificscreens within the app.
If you think that you will need such things, it will be helpful if your navigationsolution supports them directly. That support may not be required ��� just as yourother app code can navigate to destinations, your ���oh, hey, I got a deeplink��� codecan navigate to destinations. However, a navigation solution may simplify that,particularly for cases where the deeplink is from outside of the app and you needto decide what to do with the already-running app and its existing back stack.
When evaluating deeplink support, one criteria that I will strongly suggest is:deeplinks should be opt-in. Not every screen in your app should be directlyreachable by some outside party just by being tied into some navigation system��� that can lead to some security problems.
Also, consider how data in the deeplink will get mapped to your arguments (at leastfor routes that take arguments). Some navigation solutions will try to handlethat automatically for you, but be wary of solutions that use deeplinks as an excuseto be weak on type safety. Ideally, there should be an unambiguous way to convert piecesof a deeplink (e.g., path segments, query parameters) to navigation arguments, butin a way that limits any ���stringly typed��� logic to deeplinks themselves and does notbreak type safety elsewhere.
TransitionsYour designers might call for a specific way to transition from screen X to screenY, such as a slide, a fade, a slide-and-fade, a fireworks-style explosion destroyingX with Y appearing behind it, etc. Ideally, the navigation solution would handlethose sorts of transitions, particularly if you need to control the back-navigationtransition as well (e.g., the exploded fireworks somehow reassembling themselves into a screen,because that sounds like fun).
Development ConsiderationsDoes the library have clear documentation? Does it seem to be maintained? Does ithave a clear way of getting support? Does it have a license that is compatible withyour project? These are all common criteria for any library, and navigation solutionsare no exception.
Beyond that, navigation solutions have a few specific things that you might want toconsider, such as:
How easily can you support navigation where the destinations might reside in differentmodules? Particularly for projects that go with a ���feature module��� development model,it is likely that you need a screen in module A to be able to navigate to a screen inmodule B.
Are there clear patterns for using @Preview? In principle, a navigation solutionshould not get in the way of using @Preview for screen-level composables, but it wouldbe painful if it did.
Does the solution work for development targets beyond Android? Perhaps you are notplanning on Compose for Desktop or Compose for Web orCompose for iOS orCompose for Consoles. If you are, youare going to want to consider if and how navigation ties into your Kotlin/Multiplatformambitions.
This is not a complete list ��� if there are things that you think are fairly popularthat I missed, reach out!
January 17, 2022
Compose for Wear: CurvedRow() and CurvedText()
Compose UI is not just for phones, tablets, foldables, notebooks, and desktops.Compose UI is for watches as well, via the Compose for Wear set of libraries.
(Google calls it ���Wear Compose��� on that page, but that just makes me think���Wear Compose? There! There Compose!���).
(and, yes, I���m old)
Compose for Wear has a bunch of composables designed for the watch experience.In particular, Compose for Wear has support for having content curve to matchthe edges of a round Wear OS device.
The Compose for Wear edition of Scaffold() has a timeText parameter. Thisis a slot API, taking a composable as a value, where typically you will see thatcomposable delegate purely to TimeText(). That gives you the current timeacross the top of the watch screen, including curving that time on round screens:
[image error]
The implementation of TimeText() uses CurvedRow() and CurvedText() to accomplish this,if the code is running on a round device. Otherwise, it uses the normal Row() andText() composables,
TimeText() is a bit overblown, particularly for a blog post, sothis sample projecthas a SimpleTimeText() composable with a subset of the functionality:
@ExperimentalWearMaterialApi@Composablefun SimpleTimeText( modifier: Modifier = Modifier, timeSource: TimeSource = TimeTextDefaults.timeSource(TimeTextDefaults.timeFormat()), timeTextStyle: TextStyle = TimeTextDefaults.timeTextStyle(), contentPadding: PaddingValues = PaddingValues(4.dp)) { val timeText = timeSource.currentTime if (LocalConfiguration.current.isScreenRound) { CurvedRow(modifier.padding(contentPadding)) { CurvedText( text = timeText, style = CurvedTextStyle(timeTextStyle) ) } } else { Row( modifier = modifier .fillMaxSize() .padding(contentPadding), verticalAlignment = Alignment.Top, horizontalArrangement = Arrangement.Center ) { Text( text = timeText, style = timeTextStyle, ) } }}We can determine whether or not the screen is round from the isScreenRoundproperty on the Configuration, which we get via LocalConfiguration.current.If the screen is round, we display the current time in a CurvedText() and wrapthat in a CurvedRow(). CurvedText() knows how to have the letters of the textfollow the curve of the screen, and CurvedRow() knows how to have child composablesfollow the curve of the screen.
The timeText slot parameter in Scaffold() puts the time at the top of thescreen by default. That position is controlled by the anchor parameter toCurvedRow(), where the default anchor is 270f. anchor is measured in degrees,and 270f is the value for the top of the screen (probably for historical reasons).
SampleRow() in that sample project lets us display multiple separate stringsvia individual CurvedText() composables, in a CurvedRow() with a custom anchor value:
@Composableprivate fun SampleRow(anchor: Float, modifier: Modifier, vararg textBits: String) { CurvedRow( modifier = modifier.padding(4.dp), anchor = anchor ) { textBits.forEach { CurvedText(it, modifier = Modifier.padding(end = 8.dp)) } }}SampleRow() accepts a Modifier and tailors it to add a bit of padding to the CurvedRow().
We can then use SampleRow() to display text in other positions on the screen:
@ExperimentalWearMaterialApi@Composablefun MainScreen() { Scaffold( timeText = { SimpleTimeText() }, content = { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text(text = "Hello, world!") SampleRow(anchor = 180f, modifier = Modifier.align(Alignment.CenterStart), "one", "two", "three") SampleRow(anchor = 0f, modifier = Modifier.align(Alignment.CenterEnd), "uno", "dos", "tres") SampleRow(anchor = 90f, modifier = Modifier.align(Alignment.BottomCenter), "eins", "zwei", "drei") } } )}An anchor of 0f is the end edge of the screen, 90f is the bottom, and 180f is the start edge.Note that we also use align() to control the positioning within the Box(), with valuesthat line up with our chosen anchor values.
The result is that we have text on all four edges, plus a centered ���Hello, world!���:
[image error]
CurvedRow() does not handle consecutive bits of CurvedText() all that well ���ideally, use a single CurvedText() with the combined text. However, CurvedRow()is not limited to CurvedText(), and I hope to explore that more in a future blog post.
January 15, 2022
Tiramusu Thoughts
Leaks about what will be coming up Android 13 / Android T / Tiramusu are makingthe rounds, in places like XDA Developersand Android Police. Some of whatis discussed will have little impact on developers. Other things will be yourtypical ���double-edged sword��� of opportunity and pain.
So, let���s slice some tiramisu with a sword.
Notification PermissionIt appears as though being able to raise notifications will require a runtimepermission. XDA has screenshotsshowing ���Notifications��� as a permission alongside other standard runtime permissionslike ���Camera��� and ���Contacts���. This suggests that there will be a new dangerouspermission for notifications.
However, ���notifications��� is a rather broad bucket. App developers are going to have todo a fair bit of work to educate users about how the app uses notifications beforepresenting the permission. Perhaps that education process alone will help to get firmsto cut back on the number of superfluous notifications that are presented.
My biggest concern here, though, is what happens when the permission is declined by theuser. Typically, with these permissions, that triggers a SecurityException when you attempt to use an API tied to the permission. So, in this case, perhaps notify() on NotificationManagerwould throw a SecurityException.
My sincere hope is that either this does not happen or it is something we can opt out of(e.g., via StrictMode). Ideally, this is a quiet failure, logging messages to Logcatbut not crashing the app.
Google went out of their way, for a better part of a decade, to shove notificationsdown the throats of developers. With pretty much all the other dangerous permissions,Google simply made APIs available, then restricted them later. In the case of notifications,though, Google proactively took steps to try to convince developers to rely uponnotifications. Saying that ���OK, now what we told you to do is at risk of crashing your app���is just plain rude.
We also have to deal with specific notification scenarios. For example, does this permission imply thatforeground services are impossible if users decline the permission? What happens iflibraries raise notifications? And so on.
Of all the proposed changes, this one scares me the most, just in terms of how Googlemight go about implementing it and the impacts it can have on developers.
TARE: The Android Resource EconomyXDA also talks about TARE: The Android Resource Economy.
The idea that users might have some way of offering fine-grained advice on whatthey want to allow apps to do in the background is interesting. The UI shown in thatXDA article is dreadful (WTAF is a ���satiated balance���?), but the concept has some merit.
However, each year Google goes in and changes the rules as part of The War on Background Processingthat has been going on for 6+ years. Combine that withmanufacturer-specific changes and developers are completelylost as to what we can and cannot do on any given device. That in turn leads users to assumethat apps or devices are broken, just because developers cannot keep apace with documentedand undocumented rules.
IOW, it would be really rather nice if Google stuck with a plan for more than a year andtook steps (e.g., CDD rules) to get manufacturers to stick with that same plan.
Per-App Locale SettingsFor a long time, developers have resorted to hacks to allow a single app to supportmultiple languages, by messing with Locale. While this appears to have held up betterthan I would have expected over the years, there are still serious gaps. The biggestis any UI that comes from other processes, such as notification dialogs ��� thoseprocesses will not have the customized Locale and will display their contents in thedefault language specified for the device as a whole.
Through a ���panlingual��� feature,Android 13 might allow users to choose a locale per app via Settings.
On the one hand, this seems wonderful.
On the other hand���
Where does the language change end? It will be interesting to see how they distinguish���showing the system file UI via ACTION_OPEN_DOCUMENT��� and ���launching Snapchat���.The former, in theory, ought to follow the language chosen for the app that makesthe request; the latter ought to follow the language of the app that is started. Yet, inboth cases, the requesting app is just calling startActivity() or startActivityForResult().
Will Google provide Compat code that will combine the new Android 13 capabilitywith Google-supported forms of Locale switching for older devices? If yes, how willthey handle manufacturers that fail to support the Intent for allowing users to switch a language?If no, how will Google advise developers on supporting both the new approach and the oldhacks in the same app?
These are early leaks. The things shown in these leaks may not ship, or they may ship in substantiallydifferent form. And Android 13 is likely to have many more new features than these,including some that impact developers. With luck, all my worries will vanish in thebreeze and it will turn out that everything is awesome.
I���m a Murphy, though, so I���m not counting on that.
December 24, 2021
Final Books, Free for Everyone
As I announced three months ago,the Warescription program has ended. The CommonsWare sitenow has the full catalog of books available for download. Thebooks are published under the the Creative Commons Attribution-ShareAlike 4.0 International license.PDFs, EPUBs, and MOBI/Kindle editions are available for all of the books, andmany of the newer ones are also available for direct reading on the site.If you see any problem with the content, let me know!
I will be adding full-text search in the coming weeks ��� it was simpler toget the content up first and add search in a second pass. Also, I will be lookingto release the second-generation books via Amazon and other distribution channelsin the near future, and I will add links to those when they become available.
The Warescription site will remain up for another week, mostly for the final officehours chats. I will then have it simply redirect to the main CommonsWare site.
To everyone who subscribed, I am grateful for your support, and I can only hopethat I was able to help you in some small way.
As for what comes next��� I own the codetransparency.org domain, and I probably should startworking on getting some content up for that. ���� Reach outif you have been working in this space and have some stuff that I should link to!
November 7, 2021
���Elements of Android Room��� Version 0.9 Released
Subscribers now haveaccess to Version 0.9 of Elements of Android Room,in PDF, EPUB, and MOBI/Kindle formats. Just log intoyour Warescription page to download it,or set up an account and subscribe!
This update adds two more chapters, covering:
Adding triggers to Room databases
In addition:
A bunch of dependencies were updated, notably Room itself
Material tied to Android Studio is updated for Arctic Fox
Uses of startActivityForResult() were replaced with ActivityResultContracts
Various bugs were fixed
November 6, 2021
About the Environment Undeprecations
A week and a half ago, in my random musings on Android 12L,I wrote:
getExternalStorageDirectory() and getExternalStoragePublicDirectory()on Environment are now undeprecated. DATA in MediaStore.MediaColumnsis also undeprecated.
I don���t know what to make of this.
A Google engineer reached out to clarify what is going on.
Good news! These changes were not an accident! getExternalStorageDirectory() and getExternalStoragePublicDirectory()on Environment are safe for use on all supported API levels. ����
The fact that they are undeprecated means that you can rely on them to returnthe same sorts of directories that they always have. However,this does not change your access rights. Scoped storage is still a thing,and what you can read from and write to is still governed by the scoped storage rules.
(someday, I will sit down and try to write the definitive guide for what those rules are)
So, for example, just because you can now access DIRECTORY_DOWNLOADS viagetExternalStoragePublicDirectory() does not change the rules for that directory:
You can write content there
You can read back the content that you wrote there
You cannot read or write content created by other apps there, which includesprior installations of your own app (after the user uninstalled and reinstalled your app)
So, this undeprecation does not have a security impact. It just means that you willstop getting compiler Lint complaints about using a deprecated API, as soon as youstart using compileSdkVersion 32, perhaps in 2022.
I am grateful for this change. Once Android 11 restored read access to much ofexternal storage, the deprecations were an annoyance. This will help to simplifythe discussion around how to work with external storage on modern versions of Android,because that is a major pain point for newer Android app developers.
To the Google engineers who decided to make this change: thanks!


