Alright, so as I mentioned, the
calculator's working pretty well. And I
think it was a nice touch that there's
no need for a Clear button to start a
new calculation. When you want to stand
a new calculation, you just press
equals to finish the current one,
then type the new number and press equals
again. The new number is transferred
up to the result, ready to add to it or
multiply or what if you want to do next.
But as I mentioned, it's not necessarily
very intuitive. So to see what I mean,
I'm just going to bring back the emulator,
and if we actually type something like 6
equals, to start afresh. So next if I
type divide 2, click on divide 2, then
I deduct a take, 3 and then equals, we get
the answer 0.0. But if I want to multiply
9 by 5 now, I enter 9 times, but because it
tries to multiply 0 by 9, it doesn't work.
So what I actually have to do is type
equals 9 equals, then the multiply 5, and
now when I press equals I'll get the
answer
45 - basically, when I press the next
operator button. But again, that's not
very intuitive, and could actually
confuse people til they get used to it,
but I'll come back to that later. But
also notice that when you click on the
result EditText, the soft keyboard
doesn't appear because, if you recall, we
made it not focusable. But if you want to,
you can use the soft keyboard in the
data entry EditText. So in here, we can
do that and you can see the keyboard's popped
up for us there, automatically. Now we
could disable that as well, but if the
user really wants to, then there's no
good reason to stop them. And they can
always dismiss the keyboard if they
don't want it - if they don't want it anymore.
So they can come down here and get rid of
it if they want. Now remember that they
might also be using an external keyboard,
so don't restrict what your users can do,
unless there's a really good reason for
restricting them. Now there is one bit of
validation that we need to perform,
though, despite having used a numeric
EditText widget. Now we can't enter more
than one decimal point in the input
widget - we've tried that previously. But
we can enter nothing but a decimal point,
and unfortunately, that will cause the
program to crash at the moment when it
tries to convert a full stop to a valid
number. So to demonstrate that, if I type
the button, or press the button and type
equals, we can see we've got Calculator has
stopped. And if you we back to Android
Studio now and look in the logcat, and
just scroll up and have a look there, we
can see that we've got a NumberFormat
Exception for input string, and then the,
just the dot, or the period. And you can see
that the error there, is actually
happening on line 86, and the code for
line 86 is this operand2 equals value
dot toDouble. And just to confirm that,
if I just close down the logcat
and run this again. So now that the
emulator's running again, if we enter a
dot equals as the very first input,
we'll get the same error. So dot equals,
same error, but then looking at logcat,
this time it's actually on line 84, on line
84 as for operand1. Now what we could do -
I'll just close down logcat. So, we could put
in a test for value being just a dot,
but if the program was changed to allow
the input number to be negative, for
example, it would then have to check for
dot and minus dot, both of which would
cause an exception. So the way I'm
going to prevent the program crashing
here, is to catch the NumberFormat
Exception that's being raised. But before
we can do that, though, we have to decide
what action to take
when invalid input is used. Now, we could
interpret invalid input as 0, which sort
of makes sense, but may not be what the
user expects. But what we'll do here, I
think, is change the program slightly,
so that it performs the conversion from
String to Double before calling the
performOperation method. Then if the
conversion raises an exception, we'll
clear the input window. So first, what we
want to do, is change the perform
Operation method, which I can see on the
screen there now. Now its first parameter
will be a Double, which is
assigned directly to operand1 or
operand2, without conversion. So I'm going
to go ahead and change value to a Double,
and then we're going to change it so it's
assigned directly to operand1
or operand2 without conversion, because it's
now, I've typed Double. So we're going to
change this, now, so operand1 equals
value if operand1 was null. Otherwise,
operand 2 is going to be equal just to
value now, instead of value.toDouble, because of
course, the definition - the type - has been
changed from String to Double, up here.
Alright, so the conversion from String to
Double is then performed in the on
Click method, and the input field cleared
if an exception's raised. Now if there is
an exception raised when attempting to
convert the String into a Double, the
performOperation method won't actually
be called at all. So let's go back up to
that code. So back up here in the
opListener code, and what we want to
do here is introduce a try/catch block.
So after the first line here on line 65 -
the value op, where we're defining that - we're
going to introduce a try/block, and
within that try/block we're going to,
firstly copy that line, the definition of
value. Paste that in there, and then what
we're going to do is, also copy that line
and paste that in there as well. Then we
can get rid of this if test outside
of here. We no longer need that anymore.
And in fact, what we'll do is the try, we'll
finish off the try with a catch. And the
catch, it's going to be an e : There's
going to be a NumberFormatException,
and the code block in there will be new
Number.setText parentheses, and then
two double quotes to set it to an empty
value. The other thing we need to do,
though - and we've got this line here; val
value equals newNumber.text.toString - we
need to do one other thing there. We need
to do a dot toDouble, because of course,
we're converting the String to the
Double, here, because we've changed the
type in performOperation and we also
removed the conversion that was
originally in here, in the perform
Operation function. Alright so now, even
if there is an exception, the code still
sets the pendingOperation,
because the code will still be executing
down here on lines 72 and 73. Because
otherwise, the user wouldn't be able to
change the type of operation they want
to perform, without entering some number
and performing the current pending
operation. Now if you're not sure about
that, you can move these two lines here,
into the try block and see what happens
if you press divide, when you really want
it to multiply. But with the code as it
is here,
you just press multiply and carry on. Alright,
I'm going to leave that code where it is,
and let's just try running it again, and
see if things work. So we just do now,
a dot equals. We find now, that it's, in
fact, not crashing anymore. So we've
fixed that, so obviously, the try/catch is
working. But we should still be able to
do something else like seven equals, and
if we put the ten plus, and you can see that's
working and it's doing it; dot equals
again, and that's still working nicely.
Alright, so now we've fixed the crash,
and we're now no longer causing the
app to crash if a decimal point is
entered by itself. Now the last thing I'd
like to discuss before I finish this
video, are the warnings - and we'll just go
back to Android Studio - that are
sometimes showing in the right-hand
margin. And as I mentioned in the last
section, it's always a good idea to keep
an eye on these and pay attention to
what they're saying. Now support for
Kotlin in Android is very new, and the
lint checks are evolving all the time.
Now I'm sort of guessing ahead a bit
here, because I haven't got any warning
showing - there's just this one suggestion
here; Assignment can be lifted out of if.
Now you may also not have any warnings -
it depends on whether Google have
included the checks in your version of
Android Studio. But even without the
checker flagging warnings, a careful
review of the code shows something that
we should look at - operand2. Now if we
look through the code, the only place
it's used is in the performOperation
method.
So again, operand2 - we defined
that here originally on line 17. It's
only really used
in the performOperation function. Now
Android Studio makes it easy to check
that. We can select operand2 where
it's declared on line 17 - I'll just go back
up there again. So you can select that by
double-clicking it, and a pink or purple
tick will appear in all the places it's
used - you can see over here to the
right-hand side. And if I just click on
the first one, you can see the first one
is actually in the performOperation
function, and all the other ones are
below that. So in other words, that's the
only place other than the definition,
which I can click to go back up to, that
it's used in this particular class. Now
operand1 has to be a property, because
it's used in both of our
functions. Now operand2, though, isn't
used anywhere outside the perform
Operation function. Now it's a good idea
to keep the scope of variables as narrow
as possible, and that means not making
them available in places where they're
not needed. That way, they can't be
accidentally changed by code that
shouldn't be touching them in the first
place. So what I'm going to do is, on line
17, I'm going to delete that definition -
the declaration for operand2. Now
the first place it's used in perform
Operation, is to give it the value of the
value parameter. So let's just scroll down
to that code again, and you can see there's
the first time it's been used, currently
on line
87. So there's actually no need to use a
separate variable at all. We can just
change all the places where it's used in
the method to use value instead - value
being an argument to this function. So
I'm going to go ahead and do that. So I'm
going to delete this line completely,
leaving the if. Now we've got these
errors with operand2 - we just need to
change operand2 to be value instead. So
I'm just going to do that. I'm just going
to literally change all of these, by
selecting them and pasting value. Okay.
And you can see we've got a little green
box here now, which means there's no errors,
as such, and we've fixed all those errors. And
I think you'd agree, that's a lot tidier
and it also makes the code easier to
read. Now in fact,
Kotlin's got another way of
writing the code, that we've currently
got here on line 93; the if value equals
equals 0.0. Now these lint checks
are changing all the time but as of the
time I'm recording this, there
is a suggestion that Assignment can be
lifted out of if. So as you can see, there
is a suggestion now; Assignment can be
lifted out of if. And I'll just select that
again - I'll do the Command F1 so we
can see it on the screen there. And
again, you notice I can click on this line
of code, this little tick mark on the
right-hand side - the right-hand menu, this
little check - and it goes directly to the
line of code. Now, what we can then
do, is we can click on the light bulb and
click on Lift assignment out of if, and
you can see it's actually changed the
code for us. So after clicking on the
line of code, we actually get this
different assignment. We've got the
double quotes and the slash and then the
dash greater than sign, operand1 equals
if parenthesis, value equals 0.0
parenthesis, and then the code block. Then
we've got the double NaN else operand1,
where we're dividing it by value. This
is a lot more idiomatic Kotlin, rather
than Java. Now you may have another
warning, on the result, the result.set
Text line - this line here. It's possible
you'll see a warning - of course, that's
right at the end of the perform
Operation function. And it's not really a
good idea to use toString to display
numbers, because it doesn't really handle
internationalization. What that means is,
the numbers will always display with
a decimal point - even in countries like
France, for example, where a comma is used
instead to separate decimals. But we're
not really yet ready to talk about
internationalization at this stage. So if
you do get that warning, just ignore it
for now.
And let's just test the app one more
time before we finish the video.
Alright, that's working nicely and I'll
just go into landscape and just check
that's still working. That's working nicely
as well. Alright, so you'll find the
source code for this app in the
resources section for this video. And
before I put it up there, what I'm going
to do is - I'll just go back to portrait
mode there - back to the code, and I'm
going to go up to the code menu and
click on Reform, select Reformat Code.
And you can see it didn't do much - it just
reformatted things - and I suggest that
you do the same with your code. Then if
you need to use Android Studio to
compare it to mine, the diff tools that
we've talked about previously will have
an easier time of it, and you should get
better results. Now in the next video,
we're going to fix a bug in the
calculator app when it changes
orientation. Well when I say we, I mean
you, because the next video is a
challenge. The bug is quite subtle, so
don't worry if you don't spot it. Al
right, so I'll see you in the next video.