Mark L. Murphy's Blog, page 26

February 11, 2019

Full-Text Searching the CommonsWare Books

In addition to ending an era
yesterday, I also updated the CommonsWare sites to offer full-text searching.
The navbar contains a ���Search the Books��� field ��� filling in a search
expression and clicking the magnifying glass will search the CommonsWare
library of books.



Searching is available both from the public sites (i.e., this one) and
the Warescription site. Anyone can search, but
reading the books requires a Warescription.



All of the books are in the search index except Exploring Android, which will
be added in its next update, due out very soon.



If you run into any problems with the full-text search capability,
let me know!

 •  0 comments  •  flag
Share on Twitter
Published on February 11, 2019 04:38

February 10, 2019

The Busy Coder's Guide to Android Development Final Version Released

Subscribers now have
access to the final version of The Busy Coder���s Guide to Android Development,
in PDF, EPUB, MOBI/Kindle, and APK formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!



I am in the process of wrapping up my books that focused on the first generation
of Android app development (Java and the Android Support Library), in favor
of books focusing on the second generation (Kotlin and Jetpack/AndroidX). The
third of the original books to be marked ���FINAL��� is The Busy Coder���s Guide to Android Development.



This book is unchanged from Version 8.13, other than a few errata fixes. Simply
put, this book is too huge to try to move forward into second-generation techniques.
I would wind up with a book that is partly second-generation and partly first-generation
for a very long time, and that will be confusing to readers.



Note that all of the ���FINAL��� books will not show changebars or other notations for
what sections changed from their previous one. That is one of the reasons why I am
not making significant changes to these final editions.



The only first-generation book that is not ���FINAL��� is Exploring Android.
I am in the process of rewriting that book to be second-generation; the first
update for that will be available soon.



If you have questions or concerns regarding this transition from first-generation
to second-generation books, please reach out!.

 •  0 comments  •  flag
Share on Twitter
Published on February 10, 2019 05:57

January 31, 2019

A Fuchsia Future? Part Four: What Now?

Fuchsia is not just a difficult-to-spell name of a color.
It is also the name of a ���skunkworks��� project inside of Google building a next-generation
operating system for all form factors. This week, I���ll explore various aspects of this and my
expecations of its impact on Android developers and apps.



Today, I���ll look at what I think that you should be doing now, with respect to Fuchsia.





What you should be doing depends a lot on who you are.



Everyone

Keep an eye out for concrete signs that Fuchsia will ship. Obviously, the ���gold
standard��� sign would be if Fuchsia actually ships, at least at the level of
developer preview hardware. However, things like a Kotlin/Dart target would be a strong
positive indicator in my mind. News reports of hirings and stuff, by contrast,
are interesting data points but are far too squishy to give me much incremental
confidence.



Authors of Existing Android Apps

Unless your app is in desperate need of a rewrite, there is no need to race to
create a Flutter edition of your app. In all likelihood, Fuchsia will be able
to run Android apps, at least to the degree that Chrome OS can. Plus, in all likelihood,
it will be a year or two after production hardware ships before there are
significant numbers of Fuchsia users. You are unlikely to ���miss the boat��� if you
are not using Flutter on the first day.



There may be some element of ���first-mover advantage��� for having a Flutter app
when Fuchsia ships. This will depend on how well Fuchsia runs Android apps. If
Android apps behave fairly normally, such that Flutter apps are not seen by users
as a significant improvement, then there will be little first-mover advantage.
If Fuchsia cannot run Android apps at all, or does so extremely poorly, then Fuchsia
may fail in the marketplace, and so aggressively moving to Flutter might make you
a big fish in a rather small pond. The ���sweet spot��� for first-mover advantage is
where Fuchsia runs Android apps well enough to sell OK, but there is a noticeable
advantage to Flutter apps. Your guess is as good as mine as to where Fuchsia will
land on this spectrum of Android compatibility.



Developers (Developers, Developers)

There is somewhat greater ���first-mover advantage��� at the level of an individual
developer. Developers who set themselves up to be Flutter ���thought leaders��� may
be able to have lots of success if Fuchsia ships and succeeds. I am a fine
example of this: I bet heavily on Android 11 years ago, and by many measures, I have
had some success. Somebody will create the CommonsWare of Fuchsia and
be the Mark Murphy of Flutter, though I sincerely hope that whoever it is will
have better luck than I did in retaining my hair.



