Modern applications allow us to get connected to the world like never before. However, how is this structure so effective in providing a robust connection between different applications and data sharing between different devices? API(Application Programming Interface) allows developers to build complex features and expose application functionalities as resources.
The purpose of an API is to communicate between the client and the server using HTTP protocols. This includes the processes of data transfers, data security, and distributions to different networks and third-party applications.
As an android developer, you want that perfect architecture that will allow your users to get connected to data sources using APIs. You can use many libraries to facilitate communication between your android application and an external server(API). One library that stands out for API HTTP communication is Retrofit.
In this article, we will build an android application with Strapi using Retrofit.
To continue with this article, it is helpful to have the following:
Retrofit is a type-safe HTTP client for Android. Retrofit helps you consume HTTP RESTful web APIs using Java. It offers methods and simpler syntax to make and call API requests.
Strapi is an open-source headless CMS based on Node.js. It lets you develop and manage any content of your application. This allows you to build backend API faster with efficiency and customizability advantages.
Strapi allows you to run your servers as headless CMS. This allows you to manage your backend content and customize any content to your liking. You get the power and flexibility to choose, while the presentation channel best fits your content model. Strapi then generates REST API endpoints that you can use with any frontend of your choice.
We can have any API ready using the Strapi CMS with a few steps. Using Strapi we will model simple Tasks data using Strapi CMS. Let's dive in and create a Strapi server locally. Navigate o where you want Strapi to live on your computer and run the following command. We will create this application using Strapi CLI(Command Line Interface) installation script to get Strapi running locally as follows:
1#npm
2npm run develop
3
4#yarn
5yarn develop
Once you run the above command, select Quickstart**(recommended)**. This will create a strapi-android-backend folder containing Strapi ready-to-run scripts. Once the installation is done, Strapi will be launched on your default browser. Alternatively, you can change the directory to strapi-android-backend.
cd strapi-android-backend
Then run the following command to start the Strapi backend:
#npm
npm run develop
#yarn
yarn develop
Finally, navigate to http://localhost:1337/admin. Using either of the above methods, a Strapi Dashboard will be launched on your browser.
Go ahead and provide the registration credentials to access the Dashboard workspace panel and start managing the content using Strapi.
Note: The above quickstart selection will create a Strapi backend using SQLite as the default database. If you want to use a different database architecture, you can still do that using Strapi. Strapi supports major databases such as MongoDB, MySQL, PostgreSQL, and MariaDB. To set Strapi with the database of your choice, ensure you select Custom**(manual**settings), which will allow you to choose your preferred database.
Once Strapi is up and running, you only need to add the data models you consume using your Android studio. Here we are using a Task use case. Therefore, let's dive in and implement the task blueprint.
First, navigate to the Content-Type Builder section and Create new collection type.
Proceed and provide the Configurations for your task collection as follows.
Strapi will use this naming to generate the API routes and database tables/collections.
Click Continue and add the necessary field for your collection type.
In this example, we will use three fields:
Go ahead and select Text and the field title as follows:
Click Add another field and add the description of type Text.
Click Add another field and select a type of Boolean. And the name done to the new Boolean field.
Each item will have a default false value when adding a new task. On the above screen, navigate to ADVANCED SETTINGS and add false as the Default value as follows:
Click Finish and finally Save to build the above data architecture of your backend content. Below is how your structure should look like:
Now that the collection is set. Let's add a few sample data to the Tasks collection.
Navigate to the Content Manager section and click Create new entry on your tasks COLLECTION TYPE.
Create a new entry as follows:
Click Save to add the entry to the draft list and finally, click Publish to make the entry accessible outside Strapi.
Using the above method as an example, add a couple of Tasks entries to your Strapi CMS backend. You should have a list of tasks as such:
Now, to access the data, we need to allow access from the Strapi workspace. Therefore, go ahead and generate an API access token as follows:
Navigate to the Setting section and select API tokens.
Click Create new API Token and set a new token as follows:
Finally, click Save. This will generate an API token allowing you to access your backend securely.
Ensure to copy this token. You won't be able to see it again once you navigate outside this page for security reasons.
Finally, navigate to Settings → Roles and set the role of Public Permissions as follows:
Note: each permission displays a Bound route to the backend: For example:
Here each permission represents an HTTP method and its associated route. We will use these routes for this data.
Click Save to add these changes.
Let's dive and consume this backend in android using the Retrofit library.
Go ahead and launch your Android Studio. Create a new project using an Empty Activity. Click Next and set up your application as follows:
We will build this application using Java. Ensure you select it as such.
Once the application is set, navigate to the build.gradle file and add the Retrofit dependencies.
1implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
Here we are adding:
Note: To get the latest available dependencies, you can always check Retrofit documentation.
We are accessing a server that is outside this project. Therefore, we need internet permissions to access the Strapi backend. Navigate to the AndroidManifest.xml file and add the following permission.
<uses-permission android:name="android.permission.INTERNET"/>
Strapi is running locally. This means it doesn't have the cleartext network traffic for HTTP URLs.
Therefore, on your application element of the AndroidManifest.xml file, declare it as follows:
android:usesCleartextTraffic="true"
The android is ready to execute the request to the backend and back. Let's dive and implement the actual Retrofit configuration to this application and consume the task data we created on the Strapi backend.
Using Retrofit, we will connect the Backend(Strapi) with the Frontend(Android). We will use the Strapi route and the token we generated to connect the two using a retrofit instance.
First, create a new model and name it Api.
Inside this package, create a new Java class file and call it ApiClient. Here we will create the HTTP client used for requests and responses.
Inside the create class:
1public class ApiClient {
2}
1private static Retrofit retrofit;
2private static final String base_url = "http://192.168.100.8:1337/";
3private static final String token = "your_strapi_api_token"
The server is running locally; therefore, we will use the local host domain that maps to the port 1337 running the Strapi backend. Note: when using android studio, the URL http://localhost:1337/ won't work. Use your local IP address instead. To get this IP, run ipconfig on your terminal:
Copy the IPv4 Address and replace it with the localhost keyword, i.e., http://192.168.100.8:1337/.
Also, don't forget to replace the your_strapi_api_token token value with the actual token you copied earlier on the Strapi backend.
Note: OkHttp dependency shipped with Retrofit dependency
1public static final OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
2 @NonNull
3 @Override
4 public okhttp3.Response intercept(@NonNull Chain chain) throws IOException {
5 Request newRequest = chain.request().newBuilder()
6 .addHeader("Authorization", "Bearer " + token)
7 .build();
8 return chain.proceed(newRequest);
9 }
10}).build();
This will observe requests from the server and the corresponding responses coming back into the server.
1public static Retrofit ApiConnection(){
2 if (retrofit == null){
3 retrofit = new Retrofit.Builder()
4 .baseUrl(base_url)
5 .client(client)
6 .addConverterFactory(GsonConverterFactory.create())
7 .build();
8 }
9 return retrofit;
10}
Ensure you pass the base_url here. Also, execute the client Interceptor to execute the Authorization headers.
We created a model that represents the Task data on the Strapi Backend. Now, we need to represent the same model to the android studio to process these data from the server. First, we need to understand the structure of the data we have. This will allow you to perfectly serialize and deserialize the data based on its object representation.
To do so, send a simple GET request to http://localhost:1337/api/tasks using Postman as follows:
To display these data to android, we will process the response given back by the server. Each task has a response as repressed in the above Postman test.
Here is the HTTP response body that you should always expect from the API whenever you send a GET request.
1{
2 "data": [
3 {
4 "id": 1,
5 "attributes": {
6 "title": "Create a Strapi Backend",
7 "description": "Build a task Api using the Strapi backend as the headless cms",
8 "done": false,
9 "createdAt": "2022-10-04T15:12:07.989Z",
10 "updatedAt": "2022-10-04T15:12:42.658Z",
11 "publishedAt": "2022-10-04T15:12:42.653Z"
12 }
13 }
14 [
15}
The response is represented using a nested JSON object. We need to parse this structure to android. From these data, we have an object array of data. We have two objects, the id and attributes. Inside attributes, we have a nested object of title, description, done, etc.
We need to parse them into android to get the nested JSON objects. To comfortably create the correct model for the above-nested object, always start creating the models from the innermost objects.
Therefore, the first model will contain the attributes nested object of title, description, done, etc. Create a new package and name it model.
Inside this package, create a new Java file Attributes and create a model to get the attributes nested object as follows:
1public class Attributes {
2
3 @SerializedName("title")
4 @Expose
5 private String title;
6
7 @SerializedName("description")
8 @Expose
9 private String description;
10
11 @SerializedName("done")
12 @Expose
13 private Boolean done;
14
15 public Attributes(String title, String description, Boolean done) {
16 this.title = title;
17 this.description = description;
18 this.done = done;
19 }
20
21 public boolean isDone() {
22 return done;
23 }
24
25 public String getTitle() {
26 return title;
27 }
28
29 public String getDescription() {
30 return description;
31 }
32}
The next object represents the id and attributes of the nested object. Go ahead and create a new Java class Tasks inside the model package and add the following:
1public class Tasks {
2
3 @SerializedName("id")
4 private int id;
5
6 @SerializedName("attributes")
7 private Attributes attributes;
8
9 public Tasks(int id, Attributes attributes) {
10 this.id = id;
11 this.attributes = attributes;
12 }
13
14 public int getId() {
15 return id;
16 }
17 public Attributes getAttribute() {
18 return attributes;
19 }
20}
This Tasks model represents the id values and the objects associated with the Attributes model we created above.
Finally, create a model to parse the object array of data. Inside the model package, create a new Java file DataResponse. This will represent an array list of data from the Tasks created above as follows:
1public class DataResponse {
2
3 @SerializedName("data")
4 @Expose
5 private List<Tasks> data = null;
6
7 public List<Tasks> getData() {
8 return data;
9 }
10}
Up to this point, we have the model needed to serialize objects and parse them to android.
Now we need a model to deserialization the Java Objects to a JSON object that the server can understand. This will help us send new data(POST) to the server.
Typically, if you send the data using Postman, the following is the request body you send to add a new Task:
1{
2 "data": {
3 "title": "This is title",
4 "description": "This is description"
5 }
6}
We need to represent the above body using android. Replicate this request body to make a POST request using Retrofit using Android. Go ahead and create a DataRequest Java class inside the model folder as follows:
1public class DataRequest {
2
3 @SerializedName("data")
4 Attributes attributes;
5
6 public DataRequest(String title, String description, Boolean done) {
7
8 this.attributes = new Attributes(title,description,done);
9 }
10}
To process these models to the backend server, let's add the API endpoints to Android.
Under the Api package, create a new Java class and name it ApiService. Make sure you create this class as an interface.
Here we will execute HTTP built-in methods such as GET, PUT, POST, and DELETE using Retrofit @GET, @POST, @PUT, and @DELETE annotations. Each method will have a bound path/route to execute the operation on the backend and map the route to the base_url created in the Retrofit client.
Go ahead and add the following interface to the ApiService file as follows:
1public interface ApiService {
2
3 @GET("api/tasks")
4 Call<DataResponse> getTasks(
5 );
6
7 @POST("api/tasks")
8 Call<DataRequest> createTask(
9 @Body DataRequest dataRequest
10 );
11
12 @PUT("api/tasks/{id}")
13 Call<DataRequest> updateTask(
14 @Path("id") int id,
15 @Body DataRequest dataRequest
16 );
17
18 @DELETE("api/tasks/{id}")
19 Call<DataRequest> deleteTask(
20 @Path("id") int id
21 );
22}
Note that each method executes based on the Data models we created. For example, to get Tasks from the server, DataResponse. This model has the response from the server that gets the array list of the Tasks from the response Object.
To add a task(POST), pass the DataRequest as the request body. This way, a request is sent to add a new task containing the right body that the server understands.
Updating a task involves sending new update data to the server. Hence, ensure the @Body is set to send the appropriate request to the server.
Methods such as PUT(update) and DELETE execute the @Path parameter that defines the id of the specific task that such operations are being executed.
To display these data to an android app. Let's first build some UI elements. To show data, we will use android RecyclerView and map each item to the screen using RecyclerView Adapter.
First, navigate to the layout folder and create a new XML file. Name it task_list.xml. Here we will create the view that we will use to display a single task as follows:
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.cardview.widget.CardView
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 xmlns:app="http://schemas.android.com/apk/res-auto"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content"
8 android:layout_margin="4dp"
9 android:id="@+id/card"
10 app:contentPadding="8dp"
11 app:cardCornerRadius="8dp"
12 app:cardElevation="4dp">
13
14 <androidx.constraintlayout.widget.ConstraintLayout
15 android:layout_width="match_parent"
16 android:layout_height="100dp"
17 android:layout_margin="4dp"
18 android:orientation="vertical">
19
20 <TextView
21 android:id="@+id/title"
22 android:layout_width="wrap_content"
23 android:layout_height="wrap_content"
24 android:textSize="20sp"
25 android:textStyle="bold"
26 app:layout_constraintStart_toStartOf="parent"
27 app:layout_constraintTop_toTopOf="parent"
28 tools:text="Title" />
29
30 <TextView
31 android:id="@+id/description"
32 android:layout_width="wrap_content"
33 android:layout_height="wrap_content"
34 android:layout_marginTop="12dp"
35 android:textStyle="italic"
36 app:layout_constraintStart_toStartOf="parent"
37 app:layout_constraintTop_toBottomOf="@id/title"
38 tools:text="Description" />
39
40 <TextView
41 android:id="@+id/done"
42 android:layout_width="wrap_content"
43 android:layout_height="wrap_content"
44 android:layout_marginEnd="4dp"
45 android:text="COMPLETED"
46 android:visibility="gone"
47 app:layout_constraintEnd_toEndOf="parent"
48 app:layout_constraintTop_toBottomOf="@+id/description" />
49 </androidx.constraintlayout.widget.ConstraintLayout>
50</androidx.cardview.widget.CardView>
Basically, each task will be displayed in a CardView. Here a card will have three TextViews, the title, description, and done. Note that TextView for displaying the done value has its visibility set as"gone". This means this value won't be displayed in the View. The value of done is a Boolean value. Therefore, when a Task is Done, we will update the done value from the default false to true and make the TextView visible to indicate a COMPLETED task.
The above layout only displays a single task. However, we have a list of multiple tasks. Here we will set a RecyclerView that will basically"Recycle" the above layout for each available task.
Navigate to the layout folder and update the activity_main.xml file as follows:
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 tools:context=".MainActivity">
8
9 <androidx.recyclerview.widget.RecyclerView
10 android:id="@+id/recyclerview"
11 android:layout_width="match_parent"
12 android:layout_height="match_parent"
13 app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
14 tools:listitem="@layout/task_list" />
15
16 <com.google.android.material.floatingactionbutton.FloatingActionButton
17 android:id="@+id/fab"
18 android:layout_width="wrap_content"
19 android:layout_height="wrap_content"
20 android:layout_gravity="bottom|end"
21 android:layout_marginEnd="16dp"
22 android:layout_marginBottom="16dp"
23 android:elevation="12dp"
24 android:foregroundGravity="center"
25 android:src="@drawable/ic_baseline_add_24"
26 app:backgroundTint="#14D18F"
27 app:fabSize="normal"
28 tools:ignore="ImageContrastCheck"/>
29</androidx.coordinatorlayout.widget.CoordinatorLayout>
The above RecyclerView represents a list of tasks that will be displayed here. Also, note we have added a floatingactionbutton to the layout. We will use the button to invoke the activity to add new Task items to the backend server we created.
Remember to create a new vector asset in your drawable folder for ic_baseline_add_24 used in the above FloatingActionButton.
Finally, we need a layout to input new data. We will execute this operation on a different activity. Therefore, go ahead and create a new Empty activity. Call it CreateTask.
This will create a file inside the layout directory. Go ahead and add the following EditText views and an Add Button to this file as follows:
1<?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:orientation="vertical"
8 android:padding="16dp"
9 tools:context=".CreateTask">
10
11 <TextView
12 android:layout_width="wrap_content"
13 android:layout_height="wrap_content"
14 android:layout_marginTop="16dp"
15 android:text="Add New a Task"
16 android:textSize="22sp"
17 android:textStyle="bold"
18 android:layout_gravity="center"
19 android:layout_marginBottom="32dp" />
20
21 <com.google.android.material.textfield.TextInputLayout
22 android:id="@+id/title"
23 android:layout_width="match_parent"
24 android:layout_marginBottom="8dp"
25 android:layout_height="wrap_content">
26 <EditText
27 android:id="@+id/enter_title"
28 android:layout_width="match_parent"
29 android:layout_height="wrap_content"
30 android:inputType="textCapWords"
31 android:hint="Enter Title"/>
32 </com.google.android.material.textfield.TextInputLayout>
33 <com.google.android.material.textfield.TextInputLayout
34 android:id="@+id/description"
35 android:layout_width="match_parent"
36 android:layout_height="wrap_content"
37 android:layout_marginTop="16dp"
38 android:layout_marginBottom="8dp">
39 <EditText
40 android:id="@+id/enter_description"
41 android:layout_width="match_parent"
42 android:layout_height="wrap_content"
43 android:maxLines="2"
44 android:inputType="text"
45 android:hint="Enter Description" />
46 </com.google.android.material.textfield.TextInputLayout>
47
48 <Button
49 android:id="@+id/add"
50 android:layout_width="184dp"
51 android:layout_marginTop="16dp"
52 android:layout_height="wrap_content"
53 android:layout_gravity="center"
54 android:text="Add Task" />
55</LinearLayout>
Excellent work! We now have the needed layouts ready. Let's now dive in and create the actual log of handling the logic of data handling from Strapi to android and vice versa using Retrofit.
To display the data, we need to create an Adapter that will map the data to the RecyclerView. Go ahead and create a new Java file and call it TasksAdapter.
1public class TasksAdapter {
2}
This class will extend to a RecyclerView Adapter as follows:
1public class TasksAdapter extends RecyclerView.Adapter<TasksAdapter.MyViewHolder>{
2
3}
Now go ahead and implement the necessary methods:
1public class TasksAdapter extends RecyclerView.Adapter<TasksAdapter.MyViewHolder>{
2
3 @NonNull
4 @Override
5 public TasksAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
6 return null;
7 }
8
9 @Override
10 public void onBindViewHolder(@NonNull TasksAdapter.MyViewHolder holder, int position) {
11
12 }
13
14 @Override
15 public int getItemCount() {
16 return 0;
17 }
18}
Right below the TasksAdapter adapter, define the following values and a constructor TasksAdapter for the Adapter context and the data we want to process.
1Context context;
2ApiService apiService;
3List<Tasks> tasksList;
4String stringTitle;
5String stringDes;
6
7public TasksAdapter(Context context, List<Tasks> tasksList) {
8 this.tasksList = tasksList;
9 this.context = context;
10}
Inside the onCreateViewHolder, we will load the task_list.xml layout.
1@NonNull
2@Override
3public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
4
5 View view;
6 LayoutInflater layoutInflater = LayoutInflater.from(context);
7 view = layoutInflater.inflate(R.layout.task_list,parent, false);
8 return new MyViewHolder(view);
9}
Create MyViewHolder that extends to the RecyclerView ViewHolder as follows.
1public static class MyViewHolder extends RecyclerView.ViewHolder{
2
3 TextView title;
4 TextView description;
5 TextView isdone;
6 CardView card;
7
8 public MyViewHolder(@NonNull View itemView) {
9 super(itemView);
10 title = itemView.findViewById(R.id.title);
11 description = itemView.findViewById(R.id.description);
12 isdone = itemView.findViewById(R.id.done);
13 card = itemView.findViewById(R.id.card);
14 }
15}
Here, we will load the views from the task_list.xml based on their ids. This view represents a single task defined in the task_list.xml file.
Update the getItemCount() method as follows:
1@Override
2public int getItemCount() {
3 return tasksList.size();
4}
This will return the size(number) of the available Tasks.
Inside this adapter, we will handle the updating and deleting methods. To update a Task, create an update() as follows:
Here we are passing the value of the task's id to be updated.
1private void update(int id){
2
3 apiService = ApiClient.ApiConnection().create(ApiService.class);
4
5 DataRequest data = new DataRequest(stringTitle,stringDes,true);
6
7 Call<DataRequest> call = apiService.updateTask(id, data);
8 call.enqueue(new Callback<DataRequest>() {
9 @Override
10 public void onResponse(@NonNull Call<DataRequest> call, @NonNull Response<DataRequest> response) {
11
12 Toast.makeText(context, "Task Updated", Toast.LENGTH_SHORT).show();
13 }
14
15 @Override
16 public void onFailure(@NonNull Call<DataRequest> call, @NonNull Throwable t) {
17
18 Toast.makeText(context, t.getMessage(), Toast.LENGTH_LONG).show();
19 }
20 });
21}
Likewise, do the same to delete a Task:
1private void delete(int id){
2
3 //Call the interface
4 apiService = ApiClient.ApiConnection().create(ApiService.class);
5 Call<DataRequest> call = apiService.deleteTask(id);
6 call.enqueue(new Callback<DataRequest>() {
7 @Override
8 public void onResponse(@NonNull Call<DataRequest> call, @NonNull Response<DataRequest> response) {
9
10 Toast.makeText(context, "Task Deleted", Toast.LENGTH_SHORT).show();
11 }
12
13 @Override
14 public void onFailure(@NonNull Call<DataRequest> call, @NonNull Throwable t) {
15
16 Toast.makeText(context, t.getMessage(), Toast.LENGTH_LONG).show();
17 }
18 });
19}
To map the data to the RecyclerView, head over to the onBindViewHolder method and make the following changes.
1@SuppressLint("ResourceAsColor")
2@Override
3public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
4
5 Tasks item = tasksList.get(position);
6
7 holder.title.setText(item.getAttribute().getTitle());
8 holder.description.setText(item.getAttribute().getDescription());
9
10 if(item.getAttribute().()){
11 holder.isdone.setVisibility(View.VISIBLE);
12 holder.card.setCardBackgroundColor(Color.parseColor("#14D18F"));
13 }
14}
This will get the data and map it to the respective view. For isDone, we are checking if the value is true or false. If the value is true(represented by isDone), set the TextView for this element as visible and change the card background colour of that specific Task.
Still, inside the onBindViewHolder method, let's handle the updating and deleting of a task. Here, we will add an OnClickListener to each card. When clicked, we will create a dialog with delete and update buttons. When an update button is clicked, the Task will be updated to true to indicate the task is done.
First, add the following string values that will be sent alongside the update parameters inside the onBindViewHolder.
1stringTitle = item.getAttribute().getTitle();
2stringDes = item.getAttribute().getDescription();
And OnClickListener listener to the card and a dialog as follows:
1holder.card.setOnClickListener(new View.OnClickListener() {
2 @Override
3 public void onClick(View view) {
4
5 AlertDialog.Builder builder = new AlertDialog.Builder(context);
6 builder.setTitle("Introducing work from Everywhere");
7 builder.setMessage("Track your Tasks. What do you want to do with this Task?");
8 builder.setCancelable(false);
9 builder.setPositiveButton("Done", new DialogInterface.OnClickListener() {
10 @Override
11 public void onClick(DialogInterface dialog, int which) {
12 update(item.getId());
13 dialog.dismiss();
14 ((MainActivity) context).getAll();
15 }
16 });
17
18 builder.setNegativeButton("Delete", new DialogInterface.OnClickListener() {
19 @Override
20 public void onClick(DialogInterface dialog, int which) {
21 delete(item.getId());
22 dialog.dismiss();
23 ((MainActivity) context).getAll();
24 }
25 });
26
27 builder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
28 @Override
29 public void onClick(DialogInterface dialog, int i){
30 dialog.dismiss();
31 }
32 });
33 builder.show();
34 }
35});
Now we are ready to display them to the MainActivity.
Navigate to MainActivity and make some changes to display the Tasks. Inside the MainActivity class, add the following values:
1ApiService apiService;
2List<Tasks> tasksList;
3RecyclerView recyclerView;
4FloatingActionButton floatingActionButton;
Inside onCreate(), add the following to inflate the View:
1recyclerView = findViewById(R.id.recyclerview);
2floatingActionButton = findViewById(R.id.fab);
3tasksList = new ArrayList<>();
Attach an OnClickListener to the FloatingActionButton to open the CreateTask Activity.
1floatingActionButton.setOnClickListener(new View.OnClickListener() {
2 @Override
3 public void onClick(View v) {
4 startActivity(new Intent(MainActivity.this, CreateTask.class));
5 }
6});
Create a getAll() to get the Tasks and display them in the RecyclerView Adapter as follows:
1public void getAll() {
2 //Call the interface
3 apiService = ApiClient.ApiConnection().create(ApiService.class);
4
5 Call<DataResponse> call = apiService.getTasks();
6 call.enqueue(new Callback<DataResponse>() {
7 @Override
8 public void onResponse(@NonNull Call<DataResponse> call, @NonNull Response<DataResponse> response) {
9
10 DataResponse dataResponse = response.body();
11 assert dataResponse != null;
12 tasksList = new ArrayList<>(dataResponse.getData());
13 dataView(tasksList);
14 }
15
16 @Override
17 public void onFailure(@NonNull Call<DataResponse> call, @NonNull Throwable t) {
18 Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
19 }
20 });
21}
Create a dataView method to load the list of tasks to the Adapter.
1private void dataView(List<Tasks> tasks) {
2 TasksAdapter tasksAdapter = new TasksAdapter(this,tasks);
3 recyclerView.setLayoutManager(new LinearLayoutManager(this));
4 recyclerView.setAdapter(tasksAdapter);
5}
Now go ahead and call the getAll() method inside onCreate(). Likewise, this application involves other operations. Whenever the application Resumes the MainActivity, we want to always call the getAll() method. Go ahead and implement an onResume() as follows:
1@Override
2protected void onResume() {
3 super.onResume();
4 getAll();
5}
Let's test the application and see what we have up to this point. Run the application using your android emulator to test. This may not work on your real Device. The server hosting the data is running locally. Thus we can only access the data using the locally created android emulator.
You should get the above screen with the list of Tasks we added earlier. When you click each Item/Card, a dialog will be launched as follows:
If you click delete, that specific item will be deleted from the server, and the android UI will be updated with the new list of available tasks. The update button will update the task to true. Then change the background colour and set the task as completed.
If you click FloatingActionButton, the CreateTask activity will be launched. Let's go ahead and implement the log to add a new task inside the CreateTask activity.
Inside the CreateTask activity, add the following variables:
1String title, description;
2EditText editTextTitle, editTextDes;
3Button AddTask;
4ApiService apiService;
Inside the onCreate() method add the following:
1editTextTitle = findViewById(R.id.enter_title);
2editTextDes = findViewById(R.id.enter_description);
3AddTask = findViewById(R.id.add);
Add an OnClickListener to the button as follows:
1AddTask.setOnClickListener(new View.OnClickListener() {
2 @Override
3 public void onClick(View v) {
4
5 title = editTextTitle.getText().toString();
6 description = editTextDes.getText().toString();
7
8 if (title.trim().equals("")){
9 editTextDes.setError("Title is required");
10 }else if(description.trim().equals("")){
11 editTextDes.setError("Description is required");
12 }else{
13 NewTask(title, description);
14 }
15 }
16});
When the text is added to the Input field, we will save the individual to Strings, i.e. title and description.
Here we are also checking the form validation to ensure the form inputs are not empty. If Empty, an error message will be shown. If the input fields are not empty, we will execute a method to add a new Task. This will send a request to the server with the title and description values.
Go ahead and create the NewTask() method as follows:
1private void NewTask(String title, String description){
2 //Call the interface
3
4 apiService = ApiClient.ApiConnection().create(ApiService.class);
5 // passing data from our text fields to our modal class.
6 DataRequest modal = new DataRequest(title,description, false);
7
8 // calling a method to create a post and passing our modal class.
9 Call<DataRequest> call = apiService.createTask(modal);
10 call.enqueue(new Callback<DataRequest>() {
11 @Override
12 public void onResponse(@NonNull Call<DataRequest> call, @NonNull Response<DataRequest> response) {
13 // this method is called when we get a response from our API.
14
15 Toast.makeText(getApplicationContext(), response.message(), Toast.LENGTH_LONG).show();
16
17 finish();
18 }
19
20 @Override
21 public void onFailure(@NonNull Call<DataRequest> call, @NonNull Throwable t) {
22 Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
23 }
24 });
25}
Re-run the application to test if this is working as expected.
Go ahead and fill in new data for a new Task as follows:
Click the Add Task button. The new task will be added and displayed to your application.
All these changes should be visible in your Strapi backend application.
Strapi is a great content manager for your application. It allows you to model data and consume it with a fronted framework of your choice. I hope this Android Strapi tutorial was helpful.
Happy coding!
Joseph is fluent in Android Mobile Application Development and has a lot of passion for Fullstack web development. He loves to write code and document them in blogs to share ideas.