com.todoroo.astrid.notes.EditNoteActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.todoroo.astrid.notes.EditNoteActivity.java

Source

/**
 * Copyright (c) 2012 Todoroo Inc
 *
 * See the file "LICENSE" for the full license governing this code.
 */
package com.todoroo.astrid.notes;

import android.app.Activity;
import android.content.Intent;
import android.database.sqlite.SQLiteException;
import android.graphics.Color;
import android.net.Uri;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateUtils;
import android.text.util.Linkify;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;

import com.todoroo.andlib.data.Callback;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.actfm.ActFmCameraModule;
import com.todoroo.astrid.actfm.ActFmCameraModule.CameraResultCallback;
import com.todoroo.astrid.actfm.ActFmCameraModule.ClearImageCallback;
import com.todoroo.astrid.activity.AstridActivity;
import com.todoroo.astrid.activity.TaskEditFragment;
import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.UserActivityDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.service.StartupService;
import com.todoroo.astrid.service.TaskService;
import com.todoroo.astrid.timers.TimerActionControlSet.TimerActionListener;

import org.json.JSONObject;
import org.tasks.R;
import org.tasks.preferences.Preferences;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import static org.tasks.date.DateTimeUtils.newDate;
import static org.tasks.files.FileHelper.getPathFromUri;
import static org.tasks.files.ImageHelper.sampleBitmap;

public class EditNoteActivity extends LinearLayout implements TimerActionListener {

    private Task task;

    private ActFmCameraModule actFmCameraModule;
    private final Preferences preferences;
    private final MetadataDao metadataDao;
    private final UserActivityDao userActivityDao;
    private final TaskService taskService;
    private final ArrayList<NoteOrUpdate> items = new ArrayList<>();
    private EditText commentField;
    private final View commentsBar;
    private View timerView;
    private View commentButton;
    private int commentItems = 10;
    private ImageButton pictureButton;
    private Uri pendingCommentPicture = null;
    private final Fragment fragment;

    private final AstridActivity activity;

    private final int cameraButton;

    private static boolean respondToPicture = false;

    private final List<UpdatesChangedListener> listeners = new LinkedList<>();

    public interface UpdatesChangedListener {
        public void updatesChanged();

        public void commentAdded();
    }

    public EditNoteActivity(ActFmCameraModule actFmCameraModule, Preferences preferences, MetadataDao metadataDao,
            UserActivityDao userActivityDao, TaskService taskService, Fragment fragment, View parent, long t) {
        super(fragment.getActivity());
        this.actFmCameraModule = actFmCameraModule;
        this.preferences = preferences;
        this.metadataDao = metadataDao;
        this.userActivityDao = userActivityDao;
        this.taskService = taskService;

        this.fragment = fragment;

        this.activity = (AstridActivity) fragment.getActivity();

        cameraButton = getDefaultCameraButton();

        setOrientation(VERTICAL);

        commentsBar = parent.findViewById(R.id.updatesFooter);

        loadViewForTaskID(t);
    }

    private int getDefaultCameraButton() {
        TypedValue typedValue = new TypedValue();
        fragment.getActivity().getTheme().resolveAttribute(R.attr.ic_action_camera, typedValue, true);
        return typedValue.resourceId;
    }

    private void fetchTask(long id) {
        task = taskService.fetchById(id, Task.NOTES, Task.ID, Task.UUID, Task.TITLE);
    }

    public void loadViewForTaskID(long t) {
        try {
            fetchTask(t);
        } catch (SQLiteException e) {
            StartupService.handleSQLiteError(fragment.getActivity(), e);
        }
        if (task == null) {
            return;
        }
        setUpInterface();
        setUpListAdapter();
    }

    // --- UI preparation

