Full Stack Developer Course - Using ASP.NET & Azure & C# & Android Jetpack Compose + Treblle

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
this video was sponsored by treble.com what's up youtube this is dennis panyuta for tutorials.eu i'm super excited to teach you to become a full stack developer because what we're going to do in this video is first of all build an api using asp.net and c-sharp and then upload it to azure so that we can consume it then using an android application that we're going to you build with kotlin and jetpack compose so you're going to learn the latest technologies and all of that also using a very powerful tool called treble which is also the sponsor of this video more about them in a bit and the cool thing is you are going to really build the entire stack so back end as well as front end and actually i think it's like this backhand and front end so we're going to do all of that but there are some requirements so you should know c-sharp a little bit of experience in asp.net is definitely going to be helpful as well as well if you want to do the front-end part you should know cutline as well because we're doing that and if you don't know any of these things don't worry we have a bunch of videos on this channel so a full c sharp course if you want to learn about that an asp course that we have also on the channel you can learn about that and then we also have a entire kotlin basics course as well as a kotlin jetpack compose course so a bunch of courses all on this channel for free check them out hit a like as well as subscribe if you haven't done so already because well this helps us out to create these kind of videos and this video was only possible through the sponsorship by treble because this is not a course that we're selling anywhere this is simply a three and a half hours long free course that teaches you so many things along the way and this is not the only part that we are working with an api there will be a second video where we are building an application in unity which will actually be a game in unity also consuming an api and using treble in order to understand better what's going on with our api with all of that out of the way let's get started building the api using visual studio c-sharp and asp.net all right so let's get started with the actual project therefore you need to make sure that your have your visual studio installer set up the right way so therefore go to your visual studio installer modify your visual studio version and go here to your individual components and check the same that i have here specifically in asp.net here make sure that you have the dotnet framework project item templates selected and in general you can just select everything the same way that i have here just to make sure that it's going to work the same way for you so these are the net desktop development even though you will not need them in this video and then i also added.net native and framework sdk472 and the advanced asp.net features so once you have installed all of them you can launch visual studio and then create a new project and there you will find the asp.net webapplication.net framework so you can also search for asp.net web application it has to be specifically this one with the c-sharp logo here otherwise things are going to look differently and then you might get confused but if you use the same one that i do you should be fine so let's create a project called treble demo for example and select web api here as well as individual accounts here in authentication okay everything else can stay default and then click on create all right now let's look at what we have between our hands we have a new web api project with the type of off that we selected which was the authentication for individuals and it already contains a basic setup for user management with some endpoints ready to use to test the apis we will use a software called postman it will mimic how your api is going to be used once it's deployed so you see once you have this project ready there are a bunch of folders and files already in our project so you see a bunch of them okay now the next thing that we're going to require is going to be postman so i would recommend to go to postman.com downloads and download the 64-bit version and once it has been downloaded and started it will show you something like this even though it might ask you to register or sign in but you can also click just move on without registering which will be fine and it's going to send you to this screen here okay before we do anything with that let's run our project and this should then open up chrome for you or whatever extension or browser that you're using and you should get this screen here where you can then click on your apis at the very top so this is the web application that we have built so far and it's our web api help page that we open up with this approach so you can see here for example that you have and get api account user info then you have the post api account log out so for example this would be posted or executed once you want to log out or once the user wants to log out and so forth so you have some gets and post requests here as well as some values so as i stated earlier this video wouldn't be possible without treble so let's look at what treble even is because we're going to use it later on but i wanted to introduce it a little earlier in the video so that you get a feel for it already so treble helps you to stay in tune with your apis and treble makes it super easy to understand what's going on with your apis and the apps that use them and this is so important so let's have a look at the promo video this will give you an idea of what treble has to offer and the cool thing is there is a forever free plan so you can test it and use it extensively even without having to pay anything not even a credit card is required so definitely check out the link in the description and register for treble using that link because this will help us to see who came by us so now let's have a look at their video jeff a back-end developer working on apis collaborates with mobile devs front-end devs and product managers to launch apps jeff's basically mvp and the most experienced on the team obviously he's in high demand jeff doesn't mind helping out but with every api there's documentation to be written error reports to investigate team members to coordinate and a constant bombardment of questions it's a bit much but everything changes when jeff is recommended treble boom jeff can now see all requests in real time response data user location device information all grouped while treble generates full documentation with open api spec jeff can search and filter any data recreate tests for previous requests and instantly access an api quality score with continued api analytics the best part is jeff can invite team members to trouble so they can witness the magic doesn't have to carry the team anymore still mvp alright so i guess you can imagine how much time this can save you and how much of a hassle it reduces in your workflow so i know that this is a beginner course for developers who want to learn full stack development but this is a great starting point and if you know already somebody who has well who's working with apis definitely pull them aside and tell them about treble because this is going to make their lives better okay so now let's get back to the video so let's look at how to register specifically so post api account register so once we click on this we will see that we have an email a password and a confirm password so this will be the body parameters that we have they are type string all of them and they are required and then you can also see the data type even though confirm password is not required as you see but it has the well i would recommend it and then we have the request formats so this would be the example of such a request in the form of json so this is json object where we have three properties email password and confirm password with a value in each of them all right so how can we make sense of this so first we need to know our url which we are going to use to access our apis in visual studio if we open our web api config file which you can find under app start there you will find your url which is the root template so api slash controller slash id which means if you want to access any method we will use api which is fixed followed by the slash as well as the controller name that we want to use as well as the ids so you should have a general idea what a controller is so we will use the register method in our account controller it's a post method so we will select that so you can go over to your account controllers here and there you will find a bunch of code and one of them will be to register okay so here this is the change password method set password method add external login and let me just search for register here okay next one next one so there we are so this is the async task which is a method that will run asynchronously which allows us to register which is going to be the name of our controller so it will be api account register okay so let's go over to postman and then click on create a request then we enter our url so it would be https forward slash forward slash localhost and then the id that i had in my case so let me see it was 44336 like this slash avi slash account slash register so here i'm going to select post because i want to run a post request and i can go over to the body and i'm going to select raw here and here i'm going to send my json code so this is for example the json code that i'm going to use so i currently break it with an email being bb3 gmail.com then the password being this what you can see here as well as the confirm password and that would pretty much be how we want our body to look like so our json and if you recall this pretty much looks the same as what we saw here so we have email password confirm password and we're using something very similar here and now if we click send then we get the response could not get response that's because we have ssl verification activated so we can deactivate it or disable it and then we will get a response here so that's the body response that we're getting gonna get message the request entities media tax plan is not supported and so forth basically we get unsupported media type here as our response and then once you click send you will see that this is the body that we get it said says unsupported media type and that is because by default text is selected and we need to select adjacent so this is json code that we want to send to this post url so let's run it again and this time it's a bad request the request is invalid it's already taken so you see i've run it before so this account has already been created so let me create another account called bb4 gmail and there we are so now we have a header of 200 okay which is the standard response for a successful http request which means the user has been registered so now we have two users in our database one being bb4 and one being bb3 okay let's now try to log into our application so we try to register but now we need to log into this same account so how you can do that is you enter token so this time it will be a get request so we want to register with our user and therefore we need to select the xw form url and code it here so we need to enter a key which will be the grand type and the value of password description can be empty then we have the username key with bb3 at gmail.com for example the one that we had set up and the password which was this let me copy it because i don't want to run into a problem here typing in the wrong password and there was an add sign at the beginning so this will be our user login data so the controller that we are using is going to be called token this time okay we're logging in using the token so we can either use a get or a post and this will pass the email and password so now let's click send and we get 200 okay within 144 milliseconds so you see we have an access token so this is going to be the token it's a very long string that taken type is bearer it inspires in quite some time this is the username and that's when it was issued so the token was issued this token will be expire in 14 days so it means that we are going to be logged in for 14 days so to speak so this token is so to speak our authorization for any request that requires the user to be logged in we will use this token to prove that we are both registered and logged in in some apps these tokens expire very quickly and when that happens you will need to refresh it otherwise you will be automatically logged out we don't have to worry about this because using our android front-end we'll save the token locally and we will keep using it so that the user won't have to log in every single time so now if we take a look at our values controller which is this one here the values controller you will see that it needs to be authorized so if we want to use the values we need to be authorized so in postman if we send a request to the values controller with a get method and a one for the id so here for example api slash values slash one and we want to get something we sent the request and here we don't need to use any params let's go like this authorization has been denied for this request so we get this 401 error which means we are not authorized so how can we use our token to prove that we indeed have authorization so from the previous request what we can do is we can copy the token string so let me go back to the one where i logged in so with token and let me send it again let's use this token that we have the access token let's copy that and well let's create another request i think it will be a little easier to jump between them so this time actually let me copy this here as well so here let's use ipi slash values and one click on authorization and select the bearer token because if you look at it it says the token type is bearer so let me copy this once again because i copied something else and let's paste the token in here and then we can send the message once again and this time we get the value so you see the response is 200 which means okay which means that it worked so the http request that we did worked out even though like the only thing that it displays us is value but this will be different once we have an actual application where there is something to show so where does this value come from well if you look at the get method here so get api slash values for example 5 it will give us value so we get the id or we can pass an id and then we'll just get value so let's say value by dennis is what it should display so let's rerun our application and let's run the postman once again and you see it gives us value by dennis so that pretty much is what it's going to return even though obviously we want to change what it's going to return here if we want to run an actual application that is going to be useful all right so that's the part up to the postman all right and now let's change our app so that we can also save a profile image for that we will add a different controller we will call it a user profile so let's stop our application let's start debugging it and then go to our controller folder and here we need to add a new item so well actually a new scaffolded item this one here and i'm going to select mvc controller with read write actions and here i'm going to go to controllers and use the mvc5 controller with read write actions so let's call this one user controller so how i want to be able to access this user profile is first of all i need to make sure that we need to authorize so we need to be authorized in order to do that we need to have a token and then i also need to define which route i want to use so what is going to be the api location where this is going to be or the url so this will be api user profile okay so this is the route that i want to use for my controller also this will not just be a controller but it will be an api controller therefore we need to add using system web http and then it should be fine and once we do that and we import system web http now we have an ambiguous situation here ambiguous references because it still uses the system web mvc and once we get rid of that those arrows disappear as well but we still have a bunch of errors in there but no worries we're going to take care of that in a bit we're going to get rid of those anyways actually let's get rid of them directly so let's get rid of all of it up to our user profile controller being empty so now let's extend our database so that it contains a string called user profile or user image in the already existing database so first let's define our model under the models folder and therefore i'm going to create a new class in here which i'm going to call user profile model so this class is going to inherit from application user and then i'm going to have a method here public string profile image which will just be a getter and setter now to configure our database let's do the following under app start here in this folder add a new file called user profile config so let's add a new item here user profile config dot cs so this user profile config should inherit from entity type configuration of user profile model so we are basically using the entity type configuration and we need to import that so let's add this okay then we need to also import our user profile model which is using our own user profile model this one here so inside of our user profile config what we're going to have to set up is our [Music] public user profile config which will be my base and it needs or it requires a key which is going to be the username because we need to know for whom we are uploading an image so here has key and we're going to use a lambda expression here what we're saying we get the username like this so this takes care of the username and then i'm going to add the table to user profile so that's what we're going to do in the constructor here we will let it know that it requires the key to be the username and then we configure the table name that this entity is mapped to so it should be mapped to the user profile entity so now we need to migrate or enable migrations to update our database because we were just changing our database therefore let's go to tools and then to nuget package manager here and let's enable migrations like this and press enter okay so that has been done for us detect the database initialize scaffold migration all right now we need to add migration user profile so we're going to call this migration user profile because we added the user profile to our database and finally we need to apply this migration by updating our database so update database all right so the migration has been taken care of it was the user profile migration that we just created now if we look at our solution explorer we will see that a couple of classes were created so here is a migration initial create and then the user profile so if you look at this that's where we added the profile image and now let's finally define our database context which also includes the profile image so we can access it through the code so now let's add a new class to our project on the top level so let me add this class and i'm going to call it user profile db context okay so this will be a db context which means that we now need to add identity db context as the type here a user profile model to it okay so now we need to import user profile model for this to work and it's under our models and we will also need to add identitydb context here so let me show potential fixes the thing is i wrote it with a capital b first but it's under our entity framework class so we will be using this class inside our controller in the next lecture to access and edit our data in the database but before we do that let's set up a constructor so ctor and then also an override method so protect it override void on model creating all right so this is an override method from the identitydb context that we can use so one question that you might have then is is our image just a string because we will store the path of the image well no the string is to store the image itself how you might be wondering well let me show you this little tool here so image to base 64. so you can convert an image to a base64 online using this tool so let's say i would want to convert this into a base64 now i can just select the file drag it in here then encode the image to base64. so this is for example something that you can store in a string and it is actually the image this is called a base64 string think of it as the image data encoded in a very long string of course we can reverse this step by decoding it so here this was to encode an image and now you can use the base64 this code here copy it and decode it back to an image and you will see that this is the image that we get so now if i even change one letter here you will see that suddenly doesn't work anymore so i get no image anymore it's still the file solution is still there and so forth but it just doesn't know what to do with it but that's basically what this base64 can do for us so you can actually store an image as a string and of course we will do the conversion part in kotlin when building the front and more on that later however okay so that's it for setting up our database so now it will be ready to use our user profile even though we need to program our controllers so let's program our controller in the next part all right now we are ready to program our controllers to return our profile image so let's go back to our user profile and specifically the controller so here what we need to do is we need to add the following methods to get and post the user profile in case the username is also the user email so i public string get method that will require the username and let's use it like username like this and then here i will use bar context as the new profile or user profile db context and actually with brackets like this so what is it that we want to do well we get the query which will be context.users.first or defaults and here we need to use the lambdaexpression which is going to give us the username like this so that's the query for it and we need to return this query now so this this will be the query to get the username and then this will be that we want to return the query if it's not null so if now return an empty string otherwise return the query dot profile image and now we need a post request as well so here this will be the post for api slash user profile so let's quickly go again here so this would be get api user profile username okay and this one will be post api user profile that's the next one that we're going to set up so let's go ahead and create a public i http action result so that's what it's going to return and i'm going to call it post so here we use the from body keyword which makes or attribute which makes it possible to parse models from the request body and i'm going to pass a user profile let me see view model called user profile and actually it's a view not view model but model so that's the model that we just created earlier so this user profile model which just has one property called profile image okay so let me import that so using treble model so that we can access it all right now let me get rid of this stuff here at the bottom now we can go ahead and again use our db so var db also it was context early now it's going to be the db of the new new user profile db context so now we can use this profile db context again and what i'm going to create here is going to be the result which will be db dot users dot single or default and here the lambda expression again where we get the username so dot username where the user profile [Music] profile dot username are the same okay and as always i semicolon so this will be the result and then we check if uh result is not equal null there should be a var not the vowel we're not in kotlin yet we're gonna do that later so if it's not null not if it's null so if it's not now i want to set the result profile image to be user profile profile image and then i'm going to save the changes so save changes okay so this is basically once we post a profile image we want to pass the user profile and then we want to store it in the database so that's what we do here we get the user profile context then we get the result where we check for the username that we are currently at and then we can set the user profile image to the result and we need to return result okay here so return ok so this will return 200 so find the user's profile image check if the result is null and then save the image we got from the user so let's try these methods in postman so let's run our application this will save all of our files and run postman as well once the application is running okay there we are our homepage works now let's go over to postman so here go over to api and this one will be user profile user profile like this here i'm going to need authorization with our token all right that's fine let's make sure the authorization still works the request resource does not support http method get and that should have to do with me using get here so let's use a post request instead so let's send it and there we have an error as it seems exception non-static method requires a target and if that happens we are missing a body so we need to pass something in the body as well so let's cancel this request and here in the body we need to set a form so here we need to enter the username which will have the value of bb3 at gmail.com so that was the username right and the second one was the profile image and here i'm just going to enter something like hello for example okay and now let's send it and therefore we need to continue our application so let's just keep on let's let's rerun the application okay and let's run it again and then go to or over to our postman and make sure we run the request again and we see that we get a status of 200 okay so that means that posting the profile image or storing it as hello worked all right now let's fetch the pro user profile using a get method so here we need to change the url to be user profile where we are also passing the username as a url encoded variable so we add the question mark with the username being bb3 and gmail.com yeah but we also need to make sure that we are passing the token and this time it's going to be a get request okay so now let's run it and see if we get hello and there we are so we get hello so that means our data was actually stored our image was stored and it is available there even though in this case the image is actually just a string that says hello so there's no actual image there but we're going to take care of that in a bit okay so that's our controllers and now let's get over to the treble integration part of this project all right now the next step will be to deploy our app along with the database to azure this way we will be able to use this api and consume it from our android app which we will start building in the second part but how are we going to see if our users are experiencing bugs or what if we want to monitor our api usage well that's where treble comes into play so let's go ahead and create an account and login so we're using the free plan here just go ahead and create an account if you want to follow along so the cool thing is treble makes it super easy to understand what's going on with your apis and the apps that use them so imagine treble as a border controller officer that is always noting the input and output of your apis so let's create a new project in trouble i'm going to call it trouble demo and add a project name here we will leave the url empty for now we will have it once we deploy our app to azure now let's finish and save the project and there we are so this will take us to the documentation so here you can go over to.net which is what we are using and you see there are other tabs as well for example php laurel node and dotnet core ruby and go are in development but will be available soon so now let's go ahead and integrate treble to our project so we need to install the treble package via nuget so let's go over to nuget so tools nuget package manager console and you see there's a lot of stuff running from earlier and here install package treble.net and this then takes a little while and once it's done you will find this screen here okay so it opens this treble api and treble well in my case it doesn't show properly but one of them is the api key and the other one is the project id in order to get those i go to trouble again to my settings i get the project id which is the second window here and the api key can be found under your settings and there you will find your api key so copy that and add that in there as well as the treble api key and then click on the sub button so what that will do is it will set up treble in our project so let's go back to here and see whether it's added so added package configuration manager and what we're interested in is this part here where it says successfully install treblenet 1.4 to treble demo so now let's add the attribute to our account and user profile controllers adding them to individual controllers will apply the treble attribute to all the methods defined in them so let's go over to the user profile controller and here at the top let me bring down the nougat package so here at the top we have authorize and on top of that i'm going to add treble and therefore i need to also add using treble.net and now the same thing goes for let me copy that our other controller which was the api controller called account controller so here in the account controller at the very top let's go ahead and add that in there as well so let me add treble here as well let's make sure we import it so show potential fixes and add using treble.net so now we have integrated treble and this is the easiest service to integrate ever i mean seriously we just had to add a nuget package enter our account details and that's it like add this treble keyword to our controllers where we need them so that's the part and in the next part we are going to look at how to set up azure all right and now comes the moment of truth because we're going to deploy our project to azure so what we should do is first of all let's stop the application then go to our application here so the treble demo in our solution explorer and then select publish so this will then open up this window and you can see there are a bunch of targets that you can select and i want to publish it to azure so here i'm going to use the azure app service windows version so click next here as well make sure you are locked in into an azure registered account and have an active subscription okay they give you a 200 to us dollar credit for free valid for a month once you start so really make sure that that's the case once you are signed in and registered before that you will find that you have a subscription name here in my case it's azure subscription one and here we need to add a new app service instance so just press on plus and here we create a new resource group so what is a resource group well a resource group is a container that holds related resources for an azure solution the resource group can include all the resources for the solution or only the resources that you want to manage as a group in our case it's going to contain our asp app and the database so here you can change the name if you want to so for example i'm going to call this one treble demo okay group name will be treble demo then the hosting plan we can change that as well select a plan and in my case and here's something like central us could work or eu west or something like this so west europe let's select that that's where my project will be located because i'm in the west of europe and then click ok and once that works click on create let's see creating app service with our trouble demo and so forth you could have also changed the name obviously but i'm fine with the name that i have there and once that worked you will find your app service instance here treble demo in my case with the deployment steps so at this point we can click on finish now you will find that you have this trouble demo publish page let me close this here as well you can then configure your sql server database here at the bottom okay let's click on that and i'm going to select here azure sql database so it's not on premise but it's going to be in the cloud which is what i want to do so i want to store the database in the cloud so that i can consume it from from that application such as our android app that we're going to build next okay so here we need to select our sql databases that we want to use and i'm going to call the treble demo db and here we need to require a database server so let's click on new and we need to have an administrator username as well as administrator password and so forth so i'm just going to say my username is administrator username is going to be dennis then i enter a password here and here as well and then click on ok and at this point we have a database server we have a database administrator and we can click on create so this will create a new database for us once the revalidating is done and this then also takes a minute or two or maybe three and then we have our sql database so let's select that and click next and here we need to add a database connection username so again the ones that we had set up before and i think it was dennis in my case and then we have the connection string value so you can display it here by showing the secret so displaying it will show you the connection string you can also copy it or i would recommend to copy it and we can select the string to connect to it from anywhere for example from sql server explorer in visual studio if we want to all right then click next and at this point click finish so this will set everything up and it's complete so we can close it at this point we can click on publish here at the top right hand corner and once it's published here we go we have our url where we will find our application so it takes a little while to load as it seems but once that is done we can see our app and it took a little while but then it's available probably it needs to set up a little bit so there we are this is our application and it doesn't look great because it's just the default but it works so we have our api and you can even see the api that we had set up ourselves so the profile image for example our user profile and stuff like that so it's going to be a little slow probably i don't know why but for now let's just move on with our treble project so here we go to view project and we will find our api under settings so here you can now add your api url which is going to be the url of our project so this one here but with api at the end so api slash so this is the link to my project in your case it will be depending on the name what you have set up in my case it was treble demo 2011 whatsoever the time that we have now at azure websites.net and then we have api here that you need to add and then you can update your project so now to make sure our database is configured correctly let's run a full test and postman this time there's no need to run our app as a localhost as we will be using our site url so this one here so let's go over to postman and not use the localhost as we have done before but let's register with an account here so account slash register and this time let's go over to the body what we need don't need this token let me get rid of this token and no off let's go over to body and the body will be just a raw body here and let me register once again as we have done before so here treble and now let me register with the bb5 at gmail.com and let's see if this is gonna work let's run it and this has to be a post obviously not a get so let's run it again plain text is not okay again we need to run it as json but that should work now this time so sending the request this time it actually sends the request and let's see how quickly this is going to be it seems like our website is pretty slow that we have there but yeah we get an okay status so it worked so we have registered this user if we run it again it will not work because the user already exists right so now let's get a token therefore let's run a get request this time we want to get the token so here we directly enter token after the url of our project then we need to make sure that the body is of type xw form url encoded so let me copy the password from here so actually we yes we need the password so here let me add the key password and this will be the email will be five username and we need to add the grant type as well the grant type will be password let me check if the order doesn't matter so the password itself will be this here so this will be the username grant type will be password this is the xw form url encoded let's run this and there we have our token so let's copy the token all the way down here let's make sure that we have this token because we're going to test it with another post request so let me actually open up a new post request here i'm going to add authorization using the bearer token adding the token and making sure that i have the right url and body so let me copy this here let's add it here this will be not the a the token but api slash user profile like this and now the body itself should be again of a form type and let's use the username which will be rbb5 at gmail.com and then another key will be profile image and let's call this one test one and then let's run it let's see if this request is going to work out we get the status okay that's good and now let's get the profile image so therefore let's set that to get and user profile question mark user name will be this one here so bb 5 and so forth authorization is still the same token and let's run it we don't need the body here so let's go to none let's send it and we see we get test one so we see that storing the image that work out and getting the image also worked out so we did a lot of api work okay so now let's look at the treble dashboard because if we look at our project and let me see did i update the project i think i did let me go over to the dashboard yeah we are there we are so we see we have our user profile get where we had a load time of 500 milliseconds with a status code of 200 then we had the post request where we posted the username at the profile image we had the account register request where we had the email and password as well as confirm password so you see here the password itself is hidden but the confirmed password isn't hidden and that has to do with us not using the right let's say keyword here so in treble there are certain keywords assigned specifically which will be hidden okay and we are using one that is not hidden so let's make a quick change to hide this data because if we look at this specific request here we can see we have an email a password confirm password and the confirm password is actually readable and that's something that you don't want to have right you want to really avoid that so now in order to change it or to hide it let's go back to our project let's go over to our solution explorer and here specifically to the web config here we need to add a line under app settings so we have this treble app settings here let's add another one add key called confirm password and let's set the value to be secret field highly sensitive field okay so this comes from trouble itself they that's that's how they say you need to set up a key in order to hide it so secret field and highly sensitive field and this is useful for censoring all kinds of data so whatever we want to hide even if they are fields that we introduced ourselves because this confirmed password field is a field that we didn't introduce ourselves it was there by default right it's just how the pre-defined code that we had in our account controller i think it just set it up this way and yeah here we have the email and login provider has registered either way so we have those and yeah this is one way of doing it i'm going to however use a different approach so let me comment this line out here we're going to do it using a different approach we're going to use data binding so we're going to change the confirm password field in the data binding model to confirm underscore password because this is censored by treble by default all right so we will change the field name and leave solution 1 to any other data that we want to censor in the future so in case you want to ever censor something that's the way to do it so inside of models you will find account binding models and here find the registered binding model so let me search for registered binding model there we are so here we see we have the name which is required and then further down we have confirm password so this is the string that we want to change so right click on the confirm password and here i just need to change the name from confirm password to password underscore confirmation and i'm going to change the display name to it as well so it should be password confirmation okay now that we have that let's save it let's build our application once again so here go all the way to the top built the app you see the build was successful now publish the app and click on publish again and then once it's published our application should be ready to go and we can run postman again so here i'm creating a new post which will be with api account register using the body raw where i'm registering user let's say 8 for example just to be sure and this should be password con for confirmation let me see that i write it with m or with a n account binding confirmation with n okay i typed it correctly confirmation so let's send this let's send the request and let's see if we can register our user this way the status was okay so let's go over to treble and we can see that the account was registered so our email was bb8 gmail.com the password was hidden and the password confirmation was hidden as well all right so that's pretty much it for our api so we have built the back end now and now it's time to actually use the front end in order to consume our api so see you in the next part all right so you manage the first part of this course you have taken care of the api of the back end so to speak and now it's time to consume it and in order to consume it we are going to build a register screen and then we're going to allow the user to register as well as change his profile image or her profile image and this is something that you will need in almost any application where you have user management so this is going to be very useful for basically all of your future career as a developer and we're going to use kotlin as well as android studio and jetpack compose which is the latest technology when it comes to building user interfaces in kotlin so if you don't know much about it yet this will definitely help you and if you feel like this is too fast then check out my kotlin master class youtube video that we have available we also have a full course available you can check that one out if you want to where you also have quizzes exercises um q a support as well as a bunch more additional examples where you're going to learn a lot more about app development in general but also there is a jetpack compose video that we uploaded it's another three and a half hour course or so where you learn jetpack compose from scratch so definitely check that out as well okay so at this point let's get started building our application that is going to consume the api which means it's going to load data from our own website that we've just set up on azure which is basically a website right and it loads the well the data from there and allows us to communicate with that so let's get started all right so let's get started building our android application that is going to consume our api therefore i'm going to use jetpack compose which is the latest approach in terms of how to build an android application specifically the ui is done using jetpack compose so let's call this one treble demo and everything else can stay the same by default all right so once our project is loaded we can get started so the first thing that i'm going to do is i'm going to go to my main activity and well set up the ui properly here so in the main activity we have this greeting method we won't need it so let's get rid of it it's just a predefined method and then we can replace it with a new composable that we're going to create so let's create a composable and by the way if you don't know what composables are you should check out my video on japan compose so i have an entire course on that on youtube here authentication so i'm going to call this composable which is basically just a ui item or element in our screen so this authentication for now will be an empty composable so there's no content in it but we can instead display it in our surface as well as in our preview here as well so let's just set it up this way so now obviously we want to set up our authentication step by step and therefore what i'm going to do is i'm going to create a new ui element so a new screen so to speak but the main activity is going to contain the screen so let's go ahead and create a new folder here inside of ui so a new package and it's not going to be in theme but ui dot screens and inside of screens that's where i want to create a new coding class composable so here a file which i'm going to call sign up so this will be the sign up screen and in the sign up screen that's where we now can take care of all of the composables that are then in the end going to build our ui that we want to have so let's go ahead and make this a composable and call it sign up our sign up screen is going to be inside of a column okay so this column will have a bunch of content so let me import column here and inside of this column that's where well first of all i want to design it so let's make sure that it's going to take the entire screen that it has available so modifier dot fill max size which will take care of it's taking the entire width and height and at the same time i'm going to add a horizontal padding and me import padding for this to work and this will be horizontal padding of 16 density pixels and here we need to import dp as well and then i'm going to also set up the arrangement so the vertical arrangement so that everything inside of it is going to be arranging towards the center so what's going to be inside of this column well in the column we add our outline text field composable which is an input field for the email address this email must have a value and on value change property for keeping track of the entered characters we'll leave them empty for now we also add a modifier and set it to fill max width so that it takes the entire width and a label set to show the text with email address so let's get started by using this composable called outlined text field this one here so you see it's at an ad composable which has a bunch of properties okay so let's set this up you see value and on value change need to be set so for this to be working we need to set up a email state okay we need to work with states and while we're creating states let's set up a couple of states directly because we will need on a signup screen need to know the toggle state of whether the password is being displayed or not then we need the password state which is going to store this password value then the email state which is going to store the email value so whatever the user entered in the text fields so let's set those up well toggle state which is just going to be a remember so this is a state keyword that we're using remember which is basically going to be an interactive variable that contains the value and this well just allows us to then use it and override it okay so here this will be a mutable state because we want to be able to override it and by default it's going to be set to false so mutable state off let me see what's up here we need to import mutual state off all right now the same thing will be for our password state so val password state which will also be a remember actually let me copy this part here and the password state will be a string so i'm going to set the default to empty and finally we will need the email state so while email state which will also be remember of an empty string by default so we're going to override it obviously over time so the first field is going to be so this one here outlined text field will be our email okay so this one will contain the email state value and once we change the value so on value change what we want to do is we get a string that is going to be the current changed value and we assign that to the email state so email state value is going to be it okay so this it string that we're getting each time the value of our text field outline text field is changing so now i'm going to also set up some modifiers here or some more properties for our outline text so the modifier for example will be modifier dot fill max with so i wanted to take the entire width and then we can set a label to it so a label will display beautiful little text we need to import text for this to work and it's not text field but text so let's import text compose ui and this will just say email address all right so that's the label and then because this is an email entry field we want to also assign a keyboard let me actually put it not here but here the comma so we want the keyboard options to be set as well so we are not going to use the default ones but we're going to use the ones for emails so the keyboard options and here we can pass a value like this we need to import keyboard options with alt enter by the way if you're wondering how i do it and here keyboard type will be keyboard type dot and you see email so that's how you can assign different types of keyboard types on your emulator so ascii number number pass or a password password and so forth so in this case this outline text will be our email text field okay now we can do the same thing for our password text field so let me just copy this and put this one to be the password state value and we are going to overwrite the value of the password state it's going to still have a fill max actually let me add a little bit of padding here as well so padding of vertical padding of 16 dp so that we have a little bit of distance there the label should be email password keyboard options will obviously not be the email once but the passport password once but the thing is what we can do is we can actually override this directly with something else so let me show you a little trick here so what i want to have is and let me actually show it to you i want to have this screen here where we have the email address this is the label by the way then we have the password this is again the label and then here this is the toggle button that i want to use and that's where we need to toggle state so if i enter something you see we don't see the password if i toggle the button then you see we see the password so that's what we want to achieve in order to achieve that what i'm going to do is i'm going to use first of all our toggle state that we have set up at the beginning and second of all i will need the trailing icon property so this is an icon that is trailing which means it's at the end of the text field so here let me just set up the trailing icon property i can add that by going ahead like this trailing icon and then this has to be a lambda expression so here i'm going to add an icon button we're just going to well we need to import icon button for this to do anything import and then once we click on it so on click there's this other property that we can now run and the icon button needs an icon entry so this is the content of the icon button which is just going to be an icon and this will be what should happen once we click on the icon button and the only thing that we are going to do is we are going to overwrite the value of the toggle state so we're going to say it's going to be toggle state value but this time negated with this exclamation mark here so what will be the content of our i um well icon button the thing is we will need to add something to our build.gradle here to the app one we need to add a dependency here which will allow me to use certain types of icons so here please use this dependency if you want to do the same thing so android x compose material material icons extended with the compose version okay so once you added this line sync this file this build gradle and then you can go ahead to your sign up and well to any of your kotlin files in the project and you can now use the icons that are inside of this package so here i want to use the icon and we need to import icon the android compose one by the way alt enter once again in order to get the shortcut here and this will be the image vector that we need to set so the thing is i'm going to use a super quick if statement here i'm going to say toggle state dot value so if it's true then use icons dot default dot visibility and else use icons dot default dot visibility off so what this does is basically this thing here and that's a cool thing we're saying once we click on the button change the state of toggle state value and it automatically changes the image vector which means it changes the icon that we see here so either it is this i or it's this i cross through okay so that's how we can take care of that and the thing is the icon is not happy yet so let's take care of that by actually adding the content description because it just always needs this content description i don't know why they could have made this an optional field in my opinion but they decided not to okay so this is going to be our second entry which is this email password and now the thing is that you see that this is hidden and we can unhide it but in order to unhide it we need to use something called a transformation let me see so it's inside of our outlined text field which goes all the way up to here so you can see that's where the bracket starts and that's where it ends so here i'm just going to set up something called a visual transformation so there's this visual transformation property that i'm going to now override so i'm going to say if the toggle state value is true which but we can just check the value itself because it's a it's a boolean right then set the visual transformation to be none and otherwise so else set the visual transformation to be password visual transformation so this password visual transformation is a method and what it does is it uses this character here to mask something so they called it mask because that's what it does it masks the character with this so if i were to replace this let's say i replace this with an x here it's read only but we can override it so if i replace this with an x for example and this is a charge so we need to use exclamation marks and let me actually set up sign up in here sign up like this and import sign up what this will do and let me see come on let me import it now in this case the autocomplete didn't work so let me show you real quick then it will replace these dots dot starts with axes okay that's what i just overrode it with so this is our solution and whatever i enter you see the first ones were in fact axis but lower caps it's going to overwrite everything with access so that's what our visual transformation does for us and by default it's using this little dot icon which is just this little dot here okay all right so that's our password visual transformation i'm going to leave it as a default value there because i'm happy with it and now let's take care of a button where i'm going to have an on click well let me actually import button first and the compose button and then in the on click event for now i'm just going to say do nothing and the modifier will be modifier.phil with because you see that this button takes the entire width obviously it doesn't take the entire entire width because the column has a little bit of padding so if you recall the column where it is inside of it has a padding of 16 dp horizontally so towards the left and right okay so that's our button which fills the max with right now and the button itself needs a content so it needs to have for example a text content which i'm going to set to sign up and i'm going to add a modifier here being modifier dot padding with a vertical padding of 8 density pixels okay i don't want the text to be too close to anything else so that's why i'm setting up the padding here and then if we look at it we have this divider so let me get rid of this and then we have a divider with a text and another text so let's just add those two this will be a divider composable which is just this line okay and you can modify it by using the modifier modifier dot padding which will be 16 towards the top so 16 density pixels and then underneath i have a row with two texts in it so i'm going to set the modifier to be fill max with so this should take the entire width available except for a little bit of padding of top padding to being 16 dp and then i'm going to add horizontal arrangement as well which will be that it should be arranged towards the center so here arrangement center all right so this is the row and it needs a content so we need to add those closing and opening brackets here and as you see the row just has two items it has a text and the bold text so i'm going to create this text here saying the text should be already have an account question mark and then the other text should say uh login so text should be login and the font weight should be bold so font weight bold there is this value that we can directly assign so now if we run this we should get our user interface let me see yeah there we are it's yeah it's actually our solution so let's test this one two three one two three and hide the password and signing up will obviously not do anything because our on click is not implemented yet okay so that is our sign up user interface which is the first part of our application and the next part will be to set up the network libraries with the service class so let's have a quick pause now because this was quite a lot of code that we wrote and i went through it super quickly because i'm assuming that you already have an idea of what's up with jetpack compose if you had no idea but you know kotlin i think it might have been fine what i did there with the speed that i did it i tried to still explain stuff but yeah let's just keep on going in the next part of the video all right so now let's set everything up so that we can actually communicate with such a servers like treble or like our api in general therefore we will need to go back to our build.gradle file and we need to add a couple of dependencies here so i'm going to use retrofit moshi and the ok http 3 logging interceptor okay so let's implement those here you can see it's these ones you can pause the video now and add them so it's come square up retrofit to you then well retrofit 290 then comes square of moshi moshi kotlin 193 which is just a converter to retur to turn json response from the api to kotlin objects and vice versa so this will make it a lot easier to work with json objects the cool thing is a json object is actually just if you look at it really just a little bit of text right but if you can use it like a kotlin object where you have the properties and subclasses inside of it with properties and stuff like that it's a lot easier and that's what moshi helps us to do so retrofit helps to communicate with the api and then we are using the converter which helps us to work with moshi via retrofit and then we have ok http 3 which logs the network status when communicating with the api so here i'm using the logging interceptor 472 okay now that that is out of the way we will need an interface which is going to be the service that allows us to authenticate so let's go ahead and set up an extra folder for that which i'm going to just put into our project and i'm going to call this one data okay so this one is going to take care of all of the data stuff now let's create a kotlin file class which will be an interface and i'm going to call this one authentication service and without the e like this should be fine and by the way um after we have created that let's make sure we sync our build.gradle once we have added those dependencies otherwise it's not going to do anything for us so here our authentication service so this will be a so this is a data package which will allow us to do authentication we're going to get back to it in a bit let's first create our model you could of course also create an extra model folder for it and so forth but i'm just going to keep it simple i'm going to um make this a data class so here column file class data class user so we want to know how a particular user is going to look like so you could call it user or register user i'm going to call this one register user and this one is going to have an email which will be of type string then the password which is going to be of type string and then we had the password underscore confirmation you might recall from our api setup so that's exactly how the objects were called in our postman and on in our api and this was under account register so if we want to register a user we need an email a password and password confirmation so you see it's register and we're going to call this one registered user so we can register our user very easily so that's here register user and exactly the same names really important this really is going to save us a lot of headaches later on all right so now that that is out of the way let's go over and go back to our authentication service so in our authentication service we can now go ahead and create our post and this is at post so i'm using the post annotation where i need to tell where i want to go so the url i want to use the end point and this one will be account register so if you look at it this is account register so api account register so this is exactly where we want to go that's why i'm using that exact letter so those exact letters here as well so now we are going to use a suspend function which is a co routine function which allows us to do something in the background and registering a user is always something that could take some time and it's really important that we do it in the background so here i'm using the body annotation from retrofit and the content will be the register user object so here register user so we are passing the register user as the body to our account register endpoint all right so now that that is out of the way now let's go ahead and create a singleton class for building a retrofit moshi and logging interceptor so i'm just going to create a new object here and this is just common practice to do it this way so here i'm going to call this one api like that and now the content of it will be first of all our url so private val base url this will be our constant and here we need to pass the base url of our project so here it's this part here so that's what i need to pass in here like that and we also need to make sure that we have the slash at the end so this will be our base url and then we need to also have the api path so const vol api path which will just be api so once again if you want to use our api well we here at the help side of things but here you see api account register so the whole url would be this year so whatever our name of the website is then api account register and there we can now send the body with the register information as we have seen in postman earlier so now we instead of doing it in postman we're going to do it with our application right so that's really the whole idea of setting all of this stuff up all right now let's set up moshi so moshi by using the moshi dot builder and here we need to import moshi so let's make sure we have that in port moshi and here we are adding our kotlin json adapter factory this one here and we need two brackets like this and then we can build it so that's how you set up a moshi object all right so we have this mushy object now now i'm going to use our http logging interceptor which just helps us to lock the status when we are making requests so let's create a private val logging which will be in http logging interceptor double g logging interceptor like this so it will just be a logging object and then we need to set up the http client so private val http client which will be an ok http client dot builder dot apply and this builder needs to have brackets and now we can apply our changes so i'm going to use the logging that we set up earlier and set the level of it to be http logging interceptor.level body so it should lock the body and we want to add an network interceptor to it which is going to be our logging object so here we set the level and now we are adding it and at this point we can build it so let's build the http client now we need to set up our retrofit builder or retrofit object so private val retrofit and this will be not retention but retrofit.builder like this and here we are adding the converter factory and here we need to pass the converter factory that we want to use and i want to use the moshi converter factory creating it with our moshi object that we created earlier so let me put that into a separate line so it's a little more readable so that's always how you set up retrofit it's just this whole stuff it's just something that is always the same the only thing that changes is this part here where you set up your base url and your api path the rest is always the same so we are adding the converter factory we are setting the base url to be our base url we are setting the client to be our http client that we have set up and then we are building this thing so here build our retrofit object and now finally we are going to set up an off service object which will be our authentication service but we're going to only load it once we require it by lazy so using the lazy keyword and the buy keyword and then we use our retrofit object to actually create something with our authentication service so to create the authentication service so to speak so class.java this is basically how it still works so auth service so now we have our off service and we will be able to use it later on okay so that's basically the setup here authentication service this is the service that we have used so now we can go ahead and use this register user function in order to pass the register user object which is this user here this one and register a user so next let's look at how we can actually register a user alright so this part is going to be a little longer where we're going to actually register a user so so far we only have taken care of the ui and setting up retrofit but now let's actually combine it and use it so therefore i will need to add another dependency so here let me add the navigation dependency because i want to be able to move between screens and therefore we need to use the android navigation navigation compose to the 4.0 and in this case i'm using the beta 2 which is still the best stable version well it's a beta but still stable so there we are now let's go ahead and actually create a main screen nav host controller so let's create a new screen and this one will be the command file class which i'm going to call main screen so this main screen is where we want to go and this will have a nav host controller as a parameter it's in its block we will call the nav host and pass the variable we created as the argument with its start destination set as sign up so basically we want to handle our different screens elements or um yeah sign up for example we're going to display it on this main screen and so forth so let's just get started and see how this is going to pan out so first of all this will be a composable as well so that we can display composables inside of it so we're going to call this one main screen and i'm going to pass the nav host controller here as a parameter and then we can go ahead and set up the nav host here so nav host which needs an f controller and i'm going to pass the nav host controller to it and it needs a well thing is it says graph but actually we can just set up the start destination to be sign up and yeah sign up like this and let's set up the actual composable in there so the composable so the thing is like here you add all of your different screens that you want to have or there are different routes that you want to have so this route will be sign up and i need to set it up and i'm going to say that this composable sign up will actually be the start destination so here i can use my sign up variable or this one here my sign up not variable but actually the composable so instead of now in our main activity instead of going to sign up so now inside of the main activity we can instead use our main screen but the main screen is it's gonna need a nav controller or nav host controller so let's create one nav controller which will be a remember nav controller and here i'm just going to use the default and not overwrite it and pass that to our main screen so now i'm going to need a repository and now i'm going to need a repository class with an authentication service as its parameter so let's set that up in our data here and this will be a kotlin file class this time it's actually a class repository like this so here i'm going to pass a private vowel of service which will be our authentication service so authentication service like this so inside of it what i'm going to need is another suspend function which is going to run in the background and it's going to take care of registering the user so here we need to pass in the user object and then say what we want to do with it so what we want to do with it is we just want to call the auth service and register the user with registered user being the registered user that we are passing to this suspend function okay so we're going a little way around using our authentication service which contains if we look at it the suspend function called register user so that's what the repository is going to take care of now next what i'm going to set up is going to be a resource class to manage requests or response processes so if we are loading or if there is an error or when there is a success answer i want to take care of that as well so let's create another class in here instead of data which i'm going to call resource and this one will be a sealed not interface but a sealed class and it could be of many different types so i'm going to pass the data which will be a t nullable and by default i'm going to set that to null and then the message which will be of type string nullable which is going to be null by default as well so inside of it inside the sealed class that's where i'm going to set up the loading class passing t nullable by default being now and it's going to return a resource t with data so this will basically just be displaying or letting us know whether the pre-register process is loading or whether there's a success so let's set up another class called success which is going to also require data of type t which i'm gonna not set to null this time and this will resource t data all right and here class error t and this one will not have data but an actual message well it will also have data but it will also have a message all right and then let's use our resource once again and it will not return well i said it would return but it's actually going to inherit from resource okay so here data and message so so basically we're using this resource to say okay do we get a message do we get data and then if we get a message that will usually be with error so we get data and the message we can get data but it can be null so basically it's going to return a resource letting us know whether why well whether the process was a success an error or whatever it's loading all right now let's go ahead and set up view models because we are going to use the mvvm architecture here so this one will be view model actually it's under ui dot view model okay so inside of our view models that's where we want to create our main view model so this will be a current file class main view model so this will be a class where we pass our repository as its parameter which its data depends on and then we extend it to be of type viewmodel so here private val repository is going to be needed to be passed and this will be of type view model so and then we're in here importing the life cycle here like this okay so what should happen in here well we are going to create a setter variable called register request state for tracking the register user request which will return a mutable state flow with the resource type of nothing so we use nothing when we are not expecting any data this will only be mutable in the main view model class so we're going to make it private so let's go ahead and set that up private vol register request state which will be a mutable state low of type resource nothing and this will be a nullable and we pass null to it as the value so let's import resource as well as mutable state flow to our project so we have made it private and then we create a register state as the getter which can be accessible outside of this class so this is a private right and now we need to get together so i'm going to call this one register well actually just the same thing here but without the underscore here because this is not going to be a private one and this is going to return a state flow of type resource nothing so resource nothing and let's import state flow for this to work as well all right and nothing well we need to make sure that this is going to be passing as a nullable and then we have the getter here which is just going to return our register request state so now let's take care of an error handler so val error handler which will be and co routine exception handler so call routine exception handler and i'm just going to use the lambda expression here so i get a variable that i'm not going to use and i get the error so what should i do with it well i'm going to check if the error is an exception and if that's the case then set the registered request state value to be resource dot error with the error message so here error dot message so now you see how everything comes together because the register request state if you look at it is of type resource nothing so here we can now use resource arrow or we can use resource success or loading okay so that's what we're setting up here and that's if the well if it is an error so it will help us track errors that might occur during the request process and the error handler returns an exception so using the register request variable which is a type of the resource class we created will retrieve an well any error asset message from that exception so now we need to set up a function that will register the user so register user where we pass the register user object and here i'm going to set the register request state value to be resource dot loading because if we are registering a user we need to make sure that we are actually loading and then we can start our code routine so view model scope dot launch and here we can launch with our dispatcher a second view model scope let's make sure we imported it okay and let's import the launch method as well and now we need to say on which fret so to speak we want to run it so on which dispatcher and i'm going to use the io plus an error handler here so this will be the context in which we run this code routine and now we can use our repository dot register user where we pass the register user to be the registered user that we passed to our registry user main view model so this is quite a workaround you could say let's not really work around in terms of we're fixing a bug but you see like this is an entire flow where the data goes through because we use this repository method to use the auth service to call the register user so the repository method will use the auth service our our main view model however will use the repository in order to allow us to also check the state to know whether we are loading whether we get an error and so forth so we're not just building an application like let's say like a beginner's application but but this is really an advanced app where you can be very confident using this approach in your production application as well so here this will be resource dot success where i'm setting the data to know so this register request state has a mutable state flow of type resource nothing and resource can be any of those right it can be loading success or error so what we're saying is while we are registering the user we are loading and once we are done where success so once we have registered the user and that will be at this point all right so now we're going to use our main view model to set up a main view model factory so inside of here i'm going to create another class which i'm going to call main view model fact story like this and this main view model factory is going to require private val repository which will be of type repository and this will inherit from view model provider so this will be a new model provider factory so to speak so whenever you do that you need to run an override function in here so let's implement the members let's overwrite this create function which will take a viewmodel and create so this is the create method which will return a viewmodel and it's going to need a model class so here we need to implement it so when a subclass of view model so the class that we created earlier our main view model for example has a parameter which in our case is the repository then we need to create this factory class for it before it can be successfully initialized so we create and extend the viewmodel provider factory and then we overwrite the create method and specify the main viewmodel as the class we need to initialize and return it with the repository so let me show you how that works so we're going to check if the model class is assignable from our main view model and this should be my viewmodel this one the one that we created class.java so if that works and not java but java if that works then return our view model so return our main view model with the repository as well repository as t and otherwise so in the else block just throw an arrow an exception better set class not found exception okay so this was a lot of setup that we needed to do but the thing is now we can use our viewmodel and our signup function because our signup function as you might see is for now just ui so now we need to make sure that it also does all of the technical side of things where it just loads data and sends data and so forth so what i'm going to do is i'm going to do it via using a view model so we're going to pass a view model to our signup method and then what we can do is we can set up a preview here add preview if you want to be able to see it in our designer so show background well actually let me import preview first and now we can show the background to be true and this will be a composable here so add composable which will be a function called sign up free u okay it has to be called exactly like the composable here sign up with the keyword preview assigned to it and i'm just going to display the sign up with the view model that we're going to pass here so with a viewmodel okay and well we won't see it now we have to run the application before we can see it but now that we have set up that let's go back to our sign up here at the very top and take care of tracking the request progress as we create a variable and collect the state set to register user so let's go ahead and have a register state here which will be using our view model registers request state collect as state dot value so this will give us the this register request state which will let us know whether it is an error whether it was successful or whether it is loading all right and now in our main well this is what this register state will take care of so this is a state we need to collect it as a state and then we can get the value and we're assigning that to the register state variable here and then i need a context which will be the local context current so let's import local context here because i'm going to require it in one of my next steps so the thing is once we click on the button here this one the sign up button this button that says sign up in the text we want to do something we want to actually register the user so register user we need to create an object of it where we pass the email and the password as well as password confirm so where do we get all of those from well we get those from our outlined text fields okay so here register user email which will be the email state value then we need to pass the password which will be our password state value and then we need to pass the password confirmation and now we could also create another ui element for that but i'm going to be lazy and i'm going to just say password state value should be passed and then we can use our view model and not this one but the view model to register the user with the register user object being our register user object so we're assigning the register user here and doing the actual registering so we have basically put everything outside so all of the magic that is going to happen outside setting it up and the repository our factory viewmodel authentication so that we have a very easy way to register a user inside of our user interface so we've really separated everything here so now what we should do is we should display something to the user depending on whether this was successful or not so underneath this row text here i'm going to set up a when clause here where i'm going to check the register state i'm going to check is the res what is it resource dot loading if that's the case then display something to the user and let me import resource here so that we can use it and i'm just going to display this little toast here so registration in progress toast length dot show so that's what i want to display to the user okay and then if it was successful so if the registration process worked so if resource success i want to display another toast that is going to say user successfully registered and then in case that we get an error so is resource dot error then i want to display that an error has occurred now you could go ahead and read the error and display the error and so forth but i'm just going to keep it simple i'm just going to say an error has occurred in that case now if we look at it our view model here our main view well our sign up better said needs a view model now okay so if we look at our main screen we see that our sign up doesn't get a viewmodel yet so let's create a viewmodel that we can pass here but where we need to get the view model to our main screen first so here let's create a view model main view model entry for the parameters and then we can use that view model that we get to pass it here to our sign up method okay but then when we do that we also need to make sure that we create a viewmodel parameter in our authentication composable which is inside i think our main activity so let me jump over to the main activity so inside of authentication we need to pass a view model here as well and then we can pass the view model to our main screen here so view model is going to be our view model okay and now we need to make sure that we also create a repository inside of our main activity so private repository which we can do lazily so only once we need it we want to create it we don't need to create it otherwise and this will just be a repository object which will be in of service here so api of service which is this off service which then creates the retrofit authentication service for us which then actually allows us to do anything with the repository well actually there is registration with the repository and then we need to create a view model inside of here as well so private val view model which will be of type main view model by view models so here this one and what this does is it returns a lazy delegate to access the components activity view model if the factory producer is specified then the viewmodel provider factory return byte will be used to create a view model the first time so i'm going to use my main view model factory here and pass the repository repository this one that we just created and now that we do that you see that our authentication if you look at it needs a view model right so let's pass the view model to it which we just created this one up here so view model which will be our view model and then i think that should be almost it okay now there are let me see here oh yeah our authentication here at the bottom inside of our main activity also needs to have a new model here so here i'm just going to create a arbitrary view model like just a random view model not our own that we're passing but just anyone for the designer so to speak so now the thing is when we're doing all of that stuff one thing that is very quickly forgotten and then it just doesn't work and we get errors and stuff like that is that we don't have the internet permission because at this point we need to have the internet permission otherwise none of this will work so here users permission android name android permission internet and then the last thing that we need to make sure is that we look at our authentication service okay so here authentication service so you see when we are running a post request we are setting or sending it to account register but then if you look at our api you see that it's api slash account register so now we could of course just say api slash account register but what if this api changes well then we would have to go here and change it but the thing is we actually created an extra property for it which is inside of our api or an extra variable we called it api path so we don't want to have to manage it on or at multiple different spots that's why we created this api class or object better set to take care of that so let's instead just say here use api dot api path and register the user and now before we test it i found a little bug that i had and that is inside of my composable i accidentally added a closing bracket inside of the string which shouldn't happen so this route should just be sign up and well i just had this little bug so if you follow it along exactly as i did then you will have the same problem and now at this point we should be able to run the application and i actually had another problem and that is a problem with my emulator and it just i can't fix it it's just happens every single time i have to set the time manually because automatically it just doesn't set the time correctly so it's december 15th now and i had to set it back to work properly so let me create an account here i'm going to call this one dennis one add tutorials once again here at tutorials.eu and then the password will be one two three and then a special character at then a a like this okay so this will be a password that is the minimum length it has to be six in length or six characters plus there has to be special character an uppercase lowercase and a number or digit in it so let's sign up and see what's gonna happen registration is in progress and then you see user successfully registered so now our user is registered we can now also check out our logcat and we had arrows before that i had to fix which i did but now http let me see so here we can see that we have registered under our website api account register so that's cool and basically we now have another account inside of our cloud database all right now if we check treble we will find we have an account registered so here i have registered an account and is to add tutorials you with a password and password confirmation so that worked and also something that treble really helped me out with is when testing this application with the user profile which we're going to build in a bit i have created a bunch of requests because what happens if you use compose there is a bunch of recomposition going on and what i had first was the code would constantly make requests to the server each time that the ui would recompose and that was this would happen a lot like every couple of seconds or every second actually or so so quite often actually and this would create loads of requests now imagine you have a solution which sends out thousands of requests by the user by one user just using the application because of recomposition or whatever and you wouldn't notice this until you would be broke because your your server would be so expensive because of all of the requests that you're sending to it and so forth and that's really what treble helped me so trouble just let me know hey dude you are sending requests like crazy look at this like we have like 300 requests within like 15 minutes not not even within like 10 minutes or so so um that's not good and that's what we will fix or solve in the application or we're going to see a way around it but basically you see like that's what we're going to build next so we have this post request and so forth but not next next first we need to set up the profile ui then we need to fetch the user token we need to save the user token then we need to prepare the image for upload and then we can upload and fetch the profile image from the api so let's do that one by one now and the first thing that i'm going to start with is the profile ui therefore let's go back to our project and let's make sure that we create a new screen which will take care of the profile image or the profile itself so this will be another composable i'm going to call this one profile like this so this will be a composable so we need to add the composable keyword as well our annotation as well as the name of the composable so i'm going to call this one profile now inside of this profile we well we basically are going to put everything into a box so here we need to import box for this to work and this box will have a modifier which i'm going to set to film exercise like this okay so this box will take the entire screen so to speak and the content of it will just be a card and this card will have a modifier well let me import card first and the modifier will be that it's going to fill the max with and i'm going to align it towards the center so alignment dot center like this with a capital c now let me get rid of this like that so then i'm going to add padding to it as well of horizontal padding and let me import padding like this and this will be horizontal with 32 dp and let's import dp here as well okay so let me put that into a separate row so this is the modifier for the card and now actually let me add a shape as well so i'm going to add a shape property here shape should be a rounded corner shape with four percent rounding and an elevation of six density pixels so that we have a little bit of shadow so now the content of the card should be first of all a column and this column is going to be imported first and then it will have a horizontal alignment which is going to be alignment center horizontally and then i'm going to set the modifier to be modifier.rep content size so it should only be as large as the content that it requires to display and then i'm gonna have 16 density pixels of padding towards the top as well so that's how i want to style the column now let's look at the actual content of the column so so in the end what i want this to look like is something like this right so we have this card then we have the image we have the username and then we have the button that says edit profile okay so that's what we are going to build right now so we have the card we have the column in which we want to put everything and inside of it i want to use an image so let me import image here and then we're going to set the painter resource which for now i'm just going to set to r dot drawable and our i'll need to import r so let me import r here import dot r okay now we should be able to use it so drawable dot ic launcher background so i'm just going to use what we have because we don't have any other images now then the content description i'm going to set that to null the modifier i'm going to add a modifier to the image which will be that the size of it should be 200 density pixels then it should have a border which is going to have the shape of a circle so circle shape as well as a width of one like this one dp and then a color of white so color should be color dot white okay and then i'm going to clip this so clip the image with a circle shape now let me see what the problem is this will be still part of the modifier so let's import clip here like this and finally i'm going to set the background to be of color gray so color dot light gray okay so that's the image and underneath the image that's where i want to put a text so let's import text here compose ui variant this one and the text property should just say username and then we have a modifier which will be modifier.heading with top of 16 dp no actually let's use 10 dp a little bit of distance at least for the text and then underneath the text i want to have a button so we add the button for editing the profile and leaving on clicking empty for now so let's import button and this button should have actually let me make sure not importing the wrong button here so is it going to be yes the android compose one it needs an on click event so it seems like it didn't import the correct one okay that's the one and i think now we might have a problem because it doesn't understand which button it should use let me see image foundation layout color button import button android compose ui this one just doesn't want to add it let me get rid of it entirely let me see so if you have this problem as well it's like super weird okay let's try it again button just this button with an on click okay that should work but i'm going to leave empty for now and i'm going to add a modifier here modifier which will be modifier dot with padding of horizontal eight density pixels and vertical 32 density pixels and it's gonna fill the max width and this is going to be put inside of the button definition still so for some reason just jumped into the content of the button which it shouldn't have so now this is where the content of the button starts and here i'm just going to display a text which is going to say edit profile so that will be how our profile should look like and if you want to see how it's going to look like you need to add a pro preview here so add preview and this one will be an ad composable with the fun profile preview and this will be displaying our profile composable okay so the preview should show the background show show background should be set to true and now if we run the application we should be able to see how our profile is going to look like and there we are so this is what we're going to get the image username edit profile okay so that's our profile now let's go over to our sign up and let's make sure that we have the nav controller assigned here as well so that we can navigate from it like this and then we need to add the nav controller here at the preview as well so we're going to pass the remember nav controller here which is going to be an empty one and then once we click on the button of registering and we were successful let's actually navigate so here we can just say navcontroller.navigate to our profile page so profile root and here i'm going to set the launch single top value to true so i want to get rid of the h of sign up so i use pop-up 2 sign up and then i gonna set the inclusive value to true as well so if you want to know more about them so for example inclusive whether the pop-up destination should be popped from the backstack so we want to make sure that the pop-up to sign up is going to get rid of so basically it says get rid of the signup page from the backstack so when the user presses the back button he can't get back to the sign up page okay so that should happen once we were successful in registering and now we need to make sure that we have such a nav controller inside of our main screen as well so here you see we have this composable well first of all we need to make sure that we pass in the nav controller here and controller so that we can even well run from one screen to another and then we need to set up the second composable route which will be our profile and here we need to go to the profile composable okay so let's see the thing is we cannot log in yet we can only register so let's register with a different account hey one one at gmail.com password will be one two three a a and add and let's sign up registration and process and there we are so now we are on the profile screen okay cool so now the next thing will be you fetch the user token so if we check out treble now we will see that we have a new register which is hey one gmail.com with a password and so forth so we just registered with this new user okay now let's set up the user or fetch the user token because we need that token in order to be able to log in or sign in again and do anything else like uploading for example the profile image all right so let's take care of the token and therefore let's go over to our authentication service so here as you might recall we have our post with the account register and now we need to create a suspend method and annotate it with a post with token as its appended path so let's just go ahead and set it up and i'm gonna go over what's going on here in a bit so this will be a post and we're passing a token and then this will be a suspend function which i'm going to call login user all right and this will return a login user response so before we take care of that of the login user response so we are going to pass a couple things to this login user so we're going to use add field and this will be the grand type field and we need to import fields here so let's do that the grant type will be a string and so to this token we need to send the grant type which will be the password and then we need to have another field here which will be the username so here this will be username which will be of type string and then we need to pass the password as well so add field password which will be the password of type string like this so now before we look at what's up here let's create this login user response and it is under user so i'm going to put it in the user file so here i'm going to create another data class which will be a login user response so this will have the access token which will be of type string it will be having the token type which is going to be of type string then it's going to store when it expires and it's going to store the username okay so this is the login user response data class that we're going to use so you see the user class is not only taking care or file it's not only taking care of registry user but also of the login user response so that's what this login user will return so we're passing these details but it's going to return the login user response object so what is going on there well the thing is we are using the suspend function login user with that post annotation with the token as its appended path and annotated with their form url encoded this is because the request has an xww form url encoded added as its content type meaning the required values will be added to the url using the add field so if we look at our let me open a postman again so if we look at our postman you might recall once we wanted to send a request here well what do we have one here yeah here the body is of type xw form url encode and here we need to pass the grant type the username and the password okay so we have done that before and now we're using it inside of our android application so grant type username and password so why do we call this login user rather than something like fetch token well this is because it will still serve as the request method to sign a user in from the login ui which we will look at later so now let's go over to our repository and set up a login user method as well so here we have the register user and now we need to have the resuspend function which will be login user so here we need to pass in the username and the password and it will return a login user response which will be set to auth service dot login user with the username being our username and the password being our password so like this okay so that's our login user suspend function which we can now use in order to log in the user now obviously we're not fully done yet so let's look at our main view model so we have this function for registering a user now let's create a function to log in a user so login user we need to have a username which will be of type string and the password which will be a type string as well so now what's up in here well first of all we will need to have a user token okay so we need to store the user token somewhere the token is basically a very long string which just gives well authenticates a user just says okay this is the user that is already logged in and registered and so forth so let's create a private val user token which will be a mutable state flow with a resource of type well this time not nothing but specifically login user response nullable which we're going to set to null and then we're going to set the user token or create another variable called user token which will be a state flow so similar to what we've done with the register request state and here this will be a resource login user response nullable so let's import resources and now we need to add a getter so add getter and this getter will just set the user token or will just be the user token so the underscore user token to be specific so now we can use this user token inside of our login user so because we were going to set the value of it depending on resource dot loading so first it's going to load and then we're going to run our view model scope in order to run our code routine which is going to be launched on the dispatcher's io thread with error handler assigned to it so there we are now here we're going to get a result which is going to to take the repository dot login user so it's going to call this method and here we need to pass in the username username as well as and here it seems like i mistyped it so let me sure make sure that i'm not doing that and then the password so here password will be password okay so that's what we're doing here we're logging the user in and then we are setting the result to the user taken token value so usertoken.value should be resource dot success with the result so if you recall in postman we had this user token which we were getting so let me find it it was here when we were authorizing we had to pass in this user token right so whenever we wanted to set the user profile or go to the user profile you see we had to pass this very long user token okay and that's what we're passing here as well if we were successful with the connection now obviously we also need to adjust our error handler because it's not only taking care of the register request state but also of the user take token so in case we have an error we are going to set the value to here as well so resource dot error with the error message and force unwrapping it because it's available all right now when we're signing up and let me actually see what the problem is so here in the main view model what did i miss the user token i need to pass something to it do i no i don't that's because i wrote resources and not resource okay so now that we have that let's actually use our [Music] login user method inside of signup because once we have signed up and we were successful we want to log in the user and then we want to navigate him so here view model dot and actually with a capital m model dot login user with the username being email state dot value and the password being password state dot value so if you recall these states they were taking care of whatever we entered inside of the well i cannot show it here anymore inside of the register screen okay now we need to pass the viewmodel to our profile as well so here our profile needs to have the view model so that we can access its methods so view model main view model and now we will need to prepare the states and the context and so forth so let me go to the bottom here make sure that we are passing a view model to our preview as well and let me import that and now let's set up the states that we're going to need all the values that we need so we need to know this token state so val token state and where do we get that from we get it from our view model dot user token dot collect a state value all right and then we need to have the context in which you can do that and this will be the local context so the thing is as we're using japan compose here this is going to be a little different than we would have done it with just uh this as the activities context or so we're using the local context so whatever context we're in right now and then we're going to remember the data so let's remember the data by it being a mutable state of our login user response so this will be the actual data that we get from logging in so whatever the response will be if you look at it we get the access token token type when it expires and the username that we are logging in with now back in the profile we need to check the token state and depending on that we can display something in our box here so here in the box when the token state is going to be is resource dot loading so we need to import resource here so while it's loading we're going to just display a circular progress indicator which is just this a loading indicator with the modifier being modifier dot align so we want it to be in the center so i'm just going to align it towards the center alignment center and then if it is success so resource dot success then i want to check the token data so if token state data is not null then the data value should be the token state data so the token the data value is this data here which is the login user response data so once we were successfully logged in that's where we're going to get the token state data from and we're going to store it in data and then if it was an error that we get so if resource dot error then let's just display a toast saying an error has occurred so i'm just gonna use this error here that says an error has occurred which is just going to display on the screen for a bit so now what we'll need to check and we'll need to check it before we display the card so if data that value that access token is not empty so we do have an access token if all of that stuff worked so this stuff here then we will know that we have something that we can display and actually that's where we can display the entire page so the card and everything so let me put that in there so i cut out the entire card right so here and then put it in this if statement because only then do i want to display it and now finally we can go over to our main screen and pass our view model to our profile page because it also now needs a view model as we have set it up here because well that's where we get the user token from and so forth all right and then one thing that i realized that i missed and that is here to use the add form url and code it because as i showed you in postman earlier it has to be um well it has to be this this part here form url encoded this here this part and that's what i missed okay and once we do that we can run our application and see if we can actually register and be directly sent over actually let's let's use the token to do something else and that is to display the username directly so we're not going to load the username from the sign up screen itself we're going to load it from the token so here in the profile we get the access token right and if it's not empty we're displaying this card right this this card here so i want the username to not just be this username text but i actually want it to be the username that is the user that is logged in so we get that from data.value dot username which will be the data here that value that has the access token as well okay and at this point let's run it again and see if we can actually register and stuff so i'm going to call this one test two i think so i'm really like using all kinds of values here already doesn't really matter add gmail and then this one will be something okay and sign in registration in progress and there we see we are now logged in as test to gmail.com okay so the login process itself works now as well so the next thing that we need to do is to save the token to the preference data store all right so now that we fetched the token we need to save it locally using the preference data store the preference data store is an api that's for saving small values to the device in form of a key value pair that is that the key will be an identifier to the value it holds and the reason we are saving this is because we don't need to fetch user tokens every single time that app is opening up as the token doesn't expire immediately we can see the issue date and the text expiring from our okay http response so here you see it expires on wednesday the 29th okay so we have like two weeks for our token before it expires and now let's set up the preference data stored dependency in our project so therefore let's go over here and let's make sure we have the data store made available here has another dependency so it's going to be this one here android datastore preferences one zero zero and let's sync it so that we have it in our project so now let's go over to our data folder here and let's create a new file which will be a class that i'm going to call preference store actually let's make it a file so preference store and this file will first of all contain a context context dot data store and we need to import context for this let's do that and this will be of type data store preferences and we need to import data store once again here by preferences data store with the name being store or user all right so here prefab princess this one here okay so now what i want to do is i want to create a class here which will be our preference store and i'm going to pass the value which needs to be of type context which is why we created this context so we set up the context and now we will be able to use the context so first of all i need to store the token key which i'm going to create a private variable for token underscore key which will be a string preferences key which will be token key and then i'm going to create another one private vowel email key which will be a string preferences key called email key so we're going to use those as keys for our key value pair that we want to store permanently on our data store which means permanently on the device so that we don't need to log in every single time so now let's create a token pref for this which will be of type flow string and we need to import flow for this which will be cut line x co routine flow and then this will use our context data store data dot catch and here in case we get an exception and let me see what the problem here is so context should be this context here so and we have context we have data store we have the preferences string preference key preference data store and we have flow so what's the problem here yeah the empty space okay and now we need to import catch and now what we want to do here well we're gonna get an exception right and this one here exception inside of catch another empty string or empty missing here exception like this so if the exception is an io exception so an input output exception then emit the empty preference cis and otherwise throw the exception all right and this needs to be mapped map with preferences that we get we need to import map here for as well so the preferences are going to be references at the position of the token key that we had set up and if we don't have one we're going to use an empty string okay so that's how we can set this up so basically by default the data store uses code routine and returns flow values so we can create a variable to return the token value first we use the catch method to check if there is an io exception while reading the value and emit an empty preference else we throw any other exception that might occur and if there's no exception we map the value and return it so that's what we do right here okay so this will be the token pref and now we need to do the same thing with the email pref so val email pref which will return a flow string and this will also use context dot data store dot data dot catch with an exception exception will be returned to us so actually let's do the same thing here let's run it like this and the only difference will be that we're not going to use the token key but the email key so this will be the email press and now we need to set up our suspend functions which are going to help us to save those tokens so suspend fun save token which is going to require token of type string and then it's going to use the context data store now we're going to edit the token because it might change right so add it let's import edit and then here we get a user token and this user token should then be stored at the token key so whatever token that is passed should then be stored and edited so we're overwriting if it exists and if it doesn't exist then we're creating it this is to save the token and now we need to do the same thing to save an email so save email and this will get an email context data store and this time we get the user email and we set the email key to be the email that we're passing all right so that's how we save the email and that's our preference store class that we can now use and prefer rent store yeah i think that's better okay now inside of our repository we can use our preference store so here let's actually pass it to our repository so val preference store which will be of type preference storm and then down here let's create a token pref which will be the flow string and it will use well let's import flow and it will use our preference store dot token pref okay so we're using flow here which is a core routine well an asynchronous data stream that sequentially emits values and completes normally or with an exception okay so we've used it before but just so you're aware what this is and then let's create a suspend function here to save the token so we're gonna pass in the token of type string to this method and then we're gonna use our preference store to save the token and here token with the token being the token that we are passing to the safe token method and now let's set up the same thing for our email so we need to have an email flow and val email flow will be of type flow which will be of type string reference store dot email pref and we will create another suspend function calling it safe email and we need to pass the email type string again and now we can use our preference store dot a save email method to set the email to be whatever we pass as email okay now in our main view model let's go ahead and create a safe token method so fun save token which requires a token of type string and then we can use our co routine scope to launch with a little bit of delay so let's say after like 500 milliseconds let's use our repository to save the token with the token that is passed to us okay so that will be our safe token method now we need to do the same thing for our save email method so let me copy this and now replace this with email here this will be an email and this will be save email okay and now we create a setter to hold each preference value okay so private val pref token which will be a mutable state flow where we create a pref token method which will be returning a state flow type string and what it's going to do is it's going to run a code routine where it's gonna launch our repository dot token pref collect so it's going to collect the token and it's going to set the pref token variable so this private prof token value to be it all right and it's going to then return this so here return our pref token now we need to do the same thing with the email so let's set that up pref email which will be the pref email or the state flow of string it's going to collect the email flow and this will be the pref email here value to be set to be the pref email and returning that as well all right and now we can go over to our profile and i'd say at the very top where we have our token state we can now remove the data variable and collect the preference values from the main view model okay so here what i'm going to do is i'm going to set the token to be viewmodel.preftoken.collectstate.value and the same goes for the email so viewmodel.prefemail dot collect the state dot value and this will then be used now so now you see we get errors here if resource was successful we checked the token state data and instead of writing the data here now we're going to use our view model to save the token so here view model dot safe token to be the token state dot data dot access token and we are going to use the view model as well to save our email with the token state dot data dot username so the token state has these properties right and you see here the token state will be of type login user response where we get the access token token type when it expires and the user name and we're just using that okay so if the token state data isn't null we're going to use all of that and we can also log it while we're at it so we can log and import it log d what we're going to say token et and that's going to be token state dot data dot access token okay so now if the data here basically access token is not empty here we need to replace this with the token not being empty so if the token is not empty then we can do all of that stuff and at the same time we need to change the data value username to actually just be the email okay so that's this variable that we created this view model prefmail collect estate okay so at this point let's see if we can still run our application and actually i missed something and that is in my main activity and it will also be in the sign up so first of all and our main activity we will need to set the service as well as the preference store so preference store will be a new preference store that we need to set up so private val preference store which will be created lazily so only once it's required and this will just be a preference store using this as the context and then we can use this preference store here that's one problem and then the other one is that it in the sign up screen let me see i actually need to make sure that once i i have the email state all the way up here in my column we need to also set up the token pref so here let's create token prefix review model dot pref token dot collect the state value so this will be the token preferences and we can check if they aren't empty so it's not empty then we can directly navigate the user to the profile so the user doesn't have to constantly like log in and stuff we're going to send him to the or register so so far we always had to register directly even though we were registered already like a hundred times but that's how we're going to get around it okay we're going to just say go directly to the profile page okay and make sure to pop up to our login okay and this will be inclusive all right so now let's run it again and let's see if we need to register again or directly get sent over to the profile screen okay so let's register one last time test 4 or whatever it was at gmail.com and then sign up and there we are so let me now actually let me check i have the app somewhere right so i call it trouble demo so let me actually close it and open up the treble demo and you see we go to the profile of the register screen but then we are send it sent over directly to this screen where we can then see our profile and now we can take care of editing the profile next okay so that's it for this video and now for this part of the video now we're going to take care of preparing the image for an upload all right so now that we have our screen i want to edit the profile meaning the profile image and send the data obviously to my api and so forth or use my api to send the data to my server better setup so in order to achieve that we first of all need to have the permission to even use the gallery for example to change the image to load an image from our own phone for example right in order to achieve that let's go over and first of all make sure that we are using the accompanist permissions dependency so let me show you so it's this one here saying implementation com google accompanist or companies permissions zero two one one beta okay so that's the version that we're going to use for our i'd say pretty new application in the sense of we're using jetpack compose which doesn't even have everything yet and we're using a bunch of betas as you see but they still work so that's good now let's make sure that we have the right to read the external storage inside of our android manifest so here we need to add another permission user's permission and that is going to be the read external storage we're not going to write anything we're just going to read all right and now let's go ahead and set up the permissions inside of our profile screen because we are going to require them here so after we have the token and before the box i'm just going to create a permission state so val permission state here state is going to be remember permission state and this remember permission state needs a permission that we need to assign and we can get it from our manifest class inside of permission permission and there is this read external and let me make sure that i have permission imported let me see what's up here so okay closing bracket manifest dot permission doesn't use the right manifest i use the java manifest holy smokes so if that happens this makes me almost angry at what they are doing here like seriously if i'm using the permission in an android application how likely is it going to be that i'm going to need the java youtube jar manifest well actually i'm going to use the android manifest that one and then suddenly the permission exists so the permission class and now we can access the read external storage permission so let me put that into a different line or separate line and this will be my remember permission state with the manifest permission read external storage so what's the problem now oh yeah well it's an experimental permissions api that we're using so we need to annotate that so here at experimental permissions api so we're using the latest and greatest technology which is still experimental so that's what this permission state is going to allow us to do now to get permissions so now we need to get the uri so the location of our image so let's call this image uri which will be a mutable state of a uri that i'm going to set to null and this will be a uri nullable like here and let's import uri in order to allow this and here get value in mutable state off needs to be imported as well for us to be able to use the remember keyword because i think we haven't used it here yet okay now we create a launcher to open the gallery expecting a result which will be the image uri so once we click on this button we want to launch the gallery and we're going to do that by the well using the launcher remember launcher for activity result is what we're going to use here and we need to pass in the contract and on results so what should happen as the contract and i'm going to use the activity let me see activity result contracts plural get content all right and then this get content actually needs some additional so let me put that in a different line so here let's get content will be closing here and then we don't need on result but what we want to do is we're going to use the uri here and actually uri this one here so we're gonna get the uri as a result and we're going to store that inside of our image uri okay so that's what how we're going to set up the launcher and now let's just lock the image uri while we're at it so let's take it image uri and log it as this message now as you will see we have this bug now here at the bottom because we need to add this experimental permissions api to the preview as well otherwise this just doesn't work all right now let's edit the profile once we click on it so here inside of the if token is not empty which is inside of the box so in the card we have this to do which we now are going to take care of so once we click on this button here edit profile we want to do something and what i'm going to do is i'm going to check for the permissions do i have the permission to even do something so i'm going to check the permission state has permission for our launcher dot launch and like this and the input will be image star and let me add the star here so if the permission should show rational then show a message to let the user know that the permission needs to be accepted if not launch the request for permission so if we have permission then launch if we don't have permission then show the should show permission rationale okay and here we're going to say post dot make text please give us read storage rights otherwise this won't work or so okay so here i'm going to use this toast where i'm going to pass the context and a little text like this this will be my toast and then in the else case so if none of this is the case that means that i will need to ask for permission so here i can use my permission state that we had set up earlier permission state dot launch permission request okay so that's when we click on the button right now let's go over to our main screen and at the main activity but main screen we have it open somewhere doesn't seem like it well let me go to main screen kt and here you see that the profile doesn't work because we need to add experimental permissions api for our profile to be accepted inside of the main screen so pretty much on every single page we need to add this now we need to add it inside of let me see our main activity as well there because the authentication now also wants to have that so here we are using it so let's add the experimentational permission which means we need to also add it here for our own create which means we need to add it here to our preview as well and then we can go ahead and create a utils file or object which i'm going to conveniently just put into the root folder of our project and i'm going to create an object here which i'm going to call utils so our utilities where we just create functions that we want to use throughout the app or so so what i'm going to do is i'm going to say bitmap.bitmap to base 64. so you have seen what this base64 means before right we need to import bitmap for that let's do that and the thing is in order to now convert a string into a bitmap or the other way around we will need to set up a code here so this will return a string this will make a bitmap and to your base64 string okay so here we need to create a byte output stream so this will be a byte output stream and the array one then we need to compress the image so compress with bitmap dot compress format being jpg and we can set up the quality i'm going to set that to 50 and we need to assign the beta array or byte array output stream then we can go ahead and set the image bytes which will be a byte array which will be a byte array we're going to set that with byte array output stream dot 2 byte array and then finally we're going to return the base 64 dot encode to string image bytes with the base 64 default settings okay so this is basically how we can convert a bitmap to a 64 or base64 string so do you recall how we used this tool well we didn't decode it a string into an image and the other way around so that's basically what we're going to do okay we're converting a bitmap to a string because we don't want to send a bitmap to our server but the string itself because that's how we set up our database and server so now inside of our profile screen we need to set up the bitmap so here where we set up the log just before the box let's go ahead and set up the bitmap object which will be of type bitmap nullable because i have nothing that i can assign to it at this point so i'm going to make it null and let's import bitmap and then i can use my image uri and check if it's empty and then set the bitmap depending on the build version that we're using so if we're using an older android version which means an sdk int less than sdk underscore int less than 28 and let me import build here if we're using that then use one approach and if we're not using that then use the other approach okay so here we're just making sure that this is going to work on newer and older android device so it says so here the mediastore.images.media.get bitmap and we need to import mediastore let's do that and image and let me show make sure that i have the right one either way let me just finish this line context dot content resolver with it so whatever the it from the image uri is going to be so the link that we get so now let me just see if i import the right one come on where's the image it's trying to do this one here let me check i just realized that it's images okay so it's media store images media get bitmap and then the else block and by the way you see that it's crosstalk which means that it's outdated but it's fine because we're only using it for outdated android versions and here otherwise we're going to use the image decoder to create a source based on the context content resolver so content resolver with it and then we're going to use an image decoder to decode our sources bitmap so whatever resources okay so basically we're just making sure that we now have the image set up where we convert the image uri into an actual image and then inside of our column here where we are actually using the image instead of using the image from the painter resource what we can do well actually here let's first of all just set up the bitmap dot let and we get a bitmap and we take it to create the image url and then we convert it to base64 and we can log that as well so we're gonna say base64 image url is going to be it and what we want to do here is we want to for now just be able to even select an image at all okay that's the goal for now we're not storing it yet we're going to store that next but for now we want to see if we can even like assign an image here so bitmap btm image url the thing is the image itself has not been changed yet but let's see if we even get to the permissions part yeah we get there so let's allow it let's edit the profile let's select an image and we have no images here so let's just create an image with our emulator let's take an image with the camera so let me rotate and click on it all right and now let's select that image okay so now let's go over to our logcat and search not for okay http but well this is the image that we get this is the base64 so let me see this is base64 let me just search for base64 to only get base64 so let me see if i can copy this all the way up to here it should be and now we can go back to the converter and enter it here and there we are so it seems like it's breaking it down into multiple parts in my lock because it's just too long of a log entry because if you look at it that's where the second part starts so let me cut that out as well all the way up to here and paste it at the very bottom and then decode and you see we get a bigger part of the image and so forth so we're always just loading one part of the image using this or at least that's what we're getting in the log cat alright so that's it for this part of the video and the next one we're going to on the next part we're going to look at how to upload and fetch the profile image from the api all right so now it's time to actually upload an image or user profile to be precise so or use a profile image so let's go over to our authentication service right there and here we need to have another post request okay so i'm going to copy this post request that we created earlier and going to overwrite it now so instead of using a token i need to use the user profile or center user profile but we need to change the api here so this will be api dot api path which needs to be added to the post request then we're going to use the form url encoded once again and this one will be called add user image so the fields that we need to pass is going to be username so this one will be the and the fields that i'm going to pass is going to be username as well as profile image which will be an image url of type string or i'm going to call it image url of type string and i'm not going to assign a value by default and then we have author risation which is going to be the key that we need to pass as well and it's not going to return a login user response so this is going to be our method here and that we can now use in order to pass an image or better said post an image so now that we have this method and user image we also need to set it up in our repository so let's create a suspend function add user image where we need to pass the username the image url and the key like this and now inside of it we're going to use our off service and pass the username to be the username the image url to be the image url and the key to be the key all right and now the same thing goes for our main view model because as you might recall that's how we save the email save the token and so forth so here we also need to add the user image here so add user image with the username the image url and the key all right so here i'm going to again use the code routine to launch it on our dispatchers io thread with the error handler and inside of it we're going to use our repository add the user image with our username the image url and the key all right so now this by itself will not be enough i need to add a delay of 500 milliseconds so that this doesn't happen again and again and again because otherwise this could happen quite often we could try to add the user image very often so what we're saying is do it and then delay for 500 milliseconds and now in our main view model we also need to adjust our error handler here so the error handler is now also going to have to take care of the user image so let's go ahead and create a user image private val user image which will be the mutable state flow similar to what we did here where we make it a resource of string nullable with a null as a value and then we have a getter here for our user image state flow of type resource string nullable and obviously we need to add together otherwise this won't work so this will be the user image like this all right now we can set that user image inside of our error handler so here the value should be resource dot error with the error message force unwrapping it okay now in the profile kt file at the point where we get our bitmap that is inside of here where we get our bitmap and we get the base64 so this is where we need to set the image so we use our view model to call our add user image and here we can set the username to be the email the image url to be the image url that we just created and the key will be the bearer with the token like this so let me put that in the next line so here you see we have this image url that we get from the bitmap well yeah exactly so bitmap to base64. now at this point we also need to have a method to get the user profile because when we load the page we also want to be able to get the profile so here and get where we need well get with the capital e t okay the one from retrofit so here we first of all need to enter the path the api path like this and then user profile like that and then this will be a suspend function called get user profile and what it's going to require is going to be the query for the username which will be our username of type string and the header which will be authorization so we need to also author rise okay and this will be a key or i'm going to call it key and then this will return a string so this get method will return a string for us this will give us the user profile so the details about the user and we're searching for the specific username and we're passing the authorization so that we can get the details so we can load the data so to speak now that we have it in our authorization service so in our retrofit service we need to set it up in side of our repository as well and then in the main view model okay always the same procedure here suspend fung get user profile with the username and the key and this will return a string which will be of service dot get user profile with the username being the username and the key being the key okay now inside of our main view model let's set that up as well so here we are adding the user image and now get the user profile so fun get user profile so here we need the username and the key and what is it that we want to do well we want to run a call routine once again with our dispatcher's io threat dispatchers i o including the error handler so that we get an error handler while we get the result of it if there is an error so we're going to store the well actually let's have first a delay of 500 milliseconds here so that we cannot run this method again and again and let's get the result being repository get user profile was the method and we needed to pass the username and the key to this method that we've just set up and then we can use that to store it as the username value so resource says resource.success with the result and this shouldn't be username sorry that should be user image obviously okay so whatever the result is we are going to get that and store it inside of the value so whenever we get the user profile like this with an r then we will be able to store it inside of our user image and now when we add a user image let's also set the user image so get the user profile with our username and the key so the thing is that we when we initially built this solution and we didn't put those delays in our compose or jetpack compose would just reload the ui it would load it and load it again and again and again and what that would do is it would do a lot of work or put a lot of stress onto our um yeah api so you can see we have 263 requests just testing this because it was constantly just creating another one and another one and another one so here like you see two hours ago there were plenty and here you see like 21 hours ago like we had so many reruns reruns reruns it was going crazy okay so we want to avoid that and therefore we added these delays so now we also need to go to login user and here once we get the result we need to add a delay here as well of let's say 500 then we load the user token but ins i'm going to add something here which will be usertoken.value dot data dot access token nullable so if we get something there if all of that is not empty that's why all of the question marks there then we're gonna save the token and we're gonna do the same thing with our username so this was the access token and now we can do the same thing with our user name let and save email here with it all right so now inside of our profile we can get our user profile so where would be a good spot well outside of any of these methods let me see so this is i think this is still in the box let me actually put it on top so here we have all of those variables with the bitmap and before we are in the box even on top of the image uri if we want to that's fine so here we can use our viewmodel then we can get the user profile here passing the username which will be email and the key which will be bearer token and i have to write that correctly otherwise it's not going to work so this will be the bearer token so now we move the token state from the bin well from being a global variable into this box to stop continuous recomposition okay now we need to move the token state from the well it being a global variable into this box to stop continuous recomposition because that is what created a bunch of our problems so we can check here if token is empty so if token is empty we're going to get the token state which will be view model dot user token dot collect state dot value and then we check talk the token state if it is resource loading dot loading then circu [Music] i want to display the circular progress indicator in the center of the screen so modifier dot align towards the alignment center all right and then else if the token state is successful so here token state is resource success then i want to check the token state data if it's not null and save the token there and save the view the email so view model dot save token with the token state access or data dot access token and the same goes for the view model dot save email with the token state dot data dot access or the same username not access token okay which is basically what we did with this one token state here so instead of using this one here let's get rid of it we're using our if now if check all right and now inside of our box so if token is not empty or inside of the cart there we have our column and inside of this column let me see that's where it starts i'm going to check when val image result yeah we have that image result is going to be view model dot user image dot collect as state value so here we're checking for the image result from our viewmodel user image so our main viewmodel here this user image we're checking for that and then we can get the resource success so if it is successful then we will check if the image result is not null or blank so here data dot is null or blank then we're going to get the image result imageresultdata.base64 to byte code dot light okay so let me see base64 to buy let which is going to be a method that we're going to build ourselves so we need to convert this base64 string into bytecode so let's set that up real quick here so fun string this will be in our utils file dot base 64 to byte code which will return a bitmap and here what we need to do is we will get the decoded string which will be a base64 decode with this.substring of our index off where we separate by a comma and go one further one step further and this comma has to be inside of a string like this so let me put this in a separate line and then we need to say which setting we want to use so i'm going to use base64 default here and then we can return a bitmap factory decode byte array with our decoded string with an offset of zero and with the string length or size like this okay now we can use that inside of our profile this should be good now we need to import it however so let's do that all right so if we can convert it into code and the thing isn't empty then we can create an image which will be let me see we have it somewhere already i think we don't so this image is going to use the as image bitmap as the image from our it so from our bitmap that we get we make it into an image or into a bitmap then we need to set the content description for each image and i'm just going to set that to null and i'm going to modify this image to be with a size of 200 density pixels and it should be with the border of having a shape being a circle shape let me put that a little higher up here circle shape then a width of one density pixels so the border should be one density pixel wide and a white color all right so this will be the border and then i want to clip the image to make sure that it uses a circle shape so that it's not going to be square and i'm going to set the background of it to be light gray so the color should be color dot light gray and this will [Music] let me see so it becomes quite complex here because we have a lot of code here right so i really need to make sure that we have everything so first we have our token is empty check then we check if the token is not empty we create a cart with a column which starts right here so here let me push this back like this oh it just deletes it come on okay so that's where the column starts and then we check for the image result and after that we have our text here so we don't need this image anymore which we had before we well actually i could have just overwritten this code but we have this bitmap let okay and let me actually put this somewhere else so we have our button our modifier all of the good stuff and let me put the bitmap in here okay all right and at this point let's run our application but before i do let me check if the time is correct okay because my emulator really tends to break and not work because of the time not working correctly and whenever you work with retrofit and the time of your emulator is set up incorrectly it's not good okay so we're directly logged in but we're not displaying the image yet so let me see why that is so now it seems like i was shadowing something so this token state was shadowed because we had a global token state which i'm going to get rid of because i didn't want to have it globally anymore which is exactly what caused the problem of us generally rating like hundreds of requests so you can see here earlier we had 270 requests and now we have 622 requests so let's track that and that's really where treble comes in and really helps us out because it just shows me hey dude you're just creating loads of requests for nothing and that's that's what i really appreciate about treble so now if we go over to our authentication service i had a big error here where i used the field of uh well authorization field but it should actually be the header so i should send the header as authorization and then the username you see here i used a username with a capital u and here with the lower u and so forth so i should stay consistent with it so the username i'm gonna use for the lower u for all of them as i have when i locked the user in okay so this will now fix the problem of the application not loading the profile image at all but the problem is a little deeper so let's run the application let's see so i have another user here let me run treble and edit the profile let's allow that and now select an image here so the dog for example you see the dog image is loaded but if i look at the requests i will see that i'm generating loads of requests just doing that okay so the user profile i'm this is the image okay but you see i'm accessing the user profile a few seconds ago a few seconds ago so i'm accessing it a lot and that has to do with jetpack compose recomposition so after a user registered we can see more requests than we were supposed to have and this is due to the request depending on state values which causes recomposition so a state value is changing all the time right like for example our token and so forth and recomposition and jpeg compose means a change in data causing the ui component to be called again therefore calling the methods again so now we need to move our login request so in our main view model.kt what we can do is for example instead of using our registry user we're going to use the username directly here which will be of type string and password which will be of type string and then we will need to create a register user variable register user variable which will be a register user object that we created earlier where we need to pass in the email which will be the username the password which will be the password and the confirm password which will be password confirmation which will also be just the password okay so i'm not going to create an extra variable for that so then the register request state value can stay as it is the new modal scope launch will now do the same thing and we are registering the user with the register user as we did before but now what we need to add here is we need to add a delay so i'm going to add a delay of 500 milliseconds here as well and then i'm going to log in the user with the username and the password okay so that's where i'm going to call this login user okay so in the sign up composable now within the sign up button so this button here where we register the user this is the sign up button right that's the text of it we now need to make a change so here we need to now pass the username and the password confirmation and so forth so instead of creating the registry user as we did before we can just go ahead and pass all of these details directly to the register user method like this and this will be the username will be email state and this one how did i call over to password state let me see actually let me get rid of this entirely so we need to use just set the password to be the password state value okay this should be fine at that point so then when registration is successful there is no need to call the login method we just navigate it directly to the profile screen so when register state with this toast when we're loading and when we were successful we currently locked in the user again which is not necessary so that's the problem the signup would constantly call this method again and again and we will just create so many different requests each time so let's get rid of that so now let's run the application and actually before we do that let's close the current application also make sure that we go over to our app and we clear the storage and clear the cache and everything and now let's run the app and let's register so let's track our requests so we have 1000 let me see nobody else is hopefully working on this right now so we have 1192 requests so now let's register reg one two three whatever and add sign has to be in there if i ever find it and then let's sign up so the signing up has worked so we have this account now you see we just generated one plus another two requests so the account register and then the two user profiles so then this was trying to load the image and loading the user profile but then if we run the selection of an image and we set it up so now we have added the image you see the user profile generates a couple of requests so here we upload the image and then it loads the user profile once again and these are the requests that we have here okay so that by itself works already that's good that's the amount of requests that we were expecting but now let's say we close the application and we open it up again and then that's where it loads the profile let's see how many requests we get there so you see there we have a problem still so it loads loads of requests so it loaded i think like 20 requests or so so that's something we have to still fix all right so let's do that and our profile composable where we're calling get user profile here viewmodel.userprofile what we need to do is we need to wrap it in the lifecycle so it's only called an onresume so wrapping the life cycle in the disposable ensures that once we leave the screen the observer is cleaned up so let's do that here val life cycle owner which will be a local lifecycle owner dot current so we're going to take that one and and this will be a disposable effect that we're going to use here key one is going to be our lifecycle owner and the effect is the actual code that we want to execute so here the code that we want to execute will be to first of all have an observer and the observer will be the life cycle event live cycle event observer this one here where again we well first we get one variable that we're not going to use and then we get the event and then we are going to check if the event is going to be lifecycle dot event event dot on resume and here you see that you can access the different lifecycle events like on resume on pause and so forth so if that's the case then i want to use my view actually i just want to use this code here so let me not write it once again so that's what i want to do in there and then outside of this if statement i can then use the life cycle owner to add the observer with the uh well add the observer that we just created so officer this one here and it seems like we need to put that outside of where we created the observer like this so then if we do that we need to also run the on dispose and here we're going to remove the observer okay so now the get user profile will not be called again and again because that's the thing with composables right they're getting recreated recomposed and so forth all the time and then also within the add image method add image and i think i called it like add image like this which was in a different file so that will be inside of the main view model okay here we had our add image where is that add user image what we also called get user profile so we don't need that here so now once there is a change one in the one in the lifecycle that we just created inside of our profile here will be executed basically we're saying check only for the lifecycle events and only run it when the onresume method is actually being called so now let's try it again let's run our application and let's check so we have 1661 requests let's close our application and run it again and after bit we see our profile image and we see we get run requests here okay so that's pretty much what we were expecting to get we get our user profile once and that is that is exactly what i wanted to achieve with my application okay so that's it for this part of the video all right so now let's take care of the login screen and this will be very similar to what we had in sign up so let's go ahead and create a new screen here kotlin file and this will be a login because we want to be able to log in once we are in the application not just register right so this will be a composable again add composable and this one will be fun login so it will also need the nav controller so that we can navigate and it will beneath the view model all right so then what do we want in here well we will need to have the toggle state and those other states from our sign up as well so let's put them in here we will also need to have the token we need to know whether we are locked in or not right the token states so let's go ahead and create a token state here which will be using our view model but let me call this one view model like we've done before so view model dot user token dot collect state and value all right so this will be the token state now we will need to set up the user interface of our login screen and it will be pretty much like our sign up screen so let me copy everything up to this row here and put it in there and add another curly bracket okay so this should be fine now it should look pretty much the same email address but then password state once we click on the button itself so here instead of using register user what we want to do is we want to log in user okay with the username and the password all right and then here underneath what i'm going to check is if the token state is that we're loading so is resource dot loading and here this needs to be imported so if we're loading then show the circular progress indicator and if we're not loading and specifically if we are in the success state so token state is resource dot success and then i'm going to check the token state data actually let me see success okay yeah it's not going to be the same as we did done there so token state dot data is not equal null that's the case then we want to save the or use our viewmodel this one here to save the token and save the email so tokenstate.data. username and viewmodel once again this one here dot save token with tokenstate.data.access token and then we can navigate so here we can use our nav controller to navigate to our profile so here navigate to profile now this profile does exist but our login doesn't exist yet so here we're going to do the same thing where we launch single top and this will be sign up as well now the role that we have here with this text should be clickable so i want to be able to move from this screen to another so where we set the padding let's make this clickable as well like this and then in here we're going to navigate to sign up so here sign up okay and already have an account should be don't have an account and this one should be sign up okay now in our sign up we want to be able once we click on this text here so this has to be clickable as well and we need to be able to move to this other screen okay so in sign up we need to basically do the same thing let me copy this here we need to navigate to login all right that should be fine and now in our main screen we mustn't forget to move over so may screen kt we need to add the composable here composable called login and it should lead us to the login with the nav controller and the view model so here nav host controller and view model being the view model which is this view model and this nav host controller alright so now let's run our application and see if we can log in if we are already or if we already have an account so by default we will be well we still have our application running but now let me go over here open the app well obviously i'm registered so i need to clear the data once again oops app info clear storage and then run the app again and here go to the login screen isn't clickable yet so let me achieve see why that is so we are on the profile screen now on the sign up screen and we want to be able to click on this row which contains the two texts well that's because i didn't set up a pop up to so i set that to login now and let's run it again and now you see we can go over and then we can go okay we should change also on the login page we should also change the button content i think it really makes sense to say a login all right okay so i created an account now this one had gmail.com and then the password and this is the registration process it shouldn't work an error has occurred because i was on the signup screen so now once again this one two three f right now let's log in and there we are all right so the login also works all right and that's it for our little application here all right that's it for this video i hope you enjoyed it now you know how to set up an api using asp.net as well as consume the api using a jetpack compose front-end or an application that is using it for a profile screen as well as a login and register screen so there were a bunch of things that you learned along the way if you enjoyed the video then now is a good time to hit the like button if you haven't done so already and subscribe if you haven't subscribed already and if you want to learn a lot more about programming in general definitely check out our other sub channels that we have so we have an android sub channel or app development in general sub channel for now it's just android but in the future there will be flutter as well as ios development so check that out and then we also have a c sharp channel really recommended and we have a bunch of courses so if you feel like you want to really invest into your career and learn a lot more about it about any of the topics that we're covering then definitely check out our courses you can find the link in the description below and you get a huge discount and obviously a big shout out to treble.com thanks a lot for sponsoring this video it took us more than 10 days in total i think in order to prepare this so thanks a lot for them to making this possible so definitely check them out register there and use it for your api tools that you or to your api applications that you're going to build in the future and your projects and recommend it to all of your friends while you're at it that are also developers so thanks a lot for watching the video you're still with me after almost four hours quite a long video and yeah have a nice day and all the best on your journey as a full stack developer
Info
Channel: tutorialsEU
Views: 66,173
Rating: undefined out of 5
Keywords: Tutorials, Tutorial, Programming, Course, Learn, Step by step, guide, development, programmer, video course, video tutorial, learn how to, how to, #aspnet, full stack developer tutorial, free full stack developer course online, treblle API, build API using visual studio, android jetpack compose course, #ASP.NET course, build api with asp.net core, become a full stack developer for free, treblle api monitoring, android course, c# tutorial, use api to get data, api for beginners, azure
Id: h1hwz_Y52po
Channel Id: undefined
Length: 229min 45sec (13785 seconds)
Published: Thu Jan 13 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.