From b8b7cc2635a0289eb65ec34d02d7d8982583fab7 Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Thu, 7 Dec 2017 21:59:13 +0100 Subject: [PATCH 1/7] Implement simple URI handler activity --- src/main/AndroidManifest.xml | 29 ++++++++------ .../conversations/ui/UriHandlerActivity.java | 38 +++++++++++++++++++ 2 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 228b6c1b3..2788516ef 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -58,17 +58,10 @@ - - - - - - - - @@ -78,11 +71,12 @@ - + - + + @@ -95,6 +89,19 @@ + + + + + + + + + + Date: Wed, 6 Dec 2017 11:18:05 +0100 Subject: [PATCH 2/7] AccountAdapter: Allow hiding of state button --- .../conversations/ui/adapter/AccountAdapter.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java index d7e241798..bbcf51c9d 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java @@ -29,10 +29,18 @@ import eu.siacs.conversations.utils.UIHelper; public class AccountAdapter extends ArrayAdapter { private XmppActivity activity; + private boolean showStateButton; + + public AccountAdapter(XmppActivity activity, List objects, boolean showStateButton) { + super(activity, 0, objects); + this.activity = activity; + this.showStateButton = showStateButton; + } public AccountAdapter(XmppActivity activity, List objects) { super(activity, 0, objects); this.activity = activity; + this.showStateButton = true; } @Override @@ -68,6 +76,11 @@ public class AccountAdapter extends ArrayAdapter { final Switch tglAccountState = (Switch) view.findViewById(R.id.tgl_account_status); final boolean isDisabled = (account.getStatus() == Account.State.DISABLED); tglAccountState.setChecked(!isDisabled,false); + if (this.showStateButton) { + tglAccountState.setVisibility(View.VISIBLE); + } else { + tglAccountState.setVisibility(View.GONE); + } tglAccountState.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { From edf3c41e58684d521de3c2d108fc3857ced69f21 Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Thu, 7 Dec 2017 16:45:20 +0100 Subject: [PATCH 3/7] Add ShareViaAccountActivity --- src/main/AndroidManifest.xml | 4 + .../ui/ShareViaAccountActivity.java | 106 ++++++++++++++++++ src/main/res/values-de/strings.xml | 1 + src/main/res/values/strings.xml | 1 + 4 files changed, 112 insertions(+) create mode 100644 src/main/java/eu/siacs/conversations/ui/ShareViaAccountActivity.java diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 2788516ef..55f41662f 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -134,6 +134,10 @@ android:name=".ui.ManageAccountActivity" android:label="@string/title_activity_manage_accounts" android:launchMode="singleTask" /> + accountList = new ArrayList<>(); + protected ListView accountListView; + protected AccountAdapter mAccountAdapter; + + @Override + protected void refreshUiReal() { + synchronized (this.accountList) { + accountList.clear(); + accountList.addAll(xmppConnectionService.getAccounts()); + } + ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setHomeButtonEnabled(this.accountList.size() > 0); + actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0); + } + mAccountAdapter.notifyDataSetChanged(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.manage_accounts); + + accountListView = (ListView) findViewById(R.id.account_list); + this.mAccountAdapter = new AccountAdapter(this, accountList, false); + accountListView.setAdapter(this.mAccountAdapter); + accountListView.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView arg0, View view, + int position, long arg3) { + final Account account = accountList.get(position); + final String body = getIntent().getStringExtra(EXTRA_BODY); + + try { + final Jid contact = Jid.fromString(getIntent().getStringExtra(EXTRA_CONTACT)); + final Conversation conversation = xmppConnectionService.findOrCreateConversation( + account, contact, false, false); + switchToConversation(conversation, body, false); + } catch (InvalidJidException e) { + // ignore error + } + + finish(); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + final int theme = findTheme(); + if (this.mTheme != theme) { + recreate(); + } + } + + @Override + void onBackendConnected() { + final int numAccounts = xmppConnectionService.getAccounts().size(); + + if (numAccounts == 1) { + final String body = getIntent().getStringExtra(EXTRA_BODY); + final Account account = xmppConnectionService.getAccounts().get(0); + + try { + final Jid contact = Jid.fromString(getIntent().getStringExtra(EXTRA_CONTACT)); + final Conversation conversation = xmppConnectionService.findOrCreateConversation( + account, contact, false, false); + switchToConversation(conversation, body, false); + } catch (InvalidJidException e) { + // ignore error + } + + finish(); + } else { + refreshUiReal(); + } + } +} diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 1b0f4565a..25688d171 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -20,6 +20,7 @@ Mit Unterhaltung teilen Unterhaltung beginnen Kontakt auswählen + Über Account teilen Sperrliste gerade vor einer Minute diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 834870e7e..ab89fe8de 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -21,6 +21,7 @@ Share with Conversation Start Conversation Choose Contact + Share via account Block list just now 1 min ago From 0fac4be94da6c0822bdc48477931e6225168ef6b Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Tue, 5 Dec 2017 21:46:48 +0100 Subject: [PATCH 4/7] XmppUri: Parse 'message action' query --- .../ui/StartConversationActivity.java | 6 +--- .../eu/siacs/conversations/utils/XmppUri.java | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index 914ed6358..ecc7a891d 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -876,7 +876,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU return true; } List contacts = xmppConnectionService.findContacts(invite.getJid(),invite.account); - if (invite.isMuc()) { + if (invite.isAction(XmppUri.ACTION_JOIN)) { Conversation muc = xmppConnectionService.findFirstMuc(invite.getJid()); if (muc != null) { switchToConversation(muc,invite.getBody(),false); @@ -1202,9 +1202,5 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } return false; } - - public boolean isMuc() { - return muc; - } } } diff --git a/src/main/java/eu/siacs/conversations/utils/XmppUri.java b/src/main/java/eu/siacs/conversations/utils/XmppUri.java index c20c9a4ac..ec8fd51c0 100644 --- a/src/main/java/eu/siacs/conversations/utils/XmppUri.java +++ b/src/main/java/eu/siacs/conversations/utils/XmppUri.java @@ -14,14 +14,17 @@ import eu.siacs.conversations.xmpp.jid.Jid; public class XmppUri { protected String jid; - protected boolean muc; protected List fingerprints = new ArrayList<>(); private String body; + private String action; protected boolean safeSource = true; public static final String OMEMO_URI_PARAM = "omemo-sid-"; public static final String OTR_URI_PARAM = "otr-fingerprint"; + public static final String ACTION_JOIN = "join"; + public static final String ACTION_MESSAGE = "message"; + public XmppUri(String uri) { try { parse(Uri.parse(uri)); @@ -63,11 +66,21 @@ public class XmppUri { // sample : https://conversations.im/i/foo/bar.com jid = segments.get(1) + "@" + segments.get(2); } - muc = segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0)); + if (segments.size() > 1 && "j".equalsIgnoreCase(segments.get(0))) { + action = ACTION_JOIN; + } fingerprints = parseFingerprints(uri.getQuery(),'&'); } else if ("xmpp".equalsIgnoreCase(scheme)) { // sample: xmpp:foo@bar.com - muc = isMuc(uri.getQuery()); + + final String query = uri.getQuery(); + + if (hasAction(query, ACTION_JOIN)) { + this.action = ACTION_JOIN; + } else if (hasAction(query, ACTION_MESSAGE)) { + this.action = ACTION_MESSAGE; + } + if (uri.getAuthority() != null) { jid = uri.getAuthority(); } else { @@ -138,16 +151,24 @@ public class XmppUri { return null; } - protected boolean isMuc(String query) { + private boolean hasAction(String query, String action) { for(String pair : query == null ? new String[0] : query.split(";")) { final String[] parts = pair.split("=",2); - if (parts.length == 1 && "join".equals(parts[0])) { + if (parts.length == 1 && parts[0].equals(action)) { return true; } } return false; } + public boolean isAction(final String action) { + if (this.action == null) { + return false; + } + + return this.action.equals(action); + } + public Jid getJid() { try { return this.jid == null ? null :Jid.fromString(this.jid.toLowerCase()); From 75e16d0f54fef19854d1085e495cfe5e7ec12f23 Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Thu, 7 Dec 2017 22:13:24 +0100 Subject: [PATCH 5/7] UriHandlerActivity: Handle 'message action' XMPP URI --- .../conversations/ui/UriHandlerActivity.java | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java index f96faf31b..c725ec8cf 100644 --- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java @@ -2,6 +2,11 @@ package eu.siacs.conversations.ui; import android.app.Activity; import android.content.Intent; +import android.net.Uri; + +import eu.siacs.conversations.persistance.DatabaseBackend; +import eu.siacs.conversations.utils.XmppUri; +import eu.siacs.conversations.xmpp.jid.Jid; public class UriHandlerActivity extends Activity { @@ -16,6 +21,41 @@ public class UriHandlerActivity extends Activity { handleIntent(intent); } + private void handleUri(Uri uri) { + final Intent intent; + final XmppUri xmppUri = new XmppUri(uri); + final int numAccounts = DatabaseBackend.getInstance(this).getAccountJids().size(); + + if (numAccounts == 0) { + intent = new Intent(getApplicationContext(), WelcomeActivity.class); + startActivity(intent); + return; + } + + if (xmppUri.isAction(XmppUri.ACTION_MESSAGE)) { + final Jid jid = xmppUri.getJid(); + final String body = xmppUri.getBody(); + + if (jid != null) { + intent = new Intent(getApplicationContext(), ShareViaAccountActivity.class); + intent.putExtra(ShareViaAccountActivity.EXTRA_CONTACT, jid.toString()); + intent.putExtra(ShareViaAccountActivity.EXTRA_BODY, body); + } else { + intent = new Intent(getApplicationContext(), ShareWithActivity.class); + intent.setAction(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, body); + } + } else { + intent = new Intent(getApplicationContext(), StartConversationActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + intent.setData(uri); + } + + startActivity(intent); + } + private void handleIntent(Intent data) { if (data == null) { finish(); @@ -25,12 +65,7 @@ public class UriHandlerActivity extends Activity { switch (data.getAction()) { case Intent.ACTION_VIEW: case Intent.ACTION_SENDTO: - final Intent intent = new Intent(getApplicationContext(), - StartConversationActivity.class); - intent.setAction(data.getAction()); - intent.setData(data.getData()); - intent.setAction(data.getAction()); - startActivity(intent); + handleUri(data.getData()); } finish(); From d5f8a4dacb666eb4a50aba148e493c10b259b13b Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Sun, 10 Dec 2017 11:44:10 +0100 Subject: [PATCH 6/7] StartConversationActivity: Scan QR codes via UriHandlerActivity --- src/main/AndroidManifest.xml | 1 - .../ui/StartConversationActivity.java | 22 +++------------- .../conversations/ui/UriHandlerActivity.java | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 55f41662f..3c17bf714 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -60,7 +60,6 @@ diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index ecc7a891d..8b36ce435 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -55,9 +55,6 @@ import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; -import com.google.zxing.integration.android.IntentIntegrator; -import com.google.zxing.integration.android.IntentResult; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -644,7 +641,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU showCreateConferenceDialog(); return true; case R.id.action_scan_qr_code: - new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC","QR_CODE")); + Intent intent = new Intent(this, UriHandlerActivity.class); + intent.setAction(UriHandlerActivity.ACTION_SCAN_QR_CODE); + startActivity(intent); return true; case R.id.action_hide_offline: mHideOfflineContacts = !item.isChecked(); @@ -682,20 +681,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU @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(); - Invite invite = new Invite(data); - if (xmppConnectionServiceBound) { - invite.invite(); - } else if (invite.getJid() != null) { - this.mPendingInvite = invite; - } else { - this.mPendingInvite = null; - } - } - } else if (resultCode == RESULT_OK) { + if (resultCode == RESULT_OK) { if (xmppConnectionServiceBound) { this.mPostponedActivityResult = null; if (requestCode == REQUEST_CREATE_CONFERENCE) { diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java index c725ec8cf..043bf247e 100644 --- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java @@ -4,11 +4,17 @@ import android.app.Activity; import android.content.Intent; import android.net.Uri; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + +import java.util.Arrays; + import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.xmpp.jid.Jid; public class UriHandlerActivity extends Activity { + public static final String ACTION_SCAN_QR_CODE = "scan_qr_code"; @Override public void onStart() { @@ -66,8 +72,28 @@ public class UriHandlerActivity extends Activity { case Intent.ACTION_VIEW: case Intent.ACTION_SENDTO: handleUri(data.getData()); + break; + case ACTION_SCAN_QR_CODE: + new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC", "QR_CODE")); + return; } finish(); } + + @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(); + handleUri(Uri.parse(data)); + } + } + + finish(); + super.onActivityResult(requestCode, requestCode, intent); + } } From 7dc68522a25183a336a493fb26dec14fd8e0ff61 Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Sun, 10 Dec 2017 12:36:21 +0100 Subject: [PATCH 7/7] Handle NFC action in UriHandlerActivity --- src/main/AndroidManifest.xml | 20 +++------- .../ui/StartConversationActivity.java | 31 ---------------- .../conversations/ui/UriHandlerActivity.java | 37 +++++++++++++++++++ 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 3c17bf714..fbc403b58 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -69,14 +69,6 @@ - - - - - - - - @@ -88,20 +80,20 @@ - - + - + + = Build.VERSION_CODES.JELLY_BEAN) { - return getInviteJellyBean(record).invite(); - } else { - byte[] payload = record.getPayload(); - if (payload[0] == 0) { - return new Invite(Uri.parse(new String(Arrays.copyOfRange( - payload, 1, payload.length)))).invite(); - } - } - } - } - } - } - } } return false; } diff --git a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java index 043bf247e..9ffa1b11f 100644 --- a/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/UriHandlerActivity.java @@ -1,8 +1,14 @@ package eu.siacs.conversations.ui; +import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; import android.net.Uri; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; +import android.os.Build; +import android.os.Parcelable; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; @@ -27,6 +33,11 @@ public class UriHandlerActivity extends Activity { handleIntent(intent); } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + Uri getInviteJellyBean(NdefRecord record) { + return record.toUri(); + } + private void handleUri(Uri uri) { final Intent intent; final XmppUri xmppUri = new XmppUri(uri); @@ -62,6 +73,30 @@ public class UriHandlerActivity extends Activity { startActivity(intent); } + private void handleNfcIntent(Intent data) { + for (Parcelable message : data.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) { + if (message instanceof NdefMessage) { + for (NdefRecord record : ((NdefMessage) message).getRecords()) { + switch (record.getTnf()) { + case NdefRecord.TNF_WELL_KNOWN: + if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + handleUri(getInviteJellyBean(record)); + } else { + byte[] payload = record.getPayload(); + if (payload[0] == 0) { + Uri uri = Uri.parse(new String(Arrays.copyOfRange( + payload, 1, payload.length))); + handleUri(uri); + } + } + } + } + } + } + } + } + private void handleIntent(Intent data) { if (data == null) { finish(); @@ -76,6 +111,8 @@ public class UriHandlerActivity extends Activity { case ACTION_SCAN_QR_CODE: new IntentIntegrator(this).initiateScan(Arrays.asList("AZTEC", "QR_CODE")); return; + case NfcAdapter.ACTION_NDEF_DISCOVERED: + handleNfcIntent(data); } finish();