fixed nfc. react to ?join uris. some refactoring. code cleanup. thanks to @emdete

iNPUTmice created

Change summary

src/main/AndroidManifest.xml                                           |  42 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java      | 231 
src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java | 195 
src/main/java/eu/siacs/conversations/ui/XmppActivity.java              |  10 
4 files changed, 291 insertions(+), 187 deletions(-)

Detailed changes

src/main/AndroidManifest.xml 🔗

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-		package="eu.siacs.conversations">
+    package="eu.siacs.conversations">
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -12,16 +12,17 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.NFC" />
 
     <application
         android:allowBackup="true"
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name"
-        tools:replace="android:label"
-        android:theme="@style/ConversationsTheme" >
+        android:theme="@style/ConversationsTheme"
+        tools:replace="android:label">
         <service android:name="eu.siacs.conversations.services.XmppConnectionService" />
 
-        <receiver android:name="eu.siacs.conversations.services.EventReceiver" >
+        <receiver android:name="eu.siacs.conversations.services.EventReceiver">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
                 <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
@@ -33,7 +34,7 @@
             android:name="eu.siacs.conversations.ui.ConversationActivity"
             android:label="@string/title_activity_conversations"
             android:launchMode="singleTask"
-            android:windowSoftInputMode="stateHidden" >
+            android:windowSoftInputMode="stateHidden">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
@@ -43,8 +44,7 @@
         <activity
             android:name="eu.siacs.conversations.ui.StartConversationActivity"
             android:configChanges="orientation|screenSize"
-            android:label="@string/title_activity_start_conversation"
-            android:logo="@drawable/ic_activity" >
+            android:label="@string/title_activity_start_conversation">
             <intent-filter>
                 <action android:name="android.intent.action.SENDTO" />
 
@@ -61,42 +61,40 @@
 
                 <data android:scheme="xmpp" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="xmpp" />
+            </intent-filter>
         </activity>
         <activity
             android:name="eu.siacs.conversations.ui.SettingsActivity"
-            android:label="@string/title_activity_settings" >
-        </activity>
+            android:label="@string/title_activity_settings"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.ChooseContactActivity"
-            android:label="@string/title_activity_choose_contact" >
-        </activity>
+            android:label="@string/title_activity_choose_contact"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.ManageAccountActivity"
             android:configChanges="orientation|screenSize"
-            android:label="@string/title_activity_manage_accounts" >
-        </activity>
+            android:label="@string/title_activity_manage_accounts"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.EditAccountActivity"
-            android:windowSoftInputMode="stateHidden|adjustResize" >
-        </activity>
+            android:windowSoftInputMode="stateHidden|adjustResize"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.ConferenceDetailsActivity"
             android:label="@string/title_activity_conference_details"
-            android:windowSoftInputMode="stateHidden" >
-        </activity>
+            android:windowSoftInputMode="stateHidden"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.ContactDetailsActivity"
             android:label="@string/title_activity_contact_details"
-            android:windowSoftInputMode="stateHidden" >
-        </activity>
+            android:windowSoftInputMode="stateHidden"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.PublishProfilePictureActivity"
             android:label="@string/mgmt_account_publish_avatar"
-            android:windowSoftInputMode="stateHidden" >
-        </activity>
+            android:windowSoftInputMode="stateHidden"></activity>
         <activity
             android:name="eu.siacs.conversations.ui.ShareWithActivity"
-            android:label="@string/title_activity_conversations" >
+            android:label="@string/title_activity_conversations">
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
 

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

@@ -1,21 +1,5 @@
 package eu.siacs.conversations.ui;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import eu.siacs.conversations.R;
-import eu.siacs.conversations.entities.Contact;
-import eu.siacs.conversations.entities.Conversation;
-import eu.siacs.conversations.entities.Message;
-import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
-import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
-import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
-import eu.siacs.conversations.ui.adapter.ConversationAdapter;
-import eu.siacs.conversations.utils.ExceptionHelper;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.provider.MediaStore;
 import android.annotation.SuppressLint;
 import android.app.ActionBar;
 import android.app.AlertDialog;
@@ -23,8 +7,16 @@ import android.app.FragmentTransaction;
 import android.app.PendingIntent;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
-import android.content.IntentSender.SendIntentException;
 import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcEvent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.MediaStore;
 import android.support.v4.widget.SlidingPaneLayout;
 import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
 import android.view.KeyEvent;
