Sent automatic decline when other introducee declines.

This commit is contained in:
akwizgran
2018-04-27 15:57:56 +01:00
parent 31b69577e8
commit c0dfe3e85a
4 changed files with 121 additions and 57 deletions

View File

@@ -575,6 +575,10 @@ public class ConversationActivity extends BriarActivity
@Override @Override
public void onSuccess(String contactName) { public void onSuccess(String contactName) {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
// If the other introducee declined, we can no longer
// respond to the request
if (!m.isIntroducer() && !m.wasAccepted())
markRequestAnswered(m.getSessionId());
ConversationItem item = ConversationItem ConversationItem item = ConversationItem
.from(ConversationActivity.this, contactName, m); .from(ConversationActivity.this, contactName, m);
addConversationItem(item); addConversationItem(item);
@@ -627,6 +631,26 @@ public class ConversationActivity extends BriarActivity
}); });
} }
private void markRequestAnswered(SessionId sessionId) {
int size = adapter.getItemCount();
for (int i = 0; i < size; i++) {
ConversationItem item = adapter.getItemAt(i);
if (item instanceof ConversationRequestItem) {
ConversationRequestItem req = (ConversationRequestItem) item;
if (req.getSessionId().equals(sessionId)
&& !req.wasAnswered()) {
LOG.info("Marking request answered");
req.setAnswered(true);
int position = adapter.findItemPosition(req);
if (position != INVALID_POSITION)
adapter.notifyItemChanged(position, req);
// There shouldn't be more than one unanswered request
return;
}
}
}
}
private void markMessages(Collection<MessageId> messageIds, private void markMessages(Collection<MessageId> messageIds,
boolean sent, boolean seen) { boolean sent, boolean seen) {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
@@ -781,25 +805,18 @@ public class ConversationActivity extends BriarActivity
return; return;
} }
PromptStateChangeListener listener = new PromptStateChangeListener() { PromptStateChangeListener listener = (prompt, state) -> {
@Override if (state == STATE_DISMISSED || state == STATE_FINISHED) {
public void onPromptStateChanged( introductionOnboardingSeen();
MaterialTapTargetPrompt prompt, int state) { }
if (state == STATE_DISMISSED || };
state == STATE_FINISHED) { new MaterialTapTargetPrompt.Builder(ConversationActivity.this,
introductionOnboardingSeen(); R.style.OnboardingDialogTheme).setTarget(target)
} .setPrimaryText(R.string.introduction_onboarding_title)
} .setSecondaryText(R.string.introduction_onboarding_text)
.setIcon(R.drawable.ic_more_vert_accent)
}; .setPromptStateChangeListener(listener)
new MaterialTapTargetPrompt.Builder(ConversationActivity.this, .show();
R.style.OnboardingDialogTheme).setTarget(target)
.setPrimaryText(R.string.introduction_onboarding_title)
.setSecondaryText(R.string.introduction_onboarding_text)
.setIcon(R.drawable.ic_more_vert_accent)
.setPromptStateChangeListener(listener)
.show();
}); });
} }
@@ -865,11 +882,12 @@ introductionOnboardingSeen();
"Unknown Request Type"); "Unknown Request Type");
} }
loadMessages(); loadMessages();
} catch (ProtocolStateException e) {
// Action is no longer valid - reloading should solve the issue
if (LOG.isLoggable(INFO)) LOG.log(INFO, e.toString(), e);
} catch (DbException e) { } catch (DbException e) {
// TODO use more generic error message // TODO show an error message
introductionResponseError(); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
@@ -900,14 +918,8 @@ introductionOnboardingSeen();
@DatabaseExecutor @DatabaseExecutor
private void respondToIntroductionRequest(SessionId sessionId, private void respondToIntroductionRequest(SessionId sessionId,
boolean accept, long time) throws DbException { boolean accept, long time) throws DbException {
try { introductionManager.respondToIntroduction(contactId, sessionId, time,
introductionManager accept);
.respondToIntroduction(contactId, sessionId, time, accept);
} catch (ProtocolStateException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
introductionResponseError();
}
} }
@DatabaseExecutor @DatabaseExecutor
@@ -925,18 +937,7 @@ introductionOnboardingSeen();
@DatabaseExecutor @DatabaseExecutor
private void respondToGroupRequest(SessionId id, boolean accept) private void respondToGroupRequest(SessionId id, boolean accept)
throws DbException { throws DbException {
try { groupInvitationManager.respondToInvitation(contactId, id, accept);
groupInvitationManager.respondToInvitation(contactId, id, accept);
} catch (ProtocolStateException e) {
// this action is no longer possible
}
}
private void introductionResponseError() {
runOnUiThreadUnlessDestroyed(() ->
Toast.makeText(ConversationActivity.this,
R.string.introduction_response_error,
Toast.LENGTH_SHORT).show());
} }
private ListenableFutureTask<String> getContactNameTask() { private ListenableFutureTask<String> getContactNameTask() {

View File

@@ -84,8 +84,7 @@ abstract class AbstractProtocolEngine<S extends Session>
Message sendAcceptMessage(Transaction txn, PeerSession s, long timestamp, Message sendAcceptMessage(Transaction txn, PeerSession s, long timestamp,
byte[] ephemeralPublicKey, long acceptTimestamp, byte[] ephemeralPublicKey, long acceptTimestamp,
Map<TransportId, TransportProperties> transportProperties, Map<TransportId, TransportProperties> transportProperties,
boolean visible) boolean visible) throws DbException {
throws DbException {
Message m = messageEncoder Message m = messageEncoder
.encodeAcceptMessage(s.getContactGroupId(), timestamp, .encodeAcceptMessage(s.getContactGroupId(), timestamp,
s.getLastLocalMessageId(), s.getSessionId(), s.getLastLocalMessageId(), s.getSessionId(),

View File

@@ -357,9 +357,25 @@ class IntroduceeProtocolEngine
broadcastIntroductionResponseReceivedEvent(txn, s, broadcastIntroductionResponseReceivedEvent(txn, s,
s.getIntroducer().getId(), m); s.getIntroducer().getId(), m);
// Move back to START state if (s.getState() == AWAIT_RESPONSES) {
return IntroduceeSession.clear(s, START, s.getLastLocalMessageId(), // Mark the request message unavailable to answer
s.getLocalTimestamp(), m.getMessageId()); markRequestsUnavailableToAnswer(txn, s);
// Send a DECLINE message
Message sent =
sendDeclineMessage(txn, s, getLocalTimestamp(s), false);
// Track the message
messageTracker.trackOutgoingMessage(txn, sent);
// Move back to START state
return IntroduceeSession.clear(s, START, sent.getId(),
sent.getTimestamp(), m.getMessageId());
} else {
// Move back to START state
return IntroduceeSession.clear(s, START, s.getLastLocalMessageId(),
s.getLocalTimestamp(), m.getMessageId());
}
} }
private IntroduceeSession onRemoteResponseWhenDeclined(Transaction txn, private IntroduceeSession onRemoteResponseWhenDeclined(Transaction txn,

View File

@@ -113,7 +113,7 @@ class IntroducerProtocolEngine
return onRemoteAccept(txn, s, m); return onRemoteAccept(txn, s, m);
case A_DECLINED: case A_DECLINED:
case B_DECLINED: case B_DECLINED:
return onRemoteResponseWhenDeclined(txn, s, m); return onRemoteAcceptWhenDeclined(txn, s, m);
case START: case START:
case AWAIT_AUTHS: case AWAIT_AUTHS:
case AWAIT_AUTH_A: case AWAIT_AUTH_A:
@@ -137,7 +137,7 @@ class IntroducerProtocolEngine
return onRemoteDecline(txn, s, m); return onRemoteDecline(txn, s, m);
case A_DECLINED: case A_DECLINED:
case B_DECLINED: case B_DECLINED:
return onRemoteResponseWhenDeclined(txn, s, m); return onRemoteDeclineWhenDeclined(txn, s, m);
case START: case START:
case AWAIT_AUTHS: case AWAIT_AUTHS:
case AWAIT_AUTH_A: case AWAIT_AUTH_A:
@@ -204,8 +204,8 @@ class IntroducerProtocolEngine
} }
private IntroducerSession onLocalRequest(Transaction txn, private IntroducerSession onLocalRequest(Transaction txn,
IntroducerSession s, IntroducerSession s, @Nullable String message, long timestamp)
@Nullable String message, long timestamp) throws DbException { throws DbException {
// Send REQUEST messages // Send REQUEST messages
long maxIntroduceeTimestamp = long maxIntroduceeTimestamp =
Math.max(getLocalTimestamp(s, s.getIntroduceeA()), Math.max(getLocalTimestamp(s, s.getIntroduceeA()),
@@ -285,6 +285,50 @@ class IntroducerProtocolEngine
return m.getGroupId().equals(s.getIntroduceeA().groupId); return m.getGroupId().equals(s.getIntroduceeA().groupId);
} }
private IntroducerSession onRemoteAcceptWhenDeclined(Transaction txn,
IntroducerSession s, AcceptMessage m) throws DbException {
// The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s);
// The dependency, if any, must be the last remote message
if (isInvalidDependency(s, m.getGroupId(), m.getPreviousMessageId()))
return abort(txn, s);
// The message must be expected in the current state
boolean senderIsAlice = senderIsAlice(s, m);
if (senderIsAlice && s.getState() != B_DECLINED)
return abort(txn, s);
else if (!senderIsAlice && s.getState() != A_DECLINED)
return abort(txn, s);
// Mark the response visible in the UI
markMessageVisibleInUi(txn, m.getMessageId());
// Track the incoming message
messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Forward ACCEPT message
Introducee i = getOtherIntroducee(s, m.getGroupId());
Message sent = sendAcceptMessage(txn, i, getLocalTimestamp(s, i),
m.getEphemeralPublicKey(), m.getAcceptTimestamp(),
m.getTransportProperties(), false);
Introducee introduceeA, introduceeB;
if (senderIsAlice) {
introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introduceeB = new Introducee(s.getIntroduceeB(), sent);
} else {
introduceeA = new Introducee(s.getIntroduceeA(), sent);
introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
}
// Broadcast IntroductionResponseReceivedEvent
Author sender = senderIsAlice ? introduceeA.author : introduceeB.author;
broadcastIntroductionResponseReceivedEvent(txn, s, sender.getId(), m);
return new IntroducerSession(s.getSessionId(), START,
s.getRequestTimestamp(), introduceeA, introduceeB);
}
private IntroducerSession onRemoteDecline(Transaction txn, private IntroducerSession onRemoteDecline(Transaction txn,
IntroducerSession s, DeclineMessage m) throws DbException { IntroducerSession s, DeclineMessage m) throws DbException {
// The timestamp must be higher than the last request message // The timestamp must be higher than the last request message
@@ -334,9 +378,8 @@ class IntroducerProtocolEngine
s.getRequestTimestamp(), introduceeA, introduceeB); s.getRequestTimestamp(), introduceeA, introduceeB);
} }
private IntroducerSession onRemoteResponseWhenDeclined(Transaction txn, private IntroducerSession onRemoteDeclineWhenDeclined(Transaction txn,
IntroducerSession s, AbstractIntroductionMessage m) IntroducerSession s, DeclineMessage m) throws DbException {
throws DbException {
// The timestamp must be higher than the last request message // The timestamp must be higher than the last request message
if (m.getTimestamp() <= s.getRequestTimestamp()) if (m.getTimestamp() <= s.getRequestTimestamp())
return abort(txn, s); return abort(txn, s);
@@ -356,12 +399,17 @@ class IntroducerProtocolEngine
messageTracker messageTracker
.trackMessage(txn, m.getGroupId(), m.getTimestamp(), false); .trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
// Forward DECLINE message
Introducee i = getOtherIntroducee(s, m.getGroupId());
long timestamp = getLocalTimestamp(s, i);
Message sent = sendDeclineMessage(txn, i, timestamp, false);
Introducee introduceeA, introduceeB; Introducee introduceeA, introduceeB;
if (senderIsAlice) { if (senderIsAlice) {
introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId()); introduceeA = new Introducee(s.getIntroduceeA(), m.getMessageId());
introduceeB = s.getIntroduceeB(); introduceeB = new Introducee(s.getIntroduceeB(), sent);
} else { } else {
introduceeA = s.getIntroduceeA(); introduceeA = new Introducee(s.getIntroduceeA(), sent);
introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId()); introduceeB = new Introducee(s.getIntroduceeB(), m.getMessageId());
} }