Firebase Google Sign-In With Jetpack Compose & Clean Architecture - Android Studio Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video in this video I will show you how you can integrate a Google sign in in your jetpack compose project using the clean architectural guidelines sounds complex but it's actually something very simple we will do here so we will have a simple button to sign in and when we click that then our Google accounts on our device will show up and we can then choose an account we use to log into this application and if we then click on an account we you see I'm signing in and then we get to the profile screen where you can fetch the information of that account like the profile picture the username and more and then there's also sign out button so we get to the first screen again to yeah sign in with a different account for example this one and we'll also make sure that we stay signed in so if we relaunch this app very quickly then you can see I I will directly be navigated through the profile screen and not need to log in again and before we get started with the video a quick little reminder that there is an ongoing Easter sale active for all my premium courses till April 12th you can get 30 discount on all of my paid courses I really never had a bigger discount before and to apply it you can simply use the coupon code Easter 30 during checkout and this discount not only accounts for the single courses but also for the already discounted course bundles so if you've been thinking to get one of these for a while then there is no better chance than now so do check them out by clicking the first link in this video's description and now let's get started with the video to accomplish this you need a little bit of initial setup so on the one hand I recommend to clone the initial repository which you can find in this video's description so that will simply just contain the dependencies we need um it's basically just Gradle config so we can see we will use Firebase to achieve that we have some composed lifecycle dependencies here we have composed navigation since we of course have two screens in our app we have code compose to load images remotely since that will be used for the profile picture and we use dagger hild to inject dependencies or actually now when I think about it we don't even need dagger field I think I first decided to use it but then realized we don't really need it because in the end we just have a single dependency so let's keep things simple and just not use that we can click synchronize now to apply these changes and by the way I'm using kotlin 1.8.10 in combination with the just released stable version of compose 1.4.0 so this combination works well for me so make sure to use the same but ideally you just clone the project from my video's description okay so before we can actually implement the code that is used for signing in we should understand how such a Google sign-in Works actually and there are two ways how you can Implement that on the one hand you have your own authentication server which just verifies these incoming requests when a user wants to sign in and that back-end server implements like the the Google sign in functionality has its secure tokens and all that stuff we don't have that here and we also don't want to create a backend server and host that because that's quite a lot of work what we want to use is Firebase because Firebase kind of serves as a backend and with Firebase it's very easy to integrate such a Google sign into your application so the first thing we need to do here is we need to connect this fresh Android Studio project with our Firebase console and to do that we need to go to tools and use Android Studio's Firebase assistant click on that then a little tab will open up here and after that has been loaded we want to scroll up to authentication because that's what we want to implement we want to authenticate using Google with kotlin so let's click on that and we will get a little guide on the one hand we'll need to connect this Firebase app here to our Firebase project which we have on in the browser and then we need to add the Firebase dependencies which I already added here so if you clone the repository you already have these let's click connect to Firebase then this browser window will open up depending on which projects you have in your Firebase account this will of course look a little bit different for you but I will just create a fresh new product here by clicking add project and then we can give this a name something like Google sign in YouTube or so and then we can click continue we don't want to enable this analytics we just want to keep this as simple as possible and then click create project this will take a little moment and then I will see you back once that was successful there we go the project was created we can now click continue and then this dialogue will show up hopefully telling us that the project was connected successfully take a little moment again um no we actually need to click connect first so let's do that and then we will get to some kind of localhost URL and it tells us that our products are connected if we now go back to the studio we should hopefully see some kind of check mark here um or at least the dialog or a pop-up here your app is now connected to a Firebase project and you should also be able to verify that by going to your project view on the top left and checking if there is a Google services Json file you should exclude that from GitHub so you don't want to push this file to Gita because with that people can get access to your Firebase console if you haven't secured it properly next up we still need to configure this a little bit in our Firebase console so let's go back to Chrome I'm not sure if we can go back here hopefully yes we can um let's remove all this stuff from the URL we don't want here and just get to the plan main console then we want to go to this little settings icon go to Project settings and then here we can scroll down to our apps that are connected to this project and here we get some information about this app for example this app ID which is commonly needed for Firebase config but what we actually want to do is we want to add such an sha certificate a certificate fingerprint that is basically a unique fingerprint of our app so that filebase really knows that it's our app that is talking to this uh backend in this case and to check this fingerprint because if we click add fingerprint we of course need to add this kind of weird string I want to go to another Studio to generate that click on Gradle to open this Gradle assistant here go to I think it's Tasks Android signing report and if you don't have this section if it does not show all these tasks for you you need to open your Android Studio settings for me that's control comma but might be a bit different for you depending on your shortcuts and here want to go to experimental and you want to make sure that you toggled all this checkbox to only include test tasks and the Gradle task list generated by during the Gradle sync so make sure to toggle this off synchronize your Gradle once by going to file um sync project with Gradle files and then this task list should also appear for you we then want to double click on signing report and it will spit out this sha1 fingerprint which we want to copy and paste into Firebase or console let's go back to Chrome paste this in here it will recognize the type of time of the type of fingerprint and then we can click save next up we still need to do something in this console and that is we need to enable authentication so we need to tell our Firebase project we want to be able to use Firebase authentication with the apps we connect to this project and we do this while going to build authentication and here we need to say get started and now we need to tell it how we want users to authenticate so in a real app you will probably have more than just one Google sign in option on something like email password maybe with a phone or maybe Facebook log in apple login so there are of course lots of ways to authenticate but Google is a very common one on Android devices so let's just pick that and make sure to enable this year so to check this toggle you can see it tells us we need to add this fingerprint which we just did we then need to add a project to Port email if users have issues so we should be able to select this here just your account email let's click on that and then I don't think we need to add anything here let's open web SD Decay configuration this is only relevant if you already have some kind of web SDK you can see we can reuse a web client ID but Firebase will generate this ID which we will need in our project so we don't need to change anything here and can just click on Save there we go here's our provider it is enabled and you also will see if you click on that and scroll down to web SDK configuration I won't open this here because it contains a secret but if you open this then you will see the values Google generated for you here which we then need to pass to our app so our app knows that it is talking to this specific authentication method of this specific Firebase project let's switch back to Android Studio however and get started coding because now we have all the Firebase setup we need and we can get into writing some kotlin code so I will close these Gradle files here switch back to the Android View and now we first of all need to think a bit about the structure we're going to choose here because I intentionally want to stick to clean architecture here on the one because I know many people like that and on the other hand because with Google sign in it is a little bit tricky to properly integrate that in a clean architectural code base because we have one issue with that and that is that the Firebase SDK for Google sign is kind of on the one hand data related because it of course talks with a backend server but on the other hand it is UI related at the same time since the way this works is we call a function to sign into Google and the response from the Firebase SDK will be an intent sender so basically something we can use to send out an intend and launch an activity for a result or I think it's technically not launching an activity for a result but we launch kind of a UI sheet for a result and the result would be logging in and getting the account and that is why I will put the class that combines all this log and functionality in our presentation package since it will be directly used in our activity because we need to to launch this intent which we only do in each activities at least for such UI related intents but I would say we just get started and see how this will work so what I will do is I will create a presentation package here and then inside that assign in package and we will actually only have a presentation layer here I think because we don't really have any business logic or data related logic apart from the sign in thing but I still think this helps people who want to integrate that in a code base that uses clean architectural guidelines to know where to put things so in this sign in package we want to create a Google auth UI client that will be the class which uh yes let me enter to get that will be the class that we use to sign in to sign out and to get the user information about the signed in user and what this class needs and the Constructor is on the one hand our context which is just a context and it needs a so-called one tab client which is a sign incline so the one tab client is the client that comes from the Firebase SDK which will then in the end show this dialog to sign in and in here we also want to have a public reference actually a private reference but a global variable I mean to our object which we get from Firebase dot auth so now we can get to our functions on the one hand we want a function a suspend function to sign it because signing in of course can take a little moment and in that time we want to suspend a curtain so we can wait for the result and what this will return is a so-called intent Center so just an object we can use to send out an intent to achieve something to fulfill our specific intention we can say and to get the result we can say Val result is try catch we catch exceptions here and what I want to do is we want to take a one tab client and we say begin sign in and this will now take a begin sign in request which I like to build in a separate function so let's go below here say private function build sign in request returns a begin sign in request and here we can simply return that request so we want to return begin sign in request dot Builder so we will typically build a pattern here and we can then say set Google ID token request options which is also built via a builder so you want to say Google ID tokenrequestoptions dot Builder and here we say set supported true so we just say hey this way of authenticating is supported we want to be able to sign in Via these Google token IDs then we want to set a filter by authorized accounts to folds if we set this to true then it will already check if you are signed in with a specific account already or if you have signed in with a specific account already and then only show that account as an option but if we set this to false however then we will always get the full list of accounts we could potentially log in with so all of our Google accounts we use with that Android device and then most importantly you want to set the server client ID to something which I think Firebase like the SDK will already include in your string resources if you use their Gradle plugin which I think I haven't included here but we can also very easily get that ID from our Firebase console by clicking on our sign in method Google scrolling down opening this web SDK configuration and copying this web client ID let's then go back to Android Studio go to stringsxml copy the string call this default web client ID or just web client ID for example and then we paste in this ID we can then go back to our Google all UI client and in here when I say context get string are that string dot web client ID and then we can say that build and then for our outer begins and in request Builder we want to also add one more option and that is the set Auto Select enabled I'm going to set that to true what that will do is is if you only have a single Google account you can sign in with it will automatically select that one because yeah otherwise you would need to have that additional tab on the account which is pretty obvious here if you want to sign in you only have one account you can also directly sign in with that then we want to say that build and we have our sign in request which you can now pass to our one tab client so build sign in request and by default all these Firebase functions will return a task a task is something that executes something asynchronously and then we can get the result using for example on add-on success listener if that was successful or unfail listener if it was not successful however since we use Co routines here there's also a weight function which will simply suspend this outer core routine and wait until the signing is finished here in case there is an exception this function will throw it so we can say we print the stack trace and we also want to check for cancellation exceptions if this is a cancellation exception we simply want to re-throw that um the reason for that is a little bit more complex I talk about that in my video about color team cancellation so feel free to watch that on my channel and search for that but if we don't do this and catch General exceptions here in a suspend function this can mess up Co routine cancellation so after that we want to pass not here or rather return an ultra or result since if there is an issue we don't have an intent sender so down below this where we have our result we can then say return result pending intent and that panning intent has our desired intent center now when we take this intent sender and actually send it then what will happen is that our app will get an intent back with the information about the user's sign in and the Firebase of SDK already contains the function to kind of um deserialize that intent that we will receive from this intent sender and we want to have a function that simply handles this here so suspend function get sign in result from intent and here we pass the intent intent and then we want to return a sign in result which is a class I will create here so in our sign in package we want to have that sign in result class make that a data class and this will have two fields on the one hand our data which is basically what contains the user data so the user's name the profile picture URL and the user ID in our case but you can also get more information from your Google account so this will be a user data object something will also create don't import this one want to be able to pass an error message in case something goes wrong just a nullable string let's then Implement our user data class down here which consists of a user ID which is a string so a unique identifier for each user which comes from Firebase we want to have a username and we want to have a profile picture URL which is nullable strings since not every user might have a profile picture and the username might also be nullable I'm not sure here if every Google account has a name then going back to auth UI client it now knows this class and we can start implementing this function first of all we want to get these so-called credential which we get from this intent so the resulting intent that gets sent back to our app for that we can use our One Tap client again and we say get sign in credential from intent and we pass in our intent we can then get our Google ID token which is used for authentication from this credential so that Google ID token and our Google credentials so those will now really be the credentials we need to log in with this Google account that's equal to Google auth provider.get credential we need to pass in our Google ID token and for the Excel token we can simply pass a null here and then we can return a try and catch block again catch General exceptions same thing as in this function above we want to print the stack Trace and catch cancellation exceptions and in that case one two return a sign in result where the data is simply null and this is not nullable at the moment let's go in here and make this nullable and then say the error message is whatever the message is from our exception in the try block we want to get the user by saying auth DOT sign in with credential so here we can then pass our auth credential which in this case is our Google specific credential so our Google credentials we can say that await to make this a suspending call and then we refer to the user which is now a Firebase user so that contains information about one user this could also come from a different source for example if you have email password sign in then you would also get a Firebase user for that so Firebase really has one shared way of referring to the users you get when you log in so the stuff until here is Google specific Google sign in specific but everything you get after logging in with these Google credentials is just normal Firebase authentication stuff so we refer to the user and we can then return a sign in result again where the data is a new user data object the user ID is user dot uu use Dot uid and we can also make sure that we have a null check before so let's let's remove this first of all say user question mark.run and then we create a user data object so the uid user ID is qid the username is display name and the profile picture URL is the photo URL and since that is a Yuri we need to convert that to a string and our error message in this case is null and maybe we should also rename this function a bit since this not only gets the sign in result from the intent but rather signs in with an intent so let's rename this to sign in with intent I think that makes it a bit more clear what this function does now what is missing in this class not much anymore just a little function to also sign out again and a function that we created the current user and basically just Maps the Firebase user to our own user data object first of all the span function sign out and then here again try catch that is nothing new e.print stack Trace if e is cancellation exception we throw e again referring to my cancellation video and in here in the trial block we want to say auth or actually first one tap client DOT sign out dot await and then we say auth DOT sign out and this will just require us to that we need to log in again with our account and then last but not least we're going to have a function here to get signed in user which will return a nullable user data object because only if there is assigned user we of course can return data and that is simply auth current user question mark run and we convert that to a user data object where the ID is uid again username display name profile picture URL is photo URL dot to stream like this cool and that is really the Firebase Google sign in logic that you need in your project the rest of this video will now be a building overview model and UI so to actually be able to click on a button and sign in with that first of all let's go to sign in and create a state assign in state which will be super simple on the one hand we want to have a state that tells us if the sign in is successful or not which is false initially so we'll just use that to to observe that and then navigate to the next screen when it is successful and we want to have a sign in error message which is simply a nullable string so we can show ours if there is one then we can go to sign in again and create a little view model that will also be super simple sign in viewmodel view model and here we're going to have our state our private valve state is a mutable State flow of sign in state like this we have our public exposed immutable version of the state since we don't want to expose the mutable version to the UI and then we simply have a function on sign in result which we call when we get the sign in result after signing in here is the result and we will then update our state so state that update it.copy is signed and successful is if the results dot data is not null so if we actually get a user from that sign in then we know it was successful and the sign in error is simply result dot error message and after we change the sign in successful to true we also want to have a function to reset the state to change it back to false because if we then navigate to the next screen to the profile screen we want to make sure that if we get back to the login screen that we're not logged in so the sign-in should be unsuccessful in that case again so we will simply say state that update with the sign and state just the default State basically where the is sending successful Boolean is false again and there's also no error anymore and yes now we can actually Implement our sign in screen in the sign in package sign in screen make it a file just normal composable sign in screen this will take in our sign and state so we can upgrade update the UI properly and we have an on sign in Click Lambda which we call when we click on our sign in button first of all let's make sure we have proper error handling so we we can simply show a toast when there is a sign in error for that we need the context which we get with a local context.current and now we can use the launch effect block pass in state dots sign in error so this launched effect block will now be called whenever this piece of State changes so whenever that happens you want to check if there actually is a sign-in error with let's check here and if there is want to show a toast so toast. make text pass in the context pass in our error message toast.link long to make us A long toast and then we call show and boom there is our error handling and for the rest of the screen that's super simple we just have a box and a button in the end so box where we say okay our modifier is a modifier dot filmex size fill the whole screen size and we add a little bit of padding of 16 DP want to import DP pressing alt and enter and we want to Center our button so we say content alignment is Center and then in here we can finally put in our sign in button when we click that we say on sign and click and the text of that button will just be a text composable that says sign in and yeah that is already our very simple sign-in screen the next thing is that we need to adjust our main activity so we now need to set up navigation that we tell compose we have two screens in the end and that we also register our intent sender launcher so that we're able to send this intent we get from our auth UI client here so we first call the sign in function and then we get this intent sender we send this intent sender or the in yeah we send the underlying intent we get an intent back and that intent that we get back we will pass to sign in with intent right here and we will then get a sign in result which we however will pass to our review model which will then update the state correspondingly here so that is how this workflow will work so in our surface let's get a reference to our nav controller by using remember nav controller we don't need anything in here and then we can have our nav host so this nav host will simply host all of our different screens we need to pass enough controller oops controller and instead of the graph you know it has a start destination which is simply our sign in route like this and for this sign in route we want to have a composable so with this we now Define this is the composable that should be shown for the sign in screen so we again refer to our same route which is our start destination we get a reference to our view model by saying is equal to view model pass in the type of the view model which is a sign in viewmodel and we want the reference to our state which we can get by using a buy viewmodel State collect our state with lifecycle Alt Enter to import that and now we get updates to our state as well then we want to be able to send this intent we get from the intent sender to actually execute this intent sender and for that what we need is a so-called activity result launcher we can get this by saying remember launcher for activity result and we need to pass in a contract so basically what we want to do with that launcher in this case we want to start an intent sender and get a result back so we say Activity result contracts start intent sender for result like this and on results then called where we get the result which is in this case an activity result we can give this a name and we can then first of all check if the results dot result code is result okay which means we got a successful result and if we did we want to launch a little core routine here in lifecycle scope so we say that launch and we say we get our sign in result which is equal to our auth client we actually don't have that yet so let's scroll up to our activity here oops and create our client here so private Val Google of UI client by lazy so we initialized that the first time we access it and it's just equal to Google Earth UI client the context is our application context and the one tab client is equal to Identity from this com Google Android GMS import here dot get sign in client and we pass in our application context again then we can scroll down say valve sun in result is equal to Google or the UI client a DOT sign in with intent the intent is now equal to result dot data you can see that is simply intent and if that is null for whatever reason for example if the result was not successful we can return out of our launch block after that we can finally call our review model and say on sign in result and pass this sign in results or state gets properly updated and then right now we don't do anything with this launcher we of course want to execute it when we click on our sign in button which one to do below here so we are still in our screen composable and what we're missing is of course the actual screen composable so we say sign in screen pass in our state and I also don't like this way of writing it but rather make the on click Lambda a name perimeter so on sign and click we want to say life cycle scope that launch and in here we get the sign in intent we want to pass to the launcher by saying Google Earth UI client DOT sign in so that is now an intent sender that's actually also call it like that so sign in intent sender and we can then say launcher dot launch and what this text as an input is an intent sender request which we can construct by saying intent sender request dot Builder and in here in the Builder Constructor we need to pass in our intent sender so sign in into intent sender and we say dot build if that's not again we say we turn at launch we just ignore this but if not we will pass this intents and the request to our launcher and of course we're now missing our profile screen but I would say we already try this out because we should still be able to see the Google sign in sheet and click on an account and then get a successful response if everything works so let's actually go right here to test that and have a launch effect block which is triggered whenever our is done and successful Boolean changes if that is the case if our sign in was successful we simply show a toast for now and later we then want to navigate so here we pass application context we say sign in successful we say toast length long and call Dot show just like this and I would say we try this out maybe this doesn't work maybe it does let's see and then after that we can check if there are any issues how we can fix these and even better if there are no issues then we can resume with the profile screen Let's uh let Gradle build and install this here there we go our app is at least building and compiling if we now click sign in then our app is crashing okay so it seems like there is an issue if we scroll up what is this default Firebase app is not initialized and I feel like this might be related to what I mentioned before that we don't have the Gradle plugin for Firebase because you usually don't need to call this but I will take a look at this issue and then get back and let you know how to fix this okay it was exactly what I thought um the plugin wasn't added here but for you I will make sure that this is already added in your Gradle config so if you really clone the project then it will probably work for you right away but for me it didn't because I didn't have these graded lines here included so this one and this one and if we Now launch this and take a look here on my phone we then click sign in and then we see our sign-in sheet so that is looking quite good I haven't tried to log in with uh with my account yet so let's try that by clicking on the account clicking continue as Philip and we do get signed in successful so it worked quite well and as I said we can now take a look how we can actually get the user data and display that on the next screen in on our profile screen so let's close this Gradle config again and go to our presentation package create a profile package in which we're going to create a profile screen make that a file composable profile screen and well what do we need to display a user we need a user data object like this and for this screen we really don't need a viewmodel because it doesn't contain any state and it doesn't contain anything that changes at all it just displays static user data but we want to have a Lambda when we sign out like this and in the end we're just going to put in a column in here the column will have a modifier of modifier film exercise and we want to Center the content vertically and horizontally then instead of this column the first thing you want to see on this page is the user's profile picture so if user data profile picture URL is not now we want to show that I'm actually like this with an async image which comes from coil and the model is simply these are data profile picture URL so our UL the content description will just be a profile picture and let's give the modifier to assign a size of uh 150 DP how to enter to import DP and we then want to clip it to a circle shape so we have a circular profile picture let's make sure the content scale is crop although I think Google already crops the images when the users upload them and we add a little bit of spacing after that with a height of 16 DP then the next thing on our profile will be the username so if the username is existing we just display a text with that username so we can say okay that text will have a text align of Center we say the font size is something like 36 SP Alt Enter to input SP and the font weight is simply um bold well let's make it semi-bolt and again we can copy paste this a spacer here below because after this username we want to display our button to sign out so button when we click this we simply want to call onsign out and the text will just be sign out super simple for this but then we now need to register the screen or anaphyl so that it also knows that so let's get back to the main activity and below this composable we want to have an additional screen so another composable called profile and this actually has some arguments we need to pass which is the the user data theoretically we could also call our get user function on the other screen and also pass in or Google Earth UI client we can actually do this this is easier than using navigation arguments this is an experiment here but it should work so let's just leave out the navigation arguments and rather go to our profile screen and instead of passing in the user data oh no we can actually leave it like that and pass in the user data let's go to main activity I will show you how and here we will just have our profile screen and for the user data we will now just pass our Google auth UI client gets signed in user and for on sign out we will simply have a Lambda here which also calls our Google authy client calls sign out actually in lifecycle scope like this and it then simply shows a toast that we signed out like this toast length long that show and finally we want to say nav controller dub pop backstack to get to the previous screen and for our user data we currently are only allowed to pass non-allible objects here but let's make that notable by going into our profile screen make it nullable and simply add these null checks here and here and that should be it for the profile screen we now want to have ourselves to navigate to our profile screen so in this launched effect block here when our sign in is successful after showing the toast we also want to navigate so nav controller dot navigates and we pass in the route we want to navigate to and also after navigating we want to make sure that we call viewmodel reset State because if we then sign out we want to start with a fresh estate since the user needs to log in again and last but not least we want to have another launch defect block right up here and that will check if the user is already logged in so if we are already signed in and we relaunch the app we directly want to get to the profile screen until we sign out the key can simply be unit which will execute this launched effect block exactly once when this composable is composed and in here we can simply check if Google Earth UI client a get site in user is not null then we know there is a signed in user in that case we can simply navigate to our profile screen and then we can launch this and try this out let's see if our profile screen now works there we go we are already logged in because I did that before we are seeing my information if I click sign out then I am signed out and if we click sign in then we need to sign in again let me sign in with a different account here continuous Philip and boom there I am with my profile picture and I can sign out again or I can relaunch the app to show you again that now this account will be signed in sign out if we now relaunch the app we are not assigned in automatically anymore so our Google sign in is finally working as expected you'll of course find all the code for this in this video's description in my GitHub repository so the initial code and the final code to play around with that and apart from that if you really enjoyed this tutorial and you like how I explain things you like how I structure these things and how I integrate things in an architecture then you will definitely learn a lot more in my more advanced Android premium courses so if that sounds good to you and you want to become the industry ready Android developer then check the first link in this video's description to get to these courses and other than that I just wish you an amazing rest of your week I will see you back in the next video bye bye foreign
Info
Channel: Philipp Lackner
Views: 35,988
Rating: undefined out of 5
Keywords:
Id: zCIfBbm06QM
Channel Id: undefined
Length: 39min 57sec (2397 seconds)
Published: Wed Apr 05 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.