(though, in the interests of full disclosure: my hair was gone well before Android was
created)



Even if you decide not to race to Flutter, if Fuchsia does look like it will
ship, it will be a good idea to understand what it will take to create Fuchsia-native
apps using Flutter (whether using Dart, Kotlin, or something else). If Fuchsia
comes to pass and serves as an Android replacement, there may be a lot of work
in the 2020���s porting Android apps to Flutter.



Privacy and Security Advocates

Here, things get murky fast. On the one hand, Android is arguably the most
successful open source operating system ever. On the other hand, it is plagued
by privacy and security problems. We will need to keep an eye on:





What does the mix of open source and proprietary code look like? On Android,
Google Play Services and proprietary Google apps make up an ever-growing share of
the Android code base.




How will Fuchsia compare to Android and Chrome OS with respect to app distribution?
From a privacy standpoint, being able to install apps without having to use
the Play Store has advantages. However, the ability to load apps from other distribution
channels has security impacts. Chrome OS is a ���walled garden��� at present, akin
to iOS, and if Fuchsia is the same, then we will need to determine how best to deal
with that.




How much of Fuchsia is ���Google���s way or the highway���? For example, using
a push messaging solution other than Firebase Cloud Messaging is difficult on
Android and might result in your app being banned from the Play Store.
This forces all push communications to go through Google���s infrastructure, which
in turn poses problems for privacy. Having pre-installed Googly options is
reasonable; blocking competition for those options is, well, anti-competitive.





Others

Competitors may have a window for stealing market share. If Fuchsia comes to pass
and Google somehow screws it up, that may ���take enough wind out of the sails���
of Android+Fuchsia to open up opportunities for something else. Obviously, Apple
and iOS would stand to benefit. But if other manufacturers had some other option at the ready ���
a Chromium phone, perhaps? ��� then there might be a chance to get another entrant
into the mobile device OS space. However, competitors can���t wait to see if Fuchsia
fumbles and only then try to compete, as by the time they get a competing solution
to market, either Fuchsia will be fixed or some other entrant will have grabbed the
available market share.



Antitrust regulators looking at Google���s behavior with respect to Android need to
ensure their consent decrees and similar demands for behavior changes are not limited
to Android. What good will blocking anti-competitive behavior for Android do if Android
gets replaced by Fuchsia?



Users will just need to bide their time and focus on the devices of today, worrying
less about the devices of tomorrow.



As for me, I���ll be watching along with everyone else, waiting to see if and when it
makes sense to invest in Fuchsia.





This concludes my series of posts on Fuchsia, though I���ll write about it more
in the future if interesting stuff happens. If you are tuning in late, the
other posts were:




Part One: Build One (or Two) to Throw Away
Part Two: Is It Soup Yet?
Part Three: A Tale of Two Languages
 •  0 comments  •  flag
Share on Twitter
Published on January 31, 2019 04:31

January 30, 2019

A Fuchsia Future? Part Three: A Tale of Two Languages

Fuchsia is not just a difficult-to-spell name of a color.
It is also the name of a ���skunkworks��� project inside of Google building a next-generation
operating system for all form factors. This week, I���ll explore various aspects of this and my
expecations of its impact on Android developers and apps.



Today, I���ll look at what you���ll be using to write your Fuchsia/Flutter apps.





The official app development language for Flutter and Fuchsia is Dart.



Dart is a perfectly cromulent programming language. My guess is that when it came
time to choosing an app development language for Fuchsia, Google wanted a language
that they control, given what happened with Oracle v. Google.
Google created Dart. Dart is not limited to Fuchsia ���
in addition to all of the Flutter platform targets, Dart can be used elsewhere,
directly or as converted (���transpiled���) into JavaScript.



However, Dart is YAPL: Yet Another Programming Language. Admittedly, it it more
popular than is Kotlin in the January 2019 edition of the TIOBE programming language index.
However, I have to imagine that Kotlin has greater momentum, as while Flutter
has its fans, Android is huge, and Android app development is moving in
a Kotlin-y direction, given Google���s firm push.



