Newer
Older
package org.briarproject.android.contact;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.contact.ConversationAdapter.IntroductionHandler;
import org.briarproject.android.introduction.IntroductionActivity;
import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.android.view.TextInputView;
import org.briarproject.android.view.TextInputView.TextInputListener;
import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchContactException;
import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.event.ContactConnectedEvent;
import org.briarproject.api.event.ContactDisconnectedEvent;
import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.InvitationReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessagesSentEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.introduction.IntroductionManager;
import org.briarproject.api.introduction.IntroductionMessage;
import org.briarproject.api.introduction.IntroductionRequest;
import org.briarproject.api.introduction.IntroductionResponse;
import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessage;
import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.settings.Settings;
import org.briarproject.api.settings.SettingsManager;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import org.briarproject.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.OnHidePromptListener;
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
public class ConversationActivity extends BriarActivity
implements EventListener, IntroductionHandler, TextInputListener {
private static final Logger LOG =
Logger.getLogger(ConversationActivity.class.getName());
private static final int REQUEST_CODE_INTRODUCTION = 1;
public static final String SHOW_ONBOARDING_INTRODUCTION =
"showOnboardingIntroduction";
AndroidNotificationManager notificationManager;
ConnectionRegistry connectionRegistry;
@Inject
@CryptoExecutor
protected Executor cryptoExecutor;
private ConversationAdapter adapter;
private CircleImageView toolbarAvatar;
private ImageView toolbarStatus;
private TextView toolbarTitle;
private TextInputView textInputView;
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile ContactManager contactManager;
@Inject
protected volatile MessagingManager messagingManager;
@Inject
protected volatile EventBus eventBus;
@Inject
protected volatile SettingsManager settingsManager;
@Inject
volatile PrivateMessageFactory privateMessageFactory;
@Inject
protected volatile IntroductionManager introductionManager;
@Inject
protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile BlogSharingManager blogSharingManager;
private volatile GroupId groupId = null;
private volatile ContactId contactId = null;
private volatile String contactName = null;
private volatile byte[] contactIdenticonKey = null;
private volatile boolean connected = false;
private volatile Map<MessageId, byte[]> bodyCache = new HashMap<>();
@SuppressWarnings("ConstantConditions")
@Override
public void onCreate(Bundle state) {
byte[] b = i.getByteArrayExtra("briar.GROUP_ID");
if (b == null) throw new IllegalStateException();
groupId = new GroupId(b);
setContentView(R.layout.activity_conversation);
toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null) {
toolbarAvatar =
(CircleImageView) toolbar.findViewById(R.id.contactAvatar);
toolbarStatus = (ImageView) toolbar.findViewById(R.id.contactStatus);
toolbarTitle = (TextView) toolbar.findViewById(R.id.contactName);
setSupportActionBar(toolbar);
}
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayShowHomeEnabled(true);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowCustomEnabled(true);
ab.setDisplayShowTitleEnabled(false);
}
String hexGroupId = StringUtils.toHexString(b);
ViewCompat.setTransitionName(toolbarAvatar, "avatar" + hexGroupId);
ViewCompat.setTransitionName(toolbarStatus, "bulb" + hexGroupId);
adapter = new ConversationAdapter(this, this);
list = (BriarRecyclerView) findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
list.setEmptyText(getString(R.string.no_private_messages));
textInputView = (TextInputView) findViewById(R.id.text_input_container);
textInputView.setListener(this);
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_CODE_INTRODUCTION && result == RESULT_OK) {
Snackbar snackbar = Snackbar.make(list, R.string.introduction_sent,
Snackbar.LENGTH_SHORT);
snackbar.getView().setBackgroundResource(R.color.briar_primary);
snackbar.show();
}
}
@Override
public void onResume() {
super.onResume();
notificationManager.blockNotification(groupId);
notificationManager.clearPrivateMessageNotification(groupId);
list.startPeriodicUpdate();
@Override
public void onPause() {
super.onPause();
eventBus.removeListener(this);
notificationManager.unblockNotification(groupId);
list.stopPeriodicUpdate();
if (isFinishing()) markMessagesRead();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu items for use in the action bar
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.conversation_actions, menu);
menu.findItem(R.id.action_introduction));
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
return true;
case R.id.action_introduction:
if (contactId == null) return false;
Intent intent = new Intent(this, IntroductionActivity.class);
intent.putExtra(IntroductionActivity.CONTACT_ID,
contactId.getInt());
ActivityOptionsCompat options =
makeCustomAnimation(this, android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
ActivityCompat.startActivityForResult(this, intent,
REQUEST_CODE_INTRODUCTION, options.toBundle());
case R.id.action_social_remove_person:
askToRemoveContact();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onBackPressed() {
if (textInputView.isEmojiDrawerOpen()) {
textInputView.hideEmojiDrawer();
return;
}
// FIXME disabled exit transition, because it doesn't work for some reason #318
//supportFinishAfterTransition();
finish();
}
public void run() {
try {
long now = System.currentTimeMillis();
if (contactId == null)
contactId = messagingManager.getContactId(groupId);
if (contactName == null || contactIdenticonKey == null) {
Contact contact = contactManager.getContact(contactId);
contactName = contact.getAuthor().getName();
contactIdenticonKey =
contact.getAuthor().getId().getBytes();
}
connected = connectionRegistry.isConnected(contactId);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Loading contact took " + duration + " ms");
displayContactDetails();
// Load the messages here to make sure we have a contactId
loadMessages();
} catch (NoSuchContactException e) {
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void displayContactDetails() {
toolbarAvatar.setImageDrawable(
new IdenticonDrawable(contactIdenticonKey));
toolbarTitle.setText(contactName);
if (connected) {
toolbarStatus.setImageDrawable(ContextCompat
.getDrawable(ConversationActivity.this,
R.drawable.contact_online));
toolbarStatus
.setContentDescription(getString(R.string.online));
} else {
toolbarStatus.setImageDrawable(ContextCompat
.getDrawable(ConversationActivity.this,
R.drawable.contact_offline));
toolbarStatus
.setContentDescription(getString(R.string.offline));

Torsten Grote
committed
adapter.setContactName(contactName);
private void loadMessages() {
runOnDbThread(new Runnable() {
long now = System.currentTimeMillis();
if (contactId == null)
contactId = messagingManager.getContactId(groupId);
Collection<PrivateMessageHeader> headers =
messagingManager.getMessageHeaders(contactId);
Collection<IntroductionMessage> introductions =
introductionManager
.getIntroductionMessages(contactId);
Collection<InvitationMessage> forumInvitations =
.getInvitationMessages(contactId);
Collection<InvitationMessage> blogInvitations =
blogSharingManager
.getInvitationMessages(contactId);
List<InvitationMessage> invitations = new ArrayList<>(
forumInvitations.size() + blogInvitations.size());
invitations.addAll(forumInvitations);
invitations.addAll(blogInvitations);
long duration = System.currentTimeMillis() - now;
LOG.info("Loading headers took " + duration + " ms");
displayMessages(headers, introductions, invitations);
} catch (NoSuchContactException e) {
finishOnUiThread();
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void displayMessages(final Collection<PrivateMessageHeader> headers,
final Collection<IntroductionMessage> introductions,
final Collection<InvitationMessage> invitations) {
textInputView.setSendButtonEnabled(true);
if (headers.isEmpty() && introductions.isEmpty() &&
invitations.isEmpty()) {
// we have no messages,
// so let the list know to hide progress bar
list.showData();
} else {
for (PrivateMessageHeader h : headers) {
ConversationMessageItem item =
(ConversationMessageItem) ConversationItem
.from(h);
byte[] body = bodyCache.get(h.getId());
if (body == null) loadMessageBody(h);
else item.setBody(body);
for (IntroductionMessage m : introductions) {
ConversationItem item;
if (m instanceof IntroductionRequest) {
item = ConversationItem
.from((IntroductionRequest) m);
} else {
item = ConversationItem
.from(ConversationActivity.this,
contactName,
(IntroductionResponse) m);
}
for (InvitationMessage i : invitations) {
if (i instanceof InvitationRequest) {
InvitationRequest r =
(InvitationRequest) i;
items.add(ConversationItem.from(r));
} else if (i instanceof InvitationResponse) {
InvitationResponse r =
(InvitationResponse) i;
items.add(ConversationItem
.from(ConversationActivity.this,
contactName, r));
}
// Scroll to the bottom
list.scrollToPosition(adapter.getItemCount() - 1);
private void loadMessageBody(final PrivateMessageHeader h) {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
byte[] body = messagingManager.getMessageBody(h.getId());
long duration = System.currentTimeMillis() - now;
LOG.info("Loading message took " + duration + " ms");
displayMessageBody(h.getId(), body);
} catch (NoSuchMessageException e) {
// The item will be removed when we get the event
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void displayMessageBody(final MessageId m, final byte[] body) {
runOnUiThread(new Runnable() {
public void run() {
bodyCache.put(m, body);
SparseArray<ConversationMessageItem> messages =
adapter.getPrivateMessages();
for (int i = 0; i < messages.size(); i++) {
ConversationMessageItem item = messages.valueAt(i);
if (item.getId().equals(m)) {
item.setBody(body);
adapter.notifyItemChanged(messages.keyAt(i));
list.scrollToPosition(adapter.getItemCount() - 1);
return;
}
}
private void addConversationItem(final ConversationItem item) {
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.add(item);
// Scroll to the bottom
list.scrollToPosition(adapter.getItemCount() - 1);
}
});
}
private void markMessagesRead() {
List<MessageId> unread = new ArrayList<>();
SparseArray<IncomingItem> list = adapter.getIncomingMessages();
for (int i = 0; i < list.size(); i++) {
IncomingItem item = list.valueAt(i);
if (!item.isRead()) unread.add(item.getId());
if (unread.isEmpty()) return;
if (LOG.isLoggable(INFO))
LOG.info("Marking " + unread.size() + " messages read");
markMessagesRead(Collections.unmodifiableList(unread));
}
private void markMessagesRead(final Collection<MessageId> unread) {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
// not really clean, but the messaging manager can
// handle introduction messages as well
messagingManager.setReadFlag(m, true);
long duration = System.currentTimeMillis() - now;
LOG.info("Marking read took " + duration + " ms");
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) {
ContactRemovedEvent c = (ContactRemovedEvent) e;
if (c.getContactId().equals(contactId)) {
LOG.info("Contact removed");
finishOnUiThread();
} else if (e instanceof PrivateMessageReceivedEvent) {
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
if (p.getGroupId().equals(groupId)) {
LOG.info("Message received, adding");
PrivateMessageHeader h = p.getMessageHeader();
addConversationItem(ConversationItem.from(h));
loadMessageBody(h);
markMessageReadIfNew(h);
}
} else if (e instanceof MessagesSentEvent) {
MessagesSentEvent m = (MessagesSentEvent) e;
if (m.getContactId().equals(contactId)) {
LOG.info("Messages sent");
markMessages(m.getMessageIds(), true, false);
} else if (e instanceof MessagesAckedEvent) {
MessagesAckedEvent m = (MessagesAckedEvent) e;
if (m.getContactId().equals(contactId)) {
markMessages(m.getMessageIds(), true, true);
} else if (e instanceof ContactConnectedEvent) {
ContactConnectedEvent c = (ContactConnectedEvent) e;
if (c.getContactId().equals(contactId)) {
LOG.info("Contact connected");
connected = true;
displayContactDetails();
}
} else if (e instanceof ContactDisconnectedEvent) {
ContactDisconnectedEvent c = (ContactDisconnectedEvent) e;
if (c.getContactId().equals(contactId)) {
LOG.info("Contact disconnected");
connected = false;
displayContactDetails();
}
} else if (e instanceof IntroductionRequestReceivedEvent) {
IntroductionRequestReceivedEvent event =
(IntroductionRequestReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Introduction request received, adding...");
IntroductionRequest ir = event.getIntroductionRequest();
ConversationItem item = new ConversationIntroductionInItem(ir);
addConversationItem(item);
}
} else if (e instanceof IntroductionResponseReceivedEvent) {
IntroductionResponseReceivedEvent event =
(IntroductionResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Introduction response received, adding...");
IntroductionResponse ir = event.getIntroductionResponse();
ConversationItem item =
ConversationItem.from(this, contactName, ir);
addConversationItem(item);
} else if (e instanceof InvitationReceivedEvent) {
InvitationReceivedEvent event =
(InvitationReceivedEvent) e;
LOG.info("Invitation received, reloading...");
} else if (e instanceof InvitationResponseReceivedEvent) {
InvitationResponseReceivedEvent event =
(InvitationResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Invitation response received, reloading...");
loadMessages();
}
}
}
private void markMessageReadIfNew(final PrivateMessageHeader h) {
runOnUiThread(new Runnable() {
ConversationItem item = adapter.getLastItem();
if (item != null) {
// Mark the message read if it's the newest message
long lastMsgTime = item.getTime();
long newMsgTime = h.getTimestamp();
if (newMsgTime > lastMsgTime) markNewMessageRead(h.getId());
else loadMessages();
} else {
// mark the message as read as well if it is the first one
markNewMessageRead(h.getId());
private void markNewMessageRead(final MessageId m) {
runOnDbThread(new Runnable() {
public void run() {
try {
messagingManager.setReadFlag(m, true);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void markMessages(final Collection<MessageId> messageIds,
final boolean sent, final boolean seen) {
runOnUiThread(new Runnable() {
public void run() {
Set<MessageId> messages = new HashSet<>(messageIds);
SparseArray<OutgoingItem> list = adapter.getOutgoingMessages();
for (int i = 0; i < list.size(); i++) {
OutgoingItem item = list.valueAt(i);
if (messages.contains(item.getId())) {
item.setSent(sent);
item.setSeen(seen);
adapter.notifyItemChanged(list.keyAt(i));
}
});
}
public void onSendClick(String text) {
markMessagesRead();
if (text.equals("")) return;
long timestamp = System.currentTimeMillis();
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
createMessage(StringUtils.toUtf8(text), timestamp);
private long getMinTimestampForNewMessage() {
// Don't use an earlier timestamp than the newest message
ConversationItem item = adapter.getLastItem();
return item == null ? 0 : item.getTime() + 1;
private void createMessage(final byte[] body, final long timestamp) {
cryptoExecutor.execute(new Runnable() {
public void run() {
try {
storeMessage(privateMessageFactory
.createPrivateMessage(groupId, timestamp, null,
"text/plain", body), body);
} catch (FormatException e) {
throw new RuntimeException(e);
}
}
});
}
private void storeMessage(final PrivateMessage m, final byte[] body) {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
messagingManager.addLocalMessage(m);
long duration = System.currentTimeMillis() - now;
LOG.info("Storing message took " + duration + " ms");
PrivateMessageHeader h = new PrivateMessageHeader(
m.getMessage().getId(),
m.getMessage().getTimestamp(), m.getContentType(),
true, false, false, false);
ConversationMessageItem item =
(ConversationMessageItem) ConversationItem.from(h);
item.setBody(body);
bodyCache.put(m.getMessage().getId(), body);
addConversationItem(item);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
private void askToRemoveContact() {
DialogInterface.OnClickListener okListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
removeContact();
}
};
AlertDialog.Builder builder =
new AlertDialog.Builder(ConversationActivity.this,
R.style.BriarDialogTheme);
builder.setTitle(getString(R.string.dialog_title_delete_contact));
builder.setMessage(getString(R.string.dialog_message_delete_contact));
builder.setPositiveButton(android.R.string.ok, okListener);
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
}
private void removeContact() {
runOnDbThread(new Runnable() {
// make sure contactId is initialised
if (contactId == null)
contactId = messagingManager.getContactId(groupId);
// remove contact with that ID
contactManager.removeContact(contactId);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} finally {
finishAfterContactRemoved();
}
}
});
}
private void finishAfterContactRemoved() {
runOnUiThread(new Runnable() {
@Override
public void run() {
String deleted = getString(R.string.contact_deleted_toast);
Toast.makeText(ConversationActivity.this, deleted, LENGTH_SHORT)
.show();
finish();
}
});
}
private void showIntroductionActionIfAvailable(final MenuItem item) {
runOnDbThread(new Runnable() {
public void run() {
try {
if (contactManager.getActiveContacts().size() > 1) {
showIntroductionAction(item);
Settings settings =
settingsManager.getSettings(SETTINGS_NAMESPACE);
if (settings.getBoolean(SHOW_ONBOARDING_INTRODUCTION,
true)) {
showIntroductionOnboarding();
}
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void showIntroductionAction(final MenuItem item) {
runOnUiThread(new Runnable() {
@Override
public void run() {
item.setVisible(true);
}
});
}
private void showIntroductionOnboarding() {
runOnUiThread(new Runnable() {
@Override
public void run() {
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
// find view of overflow icon
View target = null;
for (int i = 0; i < toolbar.getChildCount(); i++) {
if (toolbar.getChildAt(i) instanceof ActionMenuView) {
ActionMenuView menu =
(ActionMenuView) toolbar.getChildAt(i);
target = menu.getChildAt(menu.getChildCount() - 1);
break;
}
}
if (target == null) {
LOG.warning("No Overflow Icon found!");
return;
}
OnHidePromptListener listener = new OnHidePromptListener() {
@Override
public void onHidePrompt(MotionEvent motionEvent,
boolean focalClicked) {
if (focalClicked) introductionOnboardingSeen();
}
@Override
public void onHidePromptComplete() {
}
};
new MaterialTapTargetPrompt.Builder(ConversationActivity.this)
.setTarget(target)
.setPrimaryText(R.string.introduction_onboarding_title)
.setSecondaryText(R.string.introduction_onboarding_text)
.setBackgroundColourFromRes(R.color.briar_primary)
.setIcon(R.drawable.ic_more_vert_accent)
.setOnHidePromptListener(listener)
.show();
}
});
}
private void introductionOnboardingSeen() {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Settings settings = new Settings();
settings.putBoolean(SHOW_ONBOARDING_INTRODUCTION, false);
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override
public void respondToIntroduction(final SessionId sessionId,
final boolean accept) {
runOnDbThread(new Runnable() {
@Override
public void run() {
long timestamp = System.currentTimeMillis();
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
introductionManager
.acceptIntroduction(contactId, sessionId,
timestamp);
introductionManager
.declineIntroduction(contactId, sessionId,
timestamp);
introductionResponseError();
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void introductionResponseError() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(ConversationActivity.this,
R.string.introduction_response_error,
Toast.LENGTH_SHORT).show();
}
});
}