An Interest In:
Web News this Week
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
- March 26, 2024
Getting started with Appwrite's Android SDK
One of the major highlights of Appwrite 0.9 is the official support for Android. We've also released a brand new Android SDK to go alongside it !
In this tutorial, we'll learn to setup Appwrite's Android SDK, interact with Appwrite's Accounts API and also learn to setup OAuth Logins in your App! We'll extensively use JetPack components like Fragments, ViewModels, Data Binding, LiveData and Kotlin Coroutines! I hope you're as excited as I am! Let's get started!
Prerequisites
At this stage, we assume that you already have an Appwrite instance up and running. If you do not have Appwrite setup yet, you can follow the super easy installation step over at appwrite.io. It's not a typo. There really is only 1 step!
You should have also setup an OAuth provider with Appwrite to be able to follow the OAuth section of this tutorial. You can learn to setup OAuth providers in Appwrite by going through our tutorial.
Create a new Android Project
Setup your Android project and choose the Empty Activity Template. Give your project a name, Choose Android 6.0 ( Marshmallow ) as your minimum SDK and click Finish.
Now is also a good time to add our Android App as a platform in the Appwrite Console. Head over to your AndroidManifest.xml
and locate the package name of your app. It should look something like com.example.myapplication
.
In your Appwrite dashboard, click on Add Platform and select a New Android App. Give your app a name, add the package name and click Register.
Once this is complete, it's time to head back to our Android project add our dependencies.
Setup Appwrite's Android SDK
Appwrite's Android SDK is hosted on Maven Central, so you'll first need to add the mavenCentral()
repository to your project's build.gradle
file ( if it's not already present ).
allprojects { repositories { google() mavenCentral() }}
Then add the Appwrite Android SDK to your app's build.gradle
file.
dependencies { // ... Other dependencies implementation 'io.appwrite:sdk-for-android:0.0.1'}
We'll also be using Data Binding and we need to enable that in our app's build.gradle
file
android { // ... buildFeatures { dataBinding true }}
Sync your gradle dependencies and if there are no errors, we're ready to proceed!
Create helper classes
The first step is to initialise the Client()
class from the Appwrite SDK. For this, we will create a new file utils/Client.kt
where we will create a Singleton object to use across our App.
package com.example.myapplication.utilsimport android.content.Contextimport io.appwrite.Clientobject Client { lateinit var client : Client fun create(context: Context) { client = Client(context) // Replace with your own endpoint and project ID .setEndpoint("https://demo.appwrite.io/v1") .setProject("6070749e6acd4") }}
If your Appwrite setup is running on localhost, you will need to make sure localhost is accessible from your emulator by using a proxy. Else, you can replace
localhost
with your private IP address which you can find using the commandhostname -I
on UNIX systems.
If you're using Appwrite on localhost, you need to set the setSelfSigned(true)
flag to true.
client = Client(context) .setEndpoint("https://192.168.1.35/v1") .setProject("6070749e6acd4") .setSelfSigned(true)
Next, we're going to create another utility class called utils/Event.kt
. This utility class will allow us to handle LiveData Events ( Changes that need to be observed only once ).
package com.example.myapplication.utils/** * Used as a wrapper for data that is exposed via a LiveData that represents an event. */open class Event<out T>(private val content: T) { var hasBeenHandled = false private set // Allow external read but not write /** * Returns the content and prevents its use again. */ fun getContentIfNotHandled(): T? { return if (hasBeenHandled) { null } else { hasBeenHandled = true content } } /** * Returns the content, even if it's already been handled. */ fun peekContent(): T = content}
Create the layouts
Now, we're going to update the layout in activity_main.xml
to allow us to host fragments. Replace the contents of activity_main.xml
with this snippet.
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container_view" android:layout_width="match_parent" android:layout_height="match_parent" /></androidx.constraintlayout.widget.ConstraintLayout>
Now, create layout file for our Fragment at layout/fragment_account.xml
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constraint_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp"> <EditText android:id="@+id/responseTV" android:layout_width="match_parent" android:layout_height="200dp" android:background="@null" android:enabled="true" android:fadeScrollbars="false" android:focusable="true" android:longClickable="true" android:scrollbars="vertical" android:textIsSelectable="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/email" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:hint="Email" android:inputType="textEmailAddress" android:text="[email protected]" app:layout_constraintStart_toStartOf="@id/responseTV" app:layout_constraintTop_toBottomOf="@id/responseTV" /> <EditText android:id="@+id/password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:hint="password" android:inputType="textPassword" android:text="testtest" app:layout_constraintStart_toStartOf="@id/email" app:layout_constraintTop_toBottomOf="@id/email" /> <EditText android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:hint="name" android:inputType="text" app:layout_constraintStart_toStartOf="@id/password" app:layout_constraintTop_toBottomOf="@id/password" /> <Button android:id="@+id/login" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Login" app:layout_constraintEnd_toStartOf="@+id/signup" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/name" /> <Button android:id="@+id/signup" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Signup" app:layout_constraintEnd_toStartOf="@id/getUser" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/login" app:layout_constraintTop_toBottomOf="@+id/name" /> <Button android:id="@+id/getUser" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Get User" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/signup" app:layout_constraintTop_toBottomOf="@+id/name" /> <Button android:id="@+id/oAuth" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Login with Facebook" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toStartOf="@+id/logout" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/signup" /> <Button android:id="@+id/logout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Logout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/oAuth" app:layout_constraintTop_toTopOf="@id/oAuth" /> </androidx.constraintlayout.widget.ConstraintLayout> </ScrollView></layout>
Create the ViewModel
Let's now create a ViewModel at ui/accounts/AccountsViewModel.kt
to manage our app state.
package com.example.myapplication.ui.accountsimport android.text.Editableimport androidx.activity.ComponentActivityimport androidx.lifecycle.*import com.example.myapplication.utils.Client.clientimport com.example.myapplication.utils.Eventimport io.appwrite.exceptions.AppwriteExceptionimport io.appwrite.services.Accountimport kotlinx.coroutines.launchimport org.json.JSONObjectclass AccountsViewModel : ViewModel() { private val _error = MutableLiveData<Event<Exception>>().apply { value = null } val error: LiveData<Event<Exception>> = _error private val _response = MutableLiveData<Event<String>>().apply { value = null } val response: LiveData<Event<String>> = _response private val accountService by lazy { Account(client) } fun onLogin(email: Editable , password : Editable) { viewModelScope.launch { try { var response = accountService.createSession(email.toString(), password.toString()) var json = response.body?.string() ?: "" json = JSONObject(json).toString(8) _response.postValue(Event(json)) } catch (e: AppwriteException) { _error.postValue(Event(e)) } } } fun onSignup(email: Editable , password : Editable, name: Editable) { viewModelScope.launch { try { var response = accountService.create(email.toString(), password.toString(), name.toString()) var json = response.body?.string() ?: "" json = JSONObject(json).toString(2) _response.postValue(Event(json)) } catch (e: AppwriteException) { _error.postValue(Event(e)) } } } fun onGetUser() { viewModelScope.launch { try { var response = accountService.get() var json = response.body?.string() ?: "" json = JSONObject(json).toString(2) _response.postValue(Event(json)) } catch (e: AppwriteException) { _error.postValue(Event(e)) } } } fun onLogout() { viewModelScope.launch { try { var response = accountService.deleteSession("current") var json = response.body?.string()?.ifEmpty { "{}" } json = JSONObject(json).toString(4) _response.postValue(Event(json)) } catch (e: AppwriteException) { _error.postValue(Event(e)) } } }}
Our ViewModel has 2 LiveData Objects: One to keep track of errors and one to keep track of the response.
We also have 4 functions
- onLogin - onClick handler for the Login Button
- onSignup - onClick handler for the Signup Button
- onLogout - onClick handler for the Logout Button
- onGetUser - onClick handler for the Get User Button
Create the Fragment
Awesome! Let's move on to create our Fragment at ui/accounts/AccountsFragment.kt
package com.example.myapplication.ui.accountsimport android.os.Bundleimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport android.widget.Toastimport androidx.activity.ComponentActivityimport androidx.databinding.DataBindingUtilimport androidx.fragment.app.Fragmentimport androidx.lifecycle.Observerimport androidx.lifecycle.ViewModelProviderimport com.example.myapplication.Rimport com.example.myapplication.databinding.FragmentAccountBindingclass AccountsFragment : Fragment() { private lateinit var binding: FragmentAccountBinding private lateinit var viewModel: AccountsViewModel override fun onCreateView( inflater: LayoutInflater , container: ViewGroup? , savedInstanceState: Bundle? ): View { viewModel = ViewModelProvider(this).get(AccountsViewModel::class.java) binding = DataBindingUtil.inflate( inflater, R.layout.fragment_account, container, false ) binding.lifecycleOwner = viewLifecycleOwner binding.login.setOnClickListener{ viewModel.onLogin(binding.email.text, binding.password.text) } binding.signup.setOnClickListener{ viewModel.onSignup(binding.email.text, binding.password.text, binding.name.text) } binding.getUser.setOnClickListener{ viewModel.onGetUser() } binding.logout.setOnClickListener{ viewModel.onLogout() } viewModel.error.observe(viewLifecycleOwner, Observer { event -> event?.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled Toast.makeText(requireContext(), it.message , Toast.LENGTH_SHORT).show() } }) viewModel.response.observe(viewLifecycleOwner, Observer { event -> event?.getContentIfNotHandled()?.let { binding.responseTV.setText(it) } }) return binding.root }}
Update Main Activity
Finally, let's update our MainActivity.kt, that will initialise our Client
singleton and add the AccountsFragment
to the FragmentContainerView.
package com.example.myapplicationimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport androidx.fragment.app.addimport androidx.fragment.app.commitimport com.example.myapplication.utils.Clientclass MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Client.create(applicationContext) if (savedInstanceState == null) { supportFragmentManager.commit { setReorderingAllowed(true) add<AccountsFragment>(R.id.fragment_container_view) } } }}
You should now be able to run your app and create users, login, logout and get information about the currently logged in user!
Adding OAuth Support
You would have noticed that we have a Login With Facebook button in our UI but it doesn't really do anything yet. So let's now add Facebook OAuth to our app!
The first step is to add a callback activity in the AndroidManifest.xml
file.
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <application <!-- Other Activities --> <activity android:name="io.appwrite.views.CallbackActivity" > <intent-filter android:label="android_web_auth"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="appwrite-callback-6070749e6acd4" /> </intent-filter> </activity> </application></manifest>
Make sure you replace the Project ID in <data android:scheme="appwrite-callback-[PROJECT-ID]" />
with your own.
Next, we'll add a function to our ViewModel to call the createOAuth2Session()
method of the Appwrite SDK.
fun oAuthLogin(activity: ComponentActivity) { viewModelScope.launch { try { accountService.createOAuth2Session(activity, "facebook", "appwrite-callback-6070749e6acd4://demo.appwrite.io/auth/oauth2/success", "appwrite-callback-6070749e6acd4://demo.appwrite.io/auth/oauth2/failure") } catch (e: Exception) { _error.postValue(Event(e)) } catch (e: AppwriteException) { _error.postValue(Event(e)) } } }
The success and failure URLs take the following formappwrite-callback-[PROJECT-ID]://[YOUR APPWRITE ENDPOINT]/auth/oauth2/[ success | failure ]
Make sure you replace the Project ID and Endpoint with that of your own.
The last step is to invoke this function from AccountsFragment.kt
override fun onCreateView( inflater: LayoutInflater , container: ViewGroup? , savedInstanceState: Bundle? ): View? { // ... Existing Methods binding.oAuth.setOnClickListener{ viewModel.oAuthLogin(activity as ComponentActivity) } }
Re-run your app and you should now be able to trigger your Facebook OAuth Flow! With that, you now know how to interact with Appwrite's Accounts API in your Android Apps!
We've built a complete app that interacts with all of Appwrite's APIs, which you can find over at our Github Repo. If you'd like to learn more about Appwrite or how Appwrite works under the hood, we've just curated all the resources for you during 30 Days of Appwrite.
Credits
Hope you enjoyed this article! We love contributions and encourage you to take a look at our open isuses and ongoing RFCs.
If you get stuck anywhere, feel free to reach out to us on our friendly support channels run by humans .
Here are some handy links for more information:
Original Link: https://dev.to/appwrite/getting-started-with-appwrite-s-android-sdk-98l
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To