Kotlin, like Dart, supports multiple runtime targets. Android app developers
focus on Kotlin/JVM, as while Android does not strictly use the JVM, the Kotlin/JVM
compiler���s Java bytecode output can be consumed by Android���s build tools, just
as javac���s output can. But there is also Kotlin/JS, where Kotlin is ���transpiled���
into JavaScript. And there is Kotlin/Native, whose tools generate LLVM bytecode
suitable for use in iOS projects and elsewhere.



Could Kotlin target something that works on Fuchsia?



If nothing else, it should be possible to create a Kotlin/Dart transpiler. Dart
is an ALGOL-family language, as is JavaScript. Dart uses classes, objects, methods,
variables, and such. Creating a transpiler may not be simple, but it should be
doable.



It is also conceivable that Kotlin could be compiled into bytecode that can be
consumed by the Flutter build tools. Dart itself supports a VM mode, and (if I understand
it correctly) Flutter apps can use that mode in development. Flutter apps get
AOT-compiled into machine code for distribution through app stores. I don���t know
all the gory details of their implementations, but if the AOT-compiler works off
of the DartVM bytecode, perhaps Kotlin could generate that bytecode much as it does JVM
or LLVM bytecode.



If Kotlin can generate Flutter/Fuchsia-compatible code, in principle the
Flutter APIs could be used from Kotlin. Worst-case, it would be more like
how Kotlin is used with iOS today, where the iOS UI is written in Swift (or Objective-C
[shudder]), but lots of business logic can be shared via a Kotlin/Multiplatform
module. In the Flutter case, the Flutter UI itself might wind up having
to use Dart, but one could share code with non-Flutter projects using Kotlin/Multiplatform.



Obviously, there are lots of technical challenges here, and it���s entirely possible
that it is impossible for Kotlin to target Dart the way it does the JVM, JavaScript, and LLVM.



But if we see Kotlin supporting Dart in the future, to me that is a fairly strong
sign that Fuchsia is ���for realz���. JetBrains is not going to throw resources at
Kotlin/Dart just for grins. JetBrains will try to build a Kotlin/Dart target if either:





They feel that adding Dart support is important for Kotlin to be available for
more platforms, and the only significant platform that is uniquely Dart is Fuchsia, or




Google throws money at them to make a Kotlin/Dart target





And while we like to think of Google as having these huge piles of money lying about,
in reality Google has budgets, no different than any other firm. Google is not
going to hand JetBrains a chunk of cash for Kotlin/Dart work unless there is a decent
chance for it to be worth their while in time.



So, I am watching JetBrains fairly closely, as a Kotlin/Dart target would say a lot
about the future of Fuchsia.





Tomorrow, I���ll take a look at what developers should be considering, as we ponder
Fuchsia���s future.

 •  0 comments  •  flag
Share on Twitter
Published on January 30, 2019 06:00

January 29, 2019

A Fuchsia Future? Part Two: Is It Soup Yet?

Fuchsia is not just a difficult-to-spell name of a color.
It is also the name of a ���skunkworks��� project inside of Google building a next-generation
operating system for all form factors. This week, I���ll explore various aspects of this and my
expecations of its impact on Android developers and apps.



Today, I���ll explore the strange time that we���re in, where Fuchsia is 90% rumor
and 10% code.





The blog post title stems from an old Lipton soup commercial.
In the 1970���s, Lipton marketed their ���Cup-a-Soup��� line of ���instant��� soup, where the
soup preparation was simply a matter of adding their soup packet���s contents to
a cup of hot water. The commercials would contrast this with ���classic��� soup cooking,
stirring a pot over a hot stove for several minutes (microwave ovens were expensive
and not yet commonplace). A child, watching this process, would ask ���is it soup yet?���,
only to be told ���not yet!���.



Fuchsia is not soup yet. We know this because there are no Fuchsia products, no
official Fuchsia announcements, and no renaming of Fuchsia to something that has a chance
of being reliably spelled and pronounced. However, if
the Bloomberg report
and related reports are to be believed, Fuchsia will be soup in the next few years.





Right now, developers are wondering if Flutter makes sense.



Flutter ostensibly is a framework for creating cross-platform
apps for Android and iOS. On its own, that would be somewhat interesting. Flutter
has its adherents, much as React Native does, and much as previous cross-platform technologies
(e.g., PhoneGap/Cordova) did. ���Write once, run anywhere��� has been a goal of many
development teams for decades.



