aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/AndroidManifest.xml4
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/OtrEngine.java72
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java34
-rw-r--r--src/main/java/eu/siacs/conversations/services/NotificationService.java29
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java19
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java129
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java296
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/utils/UIHelper.java34
-rw-r--r--src/main/res/drawable-hdpi/ic_stat_communication_import_export.pngbin0 -> 620 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_stat_communication_import_export.pngbin0 -> 392 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_stat_communication_import_export.pngbin0 -> 972 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_stat_communication_import_export.pngbin0 -> 1860 bytes
-rw-r--r--src/main/res/layout/activity_verify_otr.xml189
-rw-r--r--src/main/res/layout/dialog_verify_otr.xml60
-rw-r--r--src/main/res/values/strings.xml21
-rw-r--r--src/main/res/xml/preferences.xml5
18 files changed, 705 insertions, 191 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 30622887..937b6813 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -96,6 +96,10 @@
android:label="@string/mgmt_account_publish_avatar"
android:windowSoftInputMode="stateHidden" />
<activity
+ android:name=".ui.VerifyOTRActivity"
+ android:label="@string/verify_otr"
+ android:windowSoftInputMode="stateHidden" />
+ <activity
android:name=".ui.ShareWithActivity"
android:label="@string/title_activity_conversations" >
<intent-filter>
diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
index 912eea77..8b2b704a 100644
--- a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
@@ -18,13 +18,18 @@ import android.util.Log;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import net.java.otr4j.OtrEngineHost;
import net.java.otr4j.OtrException;
import net.java.otr4j.OtrPolicy;
import net.java.otr4j.OtrPolicyImpl;
+import net.java.otr4j.crypto.OtrCryptoEngineImpl;
+import net.java.otr4j.crypto.OtrCryptoException;
import net.java.otr4j.session.InstanceTag;
import net.java.otr4j.session.SessionID;
@@ -92,9 +97,18 @@ public class OtrEngine implements OtrEngineHost {
}
@Override
- public void askForSecret(SessionID arg0, InstanceTag arg1, String arg2) {
- // TODO Auto-generated method stub
-
+ public void askForSecret(SessionID id, InstanceTag instanceTag, String question) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
+ if (conversation!=null) {
+ conversation.smp().hint = question;
+ conversation.smp().status = Conversation.Smp.STATUS_CONTACT_REQUESTED;
+ mXmppConnectionService.updateConversationUi();
+ }
+ } catch (InvalidJidException e) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": smp in invalid session "+id.toString());
+ }
}
@Override
@@ -110,8 +124,11 @@ public class OtrEngine implements OtrEngineHost {
@Override
public byte[] getLocalFingerprintRaw(SessionID arg0) {
- // TODO Auto-generated method stub
- return null;
+ try {
+ return new OtrCryptoEngineImpl().getFingerprintRaw(getPublicKey());
+ } catch (OtrCryptoException e) {
+ return null;
+ }
}
public PublicKey getPublicKey() {
@@ -187,20 +204,31 @@ public class OtrEngine implements OtrEngineHost {
@Override
public void showError(SessionID arg0, String arg1) throws OtrException {
- // TODO Auto-generated method stub
-
+ Log.d(Config.LOGTAG,"show error");
}
@Override
- public void smpAborted(SessionID arg0) throws OtrException {
- // TODO Auto-generated method stub
+ public void smpAborted(SessionID id) throws OtrException {
+ setSmpStatus(id, Conversation.Smp.STATUS_NONE);
+ }
+
+ private void setSmpStatus(SessionID id, int status) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
+ if (conversation!=null) {
+ conversation.smp().status = status;
+ mXmppConnectionService.updateConversationUi();
+ }
+ } catch (final InvalidJidException ignored) {
+ }
}
@Override
- public void smpError(SessionID arg0, int arg1, boolean arg2)
+ public void smpError(SessionID id, int arg1, boolean arg2)
throws OtrException {
- throw new OtrException(new Exception("smp error"));
+ setSmpStatus(id, Conversation.Smp.STATUS_NONE);
}
@Override
@@ -211,19 +239,29 @@ public class OtrEngine implements OtrEngineHost {
@Override
public void unreadableMessageReceived(SessionID arg0) throws OtrException {
+ Log.d(Config.LOGTAG,"unreadable message received");
throw new OtrException(new Exception("unreadable message received"));
}
@Override
- public void unverify(SessionID arg0, String arg1) {
- // TODO Auto-generated method stub
-
+ public void unverify(SessionID id, String arg1) {
+ setSmpStatus(id, Conversation.Smp.STATUS_FAILED);
}
@Override
- public void verify(SessionID arg0, String arg1, boolean arg2) {
- // TODO Auto-generated method stub
-
+ public void verify(SessionID id, String arg1, boolean arg2) {
+ try {
+ final Jid jid = Jid.fromSessionID(id);
+ Conversation conversation = this.mXmppConnectionService.find(this.account,jid);
+ if (conversation!=null) {
+ conversation.smp().hint = null;
+ conversation.smp().status = Conversation.Smp.STATUS_VERIFIED;
+ conversation.verifyOtrFingerprint();
+ mXmppConnectionService.updateConversationUi();
+ mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
+ }
+ } catch (final InvalidJidException ignored) {
+ }
}
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index c8dedd7b..a9f4c0f1 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -63,13 +63,12 @@ public class Conversation extends AbstractEntity {
private transient SessionImpl otrSession;
private transient String otrFingerprint = null;
+ private Smp mSmp = new Smp();
private String nextMessage;
private transient MucOptions mucOptions = null;
- // private transient String latestMarkableMessageId;
-
private byte[] symmetricKey;
private Bookmark bookmark;
@@ -271,6 +270,13 @@ public class Conversation extends AbstractEntity {
public void resetOtrSession() {
this.otrFingerprint = null;
this.otrSession = null;
+ this.mSmp.hint = null;
+ this.mSmp.secret = null;
+ this.mSmp.status = Smp.STATUS_NONE;
+ }
+
+ public Smp smp() {
+ return mSmp;
}
public void startOtrIfNeeded() {
@@ -330,6 +336,14 @@ public class Conversation extends AbstractEntity {
return this.otrFingerprint;
}
+ public void verifyOtrFingerprint() {
+ getContact().addOtrFingerprint(getOtrFingerprint());
+ }
+
+ public boolean isOtrFingerprintVerified() {
+ return getContact().getOtrFingerprints().contains(getOtrFingerprint());
+ }
+
public synchronized MucOptions getMucOptions() {
if (this.mucOptions == null) {
this.mucOptions = new MucOptions(this);
@@ -403,6 +417,10 @@ public class Conversation extends AbstractEntity {
}
}
+ public boolean smpRequested() {
+ return smp().status == Smp.STATUS_CONTACT_REQUESTED;
+ }
+
public void setNextMessage(String message) {
this.nextMessage = message;
}
@@ -503,4 +521,16 @@ public class Conversation extends AbstractEntity {
this.messages.addAll(index, messages);
}
}
+
+ public class Smp {
+ public static final int STATUS_NONE = 0;
+ public static final int STATUS_CONTACT_REQUESTED = 1;
+ public static final int STATUS_WE_REQUESTED = 2;
+ public static final int STATUS_FAILED = 3;
+ public static final int STATUS_VERIFIED = 4;
+
+ public String secret = null;
+ public String hint = null;
+ public int status = 0;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java
index 385aab92..8b544efc 100644
--- a/src/main/java/eu/siacs/conversations/services/NotificationService.java
+++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java
@@ -39,6 +39,7 @@ public class NotificationService {
private LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<String, ArrayList<Message>>();
public static int NOTIFICATION_ID = 0x2342;
+ public static int FOREGROUND_NOTIFICATION_ID = 0x8899;
private Conversation mOpenConversation;
private boolean mIsInForeground;
private long mLastNotification;
@@ -298,9 +299,11 @@ public class NotificationService {
Intent viewConversationIntent = new Intent(mXmppConnectionService,
ConversationActivity.class);
viewConversationIntent.setAction(Intent.ACTION_VIEW);
- viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
- conversationUuid);
- viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
+ if (conversationUuid!=null) {
+ viewConversationIntent.putExtra(ConversationActivity.CONVERSATION,
+ conversationUuid);
+ viewConversationIntent.setType(ConversationActivity.VIEW_CONVERSATION);
+ }
stackBuilder.addNextIntent(viewConversationIntent);
@@ -312,7 +315,14 @@ public class NotificationService {
private PendingIntent createDeleteIntent() {
Intent intent = new Intent(mXmppConnectionService,
XmppConnectionService.class);
- intent.setAction("clear_notification");
+ intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION);
+ return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
+ }
+
+ private PendingIntent createDisableForeground() {
+ Intent intent = new Intent(mXmppConnectionService,
+ XmppConnectionService.class);
+ intent.setAction(XmppConnectionService.ACTION_DISABLE_FOREGROUND);
return PendingIntent.getService(mXmppConnectionService, 0, intent, 0);
}
@@ -359,4 +369,15 @@ public class NotificationService {
: Config.MINI_GRACE_PERIOD * 2;
return SystemClock.elapsedRealtime() < (this.mLastNotification + miniGrace);
}
+
+ public Notification createForegroundNotification() {
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService);
+ mBuilder.setSmallIcon(R.drawable.ic_stat_communication_import_export);
+ mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service));
+ mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_disable));
+ mBuilder.setContentIntent(createDisableForeground());
+ mBuilder.setWhen(0);
+ mBuilder.setPriority(NotificationCompat.PRIORITY_MIN);
+ return mBuilder.build();
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 2e4341f9..42ec4f77 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -99,6 +99,7 @@ public class XmppConnectionService extends Service {
public static String ACTION_CLEAR_NOTIFICATION = "clear_notification";
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+ public static String ACTION_DISABLE_FOREGROUND = "disable_foreground";
private ContentObserver contactObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
@@ -388,6 +389,9 @@ public class XmppConnectionService extends Service {
return START_NOT_STICKY;
} else if (intent.getAction().equals(ACTION_CLEAR_NOTIFICATION)) {
mNotificationService.clear();
+ } else if (intent.getAction().equals(ACTION_DISABLE_FOREGROUND)) {
+ getPreferences().edit().putBoolean("keep_foreground_service",false).commit();
+ toggleForegroundService();
}
}
this.wakeLock.acquire();
@@ -502,18 +506,23 @@ public class XmppConnectionService extends Service {
this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"XmppConnectionService");
+ toggleForegroundService();
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- this.logoutAndSave();
+ public void toggleForegroundService() {
+ if (getPreferences().getBoolean("keep_foreground_service",false)) {
+ startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
+ } else {
+ stopForeground(true);
+ }
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
- this.logoutAndSave();
+ if (!getPreferences().getBoolean("keep_foreground_service",false)) {
+ this.logoutAndSave();
+ }
}
private void logoutAndSave() {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index 8af342a7..bc609fb3 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -35,7 +35,6 @@ import net.java.otr4j.session.SessionStatus;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import eu.siacs.conversations.R;
@@ -56,7 +55,6 @@ import eu.siacs.conversations.ui.XmppActivity.OnValueEdited;
import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked;
import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked;
-import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.jid.Jid;
public class ConversationFragment extends Fragment {
@@ -149,6 +147,19 @@ public class ConversationFragment extends Fragment {
}
}
};
+ protected OnClickListener clickToVerify = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (conversation.getOtrFingerprint() != null) {
+ Intent intent = new Intent(getActivity(), VerifyOTRActivity.class);
+ intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT);
+ intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString());
+ intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString());
+ startActivity(intent);
+ }
+ }
+ };
private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>();
private boolean mDecryptJobRunning = false;
private OnEditorActionListener mEditorActionListener = new OnEditorActionListener() {
@@ -299,7 +310,7 @@ public class ConversationFragment extends Fragment {
.getConversation());
}
}
- } else {
+ } else {
Account account = message.getConversation().getAccount();
Intent intent = new Intent(activity, EditAccountActivity.class);
intent.putExtra("jid", account.getJid().toBareJid().toString());
@@ -530,6 +541,39 @@ public class ConversationFragment extends Fragment {
activity.switchToContactDetails(contact);
}
});
+ } else if (conversation.getMode() == Conversation.MODE_SINGLE) {
+ makeFingerprintWarning();
+ } else if (!conversation.getMucOptions().online()
+ && conversation.getAccount().getStatus() == Account.STATUS_ONLINE) {
+ int error = conversation.getMucOptions().getError();
+ switch (error) {
+ case MucOptions.ERROR_NICK_IN_USE:
+ showSnackbar(R.string.nick_in_use, R.string.edit,
+ clickToMuc);
+ break;
+ case MucOptions.ERROR_ROOM_NOT_FOUND:
+ showSnackbar(R.string.conference_not_found,
+ R.string.leave, leaveMuc);
+ break;
+ case MucOptions.ERROR_PASSWORD_REQUIRED:
+ showSnackbar(R.string.conference_requires_password,
+ R.string.enter_password, enterPassword);
+ break;
+ case MucOptions.ERROR_BANNED:
+ showSnackbar(R.string.conference_banned,
+ R.string.leave, leaveMuc);
+ break;
+ case MucOptions.ERROR_MEMBERS_ONLY:
+ showSnackbar(R.string.conference_members_only,
+ R.string.leave, leaveMuc);
+ break;
+ case MucOptions.KICKED_FROM_ROOM:
+ showSnackbar(R.string.conference_kicked, R.string.join,
+ joinMuc);
+ break;
+ default:
+ break;
+ }
}
for (Message message : this.conversation.getMessages()) {
if (message.getEncryption() == Message.ENCRYPTION_PGP
@@ -551,44 +595,6 @@ public class ConversationFragment extends Fragment {
updateStatusMessages();
}
this.messageListAdapter.notifyDataSetChanged();
- if (conversation.getMode() == Conversation.MODE_SINGLE) {
- if (messageList.size() >= 1) {
- makeFingerprintWarning();
- }
- } else {
- if (!conversation.getMucOptions().online()
- && conversation.getAccount().getStatus() == Account.STATUS_ONLINE) {
- int error = conversation.getMucOptions().getError();
- switch (error) {
- case MucOptions.ERROR_NICK_IN_USE:
- showSnackbar(R.string.nick_in_use, R.string.edit,
- clickToMuc);
- break;
- case MucOptions.ERROR_ROOM_NOT_FOUND:
- showSnackbar(R.string.conference_not_found,
- R.string.leave, leaveMuc);
- break;
- case MucOptions.ERROR_PASSWORD_REQUIRED:
- showSnackbar(R.string.conference_requires_password,
- R.string.enter_password, enterPassword);
- break;
- case MucOptions.ERROR_BANNED:
- showSnackbar(R.string.conference_banned,
- R.string.leave, leaveMuc);
- break;
- case MucOptions.ERROR_MEMBERS_ONLY:
- showSnackbar(R.string.conference_members_only,
- R.string.leave, leaveMuc);
- break;
- case MucOptions.KICKED_FROM_ROOM:
- showSnackbar(R.string.conference_kicked, R.string.join,
- joinMuc);
- break;
- default:
- break;
- }
- }
- }
updateChatMsgHint();
if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
activity.xmppConnectionService.markRead(conversation, true);
@@ -705,26 +711,11 @@ public class ConversationFragment extends Fragment {
}
protected void makeFingerprintWarning() {
- Set<String> knownFingerprints = conversation.getContact()
- .getOtrFingerprints();
- if (conversation.hasValidOtrSession()
- && (!conversation.isMuted())
- && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints
- .contains(conversation.getOtrFingerprint()))) {
- showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify,
- new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (conversation.getOtrFingerprint() != null) {
- AlertDialog dialog = UIHelper
- .getVerifyFingerprintDialog(
- (ConversationActivity) getActivity(),
- conversation, snackbar);
- dialog.show();
- }
- }
- });
+ if (conversation.smpRequested()) {
+ showSnackbar(R.string.smp_requested, R.string.verify, clickToVerify);
+ } else if (conversation.hasValidOtrSession() && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
+ && (!conversation.isOtrFingerprintVerified())) {
+ showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify);
}
}
@@ -851,15 +842,15 @@ public class ConversationFragment extends Fragment {
final ConversationActivity activity = (ConversationActivity) getActivity();
final XmppConnectionService xmppService = activity.xmppConnectionService;
activity.selectPresence(message.getConversation(),
- new OnPresenceSelected() {
+ new OnPresenceSelected() {
- @Override
- public void onPresenceSelected() {
- message.setCounterpart(conversation.getNextCounterpart());
- xmppService.sendMessage(message);
- messageSent();
- }
- });
+ @Override
+ public void onPresenceSelected() {
+ message.setCounterpart(conversation.getNextCounterpart());
+ xmppService.sendMessage(message);
+ messageSent();
+ }
+ });
}
public void appendText(String text) {
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index aba60175..b6f3a077 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -70,6 +70,8 @@ public class SettingsActivity extends XmppActivity implements
}
}
}
+ } else if (name.equals("keep_foreground_service")) {
+ xmppConnectionService.toggleForegroundService();
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java
new file mode 100644
index 00000000..31884bd2
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java
@@ -0,0 +1,296 @@
+package eu.siacs.conversations.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import net.java.otr4j.OtrException;
+import net.java.otr4j.session.Session;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class VerifyOTRActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
+
+ public static final String ACTION_VERIFY_CONTACT = "verify_contact";
+
+ private RelativeLayout mVerificationAreaOne;
+ private RelativeLayout mVerificationAreaTwo;
+ private TextView mErrorNoSession;
+ private TextView mRemoteJid;
+ private TextView mRemoteFingerprint;
+ private TextView mYourFingerprint;
+ private EditText mSharedSecretHint;
+ private EditText mSharedSecretSecret;
+ private Button mButtonVerifyFingerprint;
+ private Button mButtonSharedSecretPositive;
+ private Button mButtonSharedSecretNegative;
+ private TextView mStatusMessage;
+ private Account mAccount;
+ private Conversation mConversation;
+
+ private View.OnClickListener mVerifyFingerprintListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ mConversation.verifyOtrFingerprint();
+ finish();
+ }
+ };
+
+ private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ if (isAccountOnline()) {
+ final String question = mSharedSecretHint.getText().toString();
+ final String secret = mSharedSecretSecret.getText().toString();
+ initSmp(question, secret);
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (isAccountOnline()) {
+ abortSmp();
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ if (isAccountOnline()) {
+ final String question = mSharedSecretHint.getText().toString();
+ final String secret = mSharedSecretSecret.getText().toString();
+ respondSmp(question, secret);
+ updateView();
+ }
+ }
+ };
+ private View.OnClickListener mRetrySharedSecretListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ mConversation.smp().hint = null;
+ mConversation.smp().secret = null;
+ updateView();
+ }
+ };
+ private View.OnClickListener mFinishListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ finish();
+ }
+ };
+
+ protected boolean initSmp(final String question, final String secret) {
+ final Session session = mConversation.getOtrSession();
+ if (session!=null) {
+ try {
+ session.initSmp(question, secret);
+ mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean abortSmp() {
+ final Session session = mConversation.getOtrSession();
+ if (session!=null) {
+ try {
+ session.abortSmp();
+ mConversation.smp().status = Conversation.Smp.STATUS_NONE;
+ mConversation.smp().hint = null;
+ mConversation.smp().secret = null;
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean respondSmp(final String question, final String secret) {
+ final Session session = mConversation.getOtrSession();
+ if (session!=null) {
+ try {
+ session.respondSmp(question,secret);
+ return true;
+ } catch (OtrException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ protected boolean isAccountOnline() {
+ if (this.mAccount.getStatus() != Account.STATUS_ONLINE) {
+ Toast.makeText(this,R.string.not_connected_try_again,Toast.LENGTH_SHORT).show();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ protected boolean handleIntent(Intent intent) {
+ if (intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
+ try {
+ this.mAccount = this.xmppConnectionService.findAccountByJid(Jid.fromString(intent.getExtras().getString("account")));
+ } catch (final InvalidJidException ignored) {
+ return false;
+ }
+ try {
+ this.mConversation = this.xmppConnectionService.find(this.mAccount,Jid.fromString(intent.getExtras().getString("contact")));
+ if (this.mConversation == null) {
+ return false;
+ }
+ } catch (final InvalidJidException ignored) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void onBackendConnected() {
+ if (handleIntent(getIntent())) {
+ updateView();
+ }
+ }
+
+ protected void updateView() {
+ if (this.mConversation.hasValidOtrSession()) {
+ this.mVerificationAreaOne.setVisibility(View.VISIBLE);
+ this.mVerificationAreaTwo.setVisibility(View.VISIBLE);
+ this.mErrorNoSession.setVisibility(View.GONE);
+ this.mYourFingerprint.setText(this.mAccount.getOtrFingerprint(xmppConnectionService));
+ this.mRemoteFingerprint.setText(this.mConversation.getOtrFingerprint());
+ this.mRemoteJid.setText(this.mConversation.getContact().getJid().toBareJid().toString());
+ Conversation.Smp smp = mConversation.smp();
+ Session session = mConversation.getOtrSession();
+ if (mConversation.isOtrFingerprintVerified()) {
+ deactivateButton(mButtonVerifyFingerprint, R.string.verified);
+ } else {
+ activateButton(mButtonVerifyFingerprint, R.string.verify, mVerifyFingerprintListener);
+ }
+ if (smp.status == Conversation.Smp.STATUS_NONE) {
+ activateButton(mButtonSharedSecretPositive, R.string.create, mCreateSharedSecretListener);
+ deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
+ this.mSharedSecretHint.setFocusableInTouchMode(true);
+ this.mSharedSecretSecret.setFocusableInTouchMode(true);
+ this.mSharedSecretSecret.setText("");
+ this.mSharedSecretHint.setText("");
+ this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mStatusMessage.setVisibility(View.GONE);
+ } else if (smp.status == Conversation.Smp.STATUS_CONTACT_REQUESTED) {
+ this.mSharedSecretHint.setFocusable(false);
+ this.mSharedSecretHint.setText(smp.hint);
+ this.mSharedSecretSecret.setFocusableInTouchMode(true);
+ this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mStatusMessage.setVisibility(View.GONE);
+ deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
+ activateButton(mButtonSharedSecretPositive, R.string.respond, mRespondSharedSecretListener);
+ } else if (smp.status == Conversation.Smp.STATUS_FAILED) {
+ activateButton(mButtonSharedSecretNegative, R.string.cancel, mFinishListener);
+ activateButton(mButtonSharedSecretPositive, R.string.try_again, mRetrySharedSecretListener);
+ this.mSharedSecretHint.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setVisibility(View.GONE);
+ this.mStatusMessage.setVisibility(View.VISIBLE);
+ this.mStatusMessage.setText(R.string.secrets_do_not_match);
+ this.mStatusMessage.setTextColor(getWarningTextColor());
+ } else if (smp.status == Conversation.Smp.STATUS_VERIFIED) {
+ this.mSharedSecretHint.setVisibility(View.GONE);
+ this.mSharedSecretSecret.setVisibility(View.GONE);
+ this.mStatusMessage.setVisibility(View.VISIBLE);
+ this.mStatusMessage.setText(R.string.verified);
+ this.mStatusMessage.setTextColor(getPrimaryColor());
+ deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
+ activateButton(mButtonSharedSecretPositive, R.string.finish, mFinishListener);
+ } else if (session != null && session.isSmpInProgress()) {
+ deactivateButton(mButtonSharedSecretPositive, R.string.in_progress);
+ activateButton(mButtonSharedSecretNegative, R.string.cancel, mCancelSharedSecretListener);
+ this.mSharedSecretHint.setVisibility(View.VISIBLE);
+ this.mSharedSecretSecret.setVisibility(View.VISIBLE);
+ this.mSharedSecretHint.setFocusable(false);
+ this.mSharedSecretSecret.setFocusable(false);
+ }
+ } else {
+ this.mVerificationAreaOne.setVisibility(View.GONE);
+ this.mVerificationAreaTwo.setVisibility(View.GONE);
+ this.mErrorNoSession.setVisibility(View.VISIBLE);
+ }
+ }
+
+ protected void activateButton(Button button, int text, View.OnClickListener listener) {
+ button.setEnabled(true);
+ button.setTextColor(getPrimaryTextColor());
+ button.setText(text);
+ button.setOnClickListener(listener);
+ }
+
+ protected void deactivateButton(Button button, int text) {
+ button.setEnabled(false);
+ button.setTextColor(getSecondaryTextColor());
+ button.setText(text);
+ button.setOnClickListener(null);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_verify_otr);
+ this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
+ this.mRemoteJid = (TextView) findViewById(R.id.remote_jid);
+ this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
+ this.mButtonSharedSecretNegative = (Button) findViewById(R.id.button_shared_secret_negative);
+ this.mButtonSharedSecretPositive = (Button) findViewById(R.id.button_shared_secret_positive);
+ this.mButtonVerifyFingerprint = (Button) findViewById(R.id.button_verify_fingerprint);
+ this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
+ this.mSharedSecretHint = (EditText) findViewById(R.id.shared_secret_hint);
+ this.mStatusMessage= (TextView) findViewById(R.id.status_message);
+ this.mVerificationAreaOne = (RelativeLayout) findViewById(R.id.verification_area_one);
+ this.mVerificationAreaTwo = (RelativeLayout) findViewById(R.id.verification_area_two);
+ this.mErrorNoSession = (TextView) findViewById(R.id.error_no_session);
+ }
+
+ @Override
+ protected String getShareableUri() {
+ if (mAccount!=null) {
+ return "xmpp:"+mAccount.getJid().toBareJid();
+ } else {
+ return "";
+ }
+ }
+
+ public void onConversationUpdate() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ updateView();
+ }
+ });
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
index 9d78e222..992ff979 100644
--- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java
@@ -685,6 +685,7 @@ public abstract class XmppActivity extends Activity {
}
protected Bitmap createQrCodeBitmap(String input, int size) {
+ Log.d(Config.LOGTAG,"qr code requested size: "+size);
try {
final QRCodeWriter QR_CODE_WRITER = new QRCodeWriter();
final Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
@@ -700,6 +701,7 @@ public abstract class XmppActivity extends Activity {
}
}
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Log.d(Config.LOGTAG,"output size: "+width+"x"+height);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
} catch (final WriterException e) {
diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
index 1def052e..c4832d60 100644
--- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java
@@ -148,40 +148,6 @@ public class UIHelper {
mNotificationManager.notify(1111, notification);
}
- @SuppressLint("InflateParams")
- public static AlertDialog getVerifyFingerprintDialog(
- final ConversationActivity activity,
- final Conversation conversation, final View msg) {
- final Contact contact = conversation.getContact();
- final Account account = conversation.getAccount();
-
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle("Verify fingerprint");
- LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.dialog_verify_otr, null);
- TextView jid = (TextView) view.findViewById(R.id.verify_otr_jid);
- TextView fingerprint = (TextView) view
- .findViewById(R.id.verify_otr_fingerprint);
- TextView yourprint = (TextView) view
- .findViewById(R.id.verify_otr_yourprint);
-
- jid.setText(contact.getJid().toString());
- fingerprint.setText(conversation.getOtrFingerprint());
- yourprint.setText(account.getOtrFingerprint());
- builder.setNegativeButton("Cancel", null);
- builder.setPositiveButton("Verify", new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- contact.addOtrFingerprint(conversation.getOtrFingerprint());
- msg.setVisibility(View.GONE);
- activity.xmppConnectionService.syncRosterToDisk(account);
- }
- });
- builder.setView(view);
- return builder.create();
- }
-
private final static class EmoticonPattern {
Pattern pattern;
String replacement;
diff --git a/src/main/res/drawable-hdpi/ic_stat_communication_import_export.png b/src/main/res/drawable-hdpi/ic_stat_communication_import_export.png
new file mode 100644
index 00000000..f929996e
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_stat_communication_import_export.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_stat_communication_import_export.png b/src/main/res/drawable-mdpi/ic_stat_communication_import_export.png
new file mode 100644
index 00000000..b581e174
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_stat_communication_import_export.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_stat_communication_import_export.png b/src/main/res/drawable-xhdpi/ic_stat_communication_import_export.png
new file mode 100644
index 00000000..a1d62d41
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_stat_communication_import_export.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_stat_communication_import_export.png b/src/main/res/drawable-xxhdpi/ic_stat_communication_import_export.png
new file mode 100644
index 00000000..3539cd91
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_stat_communication_import_export.png
Binary files differ
diff --git a/src/main/res/layout/activity_verify_otr.xml b/src/main/res/layout/activity_verify_otr.xml
new file mode 100644
index 00000000..d907d871
--- /dev/null
+++ b/src/main/res/layout/activity_verify_otr.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="@color/secondarybackground">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/error_no_session"
+ android:layout_margin="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/no_otr_session_found"
+ android:layout_gravity="center_horizontal"
+ android:textColor="@color/primarytext"
+ android:textSize="?attr/TextSizeBody"
+ />
+ <RelativeLayout
+ android:id="@+id/verification_area_one"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="@drawable/infocard_border"
+ android:layout_margin="8dp">
+ <LinearLayout
+ android:id="@+id/fingerprint_area"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/remote_jid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/primarytext"
+ android:textSize="?attr/TextSizeHeadline"/>
+ <TextView
+ android:layout_marginTop="16dp"
+ android:id="@+id/your_fingerprint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/primarytext"
+ android:textSize="?attr/TextSizeBody"
+ android:typeface="monospace" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/secondarytext"
+ android:textSize="?attr/TextSizeInfo"
+ android:text="@string/your_fingerprint"/>
+ <TextView
+ android:layout_marginTop="16dp"
+ android:id="@+id/remote_fingerprint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/primarytext"
+ android:textSize="?attr/TextSizeBody"
+ android:typeface="monospace" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/secondarytext"
+ android:textSize="?attr/TextSizeInfo"
+ android:text="@string/remote_fingerprint"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_below="@+id/fingerprint_area"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true" >
+
+ <Button
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="7dp"
+ android:layout_marginTop="7dp"
+ android:background="@color/divider"
+ android:visibility="invisible"/>
+
+ <Button
+ android:id="@+id/button_verify_fingerprint"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/verify"
+ android:textColor="@color/primarytext" />
+ </LinearLayout>
+ </RelativeLayout>
+ <RelativeLayout
+ android:id="@+id/verification_area_two"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:background="@drawable/infocard_border">
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:id="@+id/shared_secret_box"
+ android:padding="16dp">
+ <TextView
+ android:text="@string/smp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/primarytext"
+ android:textSize="?attr/TextSizeHeadline"
+ android:layout_marginBottom="16dp"
+ />
+ <TextView
+ android:id="@+id/status_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/verified"
+ android:layout_gravity="center_horizontal"
+ android:textSize="?attr/TextSizeHeadline"
+ android:textStyle="bold"
+ android:visibility="gone"/>
+ <EditText
+ android:id="@+id/shared_secret_hint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textAutoComplete"
+ android:hint="@string/shared_secret_hint"
+ android:textColor="@color/primarytext"
+ android:textColorHint="@color/secondarytext"
+ android:textSize="?attr/TextSizeBody"
+ android:layout_marginBottom="8dp"/>
+ <EditText
+ android:id="@+id/shared_secret_secret"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/shared_secret_secret"
+ android:inputType="textPassword"
+ android:textColor="@color/primarytext"
+ android:textColorHint="@color/secondarytext"
+ android:textSize="?attr/TextSizeBody" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_below="@+id/shared_secret_box"
+ android:id="@+id/button_bar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true" >
+
+ <Button
+ android:id="@+id/button_shared_secret_negative"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:enabled="false"
+ android:text="@string/cancel"
+ android:textColor="@color/secondarytext"/>
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="fill_parent"
+ android:layout_marginBottom="7dp"
+ android:layout_marginTop="7dp"
+ android:background="@color/divider" />
+
+ <Button
+ android:id="@+id/button_shared_secret_positive"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/create"
+ android:textColor="@color/primarytext" />
+ </LinearLayout>
+ </RelativeLayout>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/src/main/res/layout/dialog_verify_otr.xml b/src/main/res/layout/dialog_verify_otr.xml
deleted file mode 100644
index 499ef6cd..00000000
--- a/src/main/res/layout/dialog_verify_otr.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingBottom="16dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:text="@string/account_settings_jabber_id"
- android:textColor="@color/primarytext"
- android:textSize="?attr/TextSizeHeadline" />
-
- <TextView
- android:id="@+id/verify_otr_jid"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="8dp"
- android:textColor="@color/secondarytext"
- android:textSize="?attr/TextSizeBody" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:text="@string/otr_fingerprint"
- android:textColor="@color/primarytext"
- android:textSize="?attr/TextSizeHeadline" />
-
- <TextView
- android:id="@+id/verify_otr_fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="8dp"
- android:textColor="@color/secondarytext"
- android:textSize="?attr/TextSizeBody"
- android:typeface="monospace" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:text="@string/your_fingerprint"
- android:textColor="@color/primarytext"
- android:textSize="?attr/TextSizeHeadline" />
-
- <TextView
- android:id="@+id/verify_otr_yourprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="8dp"
- android:textColor="@color/secondarytext"
- android:textSize="?attr/TextSizeBody"
- android:typeface="monospace" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 3d69e0f3..e720101c 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -311,6 +311,27 @@
<string name="scan_qr_code">Scan QR code</string>
<string name="show_qr_code">Show QR code</string>
<string name="account_details">Account details</string>
+ <string name="verify_otr">Verify OTR</string>
+ <string name="remote_fingerprint">Remote Fingerprint</string>
+ <string name="scan">scan</string>
+ <string name="or_touch_phones">(or touch phones)</string>
+ <string name="smp">Socialist Millionaire Protocol</string>
+ <string name="shared_secret_hint">Hint or Question</string>
+ <string name="shared_secret_secret">Shared Secret</string>
+ <string name="confirm">Confirm</string>
+ <string name="in_progress">In progress</string>
+ <string name="respond">Respond</string>
+ <string name="failed">Failed</string>
+ <string name="secrets_do_not_match">Secrets do not match</string>
+ <string name="try_again">Try again</string>;
+ <string name="finish">Finish</string>
+ <string name="verified">Verified!</string>
+ <string name="smp_requested">Contact requested SMP verification</string>
+ <string name="no_otr_session_found">No valid OTR session has been found!</string>
+ <string name="conversations_foreground_service">Conversations</string>
+ <string name="touch_to_disable">Touch to disable foreground service</string>
+ <string name="pref_keep_foreground_service">Keep service in foreground</string>
+ <string name="pref_keep_foreground_service_summary">Prevents the operating system from killing your connection</string>
<string name="choose_file">Choose file</string>
<string name="receiving_file">Receiving %1$s file (%2$d%% completed)</string>
<string name="download_file">Download %s file</string>
diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml
index 15a61e87..3be65b7a 100644
--- a/src/main/res/xml/preferences.xml
+++ b/src/main/res/xml/preferences.xml
@@ -101,6 +101,11 @@
android:key="indicate_received"
android:summary="@string/pref_use_indicate_received_summary"
android:title="@string/pref_use_indicate_received" />
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:key="keep_foreground_service"
+ android:title="@string/pref_keep_foreground_service"
+ android:summary="@string/pref_keep_foreground_service_summary" />
</PreferenceCategory>
</PreferenceScreen>