Understanding Android Architecture Components  By Example

Android Architecture Components

A new collection of libraries that help you design robust, testable, and maintainable apps.developer.android.com

We will see three main components :

  1. Room
  2. ViewModel
  3. LiveData

So first, let’s find out what these components actually are. Then, we’ll learn how we can use them.

Get Source code From HERE.

By creating an Event app I will demonstrate you the working of  Android Architecture ComponentsRoom, ViewModel, LiveData.

 

Room Persistence Library

Major problem with SQLite usage is

  • There is no compile-time verification of raw SQL queries.For example if you write a SQL query with a wrong column name that does not exist in real database then it will give exception during run time and you can not capture this issue during compile time.

 

  • As your schema changes you need to update the affected SQL queries manually. This process can be time consuming and error prone.

 

  • You need to use lots of boilerplate code to convert between SQL queries and Java data objects.

 

Room takes care of these concerns for you while providing an abstraction layer over SQLite.

There are three major components in Room:

Entity : A class annotated with the @Entity  annotation is mapped to a table in database. Every entity is persisted in its own table and every field in class represents the column name.

  • tableName attribute is used to define the name of the table
  • Every entity class must have at-least one Primary Key  field, annotated with @PrimaryKey
  • Fields in entity class can be annotated with @ColumnInfo(name = “name_of_column”) annotation to give specific column names
  • If you don’t want to include some field for the table but want to keep it in your model class than annotate those field with @Ignore
  • If you want your data to get stored in some other format than that of the type of variable you defined in your model class than create another class add your conversion logic there and annotate those methods with @TypeConverter than add that class to your database class using annotation @TypeConverters.

 

@Entity(tableName = TABLE_NAME)
public class Event {


    public static final String TABLE_NAME = "events";


    @PrimaryKey(autoGenerate = true)
    private int id;

    private String title;
    private String description;

    @TypeConverters(DateConverter.class)
    private Date date;

    public Event() {

    }

    @Ignore
    public Event(int id, String title, String description) {
        this.id = id;
        this.title = title;
        this.description = description;
        this.date = date;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }


    public void setTitle(String title) {
        this.title = title;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public String getTitle() {
        return title;
    }

 DAOData Access Object  is either be an interface or an abstract class annotated with @Doa annotation, containing all the methods to define the operations to be performed on data. The methods can be annotated with

Note:The result of SQLite queries are composed into cursor object, DAO methods abstract the conversion of cursor to Entity objects and vice-versa.

@Dao
public interface EventDao {

    @Query("SELECT * FROM " + Event.TABLE_NAME)
    LiveData<List<Event>> getEvents();

    @Insert(onConflict = REPLACE)
    void addEvent(Event event);

    @Delete
    void deleteEvent(Event event);

    @Update(onConflict = REPLACE)
    void updateEvent(Event event);

}

In case there are conflicts during such manipulation operations, we have to specify a conflict strategy too. In our example, we are using REPLACE. It means that the conflicting entry will be replaced by the current entry.

Database : Database is a container for tables. An abstract class annotated with @Database  annotation is used to create a database with given name along with database version.

  • version = intValueForDBVersion is used to define the database version
  • entities = {EntityClassOne.class, ….} is used to define list of entities for database

 

@Database(entities = {Event.class}, version = 1)
public abstract class EventDatabase extends RoomDatabase {
    private static final String DB_NAME = "Event_Database.db";
    private static EventDatabase INSTANCE;


    public static EventDatabase getEventDatabase(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context, EventDatabase.class, DB_NAME).build();

        }
        return INSTANCE;
    }

    public static void destroyInstance() {
        INSTANCE = null;
    }

    public abstract EventDao eventDao();
}

ViewModel

The ViewModel class is designed to hold and manage UI-related data in a life-cycle conscious way. This allows data to survive configuration changes such as screen rotations.

Previously, you might have used onRetainNonConfigurationInstance to save this data during a configuration change and unpack it on the other end.

