made scan and show qr code more accessible in VerifyOtrActivity

iNPUTmice created

Change summary

src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java |  52 
src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java         | 108 
src/main/java/eu/siacs/conversations/utils/XmppUri.java                |  79 
src/main/res/layout/activity_verify_otr.xml                            |  10 
src/main/res/menu/verify_otr.xml                                       |  20 
src/main/res/values/strings.xml                                        |   3 
6 files changed, 214 insertions(+), 58 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java 🔗

@@ -45,8 +45,6 @@ import android.widget.Spinner;
 import com.google.zxing.integration.android.IntentIntegrator;
 import com.google.zxing.integration.android.IntentResult;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -59,10 +57,10 @@ import eu.siacs.conversations.entities.Bookmark;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
 import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 import eu.siacs.conversations.ui.adapter.ListItemAdapter;
-import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.Validator;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
@@ -755,21 +753,17 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
 		}
 	}
 
-	private class Invite {
+	private class Invite extends XmppUri {
 		private String jid;
 		private boolean muc;
 		private String fingerprint;
 
-		Invite(Uri uri) {
-			parse(uri);
+		public Invite(Uri uri) {
+			super(uri);
 		}
 
-		Invite(String uri) {
-			try {
-				parse(Uri.parse(uri));
-			} catch (IllegalArgumentException e) {
-				jid = null;
-			}
+		public Invite(String uri) {
+			super(uri);
 		}
 
 		boolean invite() {
@@ -782,39 +776,5 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
 			}
 			return false;
 		}
-
-		void parse(Uri uri) {
-			String scheme = uri.getScheme();
-			if ("xmpp".equals(scheme)) {
-				// sample: xmpp:jid@foo.com
-				muc = "join".equalsIgnoreCase(uri.getQuery());
-				if (uri.getAuthority() != null) {
-					jid = uri.getAuthority();
-				} else {
-					jid = uri.getSchemeSpecificPart().split("\\?")[0];
-				}
-				fingerprint = parseFingerprint(uri.getQuery());
-			} else if ("imto".equals(scheme)) {
-				// sample: imto://xmpp/jid@foo.com
-				try {
-					jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1];
-				} catch (final UnsupportedEncodingException ignored) {
-				}
-			}
-		}
-
-		String parseFingerprint(String query) {
-			if (query == null) {
-				return null;
-			} else {
-				final String NEEDLE = "otr-fingerprint=";
-				int index = query.indexOf(NEEDLE);
-				if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) {
-					return CryptoHelper.prettifyFingerprint(query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40));
-				} else {
-					return null;
-				}
-			}
-		}
 	}
 }

src/main/java/eu/siacs/conversations/ui/VerifyOTRActivity.java 🔗

@@ -1,7 +1,11 @@
 package eu.siacs.conversations.ui;
 
+import android.app.AlertDialog;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.widget.Button;
 import android.widget.EditText;
@@ -9,14 +13,19 @@ import android.widget.RelativeLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
+
 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.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
 
@@ -32,22 +41,40 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 	private TextView mYourFingerprint;
 	private EditText mSharedSecretHint;
 	private EditText mSharedSecretSecret;
-	private Button mButtonVerifyFingerprint;
+	private Button mButtonScanQrCode;
+	private Button mButtonShowQrCode;
 	private Button mButtonSharedSecretPositive;
 	private Button mButtonSharedSecretNegative;
 	private TextView mStatusMessage;
 	private Account mAccount;
 	private Conversation mConversation;
 
