Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8030977d authored by cketti's avatar cketti Committed by GitHub
Browse files

Merge pull request #2125 from philipwhiuk/draftNeedsSavingImproved

Better determination of whether a draft needs saving
parents 3bd84de9 67ade961
Loading
Loading
Loading
Loading
+55 −20
Original line number Diff line number Diff line
@@ -98,7 +98,9 @@ import com.fsck.k9.ui.compose.QuotedMessagePresenter;
@SuppressWarnings("deprecation") // TODO get rid of activity dialogs and indeterminate progress bars
public class MessageCompose extends K9Activity implements OnClickListener,
        CancelListener, OnFocusChangeListener, OnCryptoModeChangedListener,
        OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback {
        OnOpenPgpInlineChangeListener, OnOpenPgpSignOnlyChangeListener, MessageBuilder.Callback,
        AttachmentPresenter.AttachmentsChangedListener, RecipientPresenter.RecipientsChangedListener {

    private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1;
    private static final int DIALOG_CONFIRM_DISCARD_ON_BACK = 2;
    private static final int DIALOG_CHOOSE_IDENTITY = 3;
@@ -126,7 +128,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    private static final String STATE_IN_REPLY_TO = "com.fsck.k9.activity.MessageCompose.inReplyTo";
    private static final String STATE_REFERENCES = "com.fsck.k9.activity.MessageCompose.references";
    private static final String STATE_KEY_READ_RECEIPT = "com.fsck.k9.activity.MessageCompose.messageReadReceipt";
    private static final String STATE_KEY_DRAFT_NEEDS_SAVING = "com.fsck.k9.activity.MessageCompose.draftNeedsSaving";
    private static final String STATE_KEY_CHANGES_MADE_SINCE_LAST_SAVE = "com.fsck.k9.activity.MessageCompose.changesMadeSinceLastSave";
    private static final String STATE_ALREADY_NOTIFIED_USER_OF_EMPTY_SUBJECT = "alreadyNotifiedUserOfEmptySubject";

    private static final String FRAGMENT_WAITING_FOR_ATTACHMENT = "waitingForAttachment";
@@ -176,8 +178,8 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    private MessageBuilder currentMessageBuilder;
    private boolean finishAfterDraftSaved;
    private boolean alreadyNotifiedUserOfEmptySubject = false;
    private boolean changesMadeSinceLastSave = false;

    private boolean draftNeedsSaving = false;
    /**
     * The database ID of this message's draft. This is used when saving drafts so the message in
     * the database is updated instead of being created anew. This property is INVALID_DRAFT_ID
@@ -251,7 +253,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
             * user to set up an account as an acceptable bailout.
             */
            startActivity(new Intent(this, Accounts.class));
            draftNeedsSaving = false;
            changesMadeSinceLastSave = false;
            finish();
            return;
        }
@@ -264,7 +266,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        RecipientMvpView recipientMvpView = new RecipientMvpView(this);
        ComposePgpInlineDecider composePgpInlineDecider = new ComposePgpInlineDecider();
        recipientPresenter = new RecipientPresenter(getApplicationContext(), getLoaderManager(), recipientMvpView,
                account, composePgpInlineDecider, new ReplyToParser());
                account, composePgpInlineDecider, new ReplyToParser(), this);
        recipientPresenter.updateCryptoStatus();


@@ -276,7 +278,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,

        QuotedMessageMvpView quotedMessageMvpView = new QuotedMessageMvpView(this);
        quotedMessagePresenter = new QuotedMessagePresenter(this, quotedMessageMvpView, account);
        attachmentPresenter = new AttachmentPresenter(getApplicationContext(), attachmentMvpView, getLoaderManager());
        attachmentPresenter = new AttachmentPresenter(getApplicationContext(), attachmentMvpView, getLoaderManager(), this);

        messageContentView = (EolConvertingEditText) findViewById(R.id.message_content);
        messageContentView.getInputExtras(true).putBoolean("allowEmoji", true);