The ViewModel exists  when  you first request a ViewModel (usually in the onCreate the Activity) until the Activity is finished and destroyed. onCreate may be called several times during the life of an Activity, such as when the app is rotated, but the ViewModel survives throughout.

There are two steps to setting up and using a ViewModel:

Step 1:  Create a ViewModel class

public class EventViewModel extends AndroidViewModel {

    private EventDatabase eventDatabase;

    private LiveData<List<Event>> eventList;

    public EventViewModel(@NonNull Application application) {
        super(application);

        eventDatabase = EventDatabase.getEventDatabase(this.getApplication());

        eventList = eventDatabase.eventDao().getEvents();
    }

    public LiveData<List<Event>> getEventList() {
        return eventList;
    }

    public EventDatabase getEventDatabase() {
        return eventDatabase;
    }

}

Step 2:  Set up communications between your ViewModel and your UI controller and Use the ViewModel in your UI Controller.

private EventDatabase eventDatabase;
private EventViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);

 
 viewModel = ViewModelProviders.of(this).get(EventViewModel.class);

 eventDatabase = viewModel.getEventDatabase();

 viewModel.getEventList();

The first time the ViewModelProviders.of method is called by MainActivity, it creates a new ViewModel instance. When this method is called again, which happens whenever onCreate is called, it will return the pre-existing ViewModel associated with the specific MainActivity. This is what preserves the data.

LiveData

LiveData is an observable lifecycle-aware data holder class. Your UI code subscribes to changes in the underlying data, tied into a LifecycleOwner, and LiveData makes sure the observer:

  • Gets updates to the data while the Lifecycle is in an active state (STARTED or RESUMED)
  • Is removed when the LifecycleOwner is destroyed
  • Gets up-to-date data when the LifecycleOwnerrestarts due to a configuration change or is restarted from the back stack

Create LiveData objects

Inside Room DAO I have defined List<Event> as LiveData object.

@Query("SELECT * FROM " + Event.TABLE_NAME)
LiveData<List<Event>> getEvents();

Observe LiveData objects

viewModel.getEventList().observe(this, new Observer<List<Event>>() {
    @Override
    public void onChanged(@Nullable List<Event> eventList) {
        events = eventList;

        adapter = new EventsAdapter(events, getApplicationContext());

        recyclerView.setAdapter(adapter);

        checkListEmptyOrNot();
    }
});

Whenever there is a change in the data, the onChanged() callback is executed and we get the new data.We can update the UI accordingly.

Creating New Project

1.In Android Studio, go to File New Project and fill all the details required to create a new project. When it prompts to select a default activity, select Blank Activity and proceed.

2.Open build.gradle and add Room ,Livedata,Viewmodel dependency and rebuild the project..

Build.gradle

dependencies {
    
    //recyclerview
    compile 'com.android.support:recyclerview-v7:26.1.0'

    //room library
    compile 'android.arch.persistence.room:runtime:1.0.0'
    annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'

    //livedata and viewmodel
    compile 'android.arch.lifecycle:extensions:1.0.0'

    
}

3.  For Room we will create the model,Data Access Object and the Database.

Creating the Model

Event.Java

package com.example.lenovo.eventapp.entity;

import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.PrimaryKey;
import android.arch.persistence.room.TypeConverters;

import com.example.lenovo.eventapp.db.DateConverter;

import java.util.Date;

import static com.example.lenovo.eventapp.entity.Event.TABLE_NAME;


@Entity(tableName = TABLE_NAME)
public class Event {


    public static final String TABLE_NAME = "events";


    @PrimaryKey(autoGenerate = true)
    private int id;

    private String title;
    private String description;
    @TypeConverters(DateConverter.class)
    private Date date;

    public Event() {

    }

    @Ignore
    public Event(int id, String title, String description) {
        this.id = id;
        this.title = title;
        this.description = description;
        this.date = date;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }


    public void setTitle(String title) {
        this.title = title;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public String getTitle() {
        return title;
    }


    @Override
    public String toString() {
        return "Event{" +
                "id=" + id +
                ", name='" + title + '\'' +
                ", description='" + description + '\'' +
                ", date='" + date + '\'' +
                '}';
    }

}

Data Access Object

EventDao.Java

package com.example.lenovo.eventapp.dao;

import android.arch.lifecycle.LiveData;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.Query;
import android.arch.persistence.room.Update;

import com.example.lenovo.eventapp.entity.Event;

import java.util.List;

import static android.arch.persistence.room.OnConflictStrategy.REPLACE;


@Dao
public interface EventDao {

    @Query("SELECT * FROM " + Event.TABLE_NAME)
    LiveData<List<Event>> getEvents();

    @Insert(onConflict = REPLACE)
    void addEvent(Event event);

    @Delete
    void deleteEvent(Event event);

    @Update(onConflict = REPLACE)
    void updateEvent(Event event);

}

Creating the database

EventDatabase.Java

package com.example.lenovo.eventapp.db;

import android.arch.persistence.room.Database;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.RoomDatabase;
import android.content.Context;

import com.example.lenovo.eventapp.dao.EventDao;
import com.example.lenovo.eventapp.entity.Event;


@Database(entities = {Event.class}, version = 1)
public abstract class EventDatabase extends RoomDatabase {
    private static final String DB_NAME = "Event_Database.db";
    private static EventDatabase INSTANCE;


    public static EventDatabase getEventDatabase(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context, EventDatabase.class, DB_NAME).build();

        }
        return INSTANCE;
    }

    public static void destroyInstance() {
        INSTANCE = null;
    }

    public abstract EventDao eventDao();
}

4.  SQL cannot store data types like Date by default. That’s why we need a way to convert it into a compatible data type to store it in the database. We use the @TypeConverters to specify the converter for the date attribute. So to help us with this conversion, we’ll create a class called  DateConverter.

DateConverter.Java

package com.example.lenovo.eventapp.db;

import android.arch.persistence.room.TypeConverter;

import java.util.Date;

public class DateConverter {

    @TypeConverter
    public static Date toDate(Long timestamp) {
        return timestamp == null ? null : new Date(timestamp);
    }

    @TypeConverter
    public static Long toTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }
}

5.  Open activity_main layout and write the below code.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view_list_events"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </android.support.v7.widget.RecyclerView>

    <TextView
        android:id="@+id/tv_no_events_found"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fontFamily="sans-serif"
        android:gravity="center"
        android:text="NO EVENTS FOUND!"
        android:textColor="@color/colorPrimaryDark"
        android:textSize="25sp"
        android:visibility="gone" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginRight="16dp"
        app:elevation="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:srcCompat="@drawable/ic_add_black_24dp" />
</android.support.constraint.ConstraintLayout>

6.  Open event_dialog.xml and write the below code.Here this layout represents the view set up for alertdialog.

event_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="8dp">

    <TextView
        android:id="@+id/dialog_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:fontFamily="sans-serif-medium"
        android:text="New Event"
        android:textColor="@color/colorAccent"
        android:textSize="22sp"
        android:textStyle="normal" />

    <EditText
        android:id="@+id/edt_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:fontFamily="sans-serif"
        android:gravity="top"
        android:hint="Title"
        android:inputType="textCapSentences|textMultiLine"
        android:lines="2"
        android:textColor="@android:color/black"
        android:textColorHint="@color/colorPrimaryDark"
        android:textSize="18sp" />

    <EditText
        android:id="@+id/edt_discription"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:fontFamily="sans-serif"
        android:gravity="top"
        android:hint="Discription"
        android:inputType="textCapSentences|textMultiLine"
        android:lines="3"
        android:textColor="@android:color/black"
        android:textColorHint="@color/colorPrimaryDark"
        android:textSize="18sp" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tv_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_marginLeft="8dp"
            android:fontFamily="sans-serif"
            android:textColor="@android:color/black"
            android:textSize="18sp" />

        <Button
            android:id="@+id/btn_setdate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:background="@color/colorAccent"
            android:text="set"
            android:textColor="@android:color/white"
            android:textStyle="bold" />

    </RelativeLayout>