What gives Flutter the advantage, in the eyes of some, is that Flutter is the native
app framework for Fuchsia. In principle, Flutter apps written today that work
on Android and iOS ��� and
to some extent on Windows, macOS, and Linux ���
would work on Fuchsia when it ships. In other words, by choosing Flutter today,
you ���get ahead of the curve��� and be ready for our reddish-purple-hued future.



That only makes a difference, though, if Fuchsia ships, and in meaningful quantities.





Just because Google is working on something called Fuchsia does not necessarily
mean that all uses of Android and Chrome OS would be replaced by Fuchsia in the future.



First, Google works on lots of things that never really materialize
(see: Project Ara), morph into
something related-but-different (see: Project Tango),
or fail to ���move the needle��� (see: Project Jacquard).



Even if Fuchsia turns into a shipping product, it might not be deployed in ways
that are conducive to Flutter. For example, it���s possible that Fuchsia only winds up
being used on connected devices ��� Chromecast, Google Home, etc. ���
as Google does something else for the future of their GUI-based operating systems.



Then, there is a question of whether there will be adoption of Fuchsia beyond
Google. Google controls whether Fuchsia winds up on future Pixel-style products
like the Pixel phones, Pixel Slate, and so on. However, while Google���s OS
market share is powerful, Google���s hardware market share is tiny. If Fuchsia
winds up only ever being used on Google���s own hardware, Fuchsia may never rise
to Android���s current level of dominance.



To a large extent, adoption of Fuchsia is up to Google. We know that Fuchsia is open
source. However:





What will the Google Play equivalent be for Fuchsia, and will its licensing terms
be something that manufacturers can agree to?




Will device manufacturers try to wrest more control from Google, either by coalescing
around some competing OS or through some more aggressive forking of Android? Or will
they be willing to adopt Fuchsia as the replacement OS for their current Android
and Chrome OS hardware lines?





The market also has a vote. Perhaps Fuchsia can run Android apps, but not very well.
Will consumers and enterprises be interested in a platform that requires app rewrites
to get decent results, or will they prefer to try to hold onto what they are currently
using for as long as possible?



Time is also a factor. We know
how Android OS updates have behaved historically. The
possibly permanently-broken device dashboards
show that it takes a few years for an Android version to achieve plurality of the
Google Play ecosystem, though things are improving somewhat. At the same time,
Fuchsia may be viewed as more than an ordinary Android upgrade, for better or
for worse in terms of adoption rates.



There is nothing intrinsically wrong with developing apps using Flutter today.
However, there are many things that could derail a ���Flutter and Fuchsia Forever!���
future. And if Fuchsia falters, Flutter failure may follow, as Google has
abandoned many things that did not succeed in the marketplace. So, make sure
that Flutter is the right choice for you today, and that you are willing to take
on the risk of a foreseeable future Flutter flame-out.



(in related news: alliteration is awesome!)





Tomorrow, I���ll look at programming languages with respect to Fuchsia and Flutter.

 •  0 comments  •  flag
Share on Twitter
Published on January 29, 2019 05:11

January 28, 2019

A Fuchsia Future? Part One: Build One (or Two) to Throw Away

Fuchsia is not just a difficult-to-spell name of a color.
It is also the name of a ���skunkworks��� project inside of Google building a next-generation
operating system for all form factors. This week, I���ll explore various aspects of this and my
expecations of its impact on Android developers and apps.



First up: um, WTF?





You might be forgiven if you think that Google is going a bit overboard with
operating systems nowadays. In addition to Android, they have:




Chrome OS
various specialized Android variants, such as Wear OS
whatever is powering the Chromecast (which reportedly is some Android/Chrome OS mashup)
whatever is powering their other smart devices
possibly other stuff that I���m forgetting


However, what has been getting a lot of attention in the past year is Fuchsia.
Bloomberg reported in July 2018 that
100+ engineers are devoting some time to Fuchsia.
Various outlets, including 9to5Google, reported a couple of weeks ago that
Google hired a long-time Apple engineer ���to help bring [Fuchsia] to market���.
And so on.



On the one hand, this seems bizarre. Google dominates mobile with Android. Chrome OS,
while not nearly as popular, has fans in various market segments, such as the US
education market. Why would Google be looking to replace one or both of these
with something new?



On the other hand��� perhaps we should expect this.