@@ -286,14 +288,14 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        TextWatcher draftNeedsChangingTextWatcher = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                draftNeedsSaving = true;
                changesMadeSinceLastSave = true;
            }
        };

        TextWatcher signTextWatcher = new SimpleTextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                draftNeedsSaving = true;
                changesMadeSinceLastSave = true;
                signatureChanged = true;
            }
        };
@@ -325,7 +327,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,

        if (initFromIntent(intent)) {
            action = Action.COMPOSE;
            draftNeedsSaving = true;
            changesMadeSinceLastSave = true;
        } else {
            String action = intent.getAction();
            if (ACTION_COMPOSE.equals(action)) {
@@ -567,7 +569,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        outState.putString(STATE_IN_REPLY_TO, repliedToMessageId);
        outState.putString(STATE_REFERENCES, referencedMessageIds);
        outState.putBoolean(STATE_KEY_READ_RECEIPT, requestReadReceipt);
        outState.putBoolean(STATE_KEY_DRAFT_NEEDS_SAVING, draftNeedsSaving);
        outState.putBoolean(STATE_KEY_CHANGES_MADE_SINCE_LAST_SAVE, changesMadeSinceLastSave);
        outState.putBoolean(STATE_ALREADY_NOTIFIED_USER_OF_EMPTY_SUBJECT, alreadyNotifiedUserOfEmptySubject);

        recipientPresenter.onSaveInstanceState(outState);
@@ -600,7 +602,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        identityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED);
        repliedToMessageId = savedInstanceState.getString(STATE_IN_REPLY_TO);
        referencedMessageIds = savedInstanceState.getString(STATE_REFERENCES);
        draftNeedsSaving = savedInstanceState.getBoolean(STATE_KEY_DRAFT_NEEDS_SAVING);
        changesMadeSinceLastSave = savedInstanceState.getBoolean(STATE_KEY_CHANGES_MADE_SINCE_LAST_SAVE);
        alreadyNotifiedUserOfEmptySubject = savedInstanceState.getBoolean(STATE_ALREADY_NOTIFIED_USER_OF_EMPTY_SUBJECT);

        updateFrom();
@@ -697,7 +699,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
            return;
        }

        if (!draftNeedsSaving) {
        if (!changesMadeSinceLastSave) {
            return;
        }

@@ -716,7 +718,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    public void performSendAfterChecks() {
        currentMessageBuilder = createMessageBuilder(false);
        if (currentMessageBuilder != null) {
            draftNeedsSaving = false;
            changesMadeSinceLastSave = false;
            setProgressBarIndeterminateVisibility(true);
            currentMessageBuilder.buildAsync(this);
        }
@@ -728,7 +730,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
            draftId = INVALID_DRAFT_ID;
        }
        internalMessageHandler.sendEmptyMessage(MSG_DISCARDED_DRAFT);
        draftNeedsSaving = false;
        changesMadeSinceLastSave = false;
        finish();
    }

