Skip to main content

Displaying a list of tasks

Lesson 6 from: Developing Android Apps with Java

Tony Hillerson

Displaying a list of tasks

Lesson 6 from: Developing Android Apps with Java

Tony Hillerson

buy this class

$00

$00
Sale Ends Soon!

starting under

$13/month*

Unlock this classplus 2200+ more >

Lesson Info

6. Displaying a list of tasks

Lessons

Class Trailer

Day 1

1

Overview: What is Android?

19:56
2

Eclipse and SDK setup and build a Red Flashlight app

38:05
3

Ad a button to a Green Flashlight

21:06
4

Add the Green Flashlight

18:48

Day 2

5

Week 1 - Layout and Build a Task Manager App

04:10
6

Displaying a list of tasks

40:44

Lesson Info

Displaying a list of tasks

the first step that we're gonna take is to display a list of tasks. Actually, before we get into this, maybe I'll go through in the emulator and kind of show you how this application is expected to work. So can everybody see the emulator? Okay, good. So this first generation again is gonna be pretty simple. We're just gonna have a bulleted list of text right here in the middle of the emulator to show uh, we're in the middle of the screen to show all the tasks that are added so far. Later on in the next couple weeks, we're gonna be making that a little more interesting to look at and better to use. But for right now, when you add a task such as the the sort of hello world of task managers get milk. Um, when I had that task that I could see that come up here in a bulleted list format and said I could at least manage a list of tasks whether I could do anything more interesting with them. Um is ah is a problem that will have to solve in the subsequent weeks. But that's we're gonna build a ...