As developers, we like to toss around the term ���tech debt���, referring to the ever-growing
pile of bad decisions that we need to revisit someday. Anything with an API has an
additional tech debt problem: how to address that tech debt while maintaining
reasonable compatibility with things using the API. Platforms, such as operating systems, have it the worst,
in that they are often expected to be able to support a fairly broad history of
stuff. In Android���s case, we are supposed to be able to run Android 1.0 apps
on Android 9.0 devices.



Yet, despite this, operating system developers do need to address the tech debt.
Sometimes, that can be done incrementally. Sometimes, that is done much more
dramatically. For example:





Microsoft had a long transition from 16-bit Windows to 32-bit Windows
environments, where they had parallel OS tracks for a while (Windows NT and Windows 2000
were Win32-centric, with Windows 95 and 98 being more Win16-centric)




Apple switched both operating systems (OS 9 to OS X/macOS) and CPU architectures
(PowerPC to x86), resulting in lots of challenges for keeping existing Mac
software running (e.g., Carbon compatibility layer)







Fred Brooks��� The Mythical Man-Month
is a legendary book about managing software projects. One of his most powerful quotes is:




Where a new system concept or new technology is used, one has to build a system to throw away, for even the best planning is not so omniscient as to get it right the first time. Hence plan to throw one away; you will, anyhow.




To some extent, Microsoft built 16-bit Windows, then threw it away and shipped 32-bit
Windows. Apple built the original Mac OS, then threw it away and shipped OS X/macOS.
It should not be all that surprising, therefore, that Google may elect to address
Android���s tech debt by replacing Android. Most likely, the developers of
the stuff that was ���thrown away��� in these scenarios were not planning on having
their stuff be ���thrown away��� ��� it���s just a matter of course.



Fuchsia appears set to run Android apps,
possibly using a container system akin to how Chrome OS does. If it does a reasonable
job of this, then it is very possible that the Android ecosystem could turn into
a Fuchsia ecosystem over time, just as Win32 replaced Win16 and OS X replaced
the original Mac OS. And, just as developers had to learn Win32 and Cocoa APIs to
develop apps native to the replacement OSes, developers will need to learn Fuchsia���s APIs
to develop apps native for Fuchsia.



It does not appear that Fuchsia is ���ready for prime time��� just yet. By the time
it does, Android might be 15 years old, measured from when Google acquired Android Inc.
in 2005. 15 years is a decent amount
of time for a consumer operating system. We should not be surprised if Google tries
to use Fuchsia as their way to move forward while still offering a transition
from the past.



The key word there is ���tries���.





Tomorrow, I���ll explore a bit more about the transition and how that impacts developers
considering Flutter.

 •  0 comments  •  flag
Share on Twitter
Published on January 28, 2019 06:01

January 27, 2019

GraphQL and Android Final Version Released

Subscribers now have
access to the final version of GraphQL and Android,
in PDF, EPUB, and MOBI/Kindle formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!



I am in the process of wrapping up my books that focused on the first generation
of Android app development (Java and the Android Support Library), in favor
of books focusing on the second generation (Kotlin and Jetpack/AndroidX). The
second of the original books to be marked ���FINAL��� is GraphQL and Android.



This book is unchanged from Version 0.3, other than a few errata fixes. That���s���
not great, as this book was written during a very odd time in Android app development
(e.g., the book���s samples use Jack for Java 8 support). However, fixing the
book requires a fairly substantial rewrite. While I would like to do that someday,
I have to prioritize other work (read: books that are more popular).



Note that all of the ���FINAL��� books will not show changebars or other notations for
what sections changed from their previous one. That is one of the reasons why I am
not making significant changes to these final editions.



If you have questions or concerns regarding this transition from first-generation
to second-generation books, please reach out!.

 •  0 comments  •  flag
Share on Twitter
Published on January 27, 2019 08:17

January 22, 2019

Android's Architecture Components Final Version Released

Subscribers now have
access to the final version of Android���s Architecture Components,
in PDF, EPUB, and MOBI/Kindle formats. Just log into
your Warescription page and download
away, or set up an account and subscribe!



I am in the process of wrapping up my books that focused on the first generation
of Android app development (Java and the Android Support Library), in favor
of books focusing on the second generation (Kotlin and Jetpack/AndroidX). The
first of the original books to be marked ���FINAL��� is Android���s Architecture Components.