</LinearLayout>

7.  Open list_item_event.xml and write the below code.Here this layout represents the recyclerview list item.

 list_item_event.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:padding="8dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="sans-serif"
            android:textColor="@android:color/holo_orange_dark"
            android:textSize="20dp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/tv_discription"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            android:fontFamily="sans-serif"
            android:textColor="@android:color/holo_green_light"
            android:textSize="16dp" />
    </LinearLayout>


    <TextView
        android:id="@+id/tv_timestamp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:fontFamily="sans-serif"
        android:textColor="@color/colorAccent"
        android:textSize="16dp"
        android:textStyle="bold" />


</RelativeLayout>

8.  Now since we will be displaying a list of items, we need a RecyclerView. So first, let’s create an adapter.

EventsAdapter.Java

package com.example.lenovo.eventapp;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.example.lenovo.eventapp.entity.Event;

import java.util.List;

public class EventsAdapter extends RecyclerView.Adapter<EventsAdapter.EventViewHolder> {
    private final Context context;
    private List<Event> items;


    public EventsAdapter(List<Event> items, Context context) {
        this.items = items;
        this.context = context;

    }

    public class EventViewHolder extends RecyclerView.ViewHolder {
        TextView titleTextView;
        TextView dateTextView;
        TextView descriptionTextView;

        EventViewHolder(View v) {
            super(v);
            titleTextView = (TextView) v.findViewById(R.id.tv_title);
            dateTextView = (TextView) v.findViewById(R.id.tv_timestamp);
            descriptionTextView = (TextView) v.findViewById(R.id.tv_discription);

        }
    }

    @Override
    public EventViewHolder onCreateViewHolder(ViewGroup parent,
                                              int viewType) {
        View v = LayoutInflater.from(context)
                .inflate(R.layout.list_item_event, parent, false);

        return new EventViewHolder(v);
    }

    @Override
    public void onBindViewHolder(EventViewHolder holder, int position) {
        Event item = items.get(position);
        holder.titleTextView.setText(item.getTitle());
        holder.descriptionTextView.setText(item.getDescription());
        holder.dateTextView.setText(item.getDate().toLocaleString().substring(0, 12));
    }


    @Override
    public int getItemCount() {

        return items.size();
    }


}

9.  For recyclerview I have created custom onitemclicklistener and the code is given below.

RecyclerTouchListener.Java

package com.example.lenovo.eventapp;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

    private ClickListener clicklistener;
    private GestureDetector gestureDetector;

    public RecyclerTouchListener(Context context, final RecyclerView recycleView, final ClickListener clicklistener) {

        this.clicklistener = clicklistener;
        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View child = recycleView.findChildViewUnder(e.getX(), e.getY());
                if (child != null && clicklistener != null) {
                    clicklistener.onLongClick(child, recycleView.getChildAdapterPosition(child));
                }
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        View child = rv.findChildViewUnder(e.getX(), e.getY());
        if (child != null && clicklistener != null && gestureDetector.onTouchEvent(e)) {
            clicklistener.onClick(child, rv.getChildAdapterPosition(child));
        }

        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {

    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }

    public interface ClickListener {
        void onClick(View view, int position);

        void onLongClick(View view, int position);
    }
}

Create the AndroidViewModel

EventViewModel.Java

package com.example.lenovo.eventapp;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;
import com.example.lenovo.eventapp.db.EventDatabase;
import com.example.lenovo.eventapp.entity.Event;
import java.util.List;

public class EventViewModel extends AndroidViewModel {