building today. And also, let's say that I'm adding another one. There's one more thing we want to do. If I cancel, um, it's gonna say you've got someone save changes here. Are you sure you want it? Get ah, get rid of those changes and say I say yes, discard and then it won't get you won't get shown on the list. And that's what we'll be building today. Okay, so the first step we're gonna take switch back to the slides here is just displaying a list of tasks. The task screen is gonna look like this for a first generation. We just have that bulleted list in the middle, like I say, and then ah, down at the bottom will be an ad task button. And what's interesting about this a screen and may not be so, uh so obvious is that the task list needs to take up all of the space on the screen. And given that the button is anchored against the bottom of the screen and that's gonna that's gonna necessary necessitate the use of our retail, a relative layout and ah, you'll see how the ins and outs of that lay out work So the steps we need to go to our we're gonna add a task model object, which is what will use to keep track of each individual task. And we'll have a list of those that the test manager will display and the ad task screen will add to. And then we'll put a text view in the middle of the screen and we'll add an ad task button and listen for one that gets clicked and then go to the ad task activity. So this is, um, probably for this first step here. Besides, the different layout is pretty similar to what we did last week. So that's a good place to start with something we're kind of familiar with. So I'm going to switch over to Android now. Ah, this is sort of a fresh, blank slate of android. All I've done besides installed the android sdk. Um, addition to the I d. As a plug in which there are instructions for on the site, I believe is ah opened up this DDM s perspective, which, um, we're not actually gonna be using too much today, but it may show you have to switch over to that. Um and that that ah also loaded the the android sdk. So that's something we need to dio. I've also made sure that I got a list of a couple different A V ds or Android virtual devices and will be using the one that's built against 1.6 using the Google Ap eyes and ah, that's already running over here. We just looked at it and I also moved the ah, package Explorer over here to the left so that I could get it out of the way. So there's not really much changed from just just opening up Eclipse installing the android. Um, esta que I de plug in for the first time. So, um, all right, to get started, I'm going to had a new project here in the Package Explorer, which is where I do most of the most of the work that has to do with my project. So I right, click here and say New Android project and I get ah little sort of form here, and I want this project to be called task Manager. I'm just gonna keep it in the workspace using the default location, and I'm gonna use the Google AP Eyes 1.6 emulator and building. It's that the application name is going to be task manager. The package name. Ah, once again, that package name is important. Um, not only is as it's important and any normal job application to name space your your specific classes to make sure the name there's no names that run into names from other libraries you might have. But that is also in the android world, the unique identify or for your application. So I'm gonna call it Calm down, O Reilly, that android dot task manager and ah eclipses generously offering to create us an activity which will also create a ah, a layout and kind of get a started off with a little bit of generated code So we don't have toe set things up so much, and I want that to be called the View Tasks activity. It's now I'll click. Finish. There's a question. Yeah, I have a really quick question. So you chose to build against Google Ap eyes instead of just the android sdk because you know you're going to include some Google functionality later. Yep, Right later on down the road, we're gonna add a map, so I just I'm just setting things up against the Google Ap eyes right now. Um, I mean, it doesn't really hurt to too much, um, and it leaves things open as a possibility. Um, and then you want to switch. But switching is not that hard either. So it really it's not too much of a decision. You have to agonize over up front. So, um, here in the resource is you saw last week we built against a 1.5 AP eyes. But now, when I build against the 1.6 FBI's there are different, um, right off the bat, you can see some of the, uh, the options for how things are not just localized and internationalized, but also set up for different screen resolutions. Um, in the resource is directory here, just like we had last week. We had a draw ble directory, which had an icon in it. In this case, it's given us three Drabble directories, one for a c p I one for low density and one for medium density. Those air different screen resolutions so you can have different draw bulls set up for different ah screen resolutions and and you have them to name the same way. And those will show up. Um, depending on what screen resolution the devices running on. So you can you can optionally add those that functionality. That's just sort of an aside here. Um, but that is a difference from last week's wanted to call that out. But, um, other than that, the rest of this project structure should look pretty familiar to your based on last week. And we have generated layout main XML, and we also have a generated strings XML and we have the manifest. And then we have the class that would created for our first activity, which was the view tasks activity. So, um, let me just run that and see where we get. Okay? So the first time you run this union to go right, click your project go to run as android application, and that should be uploading and displaying on the emulator. No, this is what our task manager looks like. So there are the two strings that we that we had ah generated into our strings file. So to get this to look like our are sort of cops, if you will like this. We need to do a few things. Let's go in and edit the layout first. So I'm going to switch over to the layout main XML and start getting that set up the way we want. Okay? And here's the sort of layout. Editor mode, The visual layout editor. Um, but I want to go into the XML. I'd rather look at that and let me see my numbers. OK, now we're ready. So the first thing is, it generated us a linear layout, which is kind of the basic, the most basic common layout. We're gonna change that in just a second, but let's get a few of the things in place We can kind of see why we want to change it. Ah, this text view says hello and that's not not helpful. Okay, so I'm gonna open up the strings file again. I can use my quick. I like to use the keyboard instead of the mouse because it's faster. I can use this open resource by on the Mac hitting commands. Shift are on, I think on windows, it's Ault shift our for open resource. Looking a bit, this strings XML and I know no longer need this this. Hello? That's not something we need. I'm gonna make this task manager look a little more human readable, and I'm gonna add a string cold tasks which will be sort of the label in our main xml. Gonna change this text for you to use that string instead. So there's a quick refresher absence, not text. We want that to be tasks. So that is a quick refresher on how strings work. Remember this at string notation means that you'll find that in some sort of, um, resource is straying XML structure inside of one of the XML files, probably called strings. If you follow the generated code inside risk resource is or rez values. Okay, Now we have a noose new ah, sort of label to sit at the top. But if we look at our cops, we wanna have underneath that another text area, and we'll just start with just a text very for now. And then what? We won't have a button underneath. So what's add those two things quickly text view, just copy and paste that we want to. And then at the bottom of that, we want a button and Ah, we'll have the layout with For all of these, be Phil parent, which is gonna fill up the screen. Um horizontally and the layout heights will be wrapped content initially on all these, although we changing that in a little bit. And the button is going to sit at the bottom and we want to the label for the button to be had task. So when I call this ad task course, when I say that there's gonna be an error here, show me that there's no such string in The resource is so I'm gonna come here and at another line. By the way, I just duplicated that line, which is a nice little eclipse. Ah, shortcut by hitting command Alz on Mac and I think control Ault on Windows Down Arrow. When I'm on this line, I can just hit that quickly and then get a duplicate line, which is nice. Makes copying pasting so much easier. Anybody likes to copy and faced a lot in their code, don't they? I'm going to call this ad task and I call the name of a string ad task. Now, over here, this should no longer be an error and I also want to cut the text out of this text of you. And now what's just before we deployed to the emulator? Let's have a look at what the layout looks like. So now we have the tasks title at the top and we have a text area in the middle. And then we have the ad task button sitting down here at the bottom of ah, the text area, but not the bottom of the screen, which is the way we want on. Let me switch this configuration over the portrait there. That's more like we're expecting it to look. Now when I look back to the XML, we're gonna fix that, um, by pegging the button to the bottom of the screen and making the text of you Philip the area in between. And we're going to do that by first of all, changing the layout from linear to relative now a relatively out Um, it's gonna complain, because I need to change this bottom tag as well. A relative layout lets you lay out the Children of the of the layout. I just invent these toe, get a little bit more visual flow. It lets you lay out the Children of the layout relative to each other, which, um, makes it easier to do a little bit more complex views, um, than just sort of stacking things on top of each other. So what I'm gonna do is add one attribute here to the button that is, ah, gonna anchor the bottom to the bottom, anger the button to the bottom of the screen. And that is android layout. And you could see I'm getting some completion here, which makes it easier to find what the possibilities are. I'm just going to, ah, Barrow down to Android layout a line parent bottom and set that equal to true. Now, when I go back to my layout view, you can see that the button is lined out lined up against the bottom of the screen, which is exactly what we want. So, Tony, what's the difference between a line parent bottom and just a line bottom align bottom takes a reference to another item on the screen. So, um, I could say a line bottom equals Well, here, let me just jump ahead, because it's so this will, uh, illustrate this point. Um, later on, I'm gonna need to get a reference to this to this text view eso that Aiken do some layout stuff. So I might as well set this up right now. I'm gonna give this an I d of at I d plus, uh, tasks title. So now I have a title here. Um, oops. At the backward. Sorry, guys. Plus. Okay, so now I have an I d. On here. Ah, that means I can reference things in Java. Um, with an I d. But I can also reference them inside this view. So if I wanted to say light, lay out the button directly underneath that text for you, I could say android layout bottom equals the tasks title button. Um, the idea of that. Like this. Ah. What is it complaining about? Is it a line? Bottom line. Bottom. Yes. There you go. You're right. Okay. So now when I go to the layout, actually think I need to grab this out of here for just now. Now you can see it's lined. The bottom of the of the label on the bottom of the buttons are aligned with each other. Okay? Yeah, That makes total sense. So that's Ah, that I think, actually, let me make sure that that's the case real quick If I change this to a linear layout and save it, Uh, no, I could still do a way out of line bottom. Um, some, some of these some of these properties aren't available inside of certainly outs, and I thought only online bottom was one of them, but it looks like it's OK. You can still do that, which is a little strange, but we contest that later. If anybody has any more questions about layouts of general, we could do that on the forums. But this particular layout, we'll we'll have time for questions in a little bit. So if any of this is not making sense, I can clear that up for you. In any case, we don't want lay out of line bottom here. We want the Aligned parent bottom. Ah, and and as you saw in that list of of ah options, I came up. There's also a line on the line, parent top left and right, so you can put things on two different sides of the screen. Okay, so now we have the button on the bottom switch back to the layout view. Um, there is still the need to put the text that is sitting underneath of the title, which is gonna be our main task view area. We need Teoh. Make that expand from the bottom of our title to the top of our button. And the way to do that is, um, actually, there's a couple different ways, and this is kind of exposes one of the weird quirks of relatively out that I wish, uh, that I wish would be fixed in later versions of the Android Sdk And I also alluded to a little bit and lasts at least tangentially in last week's in last week's discussion about the the I DS of the linear layout. So let me make that more clear. Here's what we can do. There's two things we can do and let me do see, I'll do the 1st 1 first, which is kind of the weirdest and what I thought was the only possible way to do this at first. But then I found out there's a better way. So what I need to do when I, um, pegged the top of this this text view for the list of tasks to the bottom of this test title text view and the bottom of the text of you to the top of the button is referenced those I ds and I can only reference and I d once it's been initially sort of declared like this with the at plus I D, which which actually generates the I d with this name. So it asked the first time I referenced the I. D. It has to be with this plus sign. Which sort of initialize is it, if you will, And if I need to refer to it elsewhere in the XML, it needs to be already initialized somewhere above it in the in a lower line number, basically, which is kind of a pain. Um, so, ah, by the way, I need to add an I d for this button, someone a copy and paste in some code here. So now I have an I d. For both the button and the task title. And what makes the most sense to someone, I would assume is that you define the I d. When you define the tag. So I'm defining the idea or adding the plus the plus sign Aaron. Stan Shih ating it sort of where I to find the tag that it goes along with. And if that's the case, when I do these, um, I'm gonna paste in some code here, too. We bumped this up on the screen and make it a little more viewable when I when I do what I need to do next, which is layout below and layout above. Just like Beth kind of alluded to, um, the idea of task title and laying up above the button, which is gonna put this views bounds between those two other components on the screen. I need to have those ideas already reference, which is weird. It puts us in a weird position because now I have to define the text view at the top on the button next and then the text view after those, even though this one is going to sit in the middle of these two in the view and the problem occurs because I have to do this, this initialization of the I d. First, I hope that's making sense. So now we get what we want with this text view. If you could see the red line, um, sitting underneath the title and above the button. But it's weird when you look at the view because it doesn't follow that same continuity. So that is one way that you can use. You can set up your relative layouts, but I don't doesn't It doesn't sit well with me. So there is another thing that you can do. And this is kind of what I alluded to last week. I could put this, which I think is a win in the order, that I expect to see them on the screen. Except now, when I save them, I mean in an error over here online. 13. Referencing that text view. I'm referring to this button, but the button I D. Has been in Stan sheeted down here so I could do and this is weirder. But I think I think still sort of a better situation. I'm going to define the I D button at the idea of the button inside the text view and then reference inside the the button itself. Um, I hope that makes sense to everybody, but we're we'll have time for questions in a second. Actually, let's let's just take some questions on that. That kind of That's kind of a weird deal. Is anybody have any problems with that? Does anybody have any questions about that? We did get one question in the chat room that asked about using linear layouts with layout weights instead. But so that would be a completely alternative way of doing it. Yeah, let's let's save that question to the ah, the forums. But that may be another way of doing this same screen. Yeah, Um, yes. So basically, layout, weights, or you can put a weight on these components and it'll it'll say, take up more of the screen or not. I usually find that as my views get more complex, I wanted a relative you anyway, or a relatively out anyway. So now I've kind of gotten in the habit of starting in there. Well, let me just summarize to make sure that I understand what you've done. And then hopefully it'll be understandable to other people in the same way. So basically you had to choose between doing the layout in the order in which the elements appear on the screen or doing them by their I d. When their ideas being declared. And what you prefer is to do them in the order that they're laid out on the screen. So we have the two text views and then the button, and because of the I. D of the button isn't declared when we need it, you're changing the declaration of the button to be inside the text view rather than on the button itself. Yeah, because I I would much rather have the tags for the components be in the correct order because that's I'm thinking about when I if there's a problem or if I need to add a new layout element or something. As I'm scanning the XML, I'm expecting to see the components in a certain order. Now that that just maybe a quirk for me. But I don't know. I feel like that's more important things to optimize for, because that's that's what I expect to see in the I. D. Weirdness is just sort of a red tape you have to jump through to get to that point and and you you don't recommend necessarily one over the other. Just whichever makes the most sense to you is the programmer. Yeah, and your team and you know, if it's just you that you could do whatever you want. If you're If you confuse your team by doing one thing, then um maybe that's not baby. That's something you have to optimize for as well. Okay. Okay, cool. So we spent a little bit more time, then that probably warranted. But I wanted to make sure everybody understands that, because that's kind of a weird quirk of the relatively out. All right, so now that we have the button referenced with an I d. And we have Thea Well, no, we don't. We need to add the idea here for the text of you that may just copy and paste in I d. Uh oh. Wrong place. Okay, Now, I haven't I d s for all these guys. Let's go look at the Java activity because we need to get reference to a few of those views that weaken work with them. So here, this is the generated activity. Um, all the boilerplate code that eclipse generated for us. I'm gonna clean this up a little bit here to the comments, um, and then set content view associates. This activity with that view loads that view and then kicks things off. Now I need to get a reference to a few of those elements. So I'm going to do what I do often and push that off into a different helper function called set up views so I can keep the on create clean. Ah, this is an interesting feature of Eclipse that I like, because it kind of lets me code the way I think, instead of instead of working ahead. So I want to call a method called set of views here. But when I piped type this into clips that says, Hey, what are you doing? That's that's not a method that I know about so I can come and hover over this with my mouse and shoes a quick what's called a quick fix. And it says that it expects that I probably want to fix this by creating a method called set up views. So I'll click that. And there we go. We have a private method, said abuse, which is exactly what I wanted. And now, um, I want to get a reference that button and also the text area. So I'm gonna create ah, field level variable, but I'm gonna do it kind of the same way, Um, using an eclipse quick fix. But let me type out the whole thing so that it has more information to go on. So just like we did last week to get a reference to, um a view that is set up on the the view that said as our content view, we needed use find view by I d. So, uh, I want to get a reference the add button, and that is a button. So I'm gonna come hover over here and say important and then find for you by I d which will find the view called Are that I d dot ad? It didn't had button. Okay, now I can hover over this and ah, clips gives me a couple different options, and I want to create and not a local variable. I want to have a field so I can get referenced. If I need to reference that elsewhere in the class, it'll be something I could do. So I'm gonna click, create field, Then you could see it added a line appear online. Nine private button, um, add button. So now we have a field for that button, and I'm gonna do the same thing here for task text and paste this in. Then come here. Control space will import text view where I can have her over. It is another option, and then I want to. Also, instead of just hovering over this with your mouse, you can hit command one or I believe, vault one on windows, and it will give you this many or so far from using my keyboard instead, I can say command one and then tap down there. Ah, arrow down to the option that I want. Okay, so now I have to Private fields, one for the button and one for the task, uh, text, which will call task text. And the first thing I want to do after I have reference to those is quickly set up a click listener on the button. So I'm gonna paste in some code here to save some typing. This is exactly what we saw done last time last week. I'm gonna, um, set up a new anonymous class here which implements the viewed on click listener, which has a method on click. Um, and then I'm gonna fire upon intent, which comes from view task activity Does this, which remember, is sort of the red tape we have to jump through because we're inside of this anonymous class and I want to go to a class called ad task activity. And then I will start that activity with that intent. Um, but we need to create that class, so I'm gonna create the class right now. Um, I could hover over this, um, in eclipse, and it says that Well, I don't know what this classes, so I expect that you would fix that by creating the class so I can click on create class, add task activity, and then I'll get this this form to set up a new class and a lot of things already filled in for May, which is useful. I wanted to be in this package. Um, there's no enclosing type. I'll just leave it as public super class, though, is gonna be an activity. And I can hit control space and here to you remember, from last week and now I have everything set up. It will create the class for me. And now let's do one more bit of useful eclipse magic and will go to source and we'll say just like we did last week. I believe is well over Rider implement methods and we'll choose on activity. The on create method and well say set content have you to be our dot layout dot add tasks and that doesn't exist yet. So that's not going to show up. I was gonna show up is an error, so I'm gonna go create that as well, and I create that inside of rez for resource is layout. I could come here and say new file and we call this ad tasks that XML So basically what you're doing here, Tony's you're saying we need to have a new view called Add Tasks for adding the tasks. Yep. And right now, it's gonna be pretty thin. But I'm just sort of sticking sticking it in there so that we can navigate there and make sure our click listeners working. So what I'm gonna do is copy and paste in a little bit of XML here just to get us going. And instead of a relatively out, I want this to be a linear loud. So we had a couple of comments, Tony, that you went a little too fast on that section. Could would you mind just kind of going back just a couple of minutes and going over that again? Sure. So what I could I did from the view. Task activity is just like last week. Um, I want to be able to click on that button, Uh, which looks like this in the layout that task button and go to a screen where I can add task. So to do that, I have to have an activity for adding tasks. And I need to have a click listener on this activity to interpret that click to mean, um, to open up that task. Ah, a task of you. So back here in the activity, I have set up a click listener on the button on the add button, which starts a new intent, just like we looked at last week. Intense of this type allow you to go from a certain context. In this case, the context is this instance of the view task activity, and then I want to go to the ad task activity. But since that wasn't created yet, I'm just kind of showing you a bit of a workflow that I like to use an eclipse, which is I type the code that I want to have. And then when eclipse doesn't understand that it usually helps me by giving me options for generating code or setting up variables. So all I did was type that before I had the code that that would actually make up the ad task activity class. And then, as I typed up that it gave me the option to create the class. So I created this ad task activity over here and then added a method called on creating, which is you remember from last week as well. The first method is going to be set up, Um are run during our life cycle of this activity, and that lets us set up the view just like we do inside the view task activity. When I switch over there, you could say the same thing happened here. We loaded the content view that's named Main, which is in the main XML file. So I loaded a content view that didn't exist yet ardently out that add tasks. And then I just went and created that XML layout here, and I set that up is a linear layout and there's gonna be nothing in it. But at least everything should be ready for us to to ah, click that button and go to that activity. So, um, at this point in last week's episode, we're kind of rushing a little bit. But does anybody remember what the next thing we need to do to make this ad button actually work and not crash? There's one other thing that we needed to do. Manifest. Yeah, somebody says, Manifest, Yes. Yep. So the manifest needs to know about all of the activities that we want a reference. And so if I open up the android manifest, you'll see that it's well, they give us this form editor view, which I don't really like. I like to see the maximal eso eso android knows so far. Remember from last week that this package is that identify her for our app and then inside the application tag. We list all over activities and there is an activity for the view task activity. But we also need to know that there is a add tasks activity add task. I called it not Task Sue. We're not tasks and then I'll just keep the label the same. Since we already have that string set up. Oops. Closed the tag. And now the manifest knows about the other activity. So therefore we can call it so I should be able to go from here and run our applications. I could go to the big play button at the top, since I've already run this once and click on task manager and we should be able to at least see our button laid out at the bottom of the screen. And when I click it, I could go to this next view, which is blank. And if I click the back button, I could go back to the original view inside of the first activity, so that I think completes our first steps. So now we have a text area where we can put tasks. We have a title at the top. We have the button at the bottom. Um, we don't have any tests to show yet, but the button does work, and that will allow us to go to the next step, which is where we'll put in tasks. And by the way, here is a list of the files that were created by Eclipse during this step. So we have a view, a few tasks activity, which is the controller that we set up, are the activity that we set up and, ah, layout of the main XML file, which is used by that view task activity. Ah, And then one thing I forgot to create is the tasks model item. So let me Well, let's list. Let's just go on. I'll create that right away during the next steps. I want to make sure that everybody understands where we've gotten so far during these steps because I did take a few detours there. But But hopefully that explain more why I took a detour last week, which was that little discussion about the I DS and their placement. So has any questions about what we've done so far during these steps. We do have a few questions, and a lot of people are asking to see the code for ad task activity again. A very common request on my at common request there, as well as the manifest again. Okay, So, uh, can you put Could you do the manifest on a split screen? Awesome. Awesome. So um we did. We had a couple of questions. One was Can view tasks activity dot This be replaced with this dot Get context. Um, so let me switch to that code. Sorry, everybody who's still typing that out. But I'm gonna get rid of this manifest to you. They're fun is a little too big for that to make since. Okay, so the problem is, if I did that, um, well, maybe that would work. It's It's not, um, the on click listener is not Does not have a get context. Okay. In the ad tasks, activity when you are when you set up the set content view part when you did the r dot layout dot Add task. What was the hot key that you used to create a new layout? I didn't use a new layout there. I didn't use a hot key there. I just actually went in and created a new layout by going here to the package manager and right clicking, um, the layout directory and saying new file. So no hockey. Okay, good. Let's get on to creating a little bit more and more questions. It was one more question. I wanted to ask you if somebody had asked about using Phil parent as a as a layout option. Okay, what was the question? Um, I'm trying to find it here. So they had suggested using layout. Height equals Phil Parent. Oh, you did use that. Well, okay, I see what they're asking. So back here in Maine Ah, they're asking, Why can't I do? I think that's what they're asking there. Saying, Why can't I use the layout hype of feel parent for this middle view to get it to lay out and take up more space? The problem is, if I did that, it would overlap the title and the button as well, because it would actually fill the parent the whole thing. There's no ah, it doesn't do any automatically out against other components on the screen. And when you're using a relative way out, Got it. Okay. Great. So I the question regarding ad test captivity. Somebody commented. Club order commented. It looks like a view test activity, except with a different layout. Is this correct? Um, sort of. Well, let's get to that one next, because that's what we're gonna be doing next. Okay. All right. Let's go. on, then we'll get to We're saving up questions for the end to so if you question didn't get answered, will come back to it.

Ratings and Reviews

a Creativelive Student
 

I thought everything on this site was free and now they want 99 dollars for a course

a Creativelive Student
 

very good

Student Work

RELATED ARTICLES

RELATED ARTICLES