com.dycody.android.idealnote.DetailFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.dycody.android.idealnote.DetailFragment.java

Source

/*
 * Copyright (C) 2015 Federico Iosue (federico.iosue@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.dycody.android.idealnote;

import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.DatePickerDialog.OnDateSetListener;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.*;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.*;
import butterknife.Bind;
import butterknife.ButterKnife;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.load.resource.bitmap.GlideBitmapDrawable;
import com.dycody.android.idealnote.async.AttachmentTask;
import com.dycody.android.idealnote.async.bus.PushbulletReplyEvent;
import com.dycody.android.idealnote.async.bus.SwitchFragmentEvent;
import com.dycody.android.idealnote.db.DbHelper;
import com.dycody.android.idealnote.helpers.date.DateHelper;
import com.dycody.android.idealnote.models.Category;
import com.dycody.android.idealnote.models.Note;
import com.dycody.android.idealnote.models.Tag;
import com.dycody.android.idealnote.models.adapters.AttachmentAdapter;
import com.dycody.android.idealnote.models.adapters.NavDrawerCategoryAdapter;
import com.dycody.android.idealnote.models.listeners.OnGeoUtilResultListener;
import com.dycody.android.idealnote.models.views.ExpandableHeightGridView;
import com.dycody.android.idealnote.utils.AlphaManager;
import com.dycody.android.idealnote.utils.ConnectionManager;
import com.dycody.android.idealnote.utils.Constants;
import com.dycody.android.idealnote.utils.FileHelper;
import com.dycody.android.idealnote.utils.KeyboardUtils;
import com.dycody.android.idealnote.utils.PasswordHelper;
import com.dycody.android.idealnote.utils.ShortcutHelper;
import com.dycody.android.idealnote.utils.TagsHelper;
import com.dycody.android.idealnote.utils.TextHelper;
import com.neopixl.pixlui.components.edittext.EditText;
import com.neopixl.pixlui.components.textview.TextView;
import com.pushbullet.android.extension.MessagingExtension;
import de.greenrobot.event.EventBus;
import de.keyboardsurfer.android.widget.crouton.Style;
import it.feio.android.checklistview.exceptions.ViewNotSupportedException;
import it.feio.android.checklistview.interfaces.CheckListChangedListener;
import it.feio.android.checklistview.models.CheckListViewItem;
import it.feio.android.checklistview.models.ChecklistManager;
import it.feio.android.checklistview.utils.DensityUtil;
import com.dycody.android.idealnote.models.Attachment;
import com.dycody.android.idealnote.models.ONStyle;
import com.dycody.android.idealnote.utils.Fonts;
import com.dycody.android.idealnote.utils.GeocodeHelper;
import com.dycody.android.idealnote.utils.IntentChecker;
import com.dycody.android.idealnote.utils.ReminderHelper;
import com.dycody.android.idealnote.utils.StorageHelper;
import com.dycody.android.idealnote.async.bus.NotesUpdatedEvent;
import com.dycody.android.idealnote.async.notes.NoteProcessorDelete;
import com.dycody.android.idealnote.async.notes.SaveNoteTask;
import com.dycody.android.idealnote.helpers.AttachmentsHelper;
import com.dycody.android.idealnote.helpers.PermissionsHelper;
import com.dycody.android.idealnote.models.adapters.PlacesAutoCompleteAdapter;
import com.dycody.android.idealnote.models.listeners.OnAttachingFileListener;
import com.dycody.android.idealnote.models.listeners.OnNoteSaved;
import com.dycody.android.idealnote.models.listeners.OnReminderPickedListener;
import com.dycody.android.idealnote.utils.Display;
import com.dycody.android.idealnote.utils.date.DateUtils;
import com.dycody.android.idealnote.utils.date.ReminderPickers;
import it.feio.android.pixlui.links.TextLinkClickListener;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
import static java.lang.Integer.parseInt;

public class DetailFragment extends BaseFragment
        implements OnReminderPickedListener, OnTouchListener, OnGlobalLayoutListener, OnAttachingFileListener,
        TextWatcher, CheckListChangedListener, OnNoteSaved, OnGeoUtilResultListener {

    private static final int TAKE_PHOTO = 1;
    private static final int TAKE_VIDEO = 2;
    private static final int SET_PASSWORD = 3;
    private static final int SKETCH = 4;
    private static final int CATEGORY = 5;
    private static final int DETAIL = 6;
    private static final int FILES = 7;

    @Bind(R.id.detail_root)
    ViewGroup root;
    @Bind(R.id.detail_title)
    EditText title;
    @Bind(R.id.detail_content)
    EditText content;
    @Bind(R.id.detail_attachments_above)
    ViewStub attachmentsAbove;
    @Bind(R.id.detail_attachments_below)
    ViewStub attachmentsBelow;
    @Nullable
    @Bind(R.id.gridview)
    ExpandableHeightGridView mGridView;
    @Bind(R.id.location)
    TextView locationTextView;
    @Bind(R.id.detail_timestamps)
    View timestampsView;
    @Bind(R.id.reminder_layout)
    LinearLayout reminder_layout;
    @Bind(R.id.reminder_icon)
    ImageView reminderIcon;
    @Bind(R.id.datetime)
    TextView datetime;
    @Bind(R.id.detail_tile_card)
    View titleCardView;
    @Bind(R.id.content_wrapper)
    ScrollView scrollView;
    @Bind(R.id.creation)
    TextView creationTextView;
    @Bind(R.id.last_modification)
    TextView lastModificationTextView;
    @Bind(R.id.title_wrapper)
    View titleWrapperView;
    @Bind(R.id.tag_marker)
    View tagMarkerView;
    @Bind(R.id.detail_wrapper)
    ViewManager detailWrapperView;
    @Bind(R.id.snackbar_placeholder)
    View snackBarPlaceholder;

    public OnDateSetListener onDateSetListener;
    public OnTimeSetListener onTimeSetListener;
    public boolean goBack = false;
    View toggleChecklistView;
    private Uri attachmentUri;
    private AttachmentAdapter mAttachmentAdapter;
    private PopupWindow attachmentDialog;
    private Note note;
    private Note noteTmp;
    private Note noteOriginal;
    // Audio recording
    private String recordName;
    private MediaRecorder mRecorder = null;
    private MediaPlayer mPlayer = null;
    private boolean isRecording = false;
    private View isPlayingView = null;
    private Bitmap recordingBitmap;
    private ChecklistManager mChecklistManager;
    // Values to print result
    private String exitMessage;
    private Style exitCroutonStyle = ONStyle.CONFIRM;
    // Flag to check if after editing it will return to ListActivity or not
    // and in the last case a Toast will be shown instead than Crouton
    private boolean afterSavedReturnsToList = true;
    private boolean showKeyboard = false;
    private boolean swiping;
    private int startSwipeX;
    private SharedPreferences prefs;
    private View keyboardPlaceholder;
    private boolean orientationChanged;
    private long audioRecordingTimeStart;
    private long audioRecordingTime;
    private DetailFragment mFragment;
    private Attachment sketchEdited;
    private int contentLineCounter = 1;
    private int contentCursorPosition;
    private ArrayList<String> mergedNotesIds;
    private MainActivity mainActivity;
    private boolean activityPausing;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mFragment = this;
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().post(new SwitchFragmentEvent(SwitchFragmentEvent.Direction.CHILDREN));
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
        GeocodeHelper.stop();
    }

    @Override
    public void onResume() {
        super.onResume();
        // Adding a layout observer to perform calculus when showing keyboard
        if (root != null) {
            root.getViewTreeObserver().addOnGlobalLayoutListener(this);
        }

        activityPausing = false;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_detail, container, false);
        ButterKnife.bind(this, view);
        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mainActivity = (MainActivity) getActivity();

        prefs = mainActivity.prefs;

        mainActivity.getSupportActionBar().setDisplayShowTitleEnabled(false);
        mainActivity.getToolbar().setNavigationOnClickListener(v -> navigateUp());

        // Force the navigation drawer to stay opened if tablet mode is on, otherwise has to stay closed
        if (NavigationDrawerFragment.isDoublePanelActive()) {
            mainActivity.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
        } else {
            mainActivity.getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
        }

        // Restored temp note after orientation change
        if (savedInstanceState != null) {
            noteTmp = savedInstanceState.getParcelable("noteTmp");
            note = savedInstanceState.getParcelable("note");
            noteOriginal = savedInstanceState.getParcelable("noteOriginal");
            attachmentUri = savedInstanceState.getParcelable("attachmentUri");
            orientationChanged = savedInstanceState.getBoolean("orientationChanged");
        }

        // Added the sketched image if present returning from SketchFragment
        if (mainActivity.sketchUri != null) {
            Attachment mAttachment = new Attachment(mainActivity.sketchUri, Constants.MIME_TYPE_SKETCH);
            addAttachment(mAttachment);
            mainActivity.sketchUri = null;
            // Removes previous version of edited image
            if (sketchEdited != null) {
                noteTmp.getAttachmentsList().remove(sketchEdited);
                sketchEdited = null;
            }
        }

        init();

        setHasOptionsMenu(true);
        setRetainInstance(false);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (noteTmp != null) {
            noteTmp.setTitle(getNoteTitle());
            noteTmp.setContent(getNoteContent());
            outState.putParcelable("noteTmp", noteTmp);
            outState.putParcelable("note", note);
            outState.putParcelable("noteOriginal", noteOriginal);
            outState.putParcelable("attachmentUri", attachmentUri);
            outState.putBoolean("orientationChanged", orientationChanged);
        }
        super.onSaveInstanceState(outState);
    }

    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    @Override
    public void onPause() {
        super.onPause();

        activityPausing = true;

        // Checks "goBack" value to avoid performing a double saving
        if (!goBack) {
            saveNote(this);
        }

        if (mRecorder != null) {
            mRecorder.release();
            mRecorder = null;
        }

        // Unregistering layout observer
        if (root != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                root.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
                root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }
        }

        // Closes keyboard on exit
        if (toggleChecklistView != null) {
            KeyboardUtils.hideKeyboard(toggleChecklistView);
            content.clearFocus();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (getResources().getConfiguration().orientation != newConfig.orientation) {
            orientationChanged = true;
        }
    }

    private void init() {

        // Handling of Intent actions
        handleIntents();

        if (noteOriginal == null) {
            noteOriginal = getArguments().getParcelable(Constants.INTENT_NOTE);
        }

        if (note == null) {
            note = new Note(noteOriginal);
        }

        if (noteTmp == null) {
            noteTmp = new Note(note);
        }

        if (noteTmp.isLocked() && !noteTmp.isPasswordChecked()) {
            checkNoteLock(noteTmp);
            return;
        }

        initViews();
    }

    /**
     * Checks note lock and password before showing note content
     */
    private void checkNoteLock(Note note) {
        // If note is locked security password will be requested
        if (note.isLocked() && prefs.getString(Constants.PREF_PASSWORD, null) != null
                && !prefs.getBoolean("settings_password_access", false)) {
            PasswordHelper.requestPassword(mainActivity, passwordConfirmed -> {
                if (passwordConfirmed) {
                    noteTmp.setPasswordChecked(true);
                    init();
                } else {
                    goBack = true;
                    goHome();
                }
            });
        } else {
            noteTmp.setPasswordChecked(true);
            init();
        }
    }

    private void handleIntents() {
        Intent i = mainActivity.getIntent();

        if (IntentChecker.checkAction(i, Constants.ACTION_MERGE)) {
            noteOriginal = new Note();
            note = new Note(noteOriginal);
            noteTmp = getArguments().getParcelable(Constants.INTENT_NOTE);
            if (i.getStringArrayListExtra("merged_notes") != null) {
                mergedNotesIds = i.getStringArrayListExtra("merged_notes");
            }
        }

        // Action called from home shortcut
        if (IntentChecker.checkAction(i, Constants.ACTION_SHORTCUT, Constants.ACTION_NOTIFICATION_CLICK)) {
            afterSavedReturnsToList = false;
            noteOriginal = DbHelper.getInstance().getNote(i.getLongExtra(Constants.INTENT_KEY, 0));
            // Checks if the note pointed from the shortcut has been deleted
            try {
                note = new Note(noteOriginal);
                noteTmp = new Note(noteOriginal);
            } catch (NullPointerException e) {
                mainActivity.showToast(getText(R.string.shortcut_note_deleted), Toast.LENGTH_LONG);
                mainActivity.finish();
            }
        }

        // Check if is launched from a widget
        if (IntentChecker.checkAction(i, Constants.ACTION_WIDGET, Constants.ACTION_TAKE_PHOTO)) {

            afterSavedReturnsToList = false;
            showKeyboard = true;

            //  with tags to set tag
            if (i.hasExtra(Constants.INTENT_WIDGET)) {
                String widgetId = i.getExtras().get(Constants.INTENT_WIDGET).toString();
                if (widgetId != null) {
                    String sqlCondition = prefs.getString(Constants.PREF_WIDGET_PREFIX + widgetId, "");
                    String categoryId = TextHelper.checkIntentCategory(sqlCondition);
                    if (categoryId != null) {
                        Category category;
                        try {
                            category = DbHelper.getInstance().getCategory(Long.parseLong(categoryId));
                            noteTmp = new Note();
                            noteTmp.setCategory(category);
                        } catch (NumberFormatException e) {
                            Log.e(Constants.TAG, "Category with not-numeric value!", e);
                        }
                    }
                }
            }

            // Sub-action is to take a photo
            if (IntentChecker.checkAction(i, Constants.ACTION_TAKE_PHOTO)) {
                takePhoto();
            }
        }

        /**
         * Handles third party apps requests of sharing
         */
        if (IntentChecker.checkAction(i, Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE,
                Constants.INTENT_GOOGLE_NOW) && i.getType() != null) {

            afterSavedReturnsToList = false;

            if (noteTmp == null)
                noteTmp = new Note();

            // Text title
            String title = i.getStringExtra(Intent.EXTRA_SUBJECT);
            if (title != null) {
                noteTmp.setTitle(title);
            }

            // Text content
            String content = i.getStringExtra(Intent.EXTRA_TEXT);
            if (content != null) {
                noteTmp.setContent(content);
            }

            // Single attachment data
            Uri uri = i.getParcelableExtra(Intent.EXTRA_STREAM);
            // Due to the fact that Google Now passes intent as text but with
            // audio recording attached the case must be handled in specific way
            if (uri != null && !Constants.INTENT_GOOGLE_NOW.equals(i.getAction())) {
                String name = FileHelper.getNameFromUri(mainActivity, uri);
                new AttachmentTask(this, uri, name, this).execute();
            }

            // Multiple attachment data
            ArrayList<Uri> uris = i.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
            if (uris != null) {
                for (Uri uriSingle : uris) {
                    String name = FileHelper.getNameFromUri(mainActivity, uriSingle);
                    new AttachmentTask(this, uriSingle, name, this).execute();
                }
            }
        }

        if (IntentChecker.checkAction(i, Intent.ACTION_MAIN, Constants.ACTION_WIDGET_SHOW_LIST,
                Constants.ACTION_SHORTCUT_WIDGET, Constants.ACTION_WIDGET)) {
            showKeyboard = true;
        }

        i.setAction(null);
    }

    @SuppressLint("NewApi")
    private void initViews() {

        // Sets onTouchListener to the whole activity to swipe notes
        root.setOnTouchListener(this);

        // Overrides font sizes with the one selected from user
        Fonts.overrideTextSize(mainActivity, prefs, root);

        // Color of tag marker if note is tagged a function is active in preferences
        setTagMarkerColor(noteTmp.getCategory());

        initViewTitle();

        initViewContent();

        initViewLocation();

        initViewAttachments();

        initViewReminder();

        initViewFooter();
    }

    private void initViewFooter() {
        // Footer dates of creation...
        String creation = DateHelper.getFormattedDate(noteTmp.getCreation(),
                prefs.getBoolean(Constants.PREF_PRETTIFIED_DATES, true));
        creationTextView.append(creation.length() > 0 ? getString(R.string.creation) + " " + creation : "");
        if (creationTextView.getText().length() == 0)
            creationTextView.setVisibility(View.GONE);

        // ... and last modification
        String lastModification = DateHelper.getFormattedDate(noteTmp.getLastModification(),
                prefs.getBoolean(Constants.PREF_PRETTIFIED_DATES, true));
        lastModificationTextView.append(
                lastModification.length() > 0 ? getString(R.string.last_update) + " " + lastModification : "");
        if (lastModificationTextView.getText().length() == 0)
            lastModificationTextView.setVisibility(View.GONE);
    }

    private void initViewReminder() {

        // Preparation for reminder icon
        reminder_layout.setOnClickListener(v -> {
            int pickerType = prefs.getBoolean("settings_simple_calendar", false) ? ReminderPickers.TYPE_AOSP
                    : ReminderPickers.TYPE_GOOGLE;
            ReminderPickers reminderPicker = new ReminderPickers(mainActivity, mFragment, pickerType);
            Long presetDateTime = noteTmp.getAlarm() != null ? Long.parseLong(noteTmp.getAlarm())
                    : DateUtils.getNextMinute();
            reminderPicker.pick(presetDateTime, noteTmp.getRecurrenceRule());
            onDateSetListener = reminderPicker;
            onTimeSetListener = reminderPicker;
        });

        reminder_layout.setOnLongClickListener(v -> {
            MaterialDialog dialog = new MaterialDialog.Builder(mainActivity).content(R.string.remove_reminder)
                    .positiveText(R.string.ok).callback(new MaterialDialog.ButtonCallback() {
                        @Override
                        public void onPositive(MaterialDialog materialDialog) {
                            noteTmp.setAlarm(null);
                            reminderIcon.setImageResource(R.drawable.ic_alarm_black_18dp);
                            datetime.setText("");
                        }
                    }).build();
            dialog.show();
            return true;
        });

        // Reminder
        String reminderString = initReminder(noteTmp);
        if (!StringUtils.isEmpty(reminderString)) {
            reminderIcon.setImageResource(R.drawable.ic_alarm_add_black_18dp);
            datetime.setText(reminderString);
        }

        // Timestamps view
        // Bottom padding set for translucent navbar in Kitkat
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            int navBarHeight = Display.getNavigationBarHeightKitkat(mainActivity);
            int negativePadding = navBarHeight >= 27 * 3 ? -27 : 0;
            int timestampsViewPaddingBottom = navBarHeight > 0 ? navBarHeight + negativePadding
                    : timestampsView.getPaddingBottom();
            timestampsView.setPadding(timestampsView.getPaddingStart(), timestampsView.getPaddingTop(),
                    timestampsView.getPaddingEnd(), timestampsViewPaddingBottom);
        }
    }

    private void initViewLocation() {

        DetailFragment detailFragment = this;

        if (isNoteLocationValid()) {
            if (TextUtils.isEmpty(noteTmp.getAddress())) {
                //FIXME: What's this "sasd"?
                GeocodeHelper.getAddressFromCoordinates(new Location("sasd"), detailFragment);
            } else {
                locationTextView.setText(noteTmp.getAddress());
                locationTextView.setVisibility(View.VISIBLE);
            }
        }

        // Automatic location insertion
        if (prefs.getBoolean(Constants.PREF_AUTO_LOCATION, false) && noteTmp.get_id() == null) {
            getLocation(detailFragment);
        }

        locationTextView.setOnClickListener(v -> {
            String uriString = "geo:" + noteTmp.getLatitude() + ',' + noteTmp.getLongitude() + "?q="
                    + noteTmp.getLatitude() + ',' + noteTmp.getLongitude();
            Intent locationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uriString));
            if (!IntentChecker.isAvailable(mainActivity, locationIntent, null)) {
                uriString = "http://maps.google.com/maps?q=" + noteTmp.getLatitude() + ',' + noteTmp.getLongitude();
                locationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uriString));
            }
            startActivity(locationIntent);
        });
        locationTextView.setOnLongClickListener(v -> {
            MaterialDialog.Builder builder = new MaterialDialog.Builder(mainActivity);
            builder.content(R.string.remove_location);
            builder.positiveText(R.string.ok);
            builder.callback(new MaterialDialog.ButtonCallback() {
                @Override
                public void onPositive(MaterialDialog materialDialog) {
                    noteTmp.setLatitude("");
                    noteTmp.setLongitude("");
                    fade(locationTextView, false);
                }
            });
            MaterialDialog dialog = builder.build();
            dialog.show();
            return true;
        });
    }

    private void getLocation(OnGeoUtilResultListener onGeoUtilResultListener) {
        PermissionsHelper.requestPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION,
                R.string.permission_coarse_location, snackBarPlaceholder,
                () -> GeocodeHelper.getLocation(onGeoUtilResultListener));
    }

    private void initViewAttachments() {

        // Attachments position based on preferences
        if (prefs.getBoolean(Constants.PREF_ATTANCHEMENTS_ON_BOTTOM, false)) {
            attachmentsBelow.inflate();
        } else {
            attachmentsAbove.inflate();
        }
        mGridView = (ExpandableHeightGridView) root.findViewById(R.id.gridview);

        // Some fields can be filled by third party application and are always shown
        mAttachmentAdapter = new AttachmentAdapter(mainActivity, noteTmp.getAttachmentsList(), mGridView);

        // Initialzation of gridview for images
        mGridView.setAdapter(mAttachmentAdapter);
        mGridView.autoresize();

        // Click events for images in gridview (zooms image)
        mGridView.setOnItemClickListener((parent, v, position, id) -> {
            Attachment attachment = (Attachment) parent.getAdapter().getItem(position);
            Uri uri = attachment.getUri();
            Intent attachmentIntent;
            if (Constants.MIME_TYPE_FILES.equals(attachment.getMime_type())) {

                attachmentIntent = new Intent(Intent.ACTION_VIEW);
                attachmentIntent.setDataAndType(uri, StorageHelper.getMimeType(mainActivity, attachment.getUri()));
                attachmentIntent
                        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                if (IntentChecker.isAvailable(mainActivity.getApplicationContext(), attachmentIntent, null)) {
                    startActivity(attachmentIntent);
                } else {
                    Toast.makeText(getActivity(), R.string.feature_not_available_on_this_device, Toast.LENGTH_SHORT)
                            .show();
                    //mainActivity.showMessage(R.string.feature_not_available_on_this_device, ONStyle.WARN);
                }

                // Media files will be opened in internal gallery
            } else if (Constants.MIME_TYPE_IMAGE.equals(attachment.getMime_type())
                    || Constants.MIME_TYPE_SKETCH.equals(attachment.getMime_type())
                    || Constants.MIME_TYPE_VIDEO.equals(attachment.getMime_type())) {
                // Title
                noteTmp.setTitle(getNoteTitle());
                noteTmp.setContent(getNoteContent());
                String title1 = TextHelper.parseTitleAndContent(mainActivity, noteTmp)[0].toString();
                // Images
                int clickedImage = 0;
                ArrayList<Attachment> images = new ArrayList<>();
                for (Attachment mAttachment : noteTmp.getAttachmentsList()) {
                    if (Constants.MIME_TYPE_IMAGE.equals(mAttachment.getMime_type())
                            || Constants.MIME_TYPE_SKETCH.equals(mAttachment.getMime_type())
                            || Constants.MIME_TYPE_VIDEO.equals(mAttachment.getMime_type())) {
                        images.add(mAttachment);
                        if (mAttachment.equals(attachment)) {
                            clickedImage = images.size() - 1;
                        }
                    }
                }
                // Intent
                attachmentIntent = new Intent(mainActivity, GalleryActivity.class);
                attachmentIntent.putExtra(Constants.GALLERY_TITLE, title1);
                attachmentIntent.putParcelableArrayListExtra(Constants.GALLERY_IMAGES, images);
                attachmentIntent.putExtra(Constants.GALLERY_CLICKED_IMAGE, clickedImage);
                startActivity(attachmentIntent);

            } else if (Constants.MIME_TYPE_AUDIO.equals(attachment.getMime_type())) {
                playback(v, attachment.getUri());
            }

        });

        mGridView.setOnItemLongClickListener((parent, v, position, id) -> {
            // To avoid deleting audio attachment during playback
            if (mPlayer != null)
                return false;
            List<String> items = Arrays.asList(getResources().getStringArray(R.array.attachments_actions));
            if (!Constants.MIME_TYPE_SKETCH.equals(mAttachmentAdapter.getItem(position).getMime_type())) {
                items = items.subList(0, items.size() - 1);
            }
            Attachment attachment = mAttachmentAdapter.getItem(position);
            new MaterialDialog.Builder(mainActivity)
                    .title(attachment.getName() + " (" + AttachmentsHelper.getSize(attachment) + ")")
                    .items(items.toArray(new String[items.size()]))
                    .itemsCallback((materialDialog, view, i, charSequence) -> performAttachmentAction(position, i))
                    .build().show();
            return true;
        });
    }

    /**
     * Performs an action when long-click option is selected
     *
     * @param attachmentPosition
     * @param i                  item index
     */
    private void performAttachmentAction(int attachmentPosition, int i) {
        switch (getResources().getStringArray(R.array.attachments_actions_values)[i]) {
        case "share":
            Intent shareIntent = new Intent(Intent.ACTION_SEND);
            Attachment attachment = mAttachmentAdapter.getItem(attachmentPosition);
            shareIntent.setType(StorageHelper.getMimeType(IdealNote.getAppContext(), attachment.getUri()));
            shareIntent.putExtra(Intent.EXTRA_STREAM, attachment.getUri());
            if (IntentChecker.isAvailable(IdealNote.getAppContext(), shareIntent, null)) {
                startActivity(shareIntent);
            } else {
                Toast.makeText(getActivity(), R.string.feature_not_available_on_this_device, Toast.LENGTH_SHORT)
                        .show();
                //mainActivity.showMessage(R.string.feature_not_available_on_this_device, ONStyle.WARN);
            }
            break;
        case "delete":
            removeAttachment(attachmentPosition);
            mAttachmentAdapter.notifyDataSetChanged();
            mGridView.autoresize();
            break;
        case "delete all":
            new MaterialDialog.Builder(mainActivity).title(R.string.delete_all_attachments)
                    .positiveText(R.string.confirm)
                    .onPositive((materialDialog, dialogAction) -> removeAllAttachments()).build().show();
            break;
        case "edit":
            takeSketch(mAttachmentAdapter.getItem(attachmentPosition));
            break;
        default:
            Log.w(Constants.TAG, "No action available");
        }
    }

    private void initViewTitle() {

        title.setText(noteTmp.getTitle());
        title.gatherLinksForText();
        title.setOnTextLinkClickListener(textLinkClickListener);
        // To avoid dropping here the  dragged checklist items
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            title.setOnDragListener((v, event) -> {
                //               ((View)event.getLocalState()).setVisibility(View.VISIBLE);
                return true;
            });
        }
        //When editor action is pressed focus is moved to last character in content field
        title.setOnEditorActionListener((v, actionId, event) -> {
            content.requestFocus();
            content.setSelection(content.getText().length());
            return false;
        });
        requestFocus(title);
    }

    private void initViewContent() {

        content.setText(noteTmp.getContent());
        content.gatherLinksForText();
        content.setOnTextLinkClickListener(textLinkClickListener);
        // Avoids focused line goes under the keyboard
        content.addTextChangedListener(this);

        // Restore checklist
        toggleChecklistView = content;
        if (noteTmp.isChecklist()) {
            noteTmp.setChecklist(false);
            AlphaManager.setAlpha(toggleChecklistView, 0);
            toggleChecklist2();
        }
    }

    /**
     * Force focus and shows soft keyboard. Only happens if it's a new note, without shared content.
     * {@linkshowKeyboard} is used to check if the note is created from shared content.
     */
    private void requestFocus(final EditText view) {
        if (note.get_id() == null && !noteTmp.isChanged(note) && showKeyboard) {
            KeyboardUtils.showKeyboard(view);
        }
    }

    /**
     * Colors tag marker in note's title and content elements
     */
    private void setTagMarkerColor(Category tag) {

        String colorsPref = prefs.getString("settings_colors_app", Constants.PREF_COLORS_APP_DEFAULT);

        // Checking preference
        if (!"disabled".equals(colorsPref)) {

            // Choosing target view depending on another preference
            ArrayList<View> target = new ArrayList<>();
            if ("complete".equals(colorsPref)) {
                target.add(titleWrapperView);
                target.add(scrollView);
            } else {
                target.add(tagMarkerView);
            }

            // Coloring the target
            if (tag != null && tag.getColor() != null) {
                for (View view : target) {
                    view.setBackgroundColor(parseInt(tag.getColor()));
                }
            } else {
                for (View view : target) {
                    view.setBackgroundColor(Color.parseColor("#00000000"));
                }
            }
        }
    }

    private void displayLocationDialog() {
        getLocation(new OnGeoUtilResultListenerImpl(mainActivity, mFragment, noteTmp));
    }

    private static class OnGeoUtilResultListenerImpl implements OnGeoUtilResultListener {

        private final WeakReference<MainActivity> mainActivityWeakReference;
        private final WeakReference<DetailFragment> detailFragmentWeakReference;
        private final WeakReference<Note> noteTmpWeakReference;

        OnGeoUtilResultListenerImpl(MainActivity activity, DetailFragment mFragment, Note noteTmp) {
            this.mainActivityWeakReference = new WeakReference<>(activity);
            this.detailFragmentWeakReference = new WeakReference<>(mFragment);
            this.noteTmpWeakReference = new WeakReference<>(noteTmp);
        }

        @Override
        public void onAddressResolved(String address) {
        }

        @Override
        public void onCoordinatesResolved(Location location, String address) {
        }

        @Override
        public void onLocationUnavailable() {
            mainActivityWeakReference.get().showMessage(R.string.location_not_found, ONStyle.ALERT);
            //Toast.makeText(mainActivityWeakReference.get(), R.string.location_not_found,
            //Toast.LENGTH_SHORT).show();

        }

        @Override
        public void onLocationRetrieved(Location location) {

            if (!checkWeakReferences()) {
                return;
            }

            if (location == null) {
                return;
            }
            if (!ConnectionManager.internetAvailable(mainActivityWeakReference.get())) {
                noteTmpWeakReference.get().setLatitude(location.getLatitude());
                noteTmpWeakReference.get().setLongitude(location.getLongitude());
                onAddressResolved("");
                return;
            }
            LayoutInflater inflater = mainActivityWeakReference.get().getLayoutInflater();
            View v = inflater.inflate(R.layout.dialog_location, null);
            final AutoCompleteTextView autoCompView = (AutoCompleteTextView) v
                    .findViewById(R.id.auto_complete_location);
            autoCompView.setHint(mainActivityWeakReference.get().getString(R.string.search_location));
            autoCompView.setAdapter(
                    new PlacesAutoCompleteAdapter(mainActivityWeakReference.get(), R.layout.simple_text_layout));
            final MaterialDialog dialog = new MaterialDialog.Builder(mainActivityWeakReference.get())
                    .customView(autoCompView, false).positiveText(R.string.use_current_location)
                    .callback(new MaterialDialog.ButtonCallback() {
                        @Override
                        public void onPositive(MaterialDialog materialDialog) {
                            if (TextUtils.isEmpty(autoCompView.getText().toString())) {
                                noteTmpWeakReference.get().setLatitude(location.getLatitude());
                                noteTmpWeakReference.get().setLongitude(location.getLongitude());
                                GeocodeHelper.getAddressFromCoordinates(location,
                                        detailFragmentWeakReference.get());
                            } else {
                                GeocodeHelper.getCoordinatesFromAddress(autoCompView.getText().toString(),
                                        detailFragmentWeakReference.get());
                            }
                        }
                    }).build();
            autoCompView.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (s.length() != 0) {
                        dialog.setActionButton(DialogAction.POSITIVE,
                                mainActivityWeakReference.get().getString(R.string.confirm));
                    } else {
                        dialog.setActionButton(DialogAction.POSITIVE,
                                mainActivityWeakReference.get().getString(R.string.use_current_location));
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {
                }
            });
            dialog.show();
        }

        private boolean checkWeakReferences() {
            return mainActivityWeakReference.get() != null && !mainActivityWeakReference.get().isFinishing()
                    && detailFragmentWeakReference.get() != null && noteTmpWeakReference.get() != null;
        }
    }

    @Override
    public void onLocationRetrieved(Location location) {
        if (location == null) {
            Toast.makeText(getActivity(), R.string.location_not_found, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.location_not_found, ONStyle.ALERT);
        }
        if (location != null) {
            noteTmp.setLatitude(location.getLatitude());
            noteTmp.setLongitude(location.getLongitude());
            if (!TextUtils.isEmpty(noteTmp.getAddress())) {
                locationTextView.setVisibility(View.VISIBLE);
                locationTextView.setText(noteTmp.getAddress());
            } else {
                GeocodeHelper.getAddressFromCoordinates(location, mFragment);
            }
        }
    }

    @Override
    public void onLocationUnavailable() {
        Toast.makeText(getActivity(), R.string.location_not_found, Toast.LENGTH_SHORT).show();
        //mainActivity.showMessage(R.string.location_not_found, ONStyle.ALERT);
    }

    @Override
    public void onAddressResolved(String address) {
        if (TextUtils.isEmpty(address)) {
            if (!isNoteLocationValid()) {
                Toast.makeText(getActivity(), R.string.location_not_found, Toast.LENGTH_SHORT).show();
                //mainActivity.showMessage(R.string.location_not_found, ONStyle.ALERT);
                return;
            }
            address = noteTmp.getLatitude() + ", " + noteTmp.getLongitude();
        }
        if (!GeocodeHelper.areCoordinates(address)) {
            noteTmp.setAddress(address);
        }
        locationTextView.setVisibility(View.VISIBLE);
        locationTextView.setText(address);
        fade(locationTextView, true);
    }

    @Override
    public void onCoordinatesResolved(Location location, String address) {
        if (location != null) {
            noteTmp.setLatitude(location.getLatitude());
            noteTmp.setLongitude(location.getLongitude());
            noteTmp.setAddress(address);
            locationTextView.setVisibility(View.VISIBLE);
            locationTextView.setText(address);
            fade(locationTextView, true);
        } else {
            Toast.makeText(getActivity(), R.string.location_not_found, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.location_not_found, ONStyle.ALERT);
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_detail, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {

        // Closes search view if left open in List fragment
        MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
        if (searchMenuItem != null) {
            MenuItemCompat.collapseActionView(searchMenuItem);
        }

        boolean newNote = noteTmp.get_id() == null;

        menu.findItem(R.id.menu_checklist_on).setVisible(!noteTmp.isChecklist());
        menu.findItem(R.id.menu_checklist_off).setVisible(noteTmp.isChecklist());
        menu.findItem(R.id.menu_lock).setVisible(!noteTmp.isLocked());
        menu.findItem(R.id.menu_unlock).setVisible(noteTmp.isLocked());
        // If note is trashed only this options will be available from menu
        if (noteTmp.isTrashed()) {
            menu.findItem(R.id.menu_untrash).setVisible(true);
            menu.findItem(R.id.menu_delete).setVisible(true);
            // Otherwise all other actions will be available
        } else {
            menu.findItem(R.id.menu_add_shortcut).setVisible(!newNote);
            menu.findItem(R.id.menu_archive).setVisible(!newNote && !noteTmp.isArchived());
            menu.findItem(R.id.menu_unarchive).setVisible(!newNote && noteTmp.isArchived());
            menu.findItem(R.id.menu_trash).setVisible(!newNote);
        }
    }

    @SuppressLint("NewApi")
    public boolean goHome() {
        stopPlaying();

        // The activity has managed a shared intent from third party app and
        // performs a normal onBackPressed instead of returning back to ListActivity
        if (!afterSavedReturnsToList) {
            if (!TextUtils.isEmpty(exitMessage)) {
                mainActivity.showToast(exitMessage, Toast.LENGTH_SHORT);
            }
            mainActivity.finish();

        } else {

            if (!TextUtils.isEmpty(exitMessage) && exitCroutonStyle != null) {
                mainActivity.showMessage(exitMessage, exitCroutonStyle);
            }

            // Otherwise the result is passed to ListActivity
            if (mainActivity != null && mainActivity.getSupportFragmentManager() != null) {
                mainActivity.getSupportFragmentManager().popBackStack();
                if (mainActivity.getSupportFragmentManager().getBackStackEntryCount() == 1) {
                    mainActivity.getSupportActionBar().setDisplayShowTitleEnabled(true);
                    if (mainActivity.getDrawerToggle() != null) {
                        mainActivity.getDrawerToggle().setDrawerIndicatorEnabled(true);
                    }
                    EventBus.getDefault().post(new SwitchFragmentEvent(SwitchFragmentEvent.Direction.PARENT));
                }
            }
        }

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_attachment:
            showPopup(mainActivity.findViewById(R.id.menu_attachment));
            break;
        case R.id.menu_tag:
            addTags();
            break;
        case R.id.menu_category:
            categorizeNote();
            break;
        case R.id.menu_share:
            shareNote();
            break;
        case R.id.menu_checklist_on:
            toggleChecklist();
            break;
        case R.id.menu_checklist_off:
            toggleChecklist();
            break;
        case R.id.menu_lock:
            lockNote();
            break;
        case R.id.menu_unlock:
            lockNote();
            break;
        case R.id.menu_add_shortcut:
            addShortcut();
            break;
        case R.id.menu_archive:
            archiveNote(true);
            break;
        case R.id.menu_unarchive:
            archiveNote(false);
            break;
        case R.id.menu_trash:
            trashNote(true);
            break;
        case R.id.menu_untrash:
            trashNote(false);
            break;
        case R.id.menu_discard_changes:
            discard();
            break;
        case R.id.menu_delete:
            deleteNote();
            break;
        default:
            Log.w(Constants.TAG, "Invalid menu option selected");
        }

        ((IdealNote) getActivity().getApplication()).getAnalyticsHelper().trackActionFromResourceId(getActivity(),
                item.getItemId());

        return super.onOptionsItemSelected(item);
    }

    private void navigateUp() {
        afterSavedReturnsToList = true;
        saveAndExit(this);
    }

    /**
     *
     */
    private void toggleChecklist() {

        // In case checklist is active a prompt will ask about many options
        // to decide hot to convert back to simple text
        if (!noteTmp.isChecklist()) {
            toggleChecklist2();
            return;
        }

        // If checklist is active but no items are checked the conversion in done automatically
        // without prompting user
        if (mChecklistManager.getCheckedCount() == 0) {
            toggleChecklist2(true, false);
            return;
        }

        // Inflate the popup_layout.xml
        LayoutInflater inflater = (LayoutInflater) mainActivity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        final View layout = inflater.inflate(R.layout.dialog_remove_checklist_layout,
                (ViewGroup) getView().findViewById(R.id.layout_root));

        // Retrieves options checkboxes and initialize their values
        final CheckBox keepChecked = (CheckBox) layout.findViewById(R.id.checklist_keep_checked);
        final CheckBox keepCheckmarks = (CheckBox) layout.findViewById(R.id.checklist_keep_checkmarks);
        keepChecked.setChecked(prefs.getBoolean(Constants.PREF_KEEP_CHECKED, true));
        keepCheckmarks.setChecked(prefs.getBoolean(Constants.PREF_KEEP_CHECKMARKS, true));

        new MaterialDialog.Builder(mainActivity).customView(layout, false).positiveText(R.string.ok)
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog materialDialog) {
                        prefs.edit().putBoolean(Constants.PREF_KEEP_CHECKED, keepChecked.isChecked())
                                .putBoolean(Constants.PREF_KEEP_CHECKMARKS, keepCheckmarks.isChecked()).apply();
                        toggleChecklist2();
                    }
                }).build().show();
    }

    /**
     * Toggles checklist view
     */
    private void toggleChecklist2() {
        boolean keepChecked = prefs.getBoolean(Constants.PREF_KEEP_CHECKED, true);
        boolean showChecks = prefs.getBoolean(Constants.PREF_KEEP_CHECKMARKS, true);
        toggleChecklist2(keepChecked, showChecks);
    }

    @SuppressLint("NewApi")
    private void toggleChecklist2(final boolean keepChecked, final boolean showChecks) {
        // Get instance and set options to convert EditText to CheckListView

        mChecklistManager = mChecklistManager == null ? new ChecklistManager(mainActivity) : mChecklistManager;
        int checkedItemsBehavior = Integer.valueOf(prefs.getString("settings_checked_items_behavior",
                String.valueOf(it.feio.android.checklistview.Settings.CHECKED_HOLD)));
        mChecklistManager.showCheckMarks(showChecks).newEntryHint(getString(R.string.checklist_item_hint))
                .keepChecked(keepChecked).undoBarContainerView(scrollView)
                .moveCheckedOnBottom(checkedItemsBehavior);

        // Links parsing options
        mChecklistManager.setOnTextLinkClickListener(textLinkClickListener);
        mChecklistManager.addTextChangedListener(mFragment);
        mChecklistManager.setCheckListChangedListener(mFragment);

        // Switches the views
        View newView = null;
        try {
            newView = mChecklistManager.convert(toggleChecklistView);
        } catch (ViewNotSupportedException e) {
            Log.e(Constants.TAG, "Error switching checklist view", e);
        }

        // Switches the views
        if (newView != null) {
            mChecklistManager.replaceViews(toggleChecklistView, newView);
            toggleChecklistView = newView;
            animate(toggleChecklistView).alpha(1).scaleXBy(0).scaleX(1).scaleYBy(0).scaleY(1);
            noteTmp.setChecklist(!noteTmp.isChecklist());
        }
    }

    /**
     * Categorize note choosing from a list of previously created categories
     */
    private void categorizeNote() {
        // Retrieves all available categories
        final ArrayList<Category> categories = DbHelper.getInstance().getCategories();

        String currentCategory = noteTmp.getCategory() != null ? String.valueOf(noteTmp.getCategory().getId())
                : null;

        final MaterialDialog dialog = new MaterialDialog.Builder(mainActivity).title(R.string.categorize_as)
                .adapter(new NavDrawerCategoryAdapter(mainActivity, categories, currentCategory), null)
                .positiveText(R.string.add_category).positiveColorRes(R.color.colorPrimary)
                .negativeText(R.string.remove_category).negativeColorRes(R.color.colorAccent)
                .callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                        Intent intent = new Intent(mainActivity, CategoryActivity.class);
                        intent.putExtra("noHome", true);
                        startActivityForResult(intent, CATEGORY);
                    }

                    @Override
                    public void onNegative(MaterialDialog dialog) {
                        noteTmp.setCategory(null);
                        setTagMarkerColor(null);
                    }
                }).build();

        dialog.getListView().setOnItemClickListener((parent, view, position, id) -> {
            noteTmp.setCategory(categories.get(position));
            setTagMarkerColor(categories.get(position));
            dialog.dismiss();
        });

        dialog.show();
    }

    // The method that displays the popup.
    @SuppressWarnings("deprecation")
    private void showPopup(View anchor) {
        DisplayMetrics metrics = new DisplayMetrics();
        mainActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

        // Inflate the popup_layout.xml
        LayoutInflater inflater = (LayoutInflater) mainActivity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        View layout = inflater.inflate(R.layout.attachment_dialog, null);

        // Creating the PopupWindow
        attachmentDialog = new PopupWindow(mainActivity);
        attachmentDialog.setContentView(layout);
        attachmentDialog.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
        attachmentDialog.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
        attachmentDialog.setFocusable(true);
        attachmentDialog.setOnDismissListener(() -> {
            if (isRecording) {
                isRecording = false;
                stopRecording();
            }
        });

        // Clear the default translucent background
        attachmentDialog.setBackgroundDrawable(new BitmapDrawable());

        // Camera
        android.widget.TextView cameraSelection = (android.widget.TextView) layout.findViewById(R.id.camera);
        cameraSelection.setOnClickListener(new AttachmentOnClickListener());
        // Audio recording
        android.widget.TextView recordingSelection = (android.widget.TextView) layout.findViewById(R.id.recording);
        recordingSelection.setOnClickListener(new AttachmentOnClickListener());
        // Video recording
        android.widget.TextView videoSelection = (android.widget.TextView) layout.findViewById(R.id.video);
        videoSelection.setOnClickListener(new AttachmentOnClickListener());
        // Files
        android.widget.TextView filesSelection = (android.widget.TextView) layout.findViewById(R.id.files);
        filesSelection.setOnClickListener(new AttachmentOnClickListener());
        // Sketch
        android.widget.TextView sketchSelection = (android.widget.TextView) layout.findViewById(R.id.sketch);
        sketchSelection.setOnClickListener(new AttachmentOnClickListener());
        // Location
        android.widget.TextView locationSelection = (android.widget.TextView) layout.findViewById(R.id.location);
        locationSelection.setOnClickListener(new AttachmentOnClickListener());
        // Desktop note with PushBullet
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            android.widget.TextView pushbulletSelection = (android.widget.TextView) layout
                    .findViewById(R.id.pushbullet);
            pushbulletSelection.setVisibility(View.VISIBLE);
            pushbulletSelection.setOnClickListener(new AttachmentOnClickListener());
        }

        try {
            attachmentDialog.showAsDropDown(anchor);
        } catch (Exception e) {
            Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.error, ONStyle.ALERT);

        }
    }

    private void takePhoto() {
        // Checks for camera app available
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (!IntentChecker.isAvailable(mainActivity, intent, new String[] { PackageManager.FEATURE_CAMERA })) {
            //mainActivity.showMessage(R.string.feature_not_available_on_this_device, ONStyle.ALERT);
            Toast.makeText(getActivity(), R.string.feature_not_available_on_this_device, Toast.LENGTH_SHORT).show();

            return;
        }
        // Checks for created file validity
        File f = StorageHelper.createNewAttachmentFile(mainActivity, Constants.MIME_TYPE_IMAGE_EXT);
        if (f == null) {
            Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.error, ONStyle.ALERT);
            return;
        }
        // Launches intent
        attachmentUri = Uri.fromFile(f);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, attachmentUri);
        startActivityForResult(intent, TAKE_PHOTO);
    }

    private void takeVideo() {
        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        if (!IntentChecker.isAvailable(mainActivity, takeVideoIntent,
                new String[] { PackageManager.FEATURE_CAMERA })) {
            //mainActivity.showMessage(R.string.feature_not_available_on_this_device, ONStyle.ALERT);
            Toast.makeText(getActivity(), R.string.feature_not_available_on_this_device, Toast.LENGTH_SHORT).show();

            return;
        }
        // File is stored in custom ON folder to speedup the attachment
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
            File f = StorageHelper.createNewAttachmentFile(mainActivity, Constants.MIME_TYPE_VIDEO_EXT);
            if (f == null) {
                //mainActivity.showMessage(R.string.error, ONStyle.ALERT);
                Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();

                return;
            }
            attachmentUri = Uri.fromFile(f);
            takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, attachmentUri);
        }
        String maxVideoSizeStr = "".equals(prefs.getString("settings_max_video_size", "")) ? "0"
                : prefs.getString("settings_max_video_size", "");
        int maxVideoSize = parseInt(maxVideoSizeStr);
        takeVideoIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, Long.valueOf(maxVideoSize * 1024 * 1024));
        startActivityForResult(takeVideoIntent, TAKE_VIDEO);
    }

    private void takeSketch(Attachment attachment) {

        File f = StorageHelper.createNewAttachmentFile(mainActivity, Constants.MIME_TYPE_SKETCH_EXT);
        if (f == null) {
            Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.error, ONStyle.ALERT);
            return;
        }
        attachmentUri = Uri.fromFile(f);

        // Forces portrait orientation to this fragment only
        mainActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        // Fragments replacing
        FragmentTransaction transaction = mainActivity.getSupportFragmentManager().beginTransaction();
        mainActivity.animateTransition(transaction, mainActivity.TRANSITION_HORIZONTAL);
        SketchFragment mSketchFragment = new SketchFragment();
        Bundle b = new Bundle();
        b.putParcelable(MediaStore.EXTRA_OUTPUT, attachmentUri);
        if (attachment != null) {
            b.putParcelable("base", attachment.getUri());
        }
        mSketchFragment.setArguments(b);
        transaction.replace(R.id.fragment_container, mSketchFragment, mainActivity.FRAGMENT_SKETCH_TAG)
                .addToBackStack(mainActivity.FRAGMENT_DETAIL_TAG).commit();
    }

    @SuppressLint("NewApi")
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // Fetch uri from activities, store into adapter and refresh adapter
        Attachment attachment;
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
            case TAKE_PHOTO:
                attachment = new Attachment(attachmentUri, Constants.MIME_TYPE_IMAGE);
                addAttachment(attachment);
                mAttachmentAdapter.notifyDataSetChanged();
                mGridView.autoresize();
                break;
            case TAKE_VIDEO:
                // Gingerbread doesn't allow custom folder so data are retrieved from intent
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
                    attachment = new Attachment(attachmentUri, Constants.MIME_TYPE_VIDEO);
                } else {
                    attachment = new Attachment(intent.getData(), Constants.MIME_TYPE_VIDEO);
                }
                addAttachment(attachment);
                mAttachmentAdapter.notifyDataSetChanged();
                mGridView.autoresize();
                break;
            case FILES:
                onActivityResultManageReceivedFiles(intent);
                break;
            case SET_PASSWORD:
                noteTmp.setPasswordChecked(true);
                lockUnlock();
                break;
            case SKETCH:
                attachment = new Attachment(attachmentUri, Constants.MIME_TYPE_SKETCH);
                addAttachment(attachment);
                mAttachmentAdapter.notifyDataSetChanged();
                mGridView.autoresize();
                break;
            case CATEGORY:
                Toast.makeText(getActivity(), R.string.category_saved, Toast.LENGTH_SHORT).show();
                //mainActivity.showMessage(R.string.category_saved, ONStyle.CONFIRM);
                Category category = intent.getParcelableExtra("category");
                noteTmp.setCategory(category);
                setTagMarkerColor(category);
                break;
            case DETAIL:
                Toast.makeText(getActivity(), R.string.note_updated, Toast.LENGTH_SHORT).show();
                //mainActivity.showMessage(R.string.note_updated, ONStyle.CONFIRM);
                break;
            default:
                Log.e(Constants.TAG, "Wrong element choosen: " + requestCode);
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void onActivityResultManageReceivedFiles(Intent intent) {
        List<Uri> uris = new ArrayList<>();
        if (Build.VERSION.SDK_INT > 16 && intent.getClipData() != null) {
            for (int i = 0; i < intent.getClipData().getItemCount(); i++) {
                uris.add(intent.getClipData().getItemAt(i).getUri());
            }
        } else {
            uris.add(intent.getData());
        }
        for (Uri uri : uris) {
            String name = FileHelper.getNameFromUri(mainActivity, uri);
            new AttachmentTask(this, uri, name, this).execute();
        }
    }

    /**
     * Discards changes done to the note and eventually delete new attachments
     */
    @SuppressLint("NewApi")
    private void discard() {
        // Checks if some new files have been attached and must be removed
        if (!noteTmp.getAttachmentsList().equals(note.getAttachmentsList())) {
            for (Attachment newAttachment : noteTmp.getAttachmentsList()) {
                if (!note.getAttachmentsList().contains(newAttachment)) {
                    StorageHelper.delete(mainActivity, newAttachment.getUri().getPath());
                }
            }
        }

        goBack = true;

        if (!noteTmp.equals(noteOriginal)) {
            // Restore original status of the note
            if (noteOriginal.get_id() == null) {
                mainActivity.deleteNote(noteTmp);
                goHome();
            } else {
                SaveNoteTask saveNoteTask = new SaveNoteTask(this, false);
                if (Build.VERSION.SDK_INT >= 11) {
                    saveNoteTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, noteOriginal);
                } else {
                    saveNoteTask.execute(noteOriginal);
                }
            }
            MainActivity.notifyAppWidgets(mainActivity);
        } else {
            goHome();
        }
    }

    @SuppressLint("NewApi")
    private void archiveNote(boolean archive) {
        // Simply go back if is a new note
        if (noteTmp.get_id() == null) {
            goHome();
            return;
        }

        noteTmp.setArchived(archive);
        goBack = true;
        exitMessage = archive ? getString(R.string.note_archived) : getString(R.string.note_unarchived);
        exitCroutonStyle = archive ? ONStyle.WARN : ONStyle.INFO;

        saveNote(this);
    }

    @SuppressLint("NewApi")
    private void trashNote(boolean trash) {
        // Simply go back if is a new note
        if (noteTmp.get_id() == null) {
            goHome();
            return;
        }

        noteTmp.setTrashed(trash);
        goBack = true;
        exitMessage = trash ? getString(R.string.note_trashed) : getString(R.string.note_untrashed);
        exitCroutonStyle = trash ? ONStyle.WARN : ONStyle.INFO;
        if (trash) {
            ShortcutHelper.removeshortCut(IdealNote.getAppContext(), noteTmp);
            ReminderHelper.removeReminder(IdealNote.getAppContext(), noteTmp);
        } else {
            ReminderHelper.addReminder(IdealNote.getAppContext(), note);
        }
        saveNote(this);
    }

    private void deleteNote() {
        new MaterialDialog.Builder(mainActivity).content(R.string.delete_note_confirmation)
                .positiveText(R.string.ok).callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog materialDialog) {
                        mainActivity.deleteNote(noteTmp);
                        Log.d(Constants.TAG, "Deleted note with id '" + noteTmp.get_id() + "'");
                        //mainActivity.showMessage(R.string.note_deleted, ONStyle.ALERT);
                        Toast.makeText(getActivity(), R.string.note_deleted, Toast.LENGTH_SHORT).show();
                        goHome();
                    }
                }).build().show();
    }

    public void saveAndExit(OnNoteSaved mOnNoteSaved) {
        if (isAdded()) {
            //exitMessage = getString(R.string.note_updated);
            //Toast.makeText(getActivity(), R.string.note_updated,
            //Toast.LENGTH_SHORT).show();
            //exitCroutonStyle = ONStyle.CONFIRM;
            goBack = true;
            saveNote(mOnNoteSaved);
        }
    }

    /**
     * Save new notes, modify them or archive
     */
    void saveNote(OnNoteSaved mOnNoteSaved) {

        // Changed fields
        noteTmp.setTitle(getNoteTitle());
        noteTmp.setContent(getNoteContent());

        // Check if some text or attachments of any type have been inserted or
        // is an empty note
        if (goBack && TextUtils.isEmpty(noteTmp.getTitle()) && TextUtils.isEmpty(noteTmp.getContent())
                && noteTmp.getAttachmentsList().size() == 0) {
            Log.d(Constants.TAG, "Empty note not saved");
            //exitMessage = getString(R.string.empty_note_not_saved);
            Toast.makeText(getActivity(), R.string.empty_note_not_saved, Toast.LENGTH_SHORT).show();
            //exitCroutonStyle = ONStyle.INFO;
            goHome();
            return;
        }

        if (saveNotNeeded()) {
            exitMessage = "";
            if (goBack) {
                goHome();
            }
            return;
        }

        noteTmp.setAttachmentsListOld(note.getAttachmentsList());

        new SaveNoteTask(mOnNoteSaved, lastModificationUpdatedNeeded())
                .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, noteTmp);
    }

    /**
     * Checks if nothing is changed to avoid committing if possible (check)
     */
    private boolean saveNotNeeded() {
        if (noteTmp.get_id() == null && prefs.getBoolean(Constants.PREF_AUTO_LOCATION, false)) {
            note.setLatitude(noteTmp.getLatitude());
            note.setLongitude(noteTmp.getLongitude());
        }
        return !noteTmp.isChanged(note) || (noteTmp.isLocked() && !noteTmp.isPasswordChecked());
    }

    /**
     * Checks if only tag, archive or trash status have been changed
     * and then force to not update last modification date*
     */
    private boolean lastModificationUpdatedNeeded() {
        note.setCategory(noteTmp.getCategory());
        note.setArchived(noteTmp.isArchived());
        note.setTrashed(noteTmp.isTrashed());
        note.setLocked(noteTmp.isLocked());
        return noteTmp.isChanged(note);
    }

    @Override
    public void onNoteSaved(Note noteSaved) {
        MainActivity.notifyAppWidgets(IdealNote.getAppContext());
        if (!activityPausing) {
            EventBus.getDefault().post(new NotesUpdatedEvent());
            deleteMergedNotes(mergedNotesIds);
            if (noteTmp.getAlarm() != null && !noteTmp.getAlarm().equals(note.getAlarm())) {
                ReminderHelper.showReminderMessage(noteTmp.getAlarm());
            }
        }
        note = new Note(noteSaved);
        if (goBack) {
            goHome();
        }
    }

    private void deleteMergedNotes(List<String> mergedNotesIds) {
        ArrayList<Note> notesToDelete = new ArrayList<Note>();
        if (mergedNotesIds != null) {
            for (String mergedNoteId : mergedNotesIds) {
                Note note = new Note();
                note.set_id(Long.valueOf(mergedNoteId));
                notesToDelete.add(note);
            }
            new NoteProcessorDelete(notesToDelete).process();
        }
    }

    private String getNoteTitle() {
        if (title != null && !TextUtils.isEmpty(title.getText())) {
            return title.getText().toString();
        } else {
            return "";
        }
    }

    private String getNoteContent() {
        String contentText = "";
        if (!noteTmp.isChecklist()) {
            // Due to checklist library introduction the returned EditText class is no more a
            // com.neopixl.pixlui.components.edittext.EditText but a standard android.widget.EditText
            View contentView = root.findViewById(R.id.detail_content);
            if (contentView instanceof EditText) {
                contentText = ((EditText) contentView).getText().toString();
            } else if (contentView instanceof android.widget.EditText) {
                contentText = ((android.widget.EditText) contentView).getText().toString();
            }
        } else {
            if (mChecklistManager != null) {
                mChecklistManager.keepChecked(true).showCheckMarks(true);
                contentText = mChecklistManager.getText();
            }
        }
        return contentText;
    }

    /**
     * Updates share intent
     */
    private void shareNote() {
        Note sharedNote = new Note(noteTmp);
        sharedNote.setTitle(getNoteTitle());
        sharedNote.setContent(getNoteContent());
        mainActivity.shareNote(sharedNote);
    }

    /**
     * Notes locking with security password to avoid viewing, editing or deleting from unauthorized
     */
    private void lockNote() {
        Log.d(Constants.TAG, "Locking or unlocking note " + note.get_id());

        // If security password is not set yes will be set right now
        if (prefs.getString(Constants.PREF_PASSWORD, null) == null) {
            Intent passwordIntent = new Intent(mainActivity, PasswordActivity.class);
            startActivityForResult(passwordIntent, SET_PASSWORD);
            return;
        }

        // If password has already been inserted will not be asked again
        if (noteTmp.isPasswordChecked() || prefs.getBoolean("settings_password_access", false)) {
            lockUnlock();
            return;
        }

        // Password will be requested here
        PasswordHelper.requestPassword(mainActivity, passwordConfirmed -> {
            if (passwordConfirmed) {
                lockUnlock();
            }
        });
    }

    private void lockUnlock() {
        // Empty password has been set
        if (prefs.getString(Constants.PREF_PASSWORD, null) == null) {
            mainActivity.showMessage(R.string.password_not_set, ONStyle.WARN);

            return;
        }
        // Otherwise locking is performed
        if (noteTmp.isLocked()) {
            Toast.makeText(getActivity(), R.string.save_note_to_lock_it, Toast.LENGTH_LONG).show();
            //mainActivity.showMessage(R.string.save_note_to_lock_it, ONStyle.INFO);
            mainActivity.supportInvalidateOptionsMenu();
        } else {
            Toast.makeText(getActivity(), R.string.save_note_to_lock_it, Toast.LENGTH_LONG).show();
            //mainActivity.showMessage(R.string.save_note_to_lock_it, ONStyle.INFO);
            mainActivity.supportInvalidateOptionsMenu();
        }
        noteTmp.setLocked(!noteTmp.isLocked());
        noteTmp.setPasswordChecked(true);
    }

    /**
     * Used to set actual reminder state when initializing a note to be edited
     */
    private String initReminder(Note note) {
        if (noteTmp.getAlarm() == null) {
            return "";
        }
        long reminder = Long.parseLong(note.getAlarm());
        String rrule = note.getRecurrenceRule();
        if (!TextUtils.isEmpty(rrule)) {
            return DateHelper.getNoteRecurrentReminderText(reminder, rrule);
        } else {
            return DateHelper.getNoteReminderText(reminder);
        }
    }

    /**
     * Audio recordings playback
     */
    private void playback(View v, Uri uri) {
        // Some recording is playing right now
        if (mPlayer != null && mPlayer.isPlaying()) {
            if (isPlayingView != v) {
                // If the audio actually played is NOT the one from the click view the last one is played
                stopPlaying();
                isPlayingView = v;
                startPlaying(uri);
                replacePlayingAudioBitmap(v);
            } else {
                // Otherwise just stops playing
                stopPlaying();
            }
        } else {
            // If nothing is playing audio just plays
            isPlayingView = v;
            startPlaying(uri);
            replacePlayingAudioBitmap(v);
        }
    }

    private void replacePlayingAudioBitmap(View v) {
        Drawable d = ((ImageView) v.findViewById(R.id.gridview_item_picture)).getDrawable();
        if (BitmapDrawable.class.isAssignableFrom(d.getClass())) {
            recordingBitmap = ((BitmapDrawable) d).getBitmap();
        } else {
            recordingBitmap = ((GlideBitmapDrawable) d.getCurrent()).getBitmap();
        }
        ((ImageView) v.findViewById(R.id.gridview_item_picture)).setImageBitmap(ThumbnailUtils.extractThumbnail(
                BitmapFactory.decodeResource(mainActivity.getResources(), R.drawable.stop),
                Constants.THUMBNAIL_SIZE, Constants.THUMBNAIL_SIZE));
    }

    private void startPlaying(Uri uri) {
        if (mPlayer == null) {
            mPlayer = new MediaPlayer();
        }
        try {
            mPlayer.setDataSource(mainActivity, uri);
            mPlayer.prepare();
            mPlayer.start();
            mPlayer.setOnCompletionListener(mp -> {
                mPlayer = null;
                if (isPlayingView != null) {
                    ((ImageView) isPlayingView.findViewById(R.id.gridview_item_picture))
                            .setImageBitmap(recordingBitmap);
                    recordingBitmap = null;
                    isPlayingView = null;
                }
            });
        } catch (IOException e) {
            Log.e(Constants.TAG, "prepare() failed", e);
            Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.error, ONStyle.ALERT);
        }
    }

    private void stopPlaying() {
        if (mPlayer != null) {
            if (isPlayingView != null) {
                ((ImageView) isPlayingView.findViewById(R.id.gridview_item_picture))
                        .setImageBitmap(recordingBitmap);
            }
            isPlayingView = null;
            recordingBitmap = null;
            mPlayer.release();
            mPlayer = null;
        }
    }

    private void startRecording(View v) {
        PermissionsHelper.requestPermission(getActivity(), Manifest.permission.RECORD_AUDIO,
                R.string.permission_audio_recording, snackBarPlaceholder, () -> {

                    isRecording = true;
                    android.widget.TextView mTextView = (android.widget.TextView) v;
                    mTextView.setText(getString(R.string.stop));
                    mTextView.setTextColor(Color.parseColor("#ff0000"));

                    File f = StorageHelper.createNewAttachmentFile(mainActivity, Constants.MIME_TYPE_AUDIO_EXT);
                    if (f == null) {
                        Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();
                        //mainActivity.showMessage(R.string.error, ONStyle.ALERT);
                        return;
                    }
                    if (mRecorder == null) {
                        mRecorder = new MediaRecorder();
                        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
                        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
                        mRecorder.setAudioEncodingBitRate(96000);
                        mRecorder.setAudioSamplingRate(44100);
                    }
                    recordName = f.getAbsolutePath();
                    mRecorder.setOutputFile(recordName);

                    try {
                        audioRecordingTimeStart = Calendar.getInstance().getTimeInMillis();
                        mRecorder.prepare();
                        mRecorder.start();
                    } catch (IOException | IllegalStateException e) {
                        Log.e(Constants.TAG, "prepare() failed", e);
                        Toast.makeText(getActivity(), R.string.error, Toast.LENGTH_SHORT).show();
                        //mainActivity.showMessage(R.string.error, ONStyle.ALERT);
                    }
                });
    }

    private void stopRecording() {
        if (mRecorder != null) {
            mRecorder.stop();
            audioRecordingTime = Calendar.getInstance().getTimeInMillis() - audioRecordingTimeStart;
            mRecorder.release();
            mRecorder = null;
        }
    }

    private void fade(final View v, boolean fadeIn) {

        int anim = R.animator.fade_out_support;
        int visibilityTemp = View.GONE;

        if (fadeIn) {
            anim = R.animator.fade_in_support;
            visibilityTemp = View.VISIBLE;
        }

        final int visibility = visibilityTemp;

        // Checks if user has left the app
        if (mainActivity != null) {
            Animation mAnimation = AnimationUtils.loadAnimation(mainActivity, anim);
            mAnimation.setAnimationListener(new AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                    // Nothind to do
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                    // Nothind to do
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    v.setVisibility(visibility);
                }
            });
            v.startAnimation(mAnimation);
        }
    }

    /**
     * Adding shortcut on Home screen
     */
    private void addShortcut() {
        ShortcutHelper.addShortcut(IdealNote.getAppContext(), noteTmp);
        Toast.makeText(getActivity(), R.string.shortcut_added, Toast.LENGTH_SHORT).show();
        //mainActivity.showMessage(R.string.shortcut_added, ONStyle.INFO);
    }

    TextLinkClickListener textLinkClickListener = new TextLinkClickListener() {
        @Override
        public void onTextLinkClick(View view, final String clickedString, final String url) {
            new MaterialDialog.Builder(mainActivity).content(clickedString).negativeColorRes(R.color.colorPrimary)
                    .positiveText(R.string.open).negativeText(R.string.copy)
                    .callback(new MaterialDialog.ButtonCallback() {
                        @Override
                        public void onPositive(MaterialDialog dialog) {
                            boolean error = false;
                            Intent intent = null;
                            try {
                                intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                                intent.addCategory(Intent.CATEGORY_BROWSABLE);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            } catch (NullPointerException e) {
                                error = true;
                            }

                            if (intent == null || error || !IntentChecker.isAvailable(mainActivity, intent,
                                    new String[] { PackageManager.FEATURE_CAMERA })) {
                                mainActivity.showMessage(R.string.no_application_can_perform_this_action,
                                        ONStyle.ALERT);
                            } else {
                                startActivity(intent);
                            }
                        }

                        @Override
                        public void onNegative(MaterialDialog dialog) {
                            android.content.ClipboardManager clipboard = (android.content.ClipboardManager) mainActivity
                                    .getSystemService(Activity.CLIPBOARD_SERVICE);
                            android.content.ClipData clip = android.content.ClipData.newPlainText("text label",
                                    clickedString);
                            clipboard.setPrimaryClip(clip);
                        }
                    }).build().show();
            View clickedView = noteTmp.isChecklist() ? toggleChecklistView : content;
            clickedView.clearFocus();
            KeyboardUtils.hideKeyboard(clickedView);
            new Handler().post(new Runnable() {
                @Override
                public void run() {
                    View clickedView = noteTmp.isChecklist() ? toggleChecklistView : content;
                    KeyboardUtils.hideKeyboard(clickedView);
                }
            });
        }
    };

    @SuppressLint("NewApi")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:
            Log.v(Constants.TAG, "MotionEvent.ACTION_DOWN");
            int w;

            Point displaySize = Display.getUsableSize(mainActivity);
            w = displaySize.x;

            if (x < Constants.SWIPE_MARGIN || x > w - Constants.SWIPE_MARGIN) {
                swiping = true;
                startSwipeX = x;
            }

            break;

        case MotionEvent.ACTION_UP:
            Log.v(Constants.TAG, "MotionEvent.ACTION_UP");
            if (swiping)
                swiping = false;
            break;

        case MotionEvent.ACTION_MOVE:
            if (swiping) {
                Log.v(Constants.TAG, "MotionEvent.ACTION_MOVE at position " + x + ", " + y);
                if (Math.abs(x - startSwipeX) > Constants.SWIPE_OFFSET) {
                    swiping = false;
                    FragmentTransaction transaction = mainActivity.getSupportFragmentManager().beginTransaction();
                    mainActivity.animateTransition(transaction, mainActivity.TRANSITION_VERTICAL);
                    DetailFragment mDetailFragment = new DetailFragment();
                    Bundle b = new Bundle();
                    b.putParcelable(Constants.INTENT_NOTE, new Note());
                    mDetailFragment.setArguments(b);
                    transaction.replace(R.id.fragment_container, mDetailFragment, mainActivity.FRAGMENT_DETAIL_TAG)
                            .addToBackStack(mainActivity.FRAGMENT_DETAIL_TAG).commit();
                }
            }
            break;

        default:
            Log.e(Constants.TAG, "Wrong element choosen: " + event.getAction());
        }

        return true;
    }

    @Override
    public void onGlobalLayout() {
        int screenHeight = Display.getUsableSize(mainActivity).y;
        int navBarOffset = Display.orientationLandscape(mainActivity) ? 0
                : DensityUtil.pxToDp(Display.getNavigationBarHeight(mainActivity.getWindow().getDecorView()),
                        mainActivity);
        int heightDiff = screenHeight - Display.getVisibleSize(mainActivity).y + navBarOffset;
        boolean keyboardVisible = heightDiff > 150;
        if (keyboardVisible && keyboardPlaceholder == null) {
            shrinkLayouts(heightDiff);
        } else if (!keyboardVisible && keyboardPlaceholder != null) {
            restoreLayouts();
        }
    }

    private void shrinkLayouts(int heightDiff) {
        detailWrapperView.removeView(timestampsView);
        keyboardPlaceholder = new View(mainActivity);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
                    && Display.orientationLandscape(mainActivity))) {
                root.addView(keyboardPlaceholder, LinearLayout.LayoutParams.MATCH_PARENT, heightDiff);
            }
        }
    }

    private void restoreLayouts() {
        if (root != null) {
            ViewGroup wrapper = (ViewGroup) root.findViewById(R.id.detail_wrapper);
            if (root.indexOfChild(keyboardPlaceholder) != -1) {
                root.removeView(keyboardPlaceholder);
            }
            keyboardPlaceholder = null;
            if (wrapper.indexOfChild(timestampsView) == -1) {
                wrapper.addView(timestampsView);
            }
        }
    }

    @Override
    public void onAttachingFileErrorOccurred(Attachment mAttachment) {
        Toast.makeText(getActivity(), R.string.error_saving_attachments, Toast.LENGTH_SHORT).show();
        //mainActivity.showMessage(R.string.error_saving_attachments, ONStyle.ALERT);
        if (noteTmp.getAttachmentsList().contains(mAttachment)) {
            removeAttachment(mAttachment);
            mAttachmentAdapter.notifyDataSetChanged();
            mGridView.autoresize();
        }
    }

    private void addAttachment(Attachment attachment) {
        noteTmp.addAttachment(attachment);
        //      mAttachmentAdapter.getAttachmentsList().add(attachment);
    }

    private void removeAttachment(Attachment mAttachment) {
        noteTmp.removeAttachment(mAttachment);
        //      mAttachmentAdapter.getAttachmentsList().remove(mAttachment);
    }

    private void removeAttachment(int position) {
        noteTmp.removeAttachment(noteTmp.getAttachmentsList().get(position));
        //      mAttachmentAdapter.getAttachmentsList().remove(position);
    }

    private void removeAllAttachments() {
        noteTmp.setAttachmentsList(new ArrayList<>());
        mAttachmentAdapter = new AttachmentAdapter(mainActivity, new ArrayList<>(), mGridView);
        mGridView.invalidateViews();
        mGridView.setAdapter(mAttachmentAdapter);
    }

    @Override
    public void onAttachingFileFinished(Attachment mAttachment) {
        addAttachment(mAttachment);
        mAttachmentAdapter.notifyDataSetChanged();
        mGridView.autoresize();
    }

    @Override
    public void onReminderPicked(long reminder) {
        noteTmp.setAlarm(reminder);
        if (mFragment.isAdded()) {
            reminderIcon.setImageResource(R.drawable.ic_alarm_black_18dp);
            datetime.setText(DateHelper.getNoteReminderText(reminder));
        }
    }

    @Override
    public void onRecurrenceReminderPicked(String recurrenceRule) {
        noteTmp.setRecurrenceRule(recurrenceRule);
        if (!TextUtils.isEmpty(recurrenceRule)) {
            Log.d(Constants.TAG, "Recurrent reminder set: " + recurrenceRule);
            datetime.setText(
                    DateHelper.getNoteRecurrentReminderText(Long.parseLong(noteTmp.getAlarm()), recurrenceRule));
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        scrollContent();
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // Nothing to do
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Nothing to do
    }

    @Override
    public void onCheckListChanged() {
        scrollContent();
    }

    private void scrollContent() {
        if (noteTmp.isChecklist()) {
            if (mChecklistManager.getCount() > contentLineCounter) {
                scrollView.scrollBy(0, 60);
            }
            contentLineCounter = mChecklistManager.getCount();
        } else {
            if (content.getLineCount() > contentLineCounter) {
                scrollView.scrollBy(0, 60);
            }
            contentLineCounter = content.getLineCount();
        }
    }

    /**
     * Add previously created tags to content
     */
    private void addTags() {
        contentCursorPosition = getCursorIndex();

        // Retrieves all available categories
        final List<Tag> tags = TagsHelper.getAllTags();

        // If there is no tag a message will be shown
        if (tags.size() == 0) {
            Toast.makeText(getActivity(), R.string.no_tags_created, Toast.LENGTH_SHORT).show();
            //mainActivity.showMessage(R.string.no_tags_created, ONStyle.WARN);
            return;
        }

        final Note currentNote = new Note();
        currentNote.setTitle(getNoteTitle());
        currentNote.setContent(getNoteContent());
        Integer[] preselectedTags = TagsHelper.getPreselectedTagsArray(currentNote, tags);

        // Dialog and events creation
        MaterialDialog dialog = new MaterialDialog.Builder(mainActivity).title(R.string.select_tags)
                .positiveText(R.string.ok).items(TagsHelper.getTagsArray(tags))
                .itemsCallbackMultiChoice(preselectedTags, (dialog1, which, text) -> {
                    dialog1.dismiss();
                    tagNote(tags, which, currentNote);
                    return false;
                }).build();
        dialog.show();
    }

    private void tagNote(List<Tag> tags, Integer[] selectedTags, Note note) {
        Pair<String, List<Tag>> taggingResult = TagsHelper.addTagToNote(tags, selectedTags, note);

        StringBuilder sb;
        if (noteTmp.isChecklist()) {
            CheckListViewItem mCheckListViewItem = mChecklistManager.getFocusedItemView();
            if (mCheckListViewItem != null) {
                sb = new StringBuilder(mCheckListViewItem.getText());
                sb.insert(contentCursorPosition, " " + taggingResult.first + " ");
                mCheckListViewItem.setText(sb.toString());
                mCheckListViewItem.getEditText()
                        .setSelection(contentCursorPosition + taggingResult.first.length() + 1);
            } else {
                title.append(" " + taggingResult.first);
            }
        } else {
            sb = new StringBuilder(getNoteContent());
            if (content.hasFocus()) {
                sb.insert(contentCursorPosition, " " + taggingResult.first + " ");
                content.setText(sb.toString());
                content.setSelection(contentCursorPosition + taggingResult.first.length() + 1);
            } else {
                if (getNoteContent().trim().length() > 0) {
                    sb.append(System.getProperty("line.separator")).append(System.getProperty("line.separator"));
                }
                sb.append(taggingResult.first);
                content.setText(sb.toString());
            }
        }

        // Removes unchecked tags
        if (taggingResult.second.size() > 0) {
            if (noteTmp.isChecklist()) {
                toggleChecklist2(true, true);
            }
            Pair<String, String> titleAndContent = TagsHelper.removeTag(getNoteTitle(), getNoteContent(),
                    taggingResult.second);
            title.setText(titleAndContent.first);
            content.setText(titleAndContent.second);
            if (noteTmp.isChecklist()) {
                toggleChecklist2();
            }
        }
    }

    private int getCursorIndex() {
        if (!noteTmp.isChecklist()) {
            return content.getSelectionStart();
        } else {
            CheckListViewItem mCheckListViewItem = mChecklistManager.getFocusedItemView();
            if (mCheckListViewItem != null) {
                return mCheckListViewItem.getEditText().getSelectionStart();
            } else {
                return 0;
            }
        }
    }

    /**
     * Used to check currently opened note from activity to avoid openind multiple times the same one
     */
    public Note getCurrentNote() {
        return note;
    }

    private boolean isNoteLocationValid() {
        return noteTmp.getLatitude() != null && noteTmp.getLatitude() != 0 && noteTmp.getLongitude() != null
                && noteTmp.getLongitude() != 0;
    }

    /**
     * Manages clicks on attachment dialog
     */
    @SuppressLint("InlinedApi")
    private class AttachmentOnClickListener implements OnClickListener {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            // Photo from camera
            case R.id.camera:
                takePhoto();
                attachmentDialog.dismiss();
                break;
            case R.id.recording:
                if (!isRecording) {
                    startRecording(v);
                } else {
                    isRecording = false;
                    stopRecording();
                    Attachment attachment = new Attachment(Uri.fromFile(new File(recordName)),
                            Constants.MIME_TYPE_AUDIO);
                    attachment.setLength(audioRecordingTime);
                    addAttachment(attachment);
                    mAttachmentAdapter.notifyDataSetChanged();
                    mGridView.autoresize();
                    attachmentDialog.dismiss();
                }
                break;
            case R.id.video:
                takeVideo();
                attachmentDialog.dismiss();
                break;
            case R.id.files:
                Intent filesIntent;
                filesIntent = new Intent(Intent.ACTION_GET_CONTENT);
                filesIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                filesIntent.addCategory(Intent.CATEGORY_OPENABLE);
                filesIntent.setType("*/*");
                startActivityForResult(filesIntent, FILES);
                attachmentDialog.dismiss();
                break;
            case R.id.sketch:
                takeSketch(null);
                attachmentDialog.dismiss();
                break;
            case R.id.location:
                displayLocationDialog();
                attachmentDialog.dismiss();
                break;
            case R.id.pushbullet:
                MessagingExtension.mirrorMessage(mainActivity, getString(R.string.app_name),
                        getString(R.string.pushbullet), getNoteContent(),
                        BitmapFactory.decodeResource(getResources(), R.drawable.ic_stat_literal_icon), null, 0);
                attachmentDialog.dismiss();
                break;
            default:
                Log.e(Constants.TAG, "Wrong element choosen: " + v.getId());
            }
        }
    }

    public void onEventMainThread(PushbulletReplyEvent pushbulletReplyEvent) {
        content.setText(getNoteContent() + System.getProperty("line.separator") + pushbulletReplyEvent.message);
    }
}