Many of the topics in Android���s Architecture Components will be covered in
future second-generation books. Of particular note:





ViewModel, LiveData, and Room basics will appear in Elements of Android Jetpack
(ViewModel is there already)




The rest of the Room material will go into a future Elements of Android Room, hopefully
late this year





There were few changes in this ���FINAL��� edition compared to the preceding one:





A lot of little bug fixes




Updated all of the dependencies to the now-current values




Updated the WorkManager chapter to reflect API changes




Removed the coverage of CWAC-SafeRoom, as I have not yet released 1.0.0 of that
library and did not want this book covering the previous versions





Note that all of the ���FINAL��� books will not show changebars or other notations for
what sections changed from their previous one. That is one of the reasons why I am
not making significant changes to these final editions.



If you have questions or concerns regarding this transition from first-generation
to second-generation books, please reach out!.

 •  0 comments  •  flag
Share on Twitter
Published on January 22, 2019 05:49

January 15, 2019

WorkManager Side Effects: A Follow-Up

About two months ago, I wrote about how
WorkManager has side effects.
Notably, it triggers the system to send ACTION_PACKAGE_CHANGED broadcasts
to your app. At best, those broadcasts might confuse your code that is expecting
those broadcasts to be sent for other reasons, such as components that you are enabling
or disabling yourself. At worst, we wind up in an infinite loop, if you try scheduling
work as part of handling such a broadcast��� which includes scheduling work from
onUpdate() of an AppWidgetProvider.



I argued that WorkManager should provide options to avoid or manage that side effect.



Google disagreed.



This side effect, and any others, seem to be undocumented. Apparently, blog posts
from balding authors suffices.



So, you need to be a bit careful about your use of WorkManager:





If your app responds to ACTION_PACKAGE_CHANGED broadcasts, directly or indirectly,
it may not be safe to schedule work there, lest you wind up in the infinite loop
scenario that I described in my earlier post.




If your app responds to ACTION_BATTERY_OK, ACTION_BATTERY_LOW,
ACTION_POWER_CONNECTED, ACTION_POWER_DISCONNECTED, ACTION_DEVICE_STORAGE_LOW,
ACTION_DEVICE_STORAGE_OK, CONNECTIVITY_CHANGE, ACTION_TIME_SET, or
ACTION_TIMEZONE_CHANGED, bear in mind that WorkManager has receivers for
those broadcasts in your app. These are all disabled at the outset, but presumably
WorkManager has code to enable them based on certain conditions, such as certain
constraints that you set in your work requests. Be careful about scheduling
work with WorkManager on those broadcasts as well.




If your app responds to ACTION_BOOT_COMPLETED broadcasts, bear in mind that WorkManager
also depends on this broadcast. Your respective receivers might be invoked
in any order. It may not be safe to schedule work here, as WorkManager might
assume that its own ACTION_BOOT_COMPLETED receiver has completed its work by
the time you try scheduling new work. While I would not expect an infinite
loop scenario, this is the sort of edge case that requires a lot of testing to
ensure everything will work as expected.




WorkManager has a ContentProvider that it bakes into your app as well.
While scheduling your own work from onCreate() of a ContentProvider would
be rather odd, it���s possible that somebody might want to do that. Be careful,
as WorkManager may not be fully ready for operation at that point. Note that,
last time I tested it, all ContentProvider instances are created before
onCreate() of Application, so probably it is safe to schedule work there.





There may be other edge and corner cases beyond these. So, while WorkManager
is nice, make sure that you thoroughly test your use of it.

 •  0 comments  •  flag
Share on Twitter
Published on January 15, 2019 05:49

January 7, 2019

"Elements of Android Jetpack" Version 0.2 Released

Subscribers now have
access to Version 0.2 of Elements of Android Jetpack,
in PDF, EPUB, and MOBI/Kindle formats. Just log into
your Warescription page to download it,
or set up an account and subscribe!



This update adds nine new chapters:





���Adding Libraries���




���Employing RecyclerView���




Coping With Configurations���




���Integrating ViewModel���




���Understanding Processes���




���Binding Your Data���




���Defining and Using Styles���




���Configuring the App Bar���




���Implementing Multiple Activities���





The next update to this book should be in 6-8 weeks.

 •  0 comments  •  flag
Share on Twitter
Published on January 07, 2019 05:49