initial smp support

This commit is contained in:
iNPUTmice 2014-11-10 19:23:54 +01:00
parent e555fe4b03
commit 6cd2ff0d88
10 changed files with 552 additions and 122 deletions

View file

@ -95,6 +95,10 @@
android:name=".ui.PublishProfilePictureActivity"
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" >

View file

@ -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) {
}
}
}

View file

@ -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);
@ -503,4 +517,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;
}
}

View file

@ -680,23 +680,21 @@ 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()))) {
&& (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED)
&& (!conversation.isOtrFingerprintVerified())) {
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();
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);
}
}
});

View file

@ -0,0 +1,264 @@
package eu.siacs.conversations.ui;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoException;
import net.java.otr4j.session.Session;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
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 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 mCreateSharedSecretListener = new View.OnClickListener() {
@Override
public void onClick(final View view) {
final String question = mSharedSecretHint.getText().toString();
final String secret = mSharedSecretSecret.getText().toString();
if (!initSmp(question,secret)) {
Toast.makeText(getApplicationContext(),"smp failed",Toast.LENGTH_SHORT).show();
}
updateView();
}
};
private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
abortSmp();
}
};
private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
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;
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 handleIntent(Intent intent) {
if (getIntent().getAction().equals(ACTION_VERIFY_CONTACT)) {
try {
this.mAccount = this.xmppConnectionService.findAccountByJid(Jid.fromString(getIntent().getExtras().getString("account")));
} catch (final InvalidJidException ignored) {
return false;
}
try {
this.mConversation = this.xmppConnectionService.find(this.mAccount,Jid.fromString(getIntent().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();
}
this.xmppConnectionService.setOnConversationListChangedListener(this);
}
protected void updateView() {
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 (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);
}
}
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);
}
@Override
protected String getShareableUri() {
if (mAccount!=null) {
return "xmpp:"+mAccount.getJid().toBareJid();
} else {
return "";
}
}
@Override
protected void onStop() {
if (xmppConnectionServiceBound) {
xmppConnectionService.removeOnConversationListChangedListener();
}
super.onStop();
}
@Override
public void onConversationUpdate() {
runOnUiThread(new Runnable() {
@Override
public void run() {
updateView();
}
});
}
}

View file

@ -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) {

View file

@ -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;

View file

@ -0,0 +1,177 @@
<?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">
<RelativeLayout
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: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>

View file

@ -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>

View file

@ -311,4 +311,19 @@
<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>
</resources>