    private void setUpInterface() {
        timerView = commentsBar.findViewById(R.id.timer_container);
        commentButton = commentsBar.findViewById(R.id.commentButton);
        commentField = (EditText) commentsBar.findViewById(R.id.commentField);

        final boolean showTimerShortcut = preferences.getBoolean(R.string.p_show_timer_shortcut, false);

        if (showTimerShortcut) {
            commentField.setOnFocusChangeListener(new OnFocusChangeListener() {
                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if (hasFocus) {
                        timerView.setVisibility(View.GONE);
                        commentButton.setVisibility(View.VISIBLE);
                    } else {
                        timerView.setVisibility(View.VISIBLE);
                        commentButton.setVisibility(View.GONE);
                    }
                }
            });
        }
        commentField.setHorizontallyScrolling(false);
        commentField.setMaxLines(Integer.MAX_VALUE);
        commentField.setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_ENTER) {
                    AndroidUtilities.hideSoftInputForViews(activity, commentField);
                    return true;
                }
                return false;
            }
        });
        commentField.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                commentField.setCursorVisible(true);
            }
        });

        commentField.addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable s) {
                commentButton.setVisibility(
                        (s.length() > 0 || pendingCommentPicture != null) ? View.VISIBLE : View.GONE);
                if (showTimerShortcut) {
                    timerView.setVisibility(
                            (s.length() > 0 || pendingCommentPicture != null) ? View.GONE : View.VISIBLE);
                }
            }

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

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

        commentField.setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
                if (commentField.getText().length() > 0) {
                    if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL) {
                        //                        commentField.setCursorVisible(false);
                        addComment();
                    }
                }
                return false;
            }
        });

        commentButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                addComment();
            }
        });

        final ClearImageCallback clearImage = new ClearImageCallback() {
            @Override
            public void clearImage() {
                pendingCommentPicture = null;
                pictureButton.setImageResource(cameraButton);
            }
        };
        pictureButton = (ImageButton) commentsBar.findViewById(R.id.picture);
        pictureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (pendingCommentPicture != null) {
                    actFmCameraModule.showPictureLauncher(clearImage);
                } else {
                    actFmCameraModule.showPictureLauncher(null);
                }
                respondToPicture = true;
            }
        });
        if (!TextUtils.isEmpty(task.getNotes())) {
            TextView notes = new TextView(getContext());
            notes.setLinkTextColor(Color.rgb(100, 160, 255));
            notes.setTextSize(18);
            notes.setText(task.getNotes());
            notes.setPadding(5, 10, 5, 10);
            Linkify.addLinks(notes, Linkify.ALL);
        }

        if (activity != null) {
            String uri = activity.getIntent().getStringExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS);
            if (uri != null) {
                pendingCommentPicture = Uri.parse(uri);
                setPictureButtonToPendingPicture();
            }
        }
    }

    private void setUpListAdapter() {
        items.clear();
        this.removeAllViews();
        metadataDao.byTaskAndKey(task.getId(), NoteMetadata.METADATA_KEY, new Callback<Metadata>() {
            @Override
            public void apply(Metadata metadata) {
                items.add(NoteOrUpdate.fromMetadata(metadata));
            }
        });

        userActivityDao.getCommentsForTask(task.getUuid(), new Callback<UserActivity>() {
            @Override
            public void apply(UserActivity update) {
                items.add(NoteOrUpdate.fromUpdate(update));
            }
        });

        Collections.sort(items, new Comparator<NoteOrUpdate>() {
            @Override
            public int compare(NoteOrUpdate a, NoteOrUpdate b) {
                if (a.createdAt < b.createdAt) {
                    return 1;
                } else if (a.createdAt == b.createdAt) {
                    return 0;
                } else {
                    return -1;
                }
            }
        });

        for (int i = 0; i < Math.min(items.size(), commentItems); i++) {
            View notesView = this.getUpdateNotes(items.get(i), this);
            this.addView(notesView);
        }

        if (items.size() > commentItems) {
            Button loadMore = new Button(getContext());
            loadMore.setText(R.string.TEA_load_more);
            loadMore.setTextColor(activity.getResources().getColor(R.color.task_edit_deadline_gray));
            loadMore.setBackgroundColor(Color.alpha(0));
            loadMore.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Perform action on click
                    commentItems += 10;
                    setUpListAdapter();
                }
            });
            this.addView(loadMore);
        }

        for (UpdatesChangedListener l : listeners) {
            l.updatesChanged();
        }
    }

    public View getUpdateNotes(NoteOrUpdate note, ViewGroup parent) {
        View convertView = ((Activity) getContext()).getLayoutInflater().inflate(R.layout.comment_adapter_row,
                parent, false);
        bindView(convertView, note);
        return convertView;
    }

    /** Helper method to set the contents and visibility of each field */
    public synchronized void bindView(View view, NoteOrUpdate item) {
        // name
        final TextView nameView = (TextView) view.findViewById(R.id.title);
        {
            nameView.setText(item.title);
            Linkify.addLinks(nameView, Linkify.ALL);
        }

        // date
        final TextView date = (TextView) view.findViewById(R.id.date);
        {
            CharSequence dateString = DateUtils.getRelativeTimeSpanString(item.createdAt, DateUtilities.now(),
                    DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
            date.setText(dateString);
        }

        // picture
        final ImageView commentPictureView = (ImageView) view.findViewById(R.id.comment_picture);
        setupImagePopupForCommentView(view, commentPictureView, item.commentBitmap, fragment);
    }

    private static void setupImagePopupForCommentView(View view, ImageView commentPictureView,
            final Uri updateBitmap, final Fragment fragment) {
        if (updateBitmap != null) { //$NON-NLS-1$
            commentPictureView.setVisibility(View.VISIBLE);
            String path = getPathFromUri(fragment.getActivity(), updateBitmap);
            commentPictureView.setImageBitmap(sampleBitmap(path, commentPictureView.getLayoutParams().width,
                    commentPictureView.getLayoutParams().height));

            view.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    fragment.startActivity(new Intent(Intent.ACTION_VIEW) {
                        {
                            setDataAndType(updateBitmap, "image/*");
                        }
                    });
                }
            });
        } else {
            commentPictureView.setVisibility(View.GONE);
        }
    }

    private void addComment() {
        addComment(commentField.getText().toString(), UserActivity.ACTION_TASK_COMMENT, task.getUuid(), true);
        AndroidUtilities.hideSoftInputForViews(getContext(), commentField);
        commentField.setCursorVisible(false);
    }

    private void addComment(String message, String actionCode, String uuid, boolean usePicture) {
        // Allow for users to just add picture
        if (TextUtils.isEmpty(message) && usePicture) {
            message = " ";
        }
        UserActivity userActivity = new UserActivity();
        userActivity.setMessage(message);
        userActivity.setAction(actionCode);
        userActivity.setTargetId(uuid);
        userActivity.setCreatedAt(DateUtilities.now());
        if (usePicture && pendingCommentPicture != null) {
            JSONObject pictureJson = RemoteModel.PictureHelper.savePictureJson(pendingCommentPicture);
            if (pictureJson != null) {
                userActivity.setPicture(pictureJson.toString());
            }
        }

        userActivityDao.createNew(userActivity);
        if (commentField != null) {
            commentField.setText(""); //$NON-NLS-1$
        }

        pendingCommentPicture = usePicture ? null : pendingCommentPicture;
        if (usePicture) {
            if (activity != null) {
                activity.getIntent().removeExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS);
            }
        }
        if (pictureButton != null) {
            pictureButton.setImageResource(cameraButton);
        }

        setUpListAdapter();
        for (UpdatesChangedListener l : listeners) {
            l.commentAdded();
        }
    }

    private static class NoteOrUpdate {
        private final Spanned title;
        private final Uri commentBitmap;
        private final long createdAt;

        public NoteOrUpdate(Spanned title, Uri commentBitmap, long createdAt) {
            super();
            this.title = title;
            this.commentBitmap = commentBitmap;
            this.createdAt = createdAt;
        }

        public static NoteOrUpdate fromMetadata(Metadata m) {
            if (!m.containsNonNullValue(NoteMetadata.THUMBNAIL)) {
                m.setValue(NoteMetadata.THUMBNAIL, ""); //$NON-NLS-1$
            }
            if (!m.containsNonNullValue(NoteMetadata.COMMENT_PICTURE)) {
                m.setValue(NoteMetadata.COMMENT_PICTURE, ""); //$NON-NLS-1$
            }
            Spanned title = Html.fromHtml(
                    String.format("%s\n%s", m.getValue(NoteMetadata.TITLE), m.getValue(NoteMetadata.BODY))); //$NON-NLS-1$
            return new NoteOrUpdate(title, null, m.getCreationDate());
        }

        public static NoteOrUpdate fromUpdate(UserActivity u) {
            if (u == null) {
                throw new RuntimeException("UserActivity should never be null");
            }

            Uri commentBitmap = u.getPictureUri();
            Spanned title = getUpdateComment(u);
            long createdAt = u.getCreatedAt();

            return new NoteOrUpdate(title, commentBitmap, createdAt);
        }

        private static Spanned getUpdateComment(UserActivity activity) {
            String message = activity.getMessage();
            return Html.fromHtml(message);
        }
    }

    public void addListener(UpdatesChangedListener listener) {
        listeners.add(listener);
    }

    @Override
    public void timerStarted(Task t) {
        addComment(String.format("%s %s", //$NON-NLS-1$
                getContext().getString(R.string.TEA_timer_comment_started),
                DateUtilities.getTimeString(getContext(), newDate())), UserActivity.ACTION_TASK_COMMENT,
                t.getUuid(), false);
    }

    @Override
    public void timerStopped(Task t) {
        String elapsedTime = DateUtils.formatElapsedTime(t.getElapsedSeconds());
        addComment(String.format("%s %s\n%s %s", //$NON-NLS-1$
                getContext().getString(R.string.TEA_timer_comment_stopped),
                DateUtilities.getTimeString(getContext(), newDate()),
                getContext().getString(R.string.TEA_timer_comment_spent), elapsedTime),
                UserActivity.ACTION_TASK_COMMENT, t.getUuid(), false);
    }

    /*
     * Call back from edit task when picture is added
     */
    public boolean activityResult(int requestCode, int resultCode, Intent data) {

        if (respondToPicture) {
            respondToPicture = false;
            CameraResultCallback callback = new CameraResultCallback() {
                @Override
                public void handleCameraResult(Uri uri) {
                    if (activity != null) {
                        activity.getIntent().putExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS, uri.toString());
                    }
                    pendingCommentPicture = uri;
                    setPictureButtonToPendingPicture();
                    commentField.requestFocus();
                }
            };

            return actFmCameraModule.activityResult(requestCode, resultCode, data, callback);
        } else {
            return false;
        }
    }

    private void setPictureButtonToPendingPicture() {
        String path = getPathFromUri(activity, pendingCommentPicture);
        pictureButton.setImageBitmap(sampleBitmap(path, pictureButton.getWidth(), pictureButton.getHeight()));
    }
}