-	private View.OnClickListener mVerifyFingerprintListener = new View.OnClickListener() {
+	private DialogInterface.OnClickListener mVerifyFingerprintListener = new DialogInterface.OnClickListener() {
 
 		@Override
-		public void onClick(View view) {
+		public void onClick(DialogInterface dialogInterface, int click) {
 			mConversation.verifyOtrFingerprint();
-			finish();
+			updateView();
+			xmppConnectionService.syncRosterToDisk(mConversation.getAccount());
 		}
 	};
 
+	private View.OnClickListener mShowQrCodeListener = new View.OnClickListener() {
+		@Override
+		public void onClick(final View view) {
+			showQrCode();
+		}
+	};
+
+	private View.OnClickListener mScanQrCodeListener = new View.OnClickListener() {
+
+		@Override
+		public void onClick(View view) {
+			new IntentIntegrator(VerifyOTRActivity.this).initiateScan();
+		}
+
+	};
+
 	private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
 		@Override
 		public void onClick(final View view) {
@@ -97,6 +124,8 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 		}
 	};
 
+	private XmppUri mPendingUri = null;
+
 	protected boolean initSmp(final String question, final String secret) {
 		final Session session = mConversation.getOtrSession();
 		if (session!=null) {
@@ -143,6 +172,18 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 		}
 	}
 
+	protected void verifyWithUri(XmppUri uri) {
+		Contact contact = mConversation.getContact();
+		if (this.mConversation.getContact().getJid().equals(uri.getJid()) && uri.getFingerprint() != null) {
+			contact.addOtrFingerprint(uri.getFingerprint());
+			Toast.makeText(this,R.string.verified,Toast.LENGTH_SHORT).show();
+			updateView();
+			xmppConnectionService.syncRosterToDisk(contact.getAccount());
+		} else {
+			Toast.makeText(this,R.string.could_not_verify_fingerprint,Toast.LENGTH_SHORT).show();
+		}
+	}
+
 	protected boolean isAccountOnline() {
 		if (this.mAccount.getStatus() != Account.State.ONLINE) {
 			Toast.makeText(this,R.string.not_connected_try_again,Toast.LENGTH_SHORT).show();
@@ -173,15 +214,37 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 		}
 	}
 
+	@Override
+	public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+		if ((requestCode & 0xFFFF) == IntentIntegrator.REQUEST_CODE) {
+			IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
+			if (scanResult != null && scanResult.getFormatName() != null) {
+				String data = scanResult.getContents();
+				XmppUri uri = new XmppUri(data);
+				if (xmppConnectionServiceBound) {
+					verifyWithUri(uri);
+				} else {
+					this.mPendingUri = uri;
+				}
+			}
+		}
+		super.onActivityResult(requestCode, requestCode, intent);
+	}
+
 	@Override
 	protected void onBackendConnected() {
 		if (handleIntent(getIntent())) {
+			if (mPendingUri!=null) {
+				verifyWithUri(mPendingUri);
+				mPendingUri = null;
+			}
 			updateView();
 		}
 	}
 
 	protected void updateView() {
 		if (this.mConversation.hasValidOtrSession()) {
+			invalidateOptionsMenu();
 			this.mVerificationAreaOne.setVisibility(View.VISIBLE);
 			this.mVerificationAreaTwo.setVisibility(View.VISIBLE);
 			this.mErrorNoSession.setVisibility(View.GONE);
@@ -191,9 +254,9 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 			Conversation.Smp smp = mConversation.smp();
 			Session session = mConversation.getOtrSession();
 			if (mConversation.isOtrFingerprintVerified()) {
-				deactivateButton(mButtonVerifyFingerprint, R.string.verified);
+				deactivateButton(mButtonScanQrCode, R.string.verified);
 			} else {
-				activateButton(mButtonVerifyFingerprint, R.string.verify, mVerifyFingerprintListener);
+				activateButton(mButtonScanQrCode, R.string.scan_qr_code, mScanQrCodeListener);
 			}
 			if (smp.status == Conversation.Smp.STATUS_NONE) {
 				activateButton(mButtonSharedSecretPositive, R.string.create, mCreateSharedSecretListener);
@@ -268,7 +331,9 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 		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.mButtonScanQrCode = (Button) findViewById(R.id.button_scan_qr_code);
+		this.mButtonShowQrCode = (Button) findViewById(R.id.button_show_qr_code);
+		this.mButtonShowQrCode.setOnClickListener(this.mShowQrCodeListener);
 		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);
@@ -277,6 +342,35 @@ public class VerifyOTRActivity extends XmppActivity implements XmppConnectionSer
 		this.mErrorNoSession = (TextView) findViewById(R.id.error_no_session);
 	}
 
+	@Override
+	public boolean onCreateOptionsMenu(Menu menu) {
+		getMenuInflater().inflate(R.menu.verify_otr, menu);
+		if (mConversation != null && mConversation.isOtrFingerprintVerified()) {
+			MenuItem manuallyVerifyItem = menu.findItem(R.id.manually_verify);
+			manuallyVerifyItem.setVisible(false);
+		}
+		return true;
+	}
+
+	@Override
+	public boolean onOptionsItemSelected(MenuItem menuItem) {
+		if (menuItem.getItemId() == R.id.manually_verify) {
+			showManuallyVerifyDialog();
+			return true;
+		} else {
+			return super.onOptionsItemSelected(menuItem);
+		}
+	}
+
+	private void showManuallyVerifyDialog() {
+		AlertDialog.Builder builder = new AlertDialog.Builder(this);
+		builder.setTitle(R.string.manually_verify);
+		builder.setMessage(R.string.are_you_sure_verify_fingerprint);
+		builder.setNegativeButton(R.string.cancel, null);
+		builder.setPositiveButton(R.string.verify, mVerifyFingerprintListener);
+		builder.create().show();
+	}
+
 	@Override
 	protected String getShareableUri() {
 		if (mAccount!=null) {

src/main/java/eu/siacs/conversations/utils/XmppUri.java 🔗

@@ -0,0 +1,79 @@
+package eu.siacs.conversations.utils;
+
+import android.net.Uri;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+
+public class XmppUri {
+
+	private String jid;
+	private boolean muc;
+	private String fingerprint;
+
+	public XmppUri(String uri) {
+		try {
+			parse(Uri.parse(uri));
+		} catch (IllegalArgumentException e) {
+			jid = null;
+		}
+	}
+
+	public XmppUri(Uri uri) {
+		parse(uri);
+	}
+
+	protected void parse(Uri uri) {
+		String scheme = uri.getScheme();
+		if ("xmpp".equals(scheme)) {
+			// sample: xmpp:jid@foo.com
+			muc = "join".equalsIgnoreCase(uri.getQuery());
+			if (uri.getAuthority() != null) {
+				jid = uri.getAuthority();
+			} else {
+				jid = uri.getSchemeSpecificPart().split("\\?")[0];
+			}
+			fingerprint = parseFingerprint(uri.getQuery());
+		} else if ("imto".equals(scheme)) {
+			// sample: imto://xmpp/jid@foo.com
+			try {
+				jid = URLDecoder.decode(uri.getEncodedPath(), "UTF-8").split("/")[1];
+			} catch (final UnsupportedEncodingException ignored) {
+			}
+		}
+	}
+
+	protected  String parseFingerprint(String query) {
+		if (query == null) {
+			return null;
+		} else {
+			final String NEEDLE = "otr-fingerprint=";
+			int index = query.indexOf(NEEDLE);
+			if (index >= 0 && query.length() >= (NEEDLE.length() + index + 40)) {
+				return CryptoHelper.prettifyFingerprint(query.substring(index + NEEDLE.length(), index + NEEDLE.length() + 40));
+			} else {
+				return null;
+			}
+		}
+	}
+
+	public Jid getJid() {
+		try {
+			return Jid.fromString(this.jid);
+		} catch (InvalidJidException e) {
+			return null;
+		}
+	}
+
+	public String getFingerprint() {
+		return this.fingerprint;
+	}
+
+	public boolean isMuc() {
+		return this.muc;
+	}
+}

src/main/res/layout/activity_verify_otr.xml 🔗

@@ -76,27 +76,27 @@
                 android:layout_alignParentRight="true" >
 
                 <Button
+                    android:id="@+id/button_show_qr_code"
                     style="?android:attr/borderlessButtonStyle"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
                     android:layout_weight="1"
-                    android:visibility="invisible" />
+                    android:text="@string/show_qr_code"/>
 
                 <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"/>
+                    android:background="@color/divider" />
 
                 <Button
-                    android:id="@+id/button_verify_fingerprint"
+                    android:id="@+id/button_scan_qr_code"
                     style="?android:attr/borderlessButtonStyle"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
                     android:layout_weight="1"
-                    android:text="@string/verify"
+                    android:text="@string/scan_qr_code"
                     android:textColor="@color/primarytext" />
             </LinearLayout>
         </RelativeLayout>

src/main/res/menu/verify_otr.xml 🔗

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item
+        android:id="@+id/manually_verify"
+        android:orderInCategory="10"
+        android:showAsAction="never"
+        android:title="@string/manually_verify" />
+
+    <item
+        android:id="@+id/action_accounts"
+        android:orderInCategory="90"
+        android:showAsAction="never"
+        android:title="@string/action_accounts" />
+    <item
+        android:id="@+id/action_settings"
+        android:orderInCategory="100"
+        android:showAsAction="never"
+        android:title="@string/action_settings" />
+</menu>

src/main/res/values/strings.xml 🔗

@@ -346,4 +346,7 @@
     <string name="file_transmission_failed">file transmission failed</string>
     <string name="file_deleted">The file has been deleted</string>
     <string name="no_application_found_to_open_file">No application found to open file</string>
+    <string name="could_not_verify_fingerprint">Could not verify fingerprint</string>
+    <string name="manually_verify">Manually verify</string>
+    <string name="are_you_sure_verify_fingerprint">Are you sure that you want to verify your contacts OTR fingerprint?</string>
 </resources>