    private EventDatabase eventDatabase;

    private LiveData<List<Event>> eventList;

    public EventViewModel(@NonNull Application application) {
        super(application);

        eventDatabase = EventDatabase.getEventDatabase(this.getApplication());

        eventList = eventDatabase.eventDao().getEvents();
    }

    public LiveData<List<Event>> getEventList() {
        return eventList;
    }

    public EventDatabase getEventDatabase() {
        return eventDatabase;
    }

}

10.  Now create  MainActivity.Java that extends AppCompatActivity to display a list of events.

MainActivity.Java

package com.example.lenovo.eventapp;

import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.example.lenovo.eventapp.db.EventDatabase;
import com.example.lenovo.eventapp.entity.Event;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;


public class MainActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener {

    private RecyclerView recyclerView;
    private Date date;
    private DatePickerDialog datePickerDialog;
    private Calendar calendar;
    private EventDatabase eventDatabase;
    private EventViewModel viewModel;
    private TextView noEventsFound;
    private FloatingActionButton fab;
    private EventsAdapter adapter;
    private List<Event> events = new ArrayList<>();
    private TextView tv_date;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        calendar = Calendar.getInstance();

        datePickerDialog = new DatePickerDialog(this, this, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));

        viewModel = ViewModelProviders.of(this).get(EventViewModel.class);

        eventDatabase = viewModel.getEventDatabase();

        noEventsFound = (TextView) findViewById(R.id.tv_no_events_found);

        recyclerView = (RecyclerView) findViewById(R.id.recycler_view_list_events);

        recyclerView.setItemAnimator(new DefaultItemAnimator());

        recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext(), LinearLayoutManager.VERTICAL, false));


        viewModel.getEventList().observe(this, new Observer<List<Event>>() {
            @Override
            public void onChanged(@Nullable List<Event> eventList) {
                events = eventList;

                adapter = new EventsAdapter(events, getApplicationContext());

                recyclerView.setAdapter(adapter);

                checkListEmptyOrNot();
            }
        });

        //shows NO EVENTS FOUND when list is empty
        checkListEmptyOrNot();

        recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {

            }

            @Override
            public void onLongClick(View view, int position) {

                //show Alertdialog to edit or update the event
                showActionsDialog(position);
            }
        }));

        //add listener on FloatingActionButton to add event
        fab = (FloatingActionButton) findViewById(R.id.fab_add);

        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //adding  new event
                showEventDialog(false, null, -1);
            }
        });
    }

    private class DatabaseAsync extends AsyncTask<Object, Void, Void> {

        private EventDatabase db;

        DatabaseAsync(EventDatabase eventDatabase) {
            db = eventDatabase;
        }

        @Override
        protected Void doInBackground(Object... params) {

            Boolean shouldUpdate = (Boolean) params[0];
            int position = (int) params[1];
            String title = (String) params[2];
            String detail = (String) params[3];
            Date date = (Date) params[4];

            //check whether to add add or update event
            if (shouldUpdate != null) {
                //update event
                if (shouldUpdate) {
                    Event event = events.get(position);
                    event.setTitle(title);
                    event.setDescription(detail);
                    event.setDate(date);

                    //update event into the database
                    db.eventDao().updateEvent(event);


                } else {
                    //add event
                    Event event = new Event();
                    event.setTitle(title);
                    event.setDescription(detail);
                    event.setDate(date);

                    //add event into the database
                    db.eventDao().addEvent(event);
                }

            } else {
                //delete event
                if (position != -1) {
                    Event event = events.get(position);

                    //delete event from database
                    db.eventDao().deleteEvent(event);
                }
            }
            return null;

        }
    }

    private void showActionsDialog(final int position) {
        CharSequence colors[] = new CharSequence[]{"Edit", "Delete"};

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Choose option");
        builder.setItems(colors, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (which == 0) {

                    //show Alertdialog to update the event
                    showEventDialog(true, events.get(position), position);
                } else {

                    //delete event from database
                    deleteEvent(position);
                }
            }
        });
        builder.show();
    }

    private void deleteEvent(int position) {

        new DatabaseAsync(eventDatabase).execute(null, position, null, null, null);
    }

    private void showEventDialog(final Boolean shouldUpdate, final Event event, final int position) {

        LayoutInflater layoutInflater = LayoutInflater.from(getApplicationContext());
        View view = layoutInflater.inflate(R.layout.event_dialog, null);

        AlertDialog.Builder alertDialogBuilderUserInput = new AlertDialog.Builder(this);
        alertDialogBuilderUserInput.setView(view);

        TextView dialog_title = (TextView) view.findViewById(R.id.dialog_title);

        final EditText edt_title = (EditText) view.findViewById(R.id.edt_title);

        final EditText edt_discription = (EditText) view.findViewById(R.id.edt_discription);

        tv_date = (TextView) view.findViewById(R.id.tv_date);

        Button btn_setdate = (Button) view.findViewById(R.id.btn_setdate);

        //add listener to button to open datepickerdialog
        btn_setdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                //open datepickerdialog
                datePickerDialog.show();
            }
        });

        dialog_title.setText(!shouldUpdate ? "New Event" : "Edit Event");

        //in case of update we want all the
        //fields  to be set bydefault with text
        if (shouldUpdate && event != null) {
            edt_title.setText(event.getTitle());
            edt_discription.setText(event.getDescription());
            tv_date.setText(event.getDate().toLocaleString().substring(0, 11));
        }

        alertDialogBuilderUserInput
                .setCancelable(false)
                .setPositiveButton(shouldUpdate ? "update" : "add", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogBox, int id) {

                    }
                })
                .setNegativeButton("cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialogBox, int id) {
                                dialogBox.cancel();
                            }
                        });

        final AlertDialog alertDialog = alertDialogBuilderUserInput.create();

        alertDialog.show();

        alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // Show toast message when no text is entered
                if (TextUtils.isEmpty(edt_title.getText().toString()) && !TextUtils.isEmpty(edt_discription.getText().toString())) {
                    Toast.makeText(MainActivity.this, "Enter title!", Toast.LENGTH_SHORT).show();
                } else if (!TextUtils.isEmpty(edt_title.getText().toString()) && TextUtils.isEmpty(edt_discription.getText().toString())) {
                    Toast.makeText(MainActivity.this, "Enter description!", Toast.LENGTH_SHORT).show();
                } else if (TextUtils.isEmpty(edt_title.getText().toString()) && TextUtils.isEmpty(edt_discription.getText().toString())) {
                    Toast.makeText(MainActivity.this, "Enter title and description!", Toast.LENGTH_SHORT).show();
                } else {
                    alertDialog.dismiss();
                }

                //Update or add data into the database only when both field are  filled(i.e title,description)
                if (!TextUtils.isEmpty(edt_title.getText().toString()) && !TextUtils.isEmpty(edt_discription.getText().toString())) {

                    // check if user updating note

                    if (shouldUpdate && event != null) {
                        // update event
                        new DatabaseAsync(eventDatabase).execute(shouldUpdate, position, edt_title.getText().toString(), edt_discription.getText().toString(), date);


                    } else {
                        // create new event
                        new DatabaseAsync(eventDatabase).execute(shouldUpdate, -1, edt_title.getText().toString(), edt_discription.getText().toString(), date);


                    }
                }
            }
        });


    }

    @Override
    public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month);
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        date = calendar.getTime();
        tv_date.setText(date.toLocaleString().substring(0, 12));

    }


    public void checkListEmptyOrNot() {
        if (events.isEmpty())
            noEventsFound.setVisibility(View.VISIBLE);
        else
            noEventsFound.setVisibility(View.GONE);
    }


}


When you run the app it will look like this:

                          

 

Related posts:

Leave a Reply