Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 9, 2021 07:51 am GMT

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.

create_project

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.

add_platform

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 command hostname -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 form
appwrite-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

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To