@@ -40,6 +32,19 @@ import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnMenuItemClickListener;
 import android.widget.Toast;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Contact;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
+import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
+import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
+import eu.siacs.conversations.ui.adapter.ConversationAdapter;
+import eu.siacs.conversations.utils.ExceptionHelper;
+
 public class ConversationActivity extends XmppActivity implements
 		OnAccountUpdate, OnConversationUpdate, OnRosterUpdate {
 
@@ -78,6 +83,18 @@ public class ConversationActivity extends XmppActivity implements
 
 	private Uri pendingImageUri = null;
 
+	private NfcAdapter.CreateNdefMessageCallback mNdefPushMessageCallback = new NfcAdapter.CreateNdefMessageCallback() {
+		@Override
+		public NdefMessage createNdefMessage(NfcEvent nfcEvent) {
+			Conversation conversation = getSelectedConversation();
+			NdefMessage msg = new NdefMessage(new NdefRecord[]{
+					NdefRecord.createUri("xmpp:"+conversation.getAccount().getJid()),
+					NdefRecord.createApplicationRecord("eu.siacs.conversations")
+			});
+			return msg;
+		}
+	};
+
 	public List<Conversation> getConversationList() {
 		return this.conversationList;
 	}
@@ -147,6 +164,8 @@ public class ConversationActivity extends XmppActivity implements
 		getActionBar().setDisplayHomeAsUpEnabled(false);
 		getActionBar().setHomeButtonEnabled(false);
 
+		registerNdefPushMessageCallback(this.mNdefPushMessageCallback);
+
 		this.listAdapter = new ConversationAdapter(this, conversationList);
 		listView.setAdapter(this.listAdapter);
 
@@ -154,7 +173,7 @@ public class ConversationActivity extends XmppActivity implements
 
 			@Override
 			public void onItemClick(AdapterView<?> arg0, View clickedView,
-					int position, long arg3) {
+									int position, long arg3) {
 				paneShouldBeOpen = false;
 				if (getSelectedConversation() != conversationList.get(position)) {
 					setSelectedConversation(conversationList.get(position));
@@ -224,7 +243,7 @@ public class ConversationActivity extends XmppActivity implements
 			ab.setHomeButtonEnabled(true);
 			if (getSelectedConversation().getMode() == Conversation.MODE_SINGLE
 					|| ConversationActivity.this
-							.useSubjectToIdentifyConference()) {
+					.useSubjectToIdentifyConference()) {
 				ab.setTitle(getSelectedConversation().getName());
 			} else {
 				ab.setTitle(getSelectedConversation().getContactJid()
@@ -324,7 +343,7 @@ public class ConversationActivity extends XmppActivity implements
 
 								@Override
 								public void userInputRequried(PendingIntent pi,
-										Contact contact) {
+															  Contact contact) {
 									ConversationActivity.this.runIntent(pi,
 											attachmentChoice);
 								}
@@ -348,7 +367,7 @@ public class ConversationActivity extends XmppActivity implements
 
 									@Override
 									public void onClick(DialogInterface dialog,
-											int which) {
+														int which) {
 										conversation
 												.setNextEncryption(Message.ENCRYPTION_NONE);
 										xmppConnectionService.databaseBackend
@@ -379,41 +398,41 @@ public class ConversationActivity extends XmppActivity implements
 			return true;
 		} else if (getSelectedConversation() != null) {
 			switch (item.getItemId()) {
-			case R.id.action_attach_file:
-				attachFileDialog();
-				break;
-			case R.id.action_archive:
-				this.endConversation(getSelectedConversation());
-				break;
-			case R.id.action_contact_details:
-				Contact contact = this.getSelectedConversation().getContact();
-				if (contact.showInRoster()) {
-					switchToContactDetails(contact);
-				} else {
-					showAddToRosterDialog(getSelectedConversation());
-				}
-				break;
-			case R.id.action_muc_details:
-				Intent intent = new Intent(this,
-						ConferenceDetailsActivity.class);
-				intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
-				intent.putExtra("uuid", getSelectedConversation().getUuid());
-				startActivity(intent);
-				break;
-			case R.id.action_invite:
-				inviteToConversation(getSelectedConversation());
-				break;
-			case R.id.action_security:
-				selectEncryptionDialog(getSelectedConversation());
-				break;
-			case R.id.action_clear_history:
-				clearHistoryDialog(getSelectedConversation());
-				break;
-			case R.id.action_mute:
-				muteConversationDialog(getSelectedConversation());
-				break;
-			default:
-				break;
+				case R.id.action_attach_file:
+					attachFileDialog();
+					break;
+				case R.id.action_archive:
+					this.endConversation(getSelectedConversation());
+					break;
+				case R.id.action_contact_details:
+					Contact contact = this.getSelectedConversation().getContact();
+					if (contact.showInRoster()) {
+						switchToContactDetails(contact);
+					} else {
+						showAddToRosterDialog(getSelectedConversation());
+					}
+					break;
+				case R.id.action_muc_details:
+					Intent intent = new Intent(this,
+							ConferenceDetailsActivity.class);
+					intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
+					intent.putExtra("uuid", getSelectedConversation().getUuid());
+					startActivity(intent);
+					break;
+				case R.id.action_invite:
+					inviteToConversation(getSelectedConversation());
+					break;
+				case R.id.action_security:
+					selectEncryptionDialog(getSelectedConversation());
+					break;
+				case R.id.action_clear_history:
+					clearHistoryDialog(getSelectedConversation());
+					break;
+				case R.id.action_mute:
+					muteConversationDialog(getSelectedConversation());
+					break;
+				default:
+					break;
 			}
 			return super.onOptionsItemSelected(item);
 		} else {
@@ -471,15 +490,15 @@ public class ConversationActivity extends XmppActivity implements
 					@Override
 					public boolean onMenuItemClick(MenuItem item) {
 						switch (item.getItemId()) {
-						case R.id.attach_choose_picture:
-							attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
-							break;
-						case R.id.attach_take_picture:
-							attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
-							break;
-						case R.id.attach_record_voice:
-							attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
-							break;
+							case R.id.attach_choose_picture:
+								attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
+								break;
+							case R.id.attach_take_picture:
+								attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
+								break;
+							case R.id.attach_record_voice:
+								attachFile(ATTACHMENT_CHOICE_RECORD_VOICE);
+								break;
 						}
 						return false;
 					}
@@ -501,32 +520,32 @@ public class ConversationActivity extends XmppActivity implements
 				@Override
 				public boolean onMenuItemClick(MenuItem item) {
 					switch (item.getItemId()) {
-					case R.id.encryption_choice_none:
-						conversation.setNextEncryption(Message.ENCRYPTION_NONE);
-						item.setChecked(true);
-						break;
-					case R.id.encryption_choice_otr:
-						conversation.setNextEncryption(Message.ENCRYPTION_OTR);
-						item.setChecked(true);
-						break;
-					case R.id.encryption_choice_pgp:
-						if (hasPgp()) {
-							if (conversation.getAccount().getKeys()
-									.has("pgp_signature")) {
-								conversation
-										.setNextEncryption(Message.ENCRYPTION_PGP);
-								item.setChecked(true);
+						case R.id.encryption_choice_none:
+							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+							item.setChecked(true);
+							break;
+						case R.id.encryption_choice_otr:
+							conversation.setNextEncryption(Message.ENCRYPTION_OTR);
+							item.setChecked(true);
+							break;
+						case R.id.encryption_choice_pgp:
+							if (hasPgp()) {
+								if (conversation.getAccount().getKeys()
+										.has("pgp_signature")) {
+									conversation
+											.setNextEncryption(Message.ENCRYPTION_PGP);
+									item.setChecked(true);
+								} else {
+									announcePgp(conversation.getAccount(),
+											conversation);
+								}
 							} else {
-								announcePgp(conversation.getAccount(),
-										conversation);
+								showInstallPgpDialog();
 							}
-						} else {
-							showInstallPgpDialog();
-						}
-						break;
-					default:
-						conversation.setNextEncryption(Message.ENCRYPTION_NONE);
-						break;
+							break;
+						default:
+							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
+							break;
 					}
 					xmppConnectionService.databaseBackend
 							.updateConversation(conversation);
@@ -546,20 +565,20 @@ public class ConversationActivity extends XmppActivity implements
 				}
 			}
 			switch (conversation.getNextEncryption(forceEncryption())) {
-			case Message.ENCRYPTION_NONE:
-				none.setChecked(true);
-				break;
-			case Message.ENCRYPTION_OTR:
-				otr.setChecked(true);
-				break;
-			case Message.ENCRYPTION_PGP:
-				popup.getMenu().findItem(R.id.encryption_choice_pgp)
-						.setChecked(true);
-				break;
-			default:
-				popup.getMenu().findItem(R.id.encryption_choice_none)
-						.setChecked(true);
-				break;
+				case Message.ENCRYPTION_NONE:
+					none.setChecked(true);
+					break;
+				case Message.ENCRYPTION_OTR:
+					otr.setChecked(true);
+					break;
+				case Message.ENCRYPTION_PGP:
+					popup.getMenu().findItem(R.id.encryption_choice_pgp)
+							.setChecked(true);
+					break;
+				default:
+					popup.getMenu().findItem(R.id.encryption_choice_none)
+							.setChecked(true);
+					break;
 			}
 			popup.show();
 		}
@@ -736,7 +755,7 @@ public class ConversationActivity extends XmppActivity implements
 
 	@Override
 	protected void onActivityResult(int requestCode, int resultCode,
-			final Intent data) {
+									final Intent data) {
 		super.onActivityResult(requestCode, resultCode, data);
 		if (resultCode == RESULT_OK) {
 			if (requestCode == REQUEST_DECRYPT_PGP) {
@@ -798,7 +817,7 @@ public class ConversationActivity extends XmppActivity implements
 
 					@Override
 					public void userInputRequried(PendingIntent pi,
-							Message object) {
+												  Message object) {
 						hidePrepareImageToast();
 						ConversationActivity.this.runIntent(pi,
 								ConversationActivity.REQUEST_SEND_PGP_IMAGE);
@@ -849,7 +868,7 @@ public class ConversationActivity extends XmppActivity implements
 
 					@Override
 					public void userInputRequried(PendingIntent pi,
-							Message message) {
+												  Message message) {
 						ConversationActivity.this.runIntent(pi,
 								ConversationActivity.REQUEST_SEND_MESSAGE);
 					}

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

@@ -1,11 +1,5 @@
 package eu.siacs.conversations.ui;
 
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 import android.annotation.SuppressLint;
 import android.app.ActionBar;
 import android.app.ActionBar.Tab;
@@ -19,12 +13,15 @@ import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.support.v13.app.FragmentPagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.text.Editable;
 import android.text.TextWatcher;
-import android.util.Log;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.KeyEvent;
@@ -45,7 +42,12 @@ import android.widget.Spinner;
 import com.google.zxing.integration.android.IntentIntegrator;
 import com.google.zxing.integration.android.IntentResult;
 
-import eu.siacs.conversations.Config;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Bookmark;
@@ -75,6 +77,8 @@ public class StartConversationActivity extends XmppActivity {
 	private List<String> mKnownHosts;
 	private List<String> mKnownConferenceHosts;
 
+	private Invite mPendingInvite = null;
+
 	private Menu mOptionsMenu;
 	private EditText mSearchEditText;
 
@@ -145,12 +149,12 @@ public class StartConversationActivity extends XmppActivity {
 
 		@Override
 		public void beforeTextChanged(CharSequence s, int start, int count,
-				int after) {
+									  int after) {
 		}
 
 		@Override
 		public void onTextChanged(CharSequence s, int start, int before,
-				int count) {
+								  int count) {
 		}
 	};
 	private OnRosterUpdate onRosterUpdate = new OnRosterUpdate() {
@@ -212,7 +216,7 @@ public class StartConversationActivity extends XmppActivity {
 
 					@Override
 					public void onItemClick(AdapterView<?> arg0, View arg1,
-							int position, long arg3) {
+											int position, long arg3) {
 						openConversationForBookmark(position);
 					}
 				});
@@ -225,7 +229,7 @@ public class StartConversationActivity extends XmppActivity {
 
 					@Override
 					public void onItemClick(AdapterView<?> arg0, View arg1,
-							int position, long arg3) {
+											int position, long arg3) {
 						openConversationForContact(position);
 					}
 				});
@@ -377,7 +381,7 @@ public class StartConversationActivity extends XmppActivity {
 	}
 
 	@SuppressLint("InflateParams")
-	protected void showJoinConferenceDialog() {
+	protected void showJoinConferenceDialog(String prefilledJid) {
 		AlertDialog.Builder builder = new AlertDialog.Builder(this);
 		builder.setTitle(R.string.join_conference);
 		View dialogView = getLayoutInflater().inflate(
@@ -387,6 +391,9 @@ public class StartConversationActivity extends XmppActivity {
 				.findViewById(R.id.jid);
 		jid.setAdapter(new KnownHostsAdapter(this,
 				android.R.layout.simple_list_item_1, mKnownConferenceHosts));
+		if (prefilledJid != null) {
+			jid.append(prefilledJid);
+		}
 		populateAccountSpinner(spinner);
 		final CheckBox bookmarkCheckBox = (CheckBox) dialogView
 				.findViewById(R.id.bookmark);
@@ -495,15 +502,15 @@ public class StartConversationActivity extends XmppActivity {
 	@Override
 	public boolean onOptionsItemSelected(MenuItem item) {
 		switch (item.getItemId()) {
-		case R.id.action_create_contact:
-			showCreateContactDialog(null);
-			return true;
-		case R.id.action_join_conference:
-			showJoinConferenceDialog();
-			return true;
-		case R.id.action_scan_qr_code:
-			new IntentIntegrator(this).initiateScan();
-			return true;
+			case R.id.action_create_contact:
+				showCreateContactDialog(null);
+				return true;
+			case R.id.action_join_conference:
+				showJoinConferenceDialog(null);
+				return true;
+			case R.id.action_scan_qr_code:
+				new IntentIntegrator(this).initiateScan();
+				return true;
 		}
 		return super.onOptionsItemSelected(item);
 	}
@@ -523,10 +530,21 @@ public class StartConversationActivity extends XmppActivity {
 			IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
 			if (scanResult != null && scanResult.getFormatName() != null) {
 				String data = scanResult.getContents();
-				Log.d(Config.LOGTAG, data);
+				Invite invite = parseInviteUri(data);
+				if (xmppConnectionServiceBound) {
+					if (invite.muc) {
+						showJoinConferenceDialog(invite.jid);
+					} else {
+						handleJid(invite.jid);
+					}
+				} else if (invite.jid != null) {
+					this.mPendingInvite = invite;
+				} else {
+					this.mPendingInvite = null;
+				}
 			}
 		}
-		super.onActivityResult(requestCode,requestCode,intent);
+		super.onActivityResult(requestCode, requestCode, intent);
 	}
 
 	@Override
@@ -541,35 +559,88 @@ public class StartConversationActivity extends XmppActivity {
 		this.mKnownHosts = xmppConnectionService.getKnownHosts();
 		this.mKnownConferenceHosts = xmppConnectionService
 				.getKnownConferenceHosts();
-		if (!startByIntent()) {
+		if (this.mPendingInvite != null) {
+			if (this.mPendingInvite.muc) {
+				showJoinConferenceDialog(this.mPendingInvite.jid);
+			} else {
+				handleJid(this.mPendingInvite.jid);
+			}
+			this.mPendingInvite = null;
+		} else if (!handleIntent(getIntent())) {
 			if (mSearchEditText != null) {
 				filter(mSearchEditText.getText().toString());
 			} else {
 				filter(null);
 			}
 		}
+		setIntent(null);
 	}
 
-	protected boolean startByIntent() {
-		if (getIntent() != null
-				&& Intent.ACTION_SENDTO.equals(getIntent().getAction())) {
-			try {
-				String jid = URLDecoder.decode(
-						getIntent().getData().getEncodedPath(), "UTF-8").split(
-						"/")[1];
-				setIntent(null);
-				return handleJid(jid);
-			} catch (UnsupportedEncodingException e) {
-				setIntent(null);
+	protected boolean handleIntent(Intent intent) {
+		if (intent == null || intent.getAction() == null) {
+			return false;
+		}
+		String jid;
+		Uri uri;
+		Invite invite;
+		switch (intent.getAction()) {
+			case Intent.ACTION_SENDTO:
+				try {
+					jid = URLDecoder.decode(
+							intent.getData().getEncodedPath(), "UTF-8").split(
+							"/")[1];
+					return handleJid(jid);
+				} catch (UnsupportedEncodingException e) {
+					return false;
+				}
+			case Intent.ACTION_VIEW:
+				uri = intent.getData();
+				invite = parseInviteUri(uri);
+				if (invite.muc) {
+					showJoinConferenceDialog(invite.jid);
+					return false;
+				} else {
+					return handleJid(invite.jid);
+				}
+			case NfcAdapter.ACTION_NDEF_DISCOVERED:
+				if (android.os.Build.VERSION.SDK_INT >= 16) {
+					Parcelable[] messages = getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+					NdefMessage message = (NdefMessage) messages[0];
+					NdefRecord record = message.getRecords()[0];
+					invite = parseInviteUri(record.toUri());
+					if (invite != null) {
+						if (invite.muc) {
+							showJoinConferenceDialog(invite.jid);
+							return false;
+						} else {
+							return handleJid(invite.jid);
+						}
+					}
+				} else {
+					return false;
+				}
+			default:
 				return false;
-			}
-		} else if (getIntent() != null
-				&& Intent.ACTION_VIEW.equals(getIntent().getAction())) {
-			Uri uri = getIntent().getData();
-			String jid = uri.getSchemeSpecificPart().split("\\?")[0];
-			return handleJid(jid);
 		}
-		return false;
+	}
+
+	private Invite parseInviteUri(String uri) {
+		try {
+			return parseInviteUri(Uri.parse(uri));
+		} catch (IllegalArgumentException e) {
+			return null;
+		}
+	}
+
+	private Invite parseInviteUri(Uri uri) {
+		Invite invite = new Invite();
+		invite.muc = uri.getQuery() != null && uri.getQuery().equalsIgnoreCase("join");
+		if (uri.getAuthority() != null) {
+			invite.jid = uri.getAuthority();
+		} else {
+			invite.jid = uri.getSchemeSpecificPart().split("\\?")[0];
+		}
+		return invite;
 	}
 
 	private boolean handleJid(String jid) {
@@ -583,7 +654,8 @@ public class StartConversationActivity extends XmppActivity {
 		} else {
 			if (mMenuSearchView != null) {
 				mMenuSearchView.expandActionView();
-				mSearchEditText.setText(jid);
+				mSearchEditText.setText("");
+				mSearchEditText.append(jid);
 				filter(jid);
 			} else {
 				mInitialJid = jid;
@@ -661,7 +733,7 @@ public class StartConversationActivity extends XmppActivity {
 
 		@Override
 		public void onCreateContextMenu(ContextMenu menu, View v,
-				ContextMenuInfo menuInfo) {
+										ContextMenuInfo menuInfo) {
 			super.onCreateContextMenu(menu, v, menuInfo);
 			StartConversationActivity activity = (StartConversationActivity) getActivity();
 			activity.getMenuInflater().inflate(mResContextMenu, menu);
@@ -677,22 +749,27 @@ public class StartConversationActivity extends XmppActivity {
 		public boolean onContextItemSelected(MenuItem item) {
 			StartConversationActivity activity = (StartConversationActivity) getActivity();
 			switch (item.getItemId()) {
-			case R.id.context_start_conversation:
-				activity.openConversationForContact();
-				break;
-			case R.id.context_contact_details:
-				activity.openDetailsForContact();
-				break;
-			case R.id.context_delete_contact:
-				activity.deleteContact();
-				break;
-			case R.id.context_join_conference:
-				activity.openConversationForBookmark();
-				break;
-			case R.id.context_delete_conference:
-				activity.deleteConference();
+				case R.id.context_start_conversation:
+					activity.openConversationForContact();
+					break;
+				case R.id.context_contact_details:
+					activity.openDetailsForContact();
+					break;
+				case R.id.context_delete_contact:
+					activity.deleteContact();
+					break;
+				case R.id.context_join_conference:
+					activity.openConversationForBookmark();
+					break;
+				case R.id.context_delete_conference:
+					activity.deleteConference();
 			}
 			return true;
 		}
 	}
+
+	private class Invite {
+		public String jid;
+		public boolean muc;
+	}
 }

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

@@ -38,6 +38,7 @@ import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.nfc.NfcAdapter;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -543,6 +544,15 @@ public abstract class XmppActivity extends Activity {
 		return false;
 	}
 
+	protected void registerNdefPushMessageCallback(NfcAdapter.CreateNdefMessageCallback callback) {
+		if (android.os.Build.VERSION.SDK_INT >= 16) {
+			NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+			if (nfcAdapter != null && nfcAdapter.isEnabled()) {
+				nfcAdapter.setNdefPushMessageCallback(callback, this);
+			}
+		}
+	}
+
 	public AvatarService avatarService() {
 		return xmppConnectionService.getAvatarService();
 	}