[MUSIC PLAYING]
JORDAN HAYASHI: Hello, and
welcome for lecture eight.
Today we're joined by Charlie Cheever.
Charlie did his undergrad at Harvard.
He actually took CS50 back in the
day and went on to join Facebook,
later founded a website called
Quora, and has now founded
and leading the team over at Expo.
So I will hand it over to him, and he
will do a lecture on Expo components.
So welcome Charlie.
CHARLIE CHEEVER: Cool.
Thanks, Jordan.
And thanks for having me here.
Cool.
So I'm Charlie.
And I'm gonna talk to
you about Expo today.
I think most people in
this class are already
using it to build a lot of
their projects and assignments
for this class.
So I'll talk a little bit
more about what it is,
and then go into some detail
about different parts of it
that you can use that you
might not have encountered yet.
So what is Expo?
Expo's basically two things.
One is it's like a project and
an effort that is basically
trying to take the spirit
of the web and the ideas
of the web and the accessibility and the
distribution power and things like that
and fuse it with the
sort of functionality
and gestures and
animations and capabilities
and look and feel that you
get with native mobile apps
that people like using on their phones.
Because people used to use the web.
And there are lots of people
that can develop for the web.
And that's probably the most popular
development platform in the world.
But it's really hard
to develop for mobile
even though everyone in the world
is carrying around their phones
all the time now.
And so then as part of
that, what Expo is secondly
is basically like a standard
library of components
that let you access basically
everything on your phone device
through JavaScript without having
to worry about Xcode or Android
Studio or libraries like that.
And so when I say access everything
on your phone through JavaScript,
I mean basically all
the hardware peripherals
like accelerometer,
gyroscope, GPS location,
pushing notification, things like
that, along with stuff on the screen,
like native maps, native video
player, sound, those kind of things,
camera, and then some
common libraries that people
need to use a lot of the time, like
Facebook login, Google login, ads,
things like that.
So there's actually a
ton of these things.
You can see a whole list
if you go to docs.expo.io.
I'm going to go there, like, every 45
seconds, probably, during this lecture.
So just familiar with these.
And if you just scroll down
on the left side right here,
you can see a full list where
accelerometer, AdMob, amplitude,
uploading-- all of these
things are available.
I'm only going to have time
to show you some of them.
But they all work pretty
well, and they're all tested.
And so basically, everything
here is a combination
of things that we've made at
Expo as part of the project,
along with editorially selecting
some of the best open-source things
out in the community, like vector
icons, which is the most popular way
to do icons in React Native, and
React Native Maps, which originally
was made at Airbnb, I think
by this guy Leland Richardson,
which is kind of universally
the way that everyone does maps
with React Native.
So we've pulled in a few of
those open-source projects
that are best of breed and
that are kept up to date
and maintained, and then
built the things that there
wasn't a great solution already.
And so we're going to be using
the Expo client on our phones.
And we're going to use Snack a
lot, which is at snack.expo.io.
And also, we're going to use XDE,
the sort of desktop development tool.
And I tend to use VS Code for my editor.
So the first thing we're going to
dive into is maps and location.
I'm just gonna build a really simple
demo of using maps, and then throwing
a couple of pins on it and showing
where we are and stuff like that.
So to do this, I'm gonna go into Snack,
which you can get to at snack.expo.io.
And this is just basically
the default project.
I made a few changes here.
I'll just start from scratch so
people can follow along more easily.
I'm going to reload this.
And I'm going to change this constants
thing here to just import Expo.
I'm going to delete this
import asset example and import
card because we don't need those.
And then I'm just going to
delete all this stuff in here,
and then change this to expo.constants.
So now I just have the plain view.
So I'm not even going
to use that plain view.
I'm actually just going
to do expo.mapview.
And then I'm going to
do run device so I can
take a look at this thing on my phone.
So I'm opening this up.
I'm using an iPhone so
I can mirror it here.
And so here's what this looks like now.
It just looks like a white screen.
So I will try, actually--
I'm going to get rid of files, and
I'm gonna shrink this a little bit
and try to move this over here.
Maybe we'll be able to see it then.
OK, there we go.
Now we can see both at the same time.
So part of why this map view isn't
showing up is that it's too small,
and it's sort of just not showing
up because it's not big enough.
So we'll say flex one under style.
And then now, bam, a map view appears.
And it's taking up the whole screen.
That's kind of what we want.
So one sidenote here is this is an
Apple map because I'm using an iPhone.
On an Android phone, this
would be a Google map.
But Google Maps is also
available for iPhone.
And so you can switch
the provider if you want.
So I can say provider equals
expo.mapview.provider_google,
and now this is using a Google map.
And you can see it says
Google in the corner.
And Google Maps default to, I
think, somewhere in the UK, like,
probably Greenwich meridian
or something like that.
Whereas at Apple Maps
defaults to the United States.
We'll use Apple Maps for
the rest of this demo,
but you can switch between
them pretty easily.
So I'm gonna take a look
at the maps documentation
here, just so we have a good
reference point to start.
And if you look here, you
can see the first thing
it shows is a simple example.
And I have a way to set
initial region, which
should be pretty useful because looking
at a giant map of the United States
isn't that interesting
in a lot of cases.
So I'm just going to copy this and
bring it over here and paste it in here.
And now you can see this makes this
initial region in San Francisco.
Some of you might be in San Francisco
if you're watching on stream.
But we're actually in
Cambridge right now.
So it'd be great if we could center the
map there or wherever we actually are.
And so what I'm gonna do is
actually use the location
API to figure out where we are,
and then center the map on that.
And so what we're gonna do is
slip over to the documentation
and take a look at location.
You can see the first
thing it says here is
that you need to use the permissions API
to get permission to ask for location.
If you're used to using an app on
your phone that asks for a location,
you probably have seen these
dialogs pop up that say,
like, Facebook Messenger would
like to use your location.
OK or cancel.
And you kind of only get one shot as
an app to ask for that permission.
So a lot of apps will, like,
pre-ask you and ask for permission
to ask permission for your location.
And then if you don't say
OK, they won't ask you
because they don't want
to use their one chance.
In the Expo client app
here, I've actually
already asked for location permission.
It's been granted.
So we won't see the dialog pop up.
But if it was the first
time, or if you're
in, like, a standalone app
or something like that,
we do want to ask for permission.
So we'll cover that here.
And asking for permission,
again, using the Expo
permissions API is fine
because it'll just say,
oh, you already have
permission, and it's granted.
So I'm gonna write a little
method here that's called
askForLocationPermissionAsync.
Or I'm gonna just call this,
like, getLocationAsync.
So I'm gonna ask for permission first.
So the first thing I'm gonna do
is say let status equals await.
expo.permissions.ask async
expo.permissions.location.
I can see a list of
all the permissions I
can ask for if I go to the
permissions page on the Expo docs.
You can see here's this
Expo permissions get async.
And then type is the argument it takes.
It's just a string.
And you can see a complete
list if you scroll down here.
So location, camera, audio recording,
contacts, et cetera, et cetera,
et cetera.
The one we're interested in here
is expo.permissions.location
because we're going to ask for location.
So that's right.
But one thing I made as a
mistake is that this actually
needs to be an async function
because we want to await stuff.
So I'm gonna say equals async, and
then make it an arrow function.
This is a good way to make async
functions as class methods,
or as instance methods in your classes.
So now we need to check the status.
The typical pattern
with Expo permissions
API is basically to ask
for permission, and then
say if status doesn't equal granted,
then sort of log an error or show
an error.
And here we'll just return.
But in a more
fully-featured application,
you might want to show
an error message or force
the user to another flow or whatever.
Here we'll just jump
out and not do anything.
And then the next thing
we'll do is we'll say,
OK, we're going to ask for the location.
So I can go back to the
location page in the docs.
And I can see that the
way we do this is we
use location get current position async.
And this takes bracket
bracket as an argument.
So let's figure out what that means.
This is an options array.
And it can take enable high
accuracy or maximum age.
We don't really need
to do anything special
because we're just trying to get
generally what location we're in.
And so we're just not
going to give any options.
But I think that it requires giving
the options thing as an empty object.
Enable high accuracy is nice because
it gives you higher accuracy.
But it also uses more battery on phones.
So unless you actually
need that accuracy,
you generally shouldn't use it.
So what we'll say here is say let
location equals await expo.location get
current position async, I think it was.
And then so now, if this happens,
we should get a location.
So I'm going log it as soon as I get it.
That way, we can sort
of inspect what we get.
So for that to happen,
I need to call this.
So what I'm going to do is I'm
going to say on component did mount,
which is a React lifecycle method
that you're probably familiar with,
I'm going to say this.getLocationAsync.
So this will call this method
as soon as the component mounts.
Oh, since I changed this and the Snack
reloaded, it's already done this.
So now I can see this object
here has a time stamp,
and then it also has coords.
And inside coords is a latitude, an
altitude, a longitude, a heading,
an accuracy, an altitude accuracy
estimate, and a speed estimate.
Since we're not moving at all, it
doesn't have any concept of our speed.
So it says speed negative 1.
And since we're not moving at
all, heading is also negative 1,
which is sort of an error value.
And so what we're going to do is use
those coords from that object to, like,
populate the initial place of our map.
But the thing is in our render method,
right now we're rendering the map view,
and we're sticking in the
initial region right away.
But we don't get the location
until shortly afterwards when
we're able to have
component did mount fire,
and then we check the permissions,
which is asynchronous.
And then we ask the
GPS for the location.
And then we pull this information out.
So we aren't quite
ready right off the bat
when the application starts to show
the map with an initial location
where we are.
So we're going to have to do something
using state to make that work.
So we're going to say state
location null to start.
I'm just adding this as an
original initial way to set state.
And then here, instead of logging the
location, what I'll do is I'll say,
this.setState location.
If you're not familiar
with this syntax here,
this is just a shorthand for
saying location, colon, location.
I'll use that a couple of times because
it's a pretty convenient shorthand.
This little brackets button that says
prettier in the lower right-hand corner
that I keep hitting, it just reformats
the code so it's easier to read.
And when I make indenting
mistakes and stuff like that,
it takes care of those.
So now, when this component mounts,
it should call get location async.
And that should set a state once
it's ready to be an actual location.
So now I don't want to show the map.
I'm just gonna show a blank screen
until we have the location ready.
So what I'm gonna do here in the render
method is say if this.state.isReady--
actually, I'm not gonna use isReady.
I'm gonna say location--
if no location, then
return an empty view.
Otherwise, we'll have a
location that we can populate.
So that means if the code makes it
down to this part, this.state.location,
has a value in it that will have coords.
So I'll say
this.state.location.coords.latitude.
And then now you can see
this already updated.
Now that I've changed the
latitude but not the longitude,
I'm on some highway in the
middle of America somewhere.
Let's zoom out and see
exactly where that is.
Looks like Oregon.
So the north-south is
now correct, but I need
to change the longitude to move it
over to Cambridge, where we are.
So I'll just change that to
this.state.location.coords.longitude.
And now you can see the map now
centered on Cambridge, which is pretty
cool, because that's where we are.
And if you're following along and you
did this in Bulgaria or something,
the map should be centered
on Bulgaria for you,
as long as your GPS is working.
So it's normal if you
see something different
if you're not in the same place.
Cool.
So this is cool that this
map is centered on us.
But I think a lot of
times when you really
want to get your bearings on a map,
you want to have a pin that says,
hey, you're here.
So let's add a marker to this
map that shows where we are.
Clear these logs.
Shrink this down a little bit.
So if I go look at the
maps documentation,
you can see this is built by Airbnb.
And so the documentation basically
says, for full documentation
go check out Airbnb React native maps.
So let's go over there.
And then under component
API, you can see
it has map view, which
we already are using,
marker, callout, polygon,
polyline, circle, and overlay.
Some of these are pretty fancy and would
take a long time to go into detail on.
But marker's really simple.
So let's just start putting
a marker on the map.
You can see this isn't too complicated.
It basically just has a whole
bunch of props you can add,
and then some things you can call on it.
We'll just use the most
simple props in general,
which are sort of title, description,
pin color, and coordinate.
Coordinate is how we'll
set where the marker is,
and then pin color is self-explanatory.
And then title and description,
I'll show you how they show up.
So it's pretty easy to
put a marker on the map.
You just make-- instead of the map view
being sort of a singleton component,
you just make it a component
that has things inside of it.
And then we'll say expo.Mapview.Marker.
Now we need to give a coordinate
for the marker so it shows up.
So we'll say coordinate equals
this.state.location.coords.
Coordinate just requires latitude and
longitude, but this object has those.
So it's OK that has
other things, as well.
So now you can see over
here we have a dot right
in the center of the map where we are.
That's pretty cool.
If I zoom in, it's right there.
And you can sort of see we're actually
at Radcliffe College, which is actually
where we are.
Pretty cool.
So now we might want
to say what that means.
So we'll sort of say
title equals you are here.
OK, cool.
So now, after that reloads,
when I tap on that marker,
it now says, you are here,
which is pretty useful.
This is somewhat useful to know.
But we might also want to have
some other things on the map.
So when I went to school here, I
lived in a dorm called Eliot House.
And so it would be kind of cool to
be able to put that on this map.
And so there probably
is a way to do that.
So I know that the address of Eliot
house is, like, 101 Dunster Street.
But I don't really know how to
put a map view at 101 Dunster
Street because if you look
at the map marker API,
it takes a coordinate, which
is latitude and longitude.
And so we need some way to go from an
address to the latitude and longitude.
And what we can use for that
is this Expo API under location
called geocoding.
So if you go to location in the docs
and you scroll down a little bit,
after the stuff about
getting position, there's
something called geocode async,
which takes an address as a string
and then will return some guesses at
the lat-long based on where you are
and how popular different
places are and things like that.
So we can try just putting
in 101 Dunster Street
and seeing if that works.
So I'll do that here.
So while we're getting
location, once we do
that, I'm going to actually ask here
for the place information, as well.
So I'm going to say let
Eliot House equal away
expo.location.geocodeAsync
101 Dunster Street.
Now I'm just gonna log this, just
so we can see what we get back.
Let me see if I have other log
statements that are getting in the way.
No.
Looks like I don't.
So I can see what I get back.
Here is an array.
And the first element has a latitude
and a longitude and accuracy.
So that is-- oh, that's
actually something else.
Let me clear these.
So now if I do that--
am I doing this wrong?
No, it's not.
So the latitude and longitude of
this Eliot House is 42.3707463.
And the longitude is
negative 71.1209627.
So now we have to pull this out of
being the first element of the array
and somehow put it in
a marker on the map.
So I'm going to do this--
use parentheses here, and
then take the zero index.
So now when I scroll down, now
there's no array wrapping this,
and it's just a raw object that
has a latitude and a longitude.
And I'll add some other places, too.
So I'll say The Crimson, which
is the student newspaper,
is at 14 Plympton Street, I think.
And then [? Kitty ?]
is at 2 Holyoke Place.
So then I'm gonna set
places in state also.
So I'll just say Eliot House,
The Crimson, and Kitty.
So now if I do prettier
here, that should work.
And so now I should be able
to put additional markers here
for those places.
Map view, marker, coordinate equals
this.state.places.EliotHouse.
Title equals Eliot House.
Description equals Domus.
So now I can see here's where we are.
You are here.
And then I can also see Elliott House.
There is Domus.
So to make this map
look a little clearer,
I'm gonna actually make this pin blue.
And so now, Eliot House
is more clearly marked.
And then I'll mark the other
spots on the map, as well.
Since this is pretty
much the same thing,
I'm just going to copy paste this
and change the appropriate stuff.
So this is going to be The
Crimson, pin color crimson.
I wonder if that works.
And I'll make the coordinate of where we
are be yellow just so that stands out.
OK.
Wow, that's hard to see.
We'll use green instead
for where we are.
Cool.
So now as long as I change these--
crimson, student newspaper.
Now I have a couple
different places here.
And if I zoom in, it'll show me
the description of all of them
as I tap them.
And I can also see where I am.
Since when I changed this
code and it zooms back
to the original zoom
distance, all these things
are kind of clustered in the
center, and I'm zoomed out too far,
I actually want to make the map
zoomed in a little more by default.
So I'm going to change this latitude
delta and longitude delta a little bit.
These values that I copied from
the example are pretty reasonable.
But they're just a little bit too big.
So you want to zoom in
about, like, maybe double.
So I'm gonna do divided by 2 for
both of these and see what happens.
So now it's zoomed in a bit more.
Let me see what happens if I do
3, if that looks better or worse.
That looks a little better,
but now these buildings
are so close to the edge that we
might want to not be quite that much.
Let's just do 2.5.
OK, that looks pretty good to me.
That way I don't have to keep
zooming in every time I make this.
So then the last thing
I'll do is we might
want to know where we are instead of
just saying we're here or whatever.
So I can actually use an API called
reverse geocoding to take coordinates
and turn them into an address
or something like that.
So what I'll do is I'll
say let where equal
await expo.location.reverseGeocode
async location.coords.
So this is going to find the
coordinates of where we are and then do
a reverse geocode lookup on them.
And then I'll log this just
to see what's going on.
Clear this.
And then I can see it's giving an array.
And then it has a bunch of metadata.
And I'll just use the name from that.
So I'll also take the
zero index of this,
just like I did with those other
things that came in an array.
And then here, where I
don't have a description,
I'll just say description
equals this.state.where.name.
So now when I tap on
the green pin here, it
should say you are
here, Radcliffe College.
Or if you're in some
other location, it'll
probably say where you are based
on the reverse geocode lookup.
So now we have the beginnings
of a map-based application based
on using the maps and geolocation APIs.
So that was pretty fun and cool.
So now I'm gonna move
on to the next thing.
This is text or delete, which
is something my sister sometimes
does with me.
She's a millennial.
And so she plays this
game where you have
to go to the contacts app on your phone
and spin through it until you find--
and then close your eyes, and
then stop on a random contact.
And then you have to
decide whether you're
going to text that person
right then or you're just
going to delete them from your phone.
And so I thought would be
interesting to make an app that would
go through your contacts and do this.
It's not actually that fun
of a game in real life.
It's mostly stressful.
But we can make a way to do it.
So I'm starting again
with a fresh snack.
I'll make the change again to
just import Expo here instead
of that constants thing.
I'll delete this stuff, and
then delete this stuff, as well.
And then I have to change
this to Expo.constants
because I got rid of that.
So the first thing I need
to do is do something kind
of similar where I'm just
going to make a button that
will pick a random contact.
That'll be the first thing I do.
So import-- oops, it looks
like I deleted the import react
native line by accident.
So I'm going to Import button
so that I can use that.
And now I'm just going to say a button
title equals pick a random contact.
On press equals press.
All
Right So I'm just gonna make a
button that when you press it,
it says pressed.
So now I'm going to open this.
OK.
So now we have this button here.
When I tap this, it says pressed.
OK.
So we have a button that works.
Cool.
So now when we hit the button,
we want to open your contacts
and get a random contact.
So what I'm gonna do is get
random contact async equals async.
So now I've got to go back to
the docs and look up contacts.
This needs permissions,
actually, for your contacts.
And so if you look at the example here,
it awaits in basically the same way
that we did before.
So I'm just going to
type that really quickly.
Let status equals await
expo.permissions.ask async expo
permissions.contacts.
If status doesn't equal
granted console.error.
We'll just bail.
Otherwise, we're going to then call--
now that we have permissions, we
can call into the contacts API.
If we look at this, there's a
method called getContactsAsync.
And it takes some options.
The most naive implementation of
this would just get all your contacts
and return them in, like,
a series of nested data
structures like lists and objects.
But some people have thousands
of contacts on their phone.
And so especially for older Android
phones and things like that,
it can take a really long time
to read those off the place
where they're stored, and then
serialize them and put them into memory
and copy them over.
And so you don't really want to
have these clunky applications where
maybe they aren't even sure you need all
the contacts, but the app gets really
slowed down because it's
loading all this stuff in.
And so there's other ways to load in
stuff, like, either paginated or by ID.
So you can see in the
object options here,
there's this page size and page offset.
And so let's just see what happens
if we do a default request,
like if we don't give any options.
So let contacts equals
await expo.contacts dot--
what is it?
GetContactsAsync.
And we just won't give any options.
But we'll log what the answer is.
And we'll stop saying pressed, and we'll
just say this.getRandomContactAsync.
So now when I push this button, I
was hoping something would happen,
but it didn't.
So now I'm just going to
make sure I'm getting to here
and not having permissions not granted.
OK.
So it's not getting my contacts.
So why not?
Hmm.
Maybe I need to actually
fill out the options.
So fields, page size, and page offset.
So page size, let's just try one, and
offset zero, and fields, we will say,
phone numbers.
Ah, OK.
So now we've got some data.
So my first contact is
Jack Freece on my phone.
Has next page, has previous page.
And it says I have 1,816 total contacts.
So what I'm actually going to do is use
the fact that it's giving me the total,
and then use that to pick a random one.
Because I'll just pick a random
number between 1 and 816,
or however many contacts
I have on the phone.
So what I'll do here is I'll await that.
And then I'll say, let total equal
contacts to pull that out from it.
Then I'll say-- we'll let n
equal a random number between 0
and total minus 1.
And then we'll log that number.
So now I should see a log
every time I push this button
of a number between 0 and 815.
So that seems like it's working.
So now I just have to
fetch that random person.
So I'll say let random contact equal
await getContactsAsync, page size
one, offset n, fields phone numbers.
And now I'll log this random contact
and see if I get a random contact.
So when I clear this, I
can see what's going on.
So it looks like I got something.
I got Jack Freece, which
seems weird because that
was the random thing I got last time.
So I might be doing something wrong.
So hmm, maybe the offset isn't working.
Or maybe I need to do
page size n, offset zero.
And then we'll get a
whole bunch of contacts,
and we'll be able to
look at the last one.
So now I got 797.
But I think it's loading almost
all of my contacts into memory
and trying to put them
all into a data structure.
So that might take too long.
So that might not work.
And what if we don't ask for
any phone numbers or anything?
OK.
So now I've got 10 things.
I think it's going to make
me ask for each page of them.
Now I'll just choose 100.
So it looks like I'm going to
have to get all the pages of stuff
to make this work.
And so just in the interest of
time, I'm not gonna write this loop.
But I'm just gonna pick a random one
between 1 and 100 and pick that out.
And we'll just pretend I
only have 100 contacts.
And so from here, I think, under--
let data equals random contacts.
Let's see.
Equals data n.
So now I picked a random contact from
the first 100 people on my phone.
And it actually picked David
Malan's sister, Lauren Malan.
That's kind of funny.
And so it's basically working.
So now I just need to get phone numbers.
So now I'm going to put phone
numbers back in the fields here.
And now I'll try again.
And is it giving me his phone number?
Maybe I don't have it.
Well, I guess I'll just make do
without having phone numbers.
Or I'll check the documentation
to make sure that was right.
Phone numbers.
Ah.
This is actually not a string.
It's supposed to be
expo.contacts.PHONE_NUMBERS.
OK.
So now I'll do this again.
And someone named Steven.
And I guess I don't have a
phone number for him, actually.
Oh, I did this wrong
because I took this here.
OK.
So now it has phone numbers.
But I'm not going to show you
people's phone numbers here.
But now I'm getting the phone numbers.
And so then I'm going to do a set
state here now that I have a contact.
And then here, I'll just show
their name if I have one.
So I'll just say
this.state.randomContact and text
this.state.randomContact.name.
Or null.
Null is not an object.
Random contact.
So now I just have to set an initial
value for random contact in state.
So then when I do set state
here, it's already populated,
and now it can actually be checked here.
So now when I pick a random contact,
it shows me someone named Tom Cook.
If I hit it again, now it says
Jonas Looster, Elizabeth Windram.
These are just random
people from my phone.
And so I think with future
work, you could find a way
to make that a link that
would then send a text message
or, like, put you in the texting
app or take you to the contacts app,
depending on what you choose.
But I'm going to move
on to other things.
Cool.
Window.
So now I'm gonna show you
making a quick compass.
I'm gonna make a new Snack.
And I'm again just gonna delete
this junk and just say Expo here.
Say Expo.constants there, and
then get rid of this stuff,
and then run on device, Android.
Open this up.
So now one of the cool things that a
phone has that computers don't normally
have or whatever is a compass in them.
It's called a magnetometer formally.
And so we're going to look
at the API here for it.
And it's really, really simple to use.
You can say, basically, Expo
Magnetometer at listener.
And we'll just do that.
But what I'm going to
do is I'm actually going
to upload some files really quickly
that should make our life easier.
So I'm going to upload
this compass face.
And I'm going to include
this compass needle.
So now I'm just going
to say image and image
background we're going to import here.
And I'm just going to draw a
simple compass on the screen
here that we can actually
turn into a real compass.
So I'm going to say image background
source equals require compass face
that I just uploaded to this project.
And that is, I think,
height 320, width 320.
And then I'm going to have a
compass needle here inside of it.
OK.
So now this looks almost right.
But the needle isn't really
centered on the compass.
So I'm going to say align items,
center, justify contents, center.
Then I'm going to make this
needle a little bit transparent.
I'm going to say opacity 0.65.
Bam.
Now we have a compass
that's looking pretty good
except that I can move my phone
around in different directions,
and nothing happens.
So now I'm going to use
the magnetometer to make
it actually function like a compass.
So this is pretty easy.
So I'm gonna state is ready.
False.
And it just won't be ready until then.
And then I'm going to say component
did mount, this.setupMagnetometerAsync.
setupMagnetometerAsync
equals an async function.
And then looking at the magnetometer
docs, all you need to do
is add a listener here.
So I'm gonna add a listener.
And this just takes a function.
Oops.
And we'll just say
this is like a vector.
So we'll just have that say this
set state V for that vector.
OK, cool.
And so now there's a
little bit of math you
need to do to turn that into something.
So we'll just see what
the vector thing is.
Text this.state.V. Text.
[INAUDIBLE]
So now I'm showing what
v is on the screen here.
And you can see it's got, like,
X, Y, and Z components here to it.
And so we somehow need to turn
that into a directional heading.
And you can actually Google
around and find how to do that.
Making a compass--
I think I can remember it off
the top of my head, though.
I think it's something like
let data equals 0 radians.
If this.state.V theta equals
math.atan negative V X.
Well, let's say, let x, y, z--
we'll pull x, y, and
z out of the vector.
And then we'll do this.
If negative is greater than zero and
y is greater than zero, then nothing.
Else if y is greater than zero,
theta plus equals math.pi.
Else theta plus equals math.pi times 2.
And then so now instead
of showing the vector,
I'm going to try showing
the angle in radians.
And you can see as I
move it, that changes.
So what I'm going to do is now I'm
going to try to rotate the needle so
that it like turns that many degrees.
What we can do for that is we can
just use this transform thing, which
takes a property like
rotate, I think, or rotation.
Let me look that up.
Under Transforms in the React Native
docs, you can see it does rotation.
And so I'm just gonna
say rotation theta.
Maybe it's rotate.
Oh, yeah.
OK.
So now you can see-- it's maybe hard
to tell on the screen at the same time,
but now as I move--
I think I made a mistake somewhere
because the thing flips around
sometimes.
But in general, you basically
have a working compass now.
So that's pretty cool.
Another thing is there's actually a
whole bunch of sensors on the phone.
There's not just the
magnetometer, but there's
an accelerometer, a gyroscope,
a pedometer, GPS, and a camera
and things like that.
And so you can actually
combine all these things
and get more information because some
of them collect overlapping information.
Or you can combine it together, and
you can be pretty smart about it.
And so that whole study of
that is called sensor fusion.
And there's sort of
a standardized output
from that that's called device motion
that has been exposed in iOS APIs
and also in Android APIs.
And so if you use device motion,
you can actually get information
about the orientation of a phone
that's from the combination of all
these different sensors.
And that's actually
exposed in Expo, as well.
So we can do kind of a cool
demo if we do something where
we could even just take this and--
well, I'll just take out this stuff.
And instead of this, I'll
just put in an image.
I'm gonna upload-- show the files here.
And I'll upload these balloons.
Get rid of the files.
So I just uploaded some balloons.
And I'm gonna just show them.
And I'm gonna say source
equals require uphouse.jpeg.
Style equals-- how big is that thing?
1440 by 1280.
And then let's multiply it by,
like, 0.7 just so that it's
vaguely fitting on the screen.
Cool.
So now we have this house.
But now instead of using compass
stuff, let's use device motion instead.
So instead of setting
up the magnetometer,
let's set up device motion.
And then we do that.
We can take a look at this and say--
we just add a listener, and
we'll get something else.
But it's pretty similar.
Device motion add listener.
Device motion-- we'll just call what
the output of device motion listener
DM for device motion.
So we'll set that state.
And then instead of doing
all this junk to get data,
we'll just say let angle
equals zero if this.state.dm
and this.state.dm.rotation.gamma.
We'll say angle equals
this.state.dm.rotation.gamme.
OK.
And then we'll do a rotation transform
on this, just like we did before.
And let's see.
Ah, we need to set up the device motion.
Now, if we do this then--
let me log what we're getting here.
Huh.
All right.
We're not getting anything
listened from the device motion.
Why is that not the case?
Oh, device motion is
actually under danger zone,
which is called danger zone
because the API might change,
and it's sort of in beta.
It's safe to use, but if
you want to keep using it,
you might have to learn new
stuff when new things come out.
I forgot that, and I forgot to
put in the danger zone thing,
and that's why it's
not working right now.
But all I need to do to fix
that is say danger zone here.
And then I probably don't need this log.
But now you can see as I
rotate this, the balloons
are kind of spinning around like crazy.
What I'm trying to do is actually
make them always point up.
So you can see what I'm
doing is when I tilt it,
it actually rotates even further.
So what I want to do instead
is compensate and actually
have it be the opposite of that.
So I'm going to rotate it by the
negative of the rotation of the device.
So if I do that, then
now if you-- see this?
No matter how I rotate
my phone, the balloons
are always kind of going up in the air.
It's a little choppy.
And so what we can actually do
is we can tell the device motion
sensor to detect every frame.
So we can say
Expo.dangerZone.Devicemotion
set update interval to 16.
That's the other method you can
see here, set update interval.
The reason we do 16 is that
iPhones and Android phones
tend to render stuff at, like,
60 frames per second, which is
pretty good for viewing the human eye.
There's one model of
new iPad that renders
at, like, 120 frames per second.
But almost all sort of modern phones do
60 frames per second now when they can.
When they drop frames, it's
like a miss, and that's
when you see jerkiness
in the UI and stuff.
So if you divide 1,000
milliseconds by 60 frames,
you end up with 16 milliseconds.
And so that's why we use
16 here, because that
means we'll get an update from
the device motion API every frame.
So when we do this,
now, if you can tell,
this is really smooth because
it's updating every single frame.
And even though it looks not like
it on the screen, on my phone
the balloons are always pointing up.
Cool.
So we'll take a little
break for a while,
and then I'll come back
and do some more stuff.
Thanks.
Hey.
Welcome back.
There was one sidenote.
Somebody asked about this.
Yeah, I kind of glossed over it,
but when you add those listeners
for device motion or for magnetometer,
you should actually remove them
when your component unmounts.
Otherwise, you can accumulate
lots of these listeners that
aren't doing anything that are wasting
resources or keeping the device running
and wasting battery.
So just calling this remove
all listeners or storing
the value of [? AdListener, ?]
and then calling remove on that
is how you do that.
But in a basic demo application
or something like that,
it's not the end of the world.
It's sort of like just not
cleaning up after yourself.
So the next thing we're going to
do is do some multimedia stuff.
This is something that
people always like doing,
is stuff with video, images,
audio, stuff like that.
So I'm gonna make a basic
what I call multimedia board.
Basically, the idea is one of the
soundboards like people made--
Arnold Schwarzenegger soundboards or
"The Office" soundboards or Snoop Dogg
soundboards.
I'm gonna do the same thing,
but with videos instead.
So what I've got--
I'll just start by
showing a quick CAD video.
What I'm gonna use here
is just a text editor.
I'm using VS Code.
But you can use whatever one.
And I'm gonna use the
Expo XDE instead of Snack
because the way Snack does reloading,
when you use videos, especially,
and audio stuff, having
all the multimedia objects
plus that kind of reloading
tends to just overload the phone.
And it just kind of breaks.
So it's gonna work better
to do this this way.
So I've got it opened up here.
Here's the app, and
then here's my editor.
I'm gonna close these
and move this over.
Minimize this and this so we
can see the phone as we type.
OK.
And now I'm just gonna say--
just as a test, I'm gonna delete
this and just say cat sounds.
Save it, and then this should reload.
Takes a little longer
because we're not using Snack
because it's downloading
this whole bundle.
All right.
But it worked.
Cool.
So now the first thing we're going to do
is we're going to stick in a cat video.
So we're going to say video Expo.video.
Let's look up the docs for this
just so we have somewhere to start.
And you can see there's a
bunch of options that it takes.
But they're pretty basic.
And so we can just start by just
saying source require dot slash--
I think-- let me look at this directory.
So I have an assets folder.
And inside it, I have a font and
nine little short videos of cats,
plus some weird sound effects.
So here's one of them.
So I'm gonna try to play that one.
So I'm gonna say require assets1.mp4.
And then I'm going to give this a style.
So I'm going to say, just so we
can see it, width 400, height 400.
OK.
And we'll just close that like that.
And now if I save this, I think
we should-- oops, made a mistake.
Oops, I forgot this.
Closing bracket.
Cool.
So now I have a cat video here, but it's
not playing, and it's just right there.
So now I'm going to switch this
to say shouldPlay equals true.
I think that's right, looking at this.
Yeah, OK.
Save this.
So now-- now the video plays.
OK, cool.
There's a couple of things
that aren't great about this.
One is I can't hear any sound.
So part of this is that my phone's
on silent right now, actually.
So one thing I can do is I can turn
my phone off silent, which I just did.
And I'm going to save this
again so that it loads up again.
And now I think we
should hear the sound.
[MUSIC PLAYING]
Yep.
OK.
So the sound played, but I don't want
to have to always turn on my phone
to not silent just to hear this stuff.
Because if I'm going into this app,
half the point is to hear the sounds.
So we can actually switch
the audio mode so that it'll
play while the phone is on silent.
And the way we do that is if we
look at the docs under audio, set--
we can actually set it up.
So I'll just write a
method here that's like--
And I'll say Expo audio--
oops.
Import Expo from Expo.
OK.
Set audio mode async.
And then this is going
to take some parameters.
So one thing it's going to take
is play in silent mode on iOS.
So we're going to set this to True.
And we'll await this.
So now we're going to say component
will mount this.setAudioModeAsync.
OK.
So now I think if I save
this, this should play,
even though my phone
is back in silent mode.
Huh.
Audio mode attempted
without the required keys.
Oh, so I need to fill in more keys.
So allows recording iOS, false, because
we're not going to record anything.
If you want to watch the basketball
championship game, it's on right now.
And you can come watch
this on video later.
[INAUDIBLE] Android true.
Interruption mode Android Expo audio.
Interruption mode, Android, duck others.
Interruption mode, iOS, Expo audio.
Interruption iOS, mix with others.
Don't worry too much about what
all these things are because you
can just kind of copy these.
And then when you want to start doing
very particular things with audio,
you can kind of learn what they are.
They're basically about if
there's another sound playing
in the background-- like if
you had Spotify playing music--
then how should your
app interact with that?
Should it stop the music
temporarily, and then do stuff?
Or should it just play over it?
Et cetera, et cetera.
And you can look through the
docs to find out what these are.
But hopefully, this is enough that
by saving this, this will now work.
OK.
[MUSIC PLAYING]
There we go.
So now the sound played.
Cool.
So now the other thing is I
kind of want all these videos
to be sort of square buttons.
And so I said height 400, width 400.
But you can see that it's kind
of a rectangly type thing.
So what I can do here is I
can say resize mode equals--
and I can say cover.
And that will cause this
to make the video fill
the space that I give it in the style.
And so now I should
see a square video now.
And I do.
Cool.
And so now I want to make it so that
when this video gets to the end,
it's not a black square, which
is boring and doesn't look right,
but it actually goes back to
the first frame of the video.
And so the way I do that is I
actually have to listen for when the--
it's actually fairly
complicated to do this.
But I have to basically listen for
when the video ends, get a callback,
and then reset the frame position of it.
And so to even start doing that,
I need to use refs and stuff.
So I'm actually going to break
this out into its own component.
So I'm going to make a new one
called cat video button extends
React component.
And this has a render method,
which is just going to be this.
And I'm gonna say this.props.width
or this.props.size or 400,
this.props.height or
this.props.size or 400.
OK.
And I'm gonna say
this.props.source for this
so that I can parameterize this and
use it for all the different cat
video buttons.
So I'm just gonna throw cat video button
source equals require assets 1.mp4.
And we'll say it's just 100 now.
So if this works, I should
see a smaller cat button.
There we go.
Perfect.
So now I'm gonna try to make this so
that it goes back to the first thing.
So I'm gonna have to make
a ref here, which is--
I think you probably covered this.
But basically, this takes a function
that's passed to the component itself.
And then you can do something
so that you can, like,
in this component set a variable
that's a reference to this component.
So inside the cat
video button, I'm going
to have an instance variable
that points to the Expo
video that is the actual cat video.
And so this.video equals c.
C is for component here.
OK.
So then there's this callback
called onPlaybackStatusUpdate.
And that's going to get
playbackStatusUpdate.
And if it has this did just finish
property, it mean it just finished.
So I'm only interested in that.
So I'm only going to
write this if status
did just finish part of the code.
And I'm just gonna have this
function do nothing otherwise.
What we want to do here is we
want to kind of reset the video.
So reset async equals async
function that says if this video--
we're going to say await this.video.
Stop async.
If I look at the video API, I can
see that I can call stop async on it,
which does what you think it does.
It just stops the video.
And then I'm going to say
set position async to zero,
which is the beginning of the video.
Because set position
async takes one parameter
that's a number of milliseconds
into the video to play from.
So zero is the very beginning.
The reason I have to call stop
async is that if I just call
set position async at the
end of the video playing,
on either Android or iOS--
I forget which one--
the video is actually not fully stopped
playing when didJustFinish fires.
And so it'll reset the position.
And it'll think, oh, I
still have a bunch to play.
And it'll continue playing in a loop.
So I'm gonna call stop just to make
sure that doesn't happen and be safe.
So now if I call this here, then
if I just say this reset async
and save that, then now
the video should play,
and then it should go back
to the first frame, where
you sort of see that smiley cat.
And there we go.
You can't see his face,
but the cat's back.
So now what I want to do is make it so
when I touch this, it plays that again.
So now I'm going to have to put
some sort of touchable area on it.
So I'm gonna add touchable highlight
here of the things that I import.
And I'm gonna say this is a
view that's wrapping this.
And then I'm gonna say view here.
And then I'm gonna use a
touchable highlight here.
The reason for this inner view is
that touchable highlights and things
like that need, like, a React
Native view and not an Expo video
view because they get confused
about refs and things like that.
So that's just a general thing, is
if weird things are breaking around
touchable highlight or very native
components like video players
and things like that, then just
try adding in intermediate views.
And that might fix your
problems in some cases.
So now I'm just going to
capture the presses here
on this touchable highlight
area, and then just say--
I'm gonna log that press the cat.
So I can just test this.
OK.
So now it's downloading.
And I can see as I press
it, the highlight shows up.
But let me just make sure that that--
yep.
The log that says press the cat
shows up every time I press it.
And so now I just have
to make it play again.
So now I'm just gonna add
another method called playAsync.
And it's gonna say await
this.video.replayAsync, which is
documented in the AV function, I guess.
But basically, this will replay
the item from position zero.
So that's a pretty good thing to use.
So now we'll just say when you
press this, instead of logging
that, I'm going to say this.playAsync.
Cool.
So now if I go look at this, once
this loads, now when I press it,
it should replay again.
So I can just keep doing this
over and over and over again.
And if I press in the middle of it
playing, it'll go back to the beginning
and play it then.
Cool.
So now I just have to put
together a whole bunch of these.
But before I do that, I kind of want
to make this look a little better.
So what I'm actually
going to do is say--
let's make the background of this green.
So I'm gonna change this
background color from FFF to green.
And I'm gonna change this
text color here to yellow.
And I'm gonna make it kind of big.
And let's see how that looks.
OK.
That looks a little bit interesting.
But I kind of want a different
font because I don't like the way
that font looks with this color scheme.
So the way I can use different
fonts is I can actually
use, like, files like TTF files
or OTF files as custom fonts
that I can just load
into the app on the fly.
So let's do that now.
So I sort of showed you that
I put in some mp4 files.
I also put in this Cooper
Black Regular font.
You can just take a look at what
that looks like in this preview.
It looks like this.
That's pretty nice-looking.
So to use that, what we have to do is we
have to load in the font to begin with.
So we're going to make a method
that loads in these fonts.
And that's pretty easy.
You just do Expo.font.loadAsyn.
And then it takes a map.
And you can just say Cooper
Black Regular require
assets CooperBlackRegular.TTF
And then this
will load that font into
the font sort of dictionary
under Cooper Black Regular.
But to have it render properly,
we have to have the font loaded
before we show this text.
So we're going to use a
technique where we use state
to keep track of whether
it's loaded or not.
So we're going to say
IsReady false here in state.
And then in this render
method of this whole thing,
I'm going to say if not this.state
is ready, return Expo.AppLoading.
This app loading thing is just
like sort of a splash screen that
will show until your app is loaded.
And so this is just a convenience way to
show something that looks like your app
while it's getting ready.
Like, if I switch into
something like Facebook,
you can see for a split second, it
shows the scaffolding of Facebook.
Same thing if I go into, like,
Yelp will probably do that.
Yelp just shows the Yelp
logo and a red screen.
So the app-loading
container is basically just
a way to show something like that,
which you can configure in your app.json
and things like that.
But we're just going to show
that until the font is ready.
So that'll execute when
the state is not ready.
But the rest of the time,
we'll render the video board.
So now we have multiple things
to do before everything works.
So we're going to say await promise.all.
this.set.AudioModeAsync.
this.loadFontsAsync.
And then once those are
done, now I'm going to--
I have to put this into an async method.
Whoops.
Then I can say this.setState
is ready, true.
So it's going to set up the
audio mode and load the fonts,
and then it should be ready.
So then-- OK.
Let's see if this works.
Everything seems in order.
But now I have to actually
use the font that I set up
instead of just loading it.
So I'm going to say fontFamily
Cooper Black Regular.
And let's see if that works.
Now this should not display
until the font is actually
loaded into the program's memory.
Bam.
So now this looks basically right.
But you can also see that the
cat sounds thing showed up,
but then it took a little
while for the video to show up.
And so we can actually
preload the videos, as well,
and make sure that they're
loaded in, as well.
So we can say, like, load videos async.
We'll say and await
Expo.asset.loadAsync.
And then basically, you can just pass
in a bunch of require things in here,
and it'll work.
And this will load
these, and then return
when they're all loaded or basically
downloaded from the internet or ready.
The way that Expo works is that
everything is loaded over the air
so that you can load things like--
you can make changes to your
app without resubmitting
a binary, et cetera, et cetera.
And so all assets work either
when they're already on the device
or when they're not.
And so this load async thing will
make sure all of these things
are downloaded from the
internet before it fires.
So I'm gonna say seven, eight, nine.
And so then in setup, I'm gonna
say I want to load assets, as well.
So now we'll do all these steps
before it says it's ready.
And so there's still another step,
even after the video's downloaded,
of getting it prepared to show on
the screen and stuff like that.
So it won't be totally
instant, but it should
be a little bit snappier and a little
bit less janky when I do this now.
OK.
Now I'm just gonna add,
like, nine of these.
flexDirection row.
Put three of these in a row.
And I'm gonna make this two and three.
And then I'm actually going
to add a margin here, I think.
Come on.
So now I have three
videos all going at once.
But they're all squinched together,
so that's why I'm adding this margin.
So now that basically
seems like it's working.
So I'm gonna just format this.
And I'm gonna copy this three times
so I can just add the other videos.
Now I should have a fully-functioning
video board, if I'm not mistaken.
So now whenever I tap any of
these things, they should work.
And--
[MUSIC PLAYING]
I can make all kinds of funny
noises and things like that.
Cool.
Well, I think if you run this on older
Androids, or actually a lot different
Androids, sometimes having nine
different videos on the screen at once
will overwhelm it.
I think that the video
library can probably
be improved to make
that not blow things up.
But in the short term, you probably need
to make this only a three-entry video
board or something to have it
work on Android, unfortunately.
Cool.
You can also do stuff with
sounds instead of just videos.
I won't show that here.
But the methods and everything
for dealing with sounds and videos
are very similar in Expo.
The same person made
both of those things,
and tried to make the APIs
as similar as possible.
So things like replayAsync and stopAsync
and those kind of methods all work.
The main difference is
that you end up, like--
there isn't, like, a visual
manifestation of the sound component.
You just have an abstract
object in JavaScript memory
that you control, sort of similar to
the [? this.underscoreVideo ?] reference
that we ended up making by
doing the ref thing here.
But sound is basically just as easy.
So what I'm gonna show next is I'm
gonna try to do a little bit of stuff
with photos.
So I'm go back into
doing a Snack for that.
And minimize this.
[? This next one. ?]
And then there's a couple
different ways you can do photos.
I'm gonna do the same thing
here, where I import from Expo.
Get rid of this stuff.
And then get rid of that.
OK.
So now I can do something where I
use the camera roll on the camera.
So the first thing I'm gonna
do is just show an image,
just because that's a
good starting point.
So I'm going to just--
let's see.
I'm gonna take these dogs.
And I'm gonna do view style
equals flexDirection row.
Image source equals require.
Enable.
OK.
Unexpected token.
Hmm.
OK.
So now I have two pictures of dogs here
that I'm just gonna put in place just
to start playing around with photos.
And now I'm gonna
launch the camera roll.
I'm going to make a method
called launchCameraRollAsync.
And I'm gonna look up in
the docs how to do that.
I'm gonna use something
called image picker.
To deal with images and the camera
and those kind of things in Expo,
there's kind of two layers of stuff.
There's one thing called
image picker, which
gives you a really simple
kind of full-featured way
to access the camera roll or the camera.
But it's full-featured, but
not customizable at all.
So I'm gonna show you how to
use-- or that's not 100% true.
But only in very limited
ways is it customizable.
So if you're trying to build
something really, really
quickly or just prototype something,
or you just have very basic needs,
this can be really useful
and easy and quick.
But if you wanted to
make something where
the camera is a big focus of the app,
or you want to customize the way that it
works or things like that, you'll
need to use the camera component
or write your own camera roll component.
And so I'm just gonna show this
image picker thing first, which
a lot of people might find useful.
And then I'll go into a little
bit on the camera stuff.
So there's two main
things that image picker
has-- launch image library async,
and then launch camera async.
I'll do both of them, but we'll start
with the launch image library async.
We need to ask for permission first.
So same thing as we've done
a bunch of times before.
We'll say await expo permissions.
Ask async Expo.permissions.cameraRoll.
And let status equal await that.
If status isn't granted,
then we'll just bail.
But otherwise, we'll say
Expo ImagePicker launch--
what was the name of that?
Launch image library async.
And so right now, we'll just
launch the image picker.
We won't even do anything with
the result. Maybe I'll await it.
And then I'll just log it.
So now let's make a
button that launches this.
So we'll say stick a button here.
Launch camera roll.
On press equals-- and we
need an import button.
So we'll go back down here.
And here I'm just going to say
this.launchCameraRollAsync.
Cool.
And hit the prettier button.
So now when I tap this--
whoa.
What'd I do?
OK.
When I tap this, I should
get something logged.
Cool.
So now I can look at all these
different things and choose one of them.
But now how can I show that?
I'm gonna change this to await
this because it's an async thing,
and this is just what a promise
looks like when you log it.
So now I'm gonna do this again.
I'm gonna pick a moment.
And now I can see I get this object
back that has a bunch of metadata.
It has a URI, which is a
local file, and then has
a type image, canceled false,
and then a width and a height.
So that's pretty easy.
So I'm just gonna say--
let's add a state thing here.
Equals chosen image.
We'll say it's null to begin with.
And then after this camera roll thing,
I'll say this.state.chosenImage,
if it's true-ish, then we'll
say-- let's put an image here.
And we'll say the source is
this.state.chosenImage.uri.
And the style will be--
well, we could use the
height and width of it.
Or we could just make
it something reasonable.
So we'll just say height 200, width
200 so it doesn't get too, too big
or take it over, or null.
OK.
So now if I launch the camera roll and
choose this picture of me and Nicky,
then-- oh, I forgot to set state.
Instead of logging this, I'm gonna say
this set state chosen image is image.
OK.
So now if I launch camera roll and
choose Nicky, now that shows up here.
Cool.
And so then what I can do is now
I can use the launch camera async
if I want to.
So let's make another-- let's shrink
this down so I can actually see stuff.
Let's put another button in here that's
like button title equals launch camera.
So when you press this, it's
gonna call this.launchCameraAsync.
So now I have to write this method.
So launchCameraAsync.
Do the permissions thing again.
I'll just copy and paste from here.
We actually need different permissions
because it's not the camera roll,
but we actually need the camera.
So I'll just change this.
But otherwise, this code is the same.
And then instead of the image being
from image picker launch image
library async, it's going to be from
image picker, launchCameraAsync.
So I'm gonna call that.
And take an image.
OK.
So now if I hit launch
camera, now I have a camera.
So I can see the handful of you
who are here in the audience,
or I can flip the camera
around and see myself.
Cool.
So I take a photo.
And it'll give me a chance
to retake or use the photo.
And I can say use photo.
Now we just did set state, but we
don't do anything with that image yet.
So one thing you can control
here is there's actually
a couple of options that are
sort of interesting here.
One of them is allows editing.
If I turn on allows editing,
I can actually modify this.
So I can, like, zoom and crop
and stuff in different ways.
So that's sometimes useful.
But now I'm going to basically copy this
code here and put the same thing in,
except I'm going to choose taken
image instead of chosen image.
Oops.
So now when I launch this, and
I'm gonna take a picture of me.
Do that.
Now that shows up here.
So now I've gotten an image from
the camera and stored it here.
One thing that you
might notice is to me,
this probably looks
like a normal selfie.
But it actually looks really weird
to me because I look like I'm--
like when I watch closely, you'll
see that when the camera's open,
it sort of looks like a mirror to me.
But then when it actually
returns the photo,
it sort of flipped,
which is disorienting
if you're taking the photo.
So I kind of like--
I'll unbutton my
sweatshirt, and you can see
that "I took CS50" is backwards
in the view that I'm seeing.
But here when I take this
picture, now it looks readable.
And so we actually want it
to be the other way around,
I think, for a lot of applications.
Sometimes you might not want to,
but sometimes you do want to.
But let's just practice flipping it
around because it's sort of a thing
that you commonly want to do, especially
when you're using the selfie camera.
And the way we can do that is
something called image manipulation.
There's an Expo module
called image manipulator.
And so it takes an array of actions--
resize, rotate, flip, crop, et cetera.
What we're going to want to
do is the flip horizontal.
And so I can take this thing after this.
We'll just assume we want to do this.
And we'll say let flippedImage
equals await Expo.ImageManipulator.
And then manipulate.
And then the first argument is the URI.
And so it's just going to be image.uri.
And then the actions, it's an array.
And the first thing we
want it to do is flip.
And it takes vertical and
horizontal properties.
And we want vertical to be
false and horizontal to be true.
And so then save options
is the last argument here.
And we can probably just
leave this as the default.
So now we'll say whatever
the result of that
is, we'll pass that to take an image.
So now when I go here,
when I launch the camera,
I'll turn it around to
do the selfie thing.
And then you can see that this is
backwards here, not really readable.
I'll take this photo.
And now-- oh, there's something where
it's now been flipped vertically
for some reason.
So let's just play
around with these options
and try to figure out why that is.
Sometimes there's quirky things
with cameras on certain devices.
Hmm.
I'm super confused about why this
is not flipping this the right way.
I think it should.
Well, I don't know.
Sometimes you end up
with upside-down images.
And this is the real world of dealing
with actual devices and things
like that.
But that is how you flip an image.
Cool.
So just because we don't
have that much time left,
I'm going to jump right into using
the actual camera component that's
much more customizable.
And so we basically need to
do the same thing where we
get permissions for the camera again.
So we'll just copy this code.
And we'll make another thing that's
like launchCustomCameraAsync.
And then what we'll just say here
is instead of doing anything here,
we'll just say customCameraReady true.
And then what we're going to do
is we're just going to stick--
why is this [? going there? ?] Oh.
Forgot to say async here.
We're going to actually just stick
the camera right after these buttons.
We're just going to say
this.state.customCameraReady.
And we're just going to actually
stick in the camera component.
And it's just called Expo.camera.
So we're going to say Expo.camera.
Style equals width 400, height 400.
And type is sort of the camera constant.
There's back and front.
So let's pick one of those to start.
And we'll just say type
equals Expo.camera.constants.
We'll start with front, and
we'll maybe do back later.
Well, I'll start with back because
that's gonna be more normal.
So that will just be that.
And so then-- unexpected token.
Oops.
I forgot this.
OK.
[INAUDIBLE]
OK.
So now what I'm gonna do is
I'm gonna launch the custom
camera just if component did mount.
Did I already define that anywhere?
No.
OK.
But I'm gonna say
customCameraReady false.
Component did mount.
this.launchCustomCameraAsync.
OK.
So now actually, there's
now a camera component
that's just embedded right
in the middle of the app.
And so I can do whatever I want with it.
It doesn't do anything right now
because it doesn't take pictures.
None of the UI is built out because
it's basically fully customizable.
And so you're responsible for putting
together all the building blocks of it.
So one thing we can do is we can make it
flip back and forth between modes just
by having you tap on it.
So we can say camera type.
And we'll start it off by saying
Expo.Camera.Constants back.
And then here, let's say
this.state.cameraType.
So now it's the back camera.
But then what we can do is
we can make this a highlight.
Why not?
So now I'm using this
touchable highlight thing
that we used in the video board so
that you can touch the camera now.
I'm just gonna make sure that works
by adding a log statement here.
So now when I do this,
if I tap the camera,
I can see tap camera
showing up here in the logs.
So what I'm gonna do here
is now instead of that,
I'm gonna say if this.state.cameraType
equals Expo camera constants back,
this.setState camera type
Expo camera constants front.
Else we'll do the opposite of
that and set it to be back.
So now tapping this should switch
between the front-facing and
back-facing camera.
Or not.
Huh.
Oh.
I'll move this out into a method.
That might solve the problem.
Hmm.
I'm super confused at
why this doesn't work.
OK, so that's getting called.
Camera type starts off as something.
But now it's somehow undefined.
Huh.
OK.
Well, this is just some sort of weird
React thing that I'm confused about.
But it actually can happen.
And I've done this before when
I was practicing for this.
So it does work.
I'm not sure why it's
not working right now.
Cool.
So just to show that the other
way camera does actually work,
I'll just change this default
so that it says front.
And then now, the default here will
be instead of the back-facing camera,
it should be front.
Ah.
Type front.
That was the problem.
I forgot this type thing.
OK.
So now if I tap it, it
flips back and forth.
Cool.
I don't know what the lesson is there
except read the documentation closely.
And when it says constants.type.back,
don't miss the type part.
Cool.
So there's actually a ton
of other APIs that Expo has.
So these include push notifications,
calendar, gyroscope, accelerometer,
pedometer, OpenGL view, which lets you
do things like Instagram-like filters
and 3D-gaming type stuff with
things like Phaser and Pixi,
Facebook login and Google login, ads
like AdMob, Facebook, fingerprint
scanner like Touch ID or on iPhone
Xs, Face ID, the Secure Store, SQLite,
and then analytics
and [? sack traces, ?]
and then a few other things, as well.
It's all on the docs.expo.io
thing, that site
that I've been going to every time
I needed to look something up.
And so this Expo docs is a pretty
good augmentation to the normal React
Native docs that I'm sure you
spend a lot of time looking
at whenever you're doing development.
And I'm going to put all the code
that I was just showing in this repo--
github.com/cchever/harvardguestlecture.
And hopefully, I'll put that
up in the next hour or so.
And so you can refer to if you want to.
And thanks so much for
coming and listening.
And I hope this helps you
build cool stuff that you
want to build on your mobile phones.
Thanks.
