public class AttachmentAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {


    //DATA
    private List<Attachment> mAttachments;
    private Activity mActivity;
    private LayoutInflater mInflater;
    private ShowAttachmentHintListener showAttachmentHintListener;
    private AttachmentDataUpdatedListener attachmentDataUpdatedListener;
    private boolean mRealTimeDataPersistence;

    public AttachmentAdapter(Activity activity, List<Attachment> attachments, boolean realTimeDataPersistence) {
        mActivity = activity;
        mAttachments = attachments;
        mRealTimeDataPersistence = realTimeDataPersistence;
        mInflater = LayoutInflater.from(activity);
    }


    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        AttachmentType attachmentType = AttachmentType.values()[viewType];
        switch (attachmentType) {
            case TEXT:
                return new TextAttachmentViewHolder(mInflater.inflate(R.layout.list_item_attachment_text, parent, false));
            case LIST:
                return new ListAttachmentViewHolder(mInflater.inflate(R.layout.list_item_attachment_list, parent, false));
            case LINK:
                return new LinkAttachmentViewHolder(mInflater.inflate(R.layout.list_item_attachment_link, parent, false));
            case AUDIO:
                return new AudioAttachmentViewHolder(mInflater.inflate(R.layout.list_item_attachment_audio, parent, false));
            case IMAGE:
                return new ImageAttachmentViewHolder(mInflater.inflate(R.layout.list_item_attachment_image, parent, false));
            default:
                throw new InvalidParameterException("Wrong viewType passed to onCreateViewHolder in AttachmentAdapter");
        }
    }

    @Override
    public int getItemViewType(int position) {
        return mAttachments.get(position).getType().ordinal();
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Attachment current = mAttachments.get(position);

        switch (current.getType()) {
            case TEXT:
                TextAttachmentViewHolder tvh = (TextAttachmentViewHolder) holder;
                tvh.setData(this, mActivity, (TextAttachment)current, position, mRealTimeDataPersistence);
                tvh.setListeners();
                break;
            case LIST:
                ListAttachmentViewHolder listvh = (ListAttachmentViewHolder) holder;
                listvh.setData(this, mActivity, (ListAttachment) current, position, mRealTimeDataPersistence);
                listvh.setListeners();
                break;

            case LINK:
                LinkAttachmentViewHolder lvh = (LinkAttachmentViewHolder) holder;
                lvh.setData(this, mActivity, (LinkAttachment)current, position, mRealTimeDataPersistence);
                lvh.setListeners();
                break;

            case AUDIO:
                AudioAttachmentViewHolder avh = (AudioAttachmentViewHolder) holder;
                avh.setData(this, mActivity, (AudioAttachment) current, position, mRealTimeDataPersistence);
                //avh.setListeners();
                break;

            case IMAGE:
                ImageAttachmentViewHolder ivh = (ImageAttachmentViewHolder) holder;
                ivh.setData(this, mActivity, (ImageAttachment) current, position, mRealTimeDataPersistence);
                ivh.setListeners();
                break;

            default:
                throw new InvalidParameterException("Wrong viewType passed to onCreateViewHolder in AttachmentAdapter");
        }
    }

    @Override
    public int getItemCount() {
        return mAttachments.size();
    }


    public void deleteAttachment(int position) {            //Called from viewHolders when deleting an attachment

        Attachment attachment = mAttachments.get(position);
        switch (attachment.getType()) {
            case AUDIO:
                String audioFilename = ((AudioAttachment)attachment).getAudioFilename();
                FileUtil.deleteAudioAttachment(mActivity, audioFilename);
                break;
            case IMAGE:
                String imageFilename = ((ImageAttachment)attachment).getImageFilename();
                FileUtil.deleteImageAttachment(mActivity, imageFilename);
        }

        mAttachments.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, getItemCount());

        if(mRealTimeDataPersistence) {
            triggerAttachmentDataUpdatedListener();
        }

    }



    public void setAttachmentDataUpdatedListener(AttachmentDataUpdatedListener listener) {
        this.attachmentDataUpdatedListener = listener;
    }
    public void triggerAttachmentDataUpdatedListener() {       //Called from view holders when mRealTimeDataPersistence == true, to notify caller activity that the data has been updated
        if(attachmentDataUpdatedListener != null)
            attachmentDataUpdatedListener.onAttachmentDataUpdated();
    }
    public interface AttachmentDataUpdatedListener {
        void onAttachmentDataUpdated();
    }


    public void setShowAttachmentHintListener(ShowAttachmentHintListener listener) {
        this.showAttachmentHintListener = listener;
    }
    public void triggerShowAttachmentHintListener() {       //Called from view holders
        if(showAttachmentHintListener != null)
            showAttachmentHintListener.onShowAttachmentHint();
    }
    public interface ShowAttachmentHintListener {
        void onShowAttachmentHint();
    }
} public interface TaskDataInterface {
    void updateData();
} public enum TaskCategory {
    BUSINESS(R.string.task_category_business, R.drawable.icon_category_business),
    PERSONAL(R.string.task_category_personal, R.drawable.icon_category_personal),
    HEALTH(R.string.task_category_health, R.drawable.icon_category_health),
    REPAIRS(R.string.task_category_repairs, R.drawable.icon_category_repairs),
    SHOPPING(R.string.task_category_shopping, R.drawable.icon_category_shopping);

    private @StringRes int friendlyNameRes;
    private @DrawableRes int iconRes;

    TaskCategory(@StringRes int friendlyNameRes, @DrawableRes int iconRes) {
        this.friendlyNameRes = friendlyNameRes;
        this.iconRes = iconRes;

    }

    public int getFriendlyNameRes() {
        return friendlyNameRes;
    }
    public int getIconRes() {
        return iconRes;
    }

    public static List<String> getFriendlyValues(Context context) {
        List<String> friendlyValues = new ArrayList<>();
        for (TaskCategory tc : values()) {
            friendlyValues.add(context.getResources().getString(tc.friendlyNameRes));
        }
        return friendlyValues;
    }
} public class Task implements Serializable {

    private int id;
    private TaskStatus status;
    private String title;
    private String description;
    private TaskCategory category;
    private ReminderType reminderType;
    private Reminder reminder;
    private Calendar doneDate;
    private ArrayList<Attachment> attachments;

    public Task() {     //Empty constructor for creating new tasks
        this.id = -1;
        this.status = TaskStatus.UNPROGRAMMED;
        this.title = "";
        this.description = "";
        this.reminderType = ReminderType.NONE;
        this.attachments = new ArrayList<>();
    }

    public Task(@NonNull TaskStatus status, @NonNull String title, @NonNull String description, @NonNull TaskCategory category, @NonNull ReminderType reminderType, @Nullable Reminder reminder, @Nullable Calendar doneDate) {
        this.status = status;
        this.title = title;
        this.description = description;
        this.category = category;
        this.reminderType = reminderType;
        this.reminder = reminder;
        this.doneDate = doneDate;
        this.attachments = new ArrayList<>();
    }

    public Task(int id, @NonNull TaskStatus status, @NonNull String title, @NonNull String description, @NonNull TaskCategory category, @NonNull ReminderType reminderType, @Nullable Reminder reminder, @Nullable Calendar doneDate) {
        this(status, title, description, category, reminderType, reminder, doneDate);
        this.id = id;
    }


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

    public TaskStatus getStatus() {
        return status;
    }
    public void setStatus(TaskStatus status) {
        this.status = status;
    }

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

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

    public TaskCategory getCategory() {
        return category;
    }
    public void setCategory(TaskCategory category) {
        this.category = category;
    }

    public ReminderType getReminderType() {
        return reminderType;
    }
    public void setReminderType(ReminderType reminderType) {
        this.reminderType = reminderType;
    }

    public Reminder getReminder() {
        return reminder;
    }
    public void setReminder(Reminder reminder) {
        this.reminder = reminder;
    }

    public Calendar getDoneDate() {
        return doneDate;
    }
    public void setDoneDate(Calendar doneDate) {
        this.doneDate = doneDate;
    }

    public ArrayList<Attachment> getAttachments() {
        return attachments;
    }
    public void setAttachments(ArrayList<Attachment> attachments) {
        this.attachments = attachments;
    }
    public void addAttachment(Attachment attachment) {
        this.attachments.add(attachment);
    }
    public void clearAttachments() {
        attachments.clear();
    }



    @Override
    public String toString() {
        String                      res = "Task ID=" + id + "\r\n Status= " + status.name() + "\r\n Title=" + title + "\r\n Description=" + description + "\r\n Category=" + category.name() + "\r\n";
                                    res += " ReminderType=" + reminderType.name();
        if(reminder != null)        res += " Reminder=" + reminder.toString() + "\r\n";
        if(doneDate != null)        res += " DoneDate=" + doneDate.toString();
                                    res += " Attachments="  + attachments.size();
        return res;
    }
} public abstract class Attachment implements Serializable {
    private int id;
    private int taskId;

    public Attachment() {}   //id-less Constructor used when creating Attachments
    public Attachment(int id, int taskId) {
        this.id = id;
        this.taskId = taskId;
    }

    public abstract AttachmentType getType();

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

    public int getTaskId() {
        return taskId;
    }
    public void setTaskId(int taskId) {
        this.taskId = taskId;
    }

} public class AudioAttachment extends Attachment {

    private String audioFilename;

    public AudioAttachment() { /* Parameter-less constructor for audio attachment creation */ }
    public AudioAttachment(String audioFilename) {
        this.audioFilename = audioFilename;
    }
    public AudioAttachment(int id, int reminderId, String audioFilename) {
        super(id, reminderId);
        this.audioFilename = audioFilename;
    }

    @Override
    public AttachmentType getType() {
        return AttachmentType.AUDIO;
    }

    public String getAudioFilename() {
        return audioFilename;
    }
    public void setAudioFilename(String audioFilename) {
        this.audioFilename = audioFilename;
    }
} public class ImageAttachment extends Attachment {

    private byte[] thumbnail;
    private String imageFilename;

    public ImageAttachment() { /* Parameter-less constructor for image attachment creation */ }
    public ImageAttachment(byte[] thumbnail, String imageFilename) {
        this.thumbnail = thumbnail;
        this.imageFilename = imageFilename;
    }
    public ImageAttachment(int id, int reminderId, byte[] thumbnail, String fullImagePath) {
        super(id, reminderId);
        this.thumbnail = thumbnail;
        this.imageFilename = fullImagePath;
    }

    @Override
    public AttachmentType getType() {
        return AttachmentType.IMAGE;
    }


    public byte[] getThumbnail() {
        return thumbnail;
    }
    public void setThumbnail(byte[] thumbnail) {
        this.thumbnail = thumbnail;
    }

    public String getImageFilename() {
        return imageFilename;
    }
    public void setImageFilename(String imageFilename) {
        this.imageFilename = imageFilename;
    }
} public class LinkAttachment extends Attachment {

    private String link;

    public LinkAttachment(String link) {
        this.link = link;
    }
    public LinkAttachment(int id, int reminderId, String link) {
        super(id, reminderId);
        this.link = link;
    }

    @Override
    public AttachmentType getType() {
        return AttachmentType.LINK;
    }


    public String getLink() {
        return link;
    }
    public void setLink(String link) throws MalformedLinkException {
        if(isValid(link))
            this.link = link;
        else
            throw new MalformedLinkException("Link '" + link + "' is invalid");
    }
    private boolean isValid(String link) {
        return Patterns.WEB_URL.matcher(link).matches();
    }
} public class ListAttachment extends Attachment implements Serializable {

    private List<ListItemAttachment> items;

    public ListAttachment() {
        this.items = new ArrayList<>();
    }
    public ListAttachment(int id, int reminderId, String itemsJsonText) {
        super(id, reminderId);

        setItemsJson(itemsJsonText);
    }

    @Override
    public AttachmentType getType() {
        return AttachmentType.LIST;
    }


    public List<ListItemAttachment> getItems() {
        return items;
    }

    public void removeItem(int position) {

    }

    public String getItemsJson() {
        Gson gson = new Gson();
        return gson.toJson(items);
    }

    public void setItemsJson(String itemsJsonText) {
        Type listType = new TypeToken<List<ListItemAttachment>>() {}.getType();
        Gson gson = new Gson();
        items = gson.fromJson(itemsJsonText, listType);
    }

} public class TextAttachment extends Attachment {

    private String text;

    public TextAttachment(String text) {
        this.text = text;
    }
    public TextAttachment(int id, int reminderId, String text) {
        super(id, reminderId);
        this.text = text;
    }

    @Override
    public AttachmentType getType() {
        return AttachmentType.TEXT;
    }


    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
} public class ConversionUtil {
    /**
     * Converts dp to px
     */
    public static int dpToPx(int dp, Resources resources) {
        final float scale = resources.getDisplayMetrics().density;
        int px = (int) (dp * scale + 0.5f);
        return px;
    }

    public static LatLng placeToLatLng(Place place) {
        return new LatLng(place.getLatitude(), place.getLongitude());
    }

    public static Location placeToLocation(Place place) {
        Location loc = new Location(LocationManager.GPS_PROVIDER);
        loc.setLatitude(place.getLatitude());
        loc.setLongitude(place.getLongitude());
        return loc;
    }


    public static TaskViewModelType taskReminderTypeToTaskViewmodelType(ReminderType reminderType) {
        switch (reminderType) {
            case NONE:
                return TaskViewModelType.UNPROGRAMMED_REMINDER;
            case ONE_TIME:
                return TaskViewModelType.ONE_TIME_REMINDER;
            case REPEATING:
                return TaskViewModelType.REPEATING_REMINDER;
            case LOCATION_BASED:
                return TaskViewModelType.LOCATION_BASED_REMINDER;
            default:
                throw new InvalidParameterException("Unhandled ReminderType passed into ConversionUtil.taskReminderTypeToTaskViewmodelType()");
        }
    }

} public class SnackbarUtil {


    public static void showSnackbar(View container, @NonNull SnackbarType snackbarType,
                                    @StringRes int textStringRes, @Nullable SnackbarDuration duration,
                                    @Nullable BaseTransientBottomBar.BaseCallback<Snackbar> callback) {

        duration = (duration == null ? SnackbarDuration.LONG : duration);

        Snackbar snackbar = Snackbar.make(container, textStringRes, duration.getDuration());
        snackbar.getView().setBackgroundResource(snackbarType.getColorRes());
        TextView snackbarText = (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
        snackbarText.setCompoundDrawablesWithIntrinsicBounds(0, 0,snackbarType.getIconRes(), 0);
        snackbarText.setGravity(Gravity.CENTER);
        if(callback != null)
            snackbar.addCallback(callback);
        snackbar.show();
    }


    public enum SnackbarDuration {
        LONG(Snackbar.LENGTH_LONG),
        SHORT(Snackbar.LENGTH_SHORT),
        INDEFINITE(Snackbar.LENGTH_INDEFINITE);

        private int duration;

        SnackbarDuration(int duration){
            this.duration = duration;
        }

        public int getDuration() {
            return duration;
        }
    }

    public enum SnackbarType {
        ERROR(R.color.snackbar_error_background, R.drawable.icon_error_snackbar),
        SUCCESS(R.color.snackbar_success_background, R.drawable.icon_success_snackbar),
        NOTICE(R.color.snackbar_notice_background, R.drawable.icon_error_snackbar);

        private @ColorRes int colorRes;
        private @DrawableRes int iconRes;

        SnackbarType(@ColorRes int colorRes, @DrawableRes int iconRes){
            this.colorRes = colorRes;
            this.iconRes = iconRes;
        }

        public @ColorRes int getColorRes() {
            return colorRes;
        }

        public @DrawableRes int getIconRes() {
            return iconRes;
        }
    }

}
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.BaseTransientBottomBar;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.github.clans.fab.FloatingActionButton;
import com.github.clans.fab.FloatingActionMenu;
import com.transitionseverywhere.Slide;
import com.transitionseverywhere.TransitionManager;
import java.util.List;
import ve.com.abicelis.remindy.R;
import ve.com.abicelis.remindy.app.adapters.AttachmentAdapter;
import ve.com.abicelis.remindy.app.interfaces.TaskDataInterface;
import ve.com.abicelis.remindy.enums.TaskCategory;
import ve.com.abicelis.remindy.model.Task;
import ve.com.abicelis.remindy.model.attachment.Attachment;
import ve.com.abicelis.remindy.model.attachment.AudioAttachment;
import ve.com.abicelis.remindy.model.attachment.ImageAttachment;
import ve.com.abicelis.remindy.model.attachment.LinkAttachment;
import ve.com.abicelis.remindy.model.attachment.ListAttachment;
import ve.com.abicelis.remindy.model.attachment.TextAttachment;
import ve.com.abicelis.remindy.util.ConversionUtil;
import ve.com.abicelis.remindy.util.SnackbarUtil;
import static android.app.Activity.RESULT_CANCELED;
                    mAttachmentsFabHint.setVisibility(View.INVISIBLE);
                }
            }
        });
        mAttachmentsFabList = (FloatingActionButton) rootView.findViewById(R.id.fragment_task_add_list_attachment);
        mAttachmentsFabText = (FloatingActionButton) rootView.findViewById(R.id.fragment_task_add_text_attachment);
        mAttachmentsFabLink = (FloatingActionButton) rootView.findViewById(R.id.fragment_task_add_link_attachment);
        mAttachmentsFabImage = (FloatingActionButton) rootView.findViewById(R.id.fragment_task_add_image_attachment);
        mAttachmentsFabAudio = (FloatingActionButton) rootView.findViewById(R.id.fragment_task_add_audio_attachment);

        mAttachmentsFabList.setOnClickListener(this);
        mAttachmentsFabText.setOnClickListener(this);
        mAttachmentsFabLink.setOnClickListener(this);
        mAttachmentsFabImage.setOnClickListener(this);
        mAttachmentsFabAudio.setOnClickListener(this);

        //Hide keyboard
        ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(mContainer.getWindowToken(), 0);

        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_task_recycler);
        mNoItemsContainer = (RelativeLayout) rootView.findViewById(R.id.fragment_task_no_items_container);

        setUpRecyclerView();
        setupSpinners();
        setTaskValues();

        return rootView;
    }

    private void setUpRecyclerView() {

        mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
        mAdapter = new AttachmentAdapter(getActivity(), mTask.getAttachments(), false);
        mAdapter.setShowAttachmentHintListener(new AttachmentAdapter.ShowAttachmentHintListener() {
            @Override
            public void onShowAttachmentHint() {
                if (!mAttachmentLongClickOptionsDialogHintShown) {
                    mAttachmentLongClickOptionsDialogHintShown = true;
                    SnackbarUtil.showSnackbar(mContainer, SnackbarUtil.SnackbarType.NOTICE, R.string.fragment_task_snackbar_notice_attachments_options_hint, SnackbarUtil.SnackbarDuration.LONG, null);
                }
            }
        });
        DividerItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), mLayoutManager.getOrientation());
        itemDecoration.setDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.item_decoration_half_line));

        mRecyclerView.setNestedScrollingEnabled(false);
        mRecyclerView.addItemDecoration(itemDecoration);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
    }

    private void setupSpinners() {
        mTaskCategories = TaskCategory.getFriendlyValues(getActivity());
        ArrayAdapter reminderCategoryAdapter = new ArrayAdapter<>(getActivity(), R.layout.spinner_item, mTaskCategories);
        reminderCategoryAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
        mTaskCategory.setAdapter(reminderCategoryAdapter);
    }

    private void setTaskValues() {
        mTaskTitle.setText(mTask.getTitle());
        mTaskDescription.setText(mTask.getDescription());
        if(mTask.getCategory() != null)
            mTaskCategory.setSelection(mTask.getCategory().ordinal());
    }


    private void addAttachment(Attachment attachment) {
        mTask.addAttachment(attachment);
        if(mAdapter.getItemCount() == 1)
            mAdapter.notifyDataSetChanged();
        else
            mAdapter.notifyItemInserted(mAdapter.getItemCount());
    }


    @Override
    public void onClick(View v) {

        int id = v.getId();
        mAttachmentsFabMenu.close(true);

        if(!mHeadersVisible) {
            fadeInHeaders();
            mHeadersVisible = true;
        }

        switch (id) {
            case R.id.fragment_task_add_list_attachment:
                addAttachment(new ListAttachment());
                break;

            case R.id.fragment_task_add_text_attachment:
                addAttachment(new TextAttachment(""));
                break;

            case R.id.fragment_task_add_link_attachment:
                addAttachment(new LinkAttachment(""));
                break;

            case R.id.fragment_task_add_image_attachment:
                addAttachment(new ImageAttachment());
                break;

            case R.id.fragment_task_add_audio_attachment:
                addAttachment(new AudioAttachment());
                break;
        }
        //Scroll to added item
        if(mAdapter.getItemCount() > 0)
            mRecyclerView.smoothScrollToPosition(mAdapter.getItemCount() - 1);
    }


    private void fadeInHeaders() {
        //Fade in headers
        TransitionManager.beginDelayedTransition(mContainer);
        mHeaderBasicInfo.setVisibility(View.VISIBLE);
        mHeaderAttachments.setVisibility(View.VISIBLE);

        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mContainerBasicInfo.getLayoutParams();
        lp.setMargins(ConversionUtil.dpToPx(16, getResources()), 0, 0, 0);