diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 7bde645f4..95663a872 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + package="eu.siacs.conversations"> @@ -12,16 +12,17 @@ + + android:theme="@style/ConversationsTheme" + tools:replace="android:label"> - + @@ -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"> @@ -43,8 +44,7 @@ + android:label="@string/title_activity_start_conversation"> @@ -61,42 +61,40 @@ + + + + + - + android:label="@string/title_activity_settings"> - + android:label="@string/title_activity_choose_contact"> - + android:label="@string/title_activity_manage_accounts"> - + android:windowSoftInputMode="stateHidden|adjustResize"> - + android:windowSoftInputMode="stateHidden"> - + android:windowSoftInputMode="stateHidden"> - + android:windowSoftInputMode="stateHidden"> + android:label="@string/title_activity_conversations"> diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 1d7364d66..b4a8e56a3 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/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 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); } diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 9a7d7af4f..aa19ff0e4 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/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 mKnownHosts; private List 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); - 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); + protected boolean handleIntent(Intent intent) { + if (intent == null || intent.getAction() == null) { + return false; } - 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; + } + } + + 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; + } } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 222f32956..56aa47b0a 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/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(); }