@@ -797,7 +799,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
            }

            // test whether there is something to save
            if (draftNeedsSaving || (draftId != INVALID_DRAFT_ID)) {
            if (changesMadeSinceLastSave || (draftId != INVALID_DRAFT_ID)) {
                final long previousDraftId = draftId;
                final Account previousAccount = this.account;

@@ -839,7 +841,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    private void switchToIdentity(Identity identity) {
        this.identity = identity;
        identityChanged = true;
        draftNeedsSaving = true;
        changesMadeSinceLastSave = true;
        updateFrom();
        updateSignature();
        updateMessageFormat();
@@ -885,6 +887,20 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    public void onOpenPgpSignOnlyChange(boolean enabled) {
        recipientPresenter.onCryptoPgpSignOnlyDisabled();
    }
    @Override
    public void onAttachmentAdded() {
        changesMadeSinceLastSave = true;
    }

    @Override
    public void onAttachmentRemoved() {
        changesMadeSinceLastSave = true;
    }

    @Override
    public void onRecipientsChanged() {
        changesMadeSinceLastSave = true;
    }

    @Override
    public void onClick(View view) {
@@ -973,7 +989,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,

    @Override
    public void onBackPressed() {
        if (draftNeedsSaving) {
        if (changesMadeSinceLastSave && draftIsNotEmpty()) {
            if (!account.hasDraftsFolder()) {
                showDialog(DIALOG_CONFIRM_DISCARD_ON_BACK);
            } else {
@@ -989,6 +1005,25 @@ public class MessageCompose extends K9Activity implements OnClickListener,
        }
    }

    private boolean draftIsNotEmpty() {
        if (messageContentView.getText().length() != 0) {
            return true;
        }
        if (!attachmentPresenter.createAttachmentList().isEmpty()) {
            return true;
        }
        if (subjectView.getText().length() != 0) {
            return true;
        }
        if (!recipientPresenter.getToAddresses().isEmpty() ||
                !recipientPresenter.getCcAddresses().isEmpty() ||
                !recipientPresenter.getBccAddresses().isEmpty()) {
            return true;
        }
        return false;
    }


    public void onProgressCancel(ProgressDialogFragment fragment) {
        attachmentPresenter.attachmentProgressDialogCancelled();
    }
@@ -1075,7 +1110,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    }

    public void saveDraftEventually() {
        draftNeedsSaving = true;
        changesMadeSinceLastSave = true;
    }

    public void loadQuotedTextForEdit() {
@@ -1122,7 +1157,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
            Log.e(K9.LOG_TAG, "Error while processing source message: ", me);
        } finally {
            relatedMessageProcessed = true;
            draftNeedsSaving = false;
            changesMadeSinceLastSave = false;
        }

        updateMessageFormat();
@@ -1413,7 +1448,7 @@ public class MessageCompose extends K9Activity implements OnClickListener,
    @Override
    public void onMessageBuildSuccess(MimeMessage message, boolean isDraft) {
        if (isDraft) {
            draftNeedsSaving = false;
            changesMadeSinceLastSave = false;
            currentMessageBuilder = null;

            if (action == Action.EDIT_DRAFT && relatedMessageReference != null) {
+11 −1
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ public class AttachmentPresenter {
    private final Context context;
    private final AttachmentMvpView attachmentMvpView;
    private final LoaderManager loaderManager;
    private final AttachmentsChangedListener listener;

    // persistent state
    private LinkedHashMap<Uri, Attachment> attachments;
@@ -47,10 +48,12 @@ public class AttachmentPresenter {
    private WaitingAction actionToPerformAfterWaiting = WaitingAction.NONE;


    public AttachmentPresenter(Context context, AttachmentMvpView attachmentMvpView, LoaderManager loaderManager) {
    public AttachmentPresenter(Context context, AttachmentMvpView attachmentMvpView, LoaderManager loaderManager,
                               AttachmentsChangedListener listener) {
        this.context = context;
        this.attachmentMvpView = attachmentMvpView;
        this.loaderManager = loaderManager;
        this.listener = listener;

        attachments = new LinkedHashMap<>();
    }
@@ -178,6 +181,7 @@ public class AttachmentPresenter {

    private void addAttachmentAndStartLoader(Attachment attachment) {
        attachments.put(attachment.uri, attachment);
        listener.onAttachmentAdded();
        attachmentMvpView.addAttachmentView(attachment);

        if (attachment.state == LoadingState.URI_ONLY) {
@@ -338,6 +342,7 @@ public class AttachmentPresenter {

        attachmentMvpView.removeAttachmentView(attachment);
        attachments.remove(uri);
        listener.onAttachmentRemoved();
    }

    public void onActivityResult(int resultCode, int requestCode, Intent data) {
@@ -375,4 +380,9 @@ public class AttachmentPresenter {

        void showMissingAttachmentsPartialMessageWarning();
    }

    public interface AttachmentsChangedListener {
        void onAttachmentAdded();
        void onAttachmentRemoved();
    }
}
+17 −1
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ public class RecipientPresenter implements PermissionPingCallback {
    private final Context context;
    private final RecipientMvpView recipientMvpView;
    private final ComposePgpInlineDecider composePgpInlineDecider;
    private final RecipientsChangedListener listener;
    private ReplyToParser replyToParser;
    private Account account;
    private String cryptoProvider;
@@ -81,11 +82,13 @@ public class RecipientPresenter implements PermissionPingCallback {


    public RecipientPresenter(Context context, LoaderManager loaderManager, RecipientMvpView recipientMvpView,
            Account account, ComposePgpInlineDecider composePgpInlineDecider, ReplyToParser replyToParser) {
            Account account, ComposePgpInlineDecider composePgpInlineDecider, ReplyToParser replyToParser,
            RecipientsChangedListener recipientsChangedListener) {
        this.recipientMvpView = recipientMvpView;
        this.context = context;
        this.composePgpInlineDecider = composePgpInlineDecider;
        this.replyToParser = replyToParser;
        this.listener = recipientsChangedListener;

        recipientMvpView.setPresenter(this);
        recipientMvpView.setLoaderManager(loaderManager);
@@ -384,38 +387,47 @@ public class RecipientPresenter implements PermissionPingCallback {

    void onToTokenAdded() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onToTokenRemoved() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onToTokenChanged() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onCcTokenAdded() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onCcTokenRemoved() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onCcTokenChanged() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onBccTokenAdded() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onBccTokenRemoved() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    void onBccTokenChanged() {
        updateCryptoStatus();
        listener.onRecipientsChanged();
    }

    public void onCryptoModeChanged(CryptoMode cryptoMode) {
@@ -784,4 +796,8 @@ public class RecipientPresenter implements PermissionPingCallback {
        OPPORTUNISTIC,
        PRIVATE,
    }

    public static interface RecipientsChangedListener {
        public void onRecipientsChanged();
    }
}
+57 −1
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ public class RecipientPresenterTest {
    private ComposePgpInlineDecider composePgpInlineDecider;
    private Account account;
    private RecipientMvpView recipientMvpView;
    private RecipientPresenter.RecipientsChangedListener listener;


    @Before
@@ -69,9 +70,10 @@ public class RecipientPresenterTest {
        composePgpInlineDecider = mock(ComposePgpInlineDecider.class);
        replyToParser = mock(ReplyToParser.class);
        LoaderManager loaderManager = mock(LoaderManager.class);
        listener = mock(RecipientPresenter.RecipientsChangedListener.class);

        recipientPresenter = new RecipientPresenter(
                context, loaderManager, recipientMvpView, account, composePgpInlineDecider, replyToParser);
                context, loaderManager, recipientMvpView, account, composePgpInlineDecider, replyToParser, listener);
        recipientPresenter.updateCryptoStatus();
    }

@@ -191,6 +193,60 @@ public class RecipientPresenterTest {
        assertTrue(status.isPgpInlineModeEnabled());
    }

    @Test
    public void onToTokenAdded_notifiesListenerOfRecipientChange() {
        recipientPresenter.onToTokenAdded();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onToTokenChanged_notifiesListenerOfRecipientChange() {
        recipientPresenter.onToTokenChanged();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onToTokenRemoved_notifiesListenerOfRecipientChange() {
        recipientPresenter.onToTokenRemoved();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onCcTokenAdded_notifiesListenerOfRecipientChange() {
        recipientPresenter.onCcTokenAdded();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onCcTokenChanged_notifiesListenerOfRecipientChange() {
        recipientPresenter.onCcTokenChanged();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onCcTokenRemoved_notifiesListenerOfRecipientChange() {
        recipientPresenter.onCcTokenRemoved();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onBccTokenAdded_notifiesListenerOfRecipientChange() {
        recipientPresenter.onBccTokenAdded();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onBccTokenChanged_notifiesListenerOfRecipientChange() {
        recipientPresenter.onBccTokenChanged();
        verify(listener).onRecipientsChanged();
    }

    @Test
    public void onBccTokenRemoved_notifiesListenerOfRecipientChange() {
        recipientPresenter.onBccTokenRemoved();
        verify(listener).onRecipientsChanged();
    }

    private void setupCryptoProvider() throws android.os.RemoteException {
        Account account = mock(Account.class);
        OpenPgpServiceConnection openPgpServiceConnection = mock(OpenPgpServiceConnection.class);