Android Jetpack: what’s new in Android Support Library (Google I/O 2018)
Articles Blog

Android Jetpack: what’s new in Android Support Library (Google I/O 2018)

August 17, 2019


[TITLE MUSIC PLAYING] ALAN VIVERETTE:
I’m Alan Viverette. I’m the tech lead for the
Android Support Library. AURIMAS LIUTIKAS:
Hi, I’m Aurimas, and I’m a software engineer
on Android Support Library. KATHY KAM: Hi, and I’m Kathy
Kam, the technical program manager for the support library. ALAN VIVERETTE: And
today we’re going to be talking about what
is new in the latest version of the Android
Support Library, as well as some new things
that you can look forward to in the future. And on this beautiful spring day
in Mountain View, California, we’re going to start with a
little bit of spring cleaning. We’re going to talk about
some technical debt that has been building up
in the Support Library. So we’re on Twitter,
we’re on Stack Overflow, we’re on Reddit– maybe we’re on Reddit
a little bit too much– but we noticed some basic
aspects of the support library there were a little
bit, let’s say, messy. So we’ve reached out and
spoken with developers, and tried to find
out some ways that we can improve the basics
of Support Library and lay a strong
foundation for future work. And we got a lot of good
feedback, some very high-level, some very specific. We know about the issues with
showing and hiding the IME, but we drilled
down to the basics. We got feedback on our
Maven packaging, so our artifact and package names. Our Java packages have
become a little bit confusing as support
library has aged, and in general, we’ve built
up a lot of technical debt. So like last year,
we’re going to talk about what’s old in the
Android Support Library first. So we started in
2011 with Support v4, and as I’m sure
everyone here knows, that provided
backward compatibility for devices running SDK 4,
the first public release of Android. And we grew. We gained libraries
for watch components, car components, testing,
support for SDK 13, 4 11. So we have a lot more than just
backwards compatibility now. But we’ve still got
these artifact names. So we’ve got Support v4– everyone’s familiar with that– Support v13. Who knows what’s in Support v13? Literally less than 10 people. Aurimas knows. So Support v13 at this
moment contains nothing, because our minimum SDK
for everything is 14. v13 just redirects to v4. v4 actually redirects to a
bunch of other components. These are both just
umbrella artifacts on Maven. So why do we have all of
these weird versioning names in our Maven artifacts
and in our package names? Well, they’re kind
of hard to change, but these are confusing. It’s not a great place
to start if you’re new to Android development. We also have a lot of versions
of all of these libraries. So Recommendation is something
that was added back in 2003 and hasn’t changed
a lot since then, so we have 30
versions of basically the exact same library. And what does the
versioning scheme mean here? Well, 24 means it was
released at Google I/O when SDK 24 came out. We had an alpha 1 corresponding
with the first public release, dp 1, alpha 2 for dp
2, beta 1 for dp 3, but it doesn’t
make a lot of sense for us to be corresponding
to just the I/O releases. Wouldn’t it be great if we
had alpha and beta releases for every version of
the support library, and wouldn’t it be
great if we didn’t have to do that for
every single library, even if it hadn’t changed? So you may have seen like
a 24.0 that should have had a little bit more testing. Maybe there should have
been a beta 2 there. Or maybe you pulled in 25.4
and found some bugs that really should have been in alpha. So we’ve been doing a
lot better about testing, but wouldn’t it be great if our
dot-0 releases weren’t still alpha quality. So today we’re going
to focus on fixing how we structure our libraries,
how we handle changes to them, and how we ship
them to developers. And we’re using this to form
the foundation for Jetpack, and we’re calling it
Android Extension Libraries, or AndroidX for short. So welcome to what’s
new in AndroidX. We’ll be talking about
foundational changes, new features, and what you can
expect from us in the future. First, though, I’d like to
talk about the relationship between Jetpack, which
everyone may remember from the keynote, and AndroidX. So Jetpack is a set of
guidance, recommended libraries, and tools, and this
is going to teach you how to create good apps. This may include libraries
that are in AndroidX. This may eventually
include libraries that aren’t in AndroidX. And as guidance
changes and evolves, you may see that some
things in AndroidX become deprecated and are no
longer part of the Jetpack recommendations. Also, it’s got this cute logo. AndroidX, on the other hand,
is the libraries themselves. These are guarantees
about versioning, API surface,
dependency structure, and we do not have a cute logo. So let’s dive into details. What exactly is
going to be changing? We’re going to have logical,
smaller, more scoped artifacts. So for example, if you’re
looking for a ViewPager, it’s going to be in the
ViewPager artifact rather than Support v4. You may remember
the Support v4 split that we did last year
where we split out to core UI and a number
of other artifacts. We’ve done that again this year,
so we have smaller artifacts. If you need ViewPager, you
just pull in ViewPager. You don’t pull in a
bunch of other widgets that you may not need. This is of course
non-breaking like the split that we did last year. So if you pull in
support v4, you still get all of that
umbrella of libraries. If you pull in
core UI, you still get ViewPager and
everything else. But you also have the
option of just pulling in exactly what you need. So here’s the split
of the libraries. This is just a sample. So you’ll see that
support-compat has been broken down into collections. This is a pure Java library. It’s a JAR instead of an AAR. It has no resources. You can use it with
host tests, because it doesn’t have any dependencies
on the Android package. Core is the backwards
compatibility that you’re used
to from Support v4. You’re going to see less of the
compat moniker in the future, as AndroidX is becoming the
primary development surface for a lot of framework APIs. So here you can see if you
need swipe refresh layout, you pull in exactly that. You don’t get anything
that you didn’t need. We’re also moving to versioning
that makes more sense. So instead of
monolithic releases that are tied to
Google I/O, we’re going to reset from
28.0.0 to 1.0.0, and the major version number
now actually means something. So previously, we would
break binary compatibility on any minor version bump. And if you’re using
a library that depends on a specific
version of Support library, this can be really problematic. You may not even find
out until runtime that some method signature
that a library depends on has changed. Our move to strict semantic
versioning with AndroidX means that you can always
expect the major version number to indicate
binary compatibility. Anything with a 1.4 dependency
on a library, for example, would be compatible with 1.5,
1.6, all the way up until 2.0. We’re also going to be doing
per-artifact versioning and release, so instead
of a monolithic release, if we have a bug fix
for RecyclerView, we release a dot-1 of just
the RecyclerView library, you only have to pull
in one new artifact, and if you don’t need it,
you don’t have to pull it in. So it’s going to
be very low effort on the part of the developer All right. And we want to make
it easy to know what’s inside each artifact. So as I mentioned, our Maven
artifacts are now finer scopes. They correspond to
features instead of very broad swaths of, for
example, all of support v4. We have a consistent
scheme of AndroidX dot feature dot package
and class, according to layer and functionality,
and our Maven naming scheme reflects this. So our group IDs are going
to be AndroidX dot feature. This will correspond
directly to the Java package. The artifact ID
will be the feature, and if there is a
sub-feature, for example, RecyclerView selection, you will
see AndroidX dot RecyclerView, colon, RecyclerView,
dash, selection. You also notice
that we’ve moved all of the v7, v4 explicit
backwards compatibility or explicit compile
SDK requirements. So our min SDK requirement
is built into the Android manifests for these
libraries, and we also make heavy use of the
requires API annotations. So you may run into methods
that may return a new object. So you may see something that
returns compat, something that returns the actual
object, and you can call that if you’re
on a newer platform. All right. So let’s dive into
an example of that. Here’s an example
of some libraries that you may already be
using redefining everything under the AndroidX
top-level package. So everything that was
in compat Android Support is now in Android
Extension Library. Everything from android.arch
is now an Android Extension Library. For the most part, we’re
preserving the names, but you’ll see some
simplifications. So
android.arch.persistence.room, for example, is now just room. When you’re looking for room,
you can find it very quickly. Let’s dig down on Support compat
and card view, v7 specifically, and take a look at
some of the classes. So build compat has moved
from an explicit v4 support to Android core OS build
compat, and in the future, you’ll actually see a lot
less of the compat suffix on classes. Card view v7 is now
just cardview.widgets, and the class is card view. You’ll find other
supporting classes in cardview.util, et cetera. So hopefully this
isn’t too shocking. I think this is a very
long-awaited refactoring that we’ve really been
wanting to do for a long time, and you might be wondering,
how do I get that? So we’ll hand it
over to Aurimas. He’s going to walk
you through what it looks like to migrate
your application. AURIMAS LIUTIKAS: Hello there. All right. Thanks, Alan. So I will walk you guys
through our migration story and how you want to get to
the AndroidX library usage. So first things first, if
you are using Android Studio, we will be providing
an automated tool for migrating over. This tool is available
starting with Android Studio 3.2 Canary 14 that
shipped yesterday. This automated tool will be
in the existing Refactor menu that you probably–
hopefully –love. And what we added is we
added a new option that’s called Refactor to AndroidX. And this, with a single
click, will go and identify all the usages of Android
Support Library old classes, and it will pull them
up in the review pane, where you’re going to be
able to see what has changed, and what we’re about
to migrate for you. And after review, you
click, Do Refactor, and we’ll go ahead
and refactor it. And what this will handle–
it will go and handle your source code, including
your constant classes. It will handle
simple build scripts unless you have something
more complex, in which case, we’re going to publish
maps of old artifact to the new artifact so you can
do the migration in a slightly more manual way. And migration will also handle
resources such as your layout files. And then finally, but not least,
what you’re going to have is we’re going to handle
migration of binary dependencies for AARs and JARs. We know that many of you use
third-party libraries that depend on Support
Library, and it could be Glide or many other libraries. So to help you with that, we
wrote a tool called Jetifier, and this tool performs
binary translation using ASM, which jumps
into the jar and goes and rewrites all the uses
of the old Support Library to become the new
Support Library. This handles code
inside of the JAR. It handles XML resources. It even handles ProGuard files. We will also publish
a standalone JAR that you can run
manually if you’d like to have checked-in
versions of these pre-builds that you depend on. And now let’s jump
to the laptop where I’m going to give you a
demo of how this tool works, and hopefully the demo
works beautifully. Of course. There we go. All right, let’s
jump to the laptop. All right. So here you’re looking
at a Topeka Android app. It’s available on the
Google Samples GitHub page. It’s nothing super-amazing. This is just using
standard components and showing examples
of how it works. As you see in the Android
emulator, I’m running this app. It works. You can click on things. But now what we are
going to do, we’re going to jump into
the refactor menu. Hit the refactor to AndroidX. What this is going
to do is going to go jump in and search
through all the uses of all the old Support Library
classes in both XML, Java, and in all the other places
that we migrate for you. And after it’s
done searching for, it’s going to present all
the things that it will suggest for you to migrate. And in this case, I’m looking
at the specific sign-in fragment class, and you can see there’s
a bunch of uses of Support Library, and what is going to
happen when I hit Do Refactor, it will go and
rewrite all of these, including build script
files and everything. And of course, Gradle needs
to go and sync again now because we just changed
all of the dependencies, and the class paths
need to be reloaded. So now that’s what you twiddle
your thumbs a little bit, and wait for Studio
to do its thing. And now we are using new stuff. And now when I hit
Build and Install, hopefully in a few seconds– at home, you’d probably
would go grab a coffee, but here we can do
that on the stage. But it will go and install it
on Emulator any moment now. It will stall a little bit
longer by saying other words. Ta da. And now you see this is the same
app using a brand new AndroidX library, and migration
was fairly painless. So let’s jump back
to the slides. [APPLAUSE] All right. Of course, some of you are
now using Android Studio. And for those people we will
be providing a giant CSV file that has a mapping of
old class to the new class. And that, in addition to the
standalone jetifier tool, you should be able
to hook up together in your build
system and your IDE to do the migration manually
if you are now using studio. So in summary, we are providing
these tools in Android Studio 3.2 Canary 14. And Jetifier is already
in Google Maven. However this is
coming in really hot. Even the demo that I was
using is actually not using Canary 14, it’s
using Canary 15– that’s shipping next week
–because we found bugs when we were trying to do the demo. So please wait until Canary
15 to start using this. But when you do, in Canary
15, please take a look at it. Try to migrate your projects. And if you find any issues
with it, please file bugs. We want to make it
as easy as possible. We want you to migrate and
start using all the new stuff. However, you know,
migration takes time, so we will still ship Android
Support Library 28.0.0 alongside of AndroidX. However, note that this is
the last feature release. So this is kind of like a
little bit of a timeline for you to kind of move forward. And if you want to
know a little bit more of how this works behind
the scenes inside of Android Studio, there will be a
talk about Android Build System at 6:30 in this room. So hopefully you can go
and take a look at that. So it’s not all
about refactoring. We also added new features. And I’m going to walk you
through some of these. So the first feature
I want to talk about is the RecyclerView
selection, and this is a library that will
allow you to handle item selection a lot easier. It will help you
handle motion events and touch events, and
convert them into selection in the RecyclerView. This is a fairly
flexible library where it allows for a
custom layout managers and a lot of custom
actions that you can do. So let’s jump through
and see how you use it. As you can imagine, you
add the new dependency to your build.gradle file. An important thing
to note here is that we are using the
new AndroidX artifact. This is the same stuff that
Alan was talking about. So for the setup
what you need to do is you want to
create a new layout– a new adapter. And the adapter, important thing
is that it uses stable IDs. We’re using a stock
grid layout manager, and we set both of these
on the RecyclerView. There is no selection
code just yet. We’re just doing the
basic RecyclerView setup. The adapter, as I said,
again, nothing super exciting. The only important thing here
is that we’re doing stable IDs. And this allows for a consistent
mapping from ID to the item. And then next, when we
jump back to the activity, we set up selections,
libraries, this key provider. And this in conjunction
of stable IDs, will allow for a quick
mapping between the IDs and the items that will handle
the selection by the selection library. And now what we need to set
up is the selection tracker, which actually is the actual
machinery behind the scenes, and we pass in RecyclerView,
the key provider– both of these that
we just created, and then My Details Lookup. And My Details Lookup is
actually a fairly simple class. You need to overwrite
only one method. And then the site of that
one, your return item details. And that returns the position
and the selection key for the item that is for
the given motion event. And finally, RecyclerView has
no default selection mechanism. So what you have to do is you
have to handle it in onBind. So there you might want to
change the backgound of a view, or you can use it by
setting activated state. And to get activated
state working, you use background for the
view, and that background is a selectable drawable that
has an activated state which will allow to
indicate to the user that the item has been selected. So this is the basics. This library has a lot more. You can set up band selection. You can add custom
selection areas. You can have items
that are not square. You can have circular
handling and stuff like that. There’s a lot of stuff you
can do to this library. And the important part of this
slide, that’s my dog, Jack. All right. Another thing that we added to
RecyclerView is a list adapter. And what this does
for you is it helps you to work with
RecyclerViews that change the content over time. So all you need to do
is submit a new list. We run the DiffUtil tool
behind the scenes for you on the background
thread, and then we run the animations based on
how the list has changed. And this is all handled
via very simple API, and I’m going to
walk you through it. So what you need to do is
you create a diff callback. And diff callback has to
implement two methods. First one, make sure that
the items are the same where you compare the item IDs. And the second one, you want
to check that that content is the same where you do
a deeper comparison, essentially equals in Java. And then if there
is a change, we’ll know how to crossfade your
item from one to the next. And then the adapter,
what you need to do in onBind you
just call getItem, and you do your regular
binding, and then that’s all you need to do to
get the animation working. And then a highly complex
code in the activity, you call submitList, do
your list, and that’s it. You’re done. Note this works really
well with LiveData and are RxJava servables. So if you need to do a
slightly more advanced adapter, we also have a base adapter
called AsyncListDiffer. So if you need to do that,
just go and look into that. If you would like to know
more about ListAdapter and similar utilities,
see managing infinite lists the RecyclerView
and paging talk, on Thursday at 2:30. All right. So another thing that we added
is AndroidX WebKit library. And what it allows you
to do is to get the APIs that we’ve added in WebView
on the older versions in a backwards-compatible way. This library works
on API 21 and newer, where we introduced
the updateable WebView through Play Store. So take an example
of Safe Browsing API that we added in API 27. This prevents loading of
malicious URLs in the WebView inside of the application. And previously you could only
use it on API 27 and newer, but now you’re going to be able
to use it on older devices. So similarly, we can
add a Gradle dependency to get this working. Again we’re looking
at AndroidX artifacts. Hopefully you got this by now. And then we check if
the feature’s available, and if it is, we
start safe browsing. Is as simple as that, and
you get the safer experience of safe browsing, starting
the API 21 and newer. And similarly, many other APIs
that we’ve added since API 21 now will become
backwards-compatibly available to your application. So hopefully you’ll
check this out. So another library that
we’ve renamed is Custom Tabs. This library became
Android Browser. This doesn’t actually
change the functionality. It will continue to work
with all the browsers that already implement this. So Chrome, Firefox, and
Samsung Internet, all of them implement this. And if you’re using it,
it will continue to work. The cool thing about this,
we added a new feature inside of the library which is
called Browser Actions. And what it allows you
to do is it allows you to hook into the context
menu of the browser. So for example now
your Reddit app can finally open links
in an incognito tab, which can be very handy. It works in Chrome,
starting with Chrome 66, and it will start
working on other browsers when they adopt it. So again, to use this,
it’s fairly simple. You set up pending intents
for your browser action items. And this is optional. If you don’t need any
extra ones in your dialog, you can skip this part. Another optional part you set
up is browser action tracking. So what this allows you to do is
you can see what user ended up selecting inside of the dialog. And then, finally, you just fire
up the browser action dialog. And then you end up with
something like this. And then you can do additional
actions for yourself, or you can hook
into the browser, whereas previously you
weren’t able to do these via simple intents,
because browsers weren’t interested in exposing a lot
of this functionality directly. Another library that I’m going
to talk about is HeifWriter. Heif stands for
High-Efficiency Image Format. And we introduced the support
to the platform for Heif in Android P. And
alongside we’re launching this
library that allows you to take byte buffers,
surfaces, and bitmaps, and write them to the file. Currently it is only API 28
and newer, so not super useful. But we are working
on a backward that will allow you to use
it on older versions. Again, usage is very simple. You fire up the builder to
create a new HeifWriter object. In there, you can set
up a bunch of options, like image size or quality. You hit Build, and once you have
it, you start writing to it. You put in bitmaps,
and once you’re ready, and you’re done with it, and you
want to write it out to disk, you call stop with a
timeout, and the timeout can be zero if you want to
have an indefinite wait. And the important
part here is you want to do this work off
of UIFriend because you’re going to be doing disk. And to tell you
a little bit more about other features in
AndroidX, I invite Kathy. Thanks. KATHY KAM: Thanks. [APPLAUSE] Thanks, Aurimas. So the next feature
I want to talk about that we added in AndroidX
is a feature called Slices. Slices is a feature that
allows you to display content outside of your app. So the goal here is
to have one reusable API that both the
system and other apps can request content
from your app. Today we have already
integrated research. Our goal is to look
into integration with notification, long
parse, or even home screen in the future. So what is this content
that you get to present? This content is both
templated and interactive. It’s templated so that when
you have kind of live content, you can display it in a
rich and flexible layout. And the content is
interactive because we allow you to add existing
controls like sliders, toggles, or scroll viewer so
that together you can have LiveData with inline
actions, or even deep links into your app. You can also choose to integrate
your Slices with search. So a user can
display app content by searching your app
name, or even general terms that you’ve registered. This is a win-win for
both users and the apps, because users can get
rich LiveData immediately, and for your app to
reach millions of users. Because this is
implemented in AndroidX, it is usable immediately
up to API 19. So let’s take a look
at how you can use it. So as you would expect, we have
to first import the libraries. There’s three libraries
you need to import, and here we are
importing from AndroidX. The first is the
slice-builders that includes methods to build
content in a templative format. The next Library
is the slice-view. This contains method so that
you can present the content. And the last library to
import is slices-core that contains method
for permissions. So to build a Slice, you
have to define a Slice, implement the Slice,
and handle Slice action. Let’s take a look
at how we can do it. So the first thing
we need to do is to let the system or
other apps know that you have Slices to provide. So you do that by implementing
your Slice provider. So you register
your Slice provider in your android.manifest file. Next, you have to extend
from the Slice provider and implement your
Slice provider. You can have multiple
Slices for your app. So this is where the
business logic happens. When a platform or another
app wants to get your Slices, you get a call on onBindSlice. You get a call with the URI
of the Slice being requested, and you have to return
your Slice immediately. So any content that needs to
be loaded should be kicked off, and you would return
it in buildSlice. So let’s take a deeper look. And we’re going to
construct the Slice here. And we are able to construct it
with several builder classes, include rowBuilder,
gridRowBuilder, and listBuilder. So let’s take a look. So here we’re going
to use the listBuilder to add a very simple header. The header contains a
title and a subtitle, and you see the method, add
header, that adds the header. Then to build on
top of it, we’re going to use the gridRowBuilder. First we get the latest
weather information, and then we can loop
through it adding a cell to the gridRowBuilder. And by just calling
addGridRow at the bottom, it appends the rest of
the Slice to the header. So what you would get
here is that based on screen real estate,
if it is small, we show a shortcut Slice. The shortcut Slice pick up
an image from your Slice. So because we have the weather
image, we’ll pick that up. But if you didn’t
have the image, we would show your app icon. And then if the screen is
only allow for small Slice, it will be the
header that’s shown. And finally, if it
has enough space, we will show the full Slice. So to learn more about Slices,
there was a talk this morning that you can review. But I only just cover
very basic of it, and you can learn more
about templates, permission, and integrating your
search in the other talk. And you can also meet the
team at the office hour tent tomorrow morning at 10:30. So the next topic I want to talk
about is Material Components. We launched Material Components
for Android back in Support Library 28.0.0 alpha 1 in March,
and we’ve also launched it for AndroidX yesterday. So as you know,
material theming is designed to support
great user experience. We’ve made a lot of
improvements since 0. So one of the first
things we have done as part of the
AndroidX refactoring is that instead of being
in android.support.design, we’ve moved it to
com.google.android.material. We’ve done a lot of
extensive usability study on how to make these
widgets work well for you. So we have made them more
usable and accessible. And we’ve updated styling
so that you can better express your brand, and
included new UI components. So let’s take a look. So here is an overview of
the theming capability. On your right, there’s a
very brand-agnostic baseline, and on your left, there
is the Google branding. All the components can
pull from the theme so that it makes it
super easy for you to have app-wide theming. Let’s walk through
some of this in code. As you would expect, you will
have to import the library. Here, note that
it is coming from com.google.android.material,
and to use a baseline theme, you set the theme to be
MaterialComponents.Light. This is kind of brand-agnostic
theme for you to start with. And then we provide a
whole bunch of attributes that you can override so that
all the widgets within the app can pick up. So here we have defined primary
color and text in theme.XML. And then on top of
that, you can also define attributes for
different text styles. We have some that
comes out of box, but you can also
define your own. And all the custom widgets– if you have custom widget
that uses these attributes, it can also pick up the theme. So let’s take a look at
some of the components that we’ve updated. First off is text field. We’ve done a lot of research,
and we have improved the touch target, making it
more easier for input, and making it more
usable and accessible. We’ve also added
new states to it, which includes focus state,
error state, disable, and text counter. So we kind of think
through all of it from the start to
make your life easier. Next up is Button. As you can tell, you can
just use button the way you use today by setting
the material component light beam will automatically
inflate this little material button that
understand our themes and pick up all the attributes
that you have set previously in your theme file. You can also use custom
attributes with these. And then we’re also providing
two updated bars for you. The first is the bottom app bar. The bottom bar allows you to
add actions into your app. And what we have done here is
that we have allowed you to– we’ve done research and saw
that the phones are bigger, so we want to allow you to
position your actions anywhere you want along the
bottom app bar. So here we have a
fab that is centered and then can be animated
to be right-aligned. The other bottom bar
we have update it is the bottom navigation bar. So just to clarify, that
bottom app bar is for actions, and the bottom navigation bar
is for a different section within your app. We do not recommend
mixing these metaphors, but we provide both for
you to choose from so that you can build your app. So pulling all of this together
is the Material Card View that is a wrapper
on the existing card view in the Support
Library on AndroidX. We’ve simplified
how this was built so that it has less
elevation and shadow, and it also pulls from
the theme and color. So here you can see all the
elements coming together with the text button, and all of
that in the material cart view. With this component, you will
have to define a user Material Card View explicitly. We’re looking to see whether
we can integrate it as well. So with that, you can learn
more about this in stage 8 at 4:30, on how to incur
what’s new with material design in your code base. And AndroidX is only
one part of Jetpack. So Jetpack is a set of
components, tools, and guidance to help you build great Android
apps quickly and easily. So we’re at this talk, but we
have four more talks for you to learn more about Jetpack. So with that, thank you. Alan, Aurimas, and I will be
hanging out at the Android tent right over there. So we’ll hope to see
and talk to all of you. Thank you. [APPLAUSE] [TITLE MUSIC PLAYING]

Only registered users can comment.

Leave a Reply

Your email address will not be published. Required fields are marked *