aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2016-02-12 11:39:27 +0100
committerDaniel Gultsch <daniel@gultsch.de>2016-02-12 11:39:27 +0100
commit32da65f910207f08f50b57ba59af9474eaad75d8 (patch)
tree9556881940e100fa9587e32d244e980a5e7e1ea6 /src
parent93dad9b7379196e2d15e9101c1b75ab43ad3afbb (diff)
client side support for XEP-0357: Push Notifications
Diffstat (limited to '')
-rw-r--r--src/free/java/eu/siacs/conversations/services/PushManagementService.java20
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java1
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java16
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java31
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java13
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java14
-rw-r--r--src/main/res/layout/activity_edit_account.xml23
-rw-r--r--src/main/res/values/strings.xml1
-rw-r--r--src/playstore/AndroidManifest.xml35
-rw-r--r--src/playstore/java/eu/siacs/conversations/services/InstanceIdService.java15
-rw-r--r--src/playstore/java/eu/siacs/conversations/services/PushManagementService.java78
-rw-r--r--src/playstore/java/eu/siacs/conversations/services/PushMessageReceiver.java20
12 files changed, 250 insertions, 17 deletions
diff --git a/src/free/java/eu/siacs/conversations/services/PushManagementService.java b/src/free/java/eu/siacs/conversations/services/PushManagementService.java
new file mode 100644
index 000000000..48c576cec
--- /dev/null
+++ b/src/free/java/eu/siacs/conversations/services/PushManagementService.java
@@ -0,0 +1,20 @@
+package eu.siacs.conversations.services;
+
+import eu.siacs.conversations.entities.Account;
+
+public class PushManagementService {
+
+ protected final XmppConnectionService mXmppConnectionService;
+
+ public PushManagementService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public void registerPushTokenOnServer(Account account) {
+ //stub implementation. only affects playstore flavor
+ }
+
+ public boolean available() {
+ return false;
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 3d32a2cb3..056dd7f00 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -109,6 +109,5 @@ public final class Config {
};
private Config() {
-
}
}
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 345f68ae3..dc9472c50 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -289,7 +289,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
packet.setTo(host);
- Element request = packet.addChild("request",Xmlns.HTTP_UPLOAD);
+ Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
request.addChild("filename").setContent(file.getName());
request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
if (mime != null) {
@@ -307,4 +307,18 @@ public class IqGenerator extends AbstractGenerator {
return register;
}
+
+ public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(appServer);
+ Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
+ command.setAttribute("node","register-push-gcm");
+ command.setAttribute("action","execute");
+ Data data = new Data();
+ data.put("token", token);
+ data.put("device-id", deviceId);
+ data.submit();
+ command.addChild(data);
+ return packet;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 9c2aa50c0..e985fe07d 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -73,7 +73,6 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
import eu.siacs.conversations.entities.Presence;
-import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.Roster;
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.entities.Transferable;
@@ -127,6 +126,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public static final String ACTION_TRY_AGAIN = "try_again";
public static final String ACTION_DISABLE_ACCOUNT = "disable_account";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+ public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
+ public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received";
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor();
private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor();
private final IBinder mBinder = new XmppConnectionBinder();
@@ -198,6 +199,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
this);
private AvatarService mAvatarService = new AvatarService(this);
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
+ private PushManagementService mPushManagementService = new PushManagementService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private final FileObserver fileObserver = new FileObserver(
FileBackend.getConversationsImageDirectory()) {
@@ -265,7 +267,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private OnStatusChanged statusListener = new OnStatusChanged() {
@Override
- public void onStatusChanged(Account account) {
+ public void onStatusChanged(final Account account) {
XmppConnection connection = account.getXmppConnection();
if (mOnAccountUpdate != null) {
mOnAccountUpdate.onAccountUpdate();
@@ -296,6 +298,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
account.pendingConferenceJoins.clear();
scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
+
+ if (mPushManagementService.pushAvailable(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+
} else if (account.getStatus() == Account.State.OFFLINE) {
resetSendingToWaiting(account);
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
@@ -512,6 +519,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
refreshAllPresences();
}
break;
+ case ACTION_GCM_TOKEN_REFRESH:
+ refreshAllGcmTokens();
+ break;
+ case ACTION_GCM_MESSAGE_RECEIVED:
+ Log.d(Config.LOGTAG,"gcm push message arrived in service. extras="+intent.getExtras());
}
}
this.wakeLock.acquire();
@@ -572,7 +584,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
reconnectAccount(account, true, interactive);
}
}
-
}
if (mOnAccountUpdate != null) {
mOnAccountUpdate.onAccountUpdate();
@@ -2845,6 +2856,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
+ private void refreshAllGcmTokens() {
+ for(Account account : getAccounts()) {
+ if (account.isOnlineAndConnected() && mPushManagementService.pushAvailable(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+ }
+ }
+
public void sendOfflinePresence(final Account account) {
sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
}
@@ -3005,7 +3024,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
databaseBackend.insertDiscoveryResult(disco);
injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco);
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid+" "+presence.getVer()+" vs "+disco.getVer());
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer());
}
}
account.inProgressDiscoFetches.remove(key);
@@ -3041,6 +3060,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
});
}
+ public PushManagementService getPushManagementService() {
+ return mPushManagementService;
+ }
+
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index d30fbda2c..cfa1889a0 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -29,6 +29,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
+import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
@@ -77,6 +78,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private TextView mServerInfoBlocking;
private TextView mServerInfoPep;
private TextView mServerInfoHttpUpload;
+ private TextView mServerInfoPush;
private TextView mSessionEst;
private TextView mOtrFingerprint;
private TextView mAxolotlFingerprint;
@@ -223,6 +225,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
};
private Toast mFetchingMamPrefsToast;
+ private TableRow mPushRow;
public void refreshUiReal() {
invalidateOptionsMenu();
@@ -422,6 +425,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
+ this.mPushRow = (TableRow) findViewById(R.id.push_row);
+ this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
@@ -680,6 +685,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} else {
this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
}
+
+ this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().available() ? View.VISIBLE : View.GONE);
+
+ if (features.push()) {
+ this.mServerInfoPush.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoPush.setText(R.string.server_info_unavailable);
+ }
final String otrFingerprint = this.mAccount.getOtrFingerprint();
if (otrFingerprint != null) {
this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 8b7eae397..3a9c87a30 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -1495,17 +1495,13 @@ public class XmppConnection implements Runnable {
}
public boolean mam() {
- if (hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")) {
- return true;
- } else {
- return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
- }
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
}
- public boolean advancedStreamFeaturesLoaded() {
- synchronized (XmppConnection.this.disco) {
- return disco.containsKey(account.getServer());
- }
+ public boolean push() {
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:push:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:push:0");
}
public boolean rosterVersioning() {
diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml
index ed9358192..d308b4ce7 100644
--- a/src/main/res/layout/activity_edit_account.xml
+++ b/src/main/res/layout/activity_edit_account.xml
@@ -400,6 +400,26 @@
tools:ignore="RtlHardcoded"/>
</TableRow>
<TableRow
+ android:id="@+id/push_row"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/server_info_push"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+
+ <TextView
+ android:id="@+id/server_info_push"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ </TableRow>
+ <TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content">
@@ -416,8 +436,7 @@
android:layout_height="wrap_content"
android:layout_gravity="right"
android:textColor="@color/black87"
- android:textSize="?attr/TextSizeBody"
- tools:ignore="RtlHardcoded"/>
+ android:textSize="?attr/TextSizeBody"/>
</TableRow>
</TableLayout>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 8da14a4a3..0b726ae7c 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -193,6 +193,7 @@
<string name="server_info_stream_management">XEP-0198: Stream Management</string>
<string name="server_info_pep">XEP-0163: PEP (Avatars / OMEMO)</string>
<string name="server_info_http_upload">XEP-0363: HTTP File Upload</string>
+ <string name="server_info_push">XEP-0357: Push</string>
<string name="server_info_available">available</string>
<string name="server_info_unavailable">unavailable</string>
<string name="missing_public_keys">Missing public key announcements</string>
diff --git a/src/playstore/AndroidManifest.xml b/src/playstore/AndroidManifest.xml
new file mode 100644
index 000000000..a1b91be35
--- /dev/null
+++ b/src/playstore/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+ package="eu.siacs.conversations"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <permission android:name="eu.siacs.conversations.permission.C2D_MESSAGE"
+ android:protectionLevel="signature"/>
+ <uses-permission android:name="eu.siacs.conversations.permission.C2D_MESSAGE"/>
+
+ <application>
+
+ <receiver
+ android:name="com.google.android.gms.gcm.GcmReceiver"
+ android:exported="true"
+ android:permission="com.google.android.c2dm.permission.SEND" >
+ <intent-filter>
+ <action android:name="com.google.android.c2dm.intent.RECEIVE" />
+ <category android:name="com.example.gcm" />
+ </intent-filter>
+ </receiver>
+ <service
+ android:name=".services.PushMessageReceiver"
+ android:exported="false" >
+ <intent-filter>
+ <action android:name="com.google.android.c2dm.intent.RECEIVE" />
+ </intent-filter>
+ </service>
+
+ <service android:name=".services.InstanceIdService" android:exported="false">
+ <intent-filter>
+ <action android:name="com.google.android.gms.iid.InstanceID"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/src/playstore/java/eu/siacs/conversations/services/InstanceIdService.java b/src/playstore/java/eu/siacs/conversations/services/InstanceIdService.java
new file mode 100644
index 000000000..dc0804305
--- /dev/null
+++ b/src/playstore/java/eu/siacs/conversations/services/InstanceIdService.java
@@ -0,0 +1,15 @@
+package eu.siacs.conversations.services;
+
+import android.content.Intent;
+
+import com.google.android.gms.iid.InstanceIDListenerService;
+
+public class InstanceIdService extends InstanceIDListenerService {
+
+ @Override
+ public void onTokenRefresh() {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction(XmppConnectionService.ACTION_GCM_TOKEN_REFRESH);
+ startService(intent);
+ }
+}
diff --git a/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java b/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java
new file mode 100644
index 000000000..d65170eec
--- /dev/null
+++ b/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java
@@ -0,0 +1,78 @@
+package eu.siacs.conversations.services;
+
+import android.provider.Settings;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+import com.google.android.gms.gcm.GoogleCloudMessaging;
+import com.google.android.gms.iid.InstanceID;
+
+import java.io.IOException;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jid.InvalidJidException;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class PushManagementService {
+
+ private static final String APP_SERVER = "push.conversations.im";
+
+ protected final XmppConnectionService mXmppConnectionService;
+
+ public PushManagementService(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public void registerPushTokenOnServer(final Account account) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": has push support");
+ retrieveGcmInstanceToken(new OnGcmInstanceTokenRetrieved() {
+ @Override
+ public void onGcmInstanceTokenRetrieved(String token) {
+ try {
+ final String deviceId = Settings.Secure.getString(mXmppConnectionService.getContentResolver(), Settings.Secure.ANDROID_ID);
+ IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(Jid.fromString(APP_SERVER), token, deviceId);
+ mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Log.d(Config.LOGTAG, "push to app server result: " + packet.toString());
+ }
+ });
+ } catch (InvalidJidException ignored) {
+
+ }
+ }
+ });
+ }
+
+ private void retrieveGcmInstanceToken(final OnGcmInstanceTokenRetrieved instanceTokenRetrieved) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ InstanceID instanceID = InstanceID.getInstance(mXmppConnectionService);
+ try {
+ String token = instanceID.getToken(mXmppConnectionService.getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
+ instanceTokenRetrieved.onGcmInstanceTokenRetrieved(token);
+ } catch (IOException e) {
+ }
+ }
+ }).start();
+
+ }
+
+ public boolean available() {
+ return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mXmppConnectionService) == ConnectionResult.SUCCESS;
+ }
+
+ public boolean pushAvailable(Account account) {
+ return account.getXmppConnection().getFeatures().push() && available();
+ }
+
+ interface OnGcmInstanceTokenRetrieved {
+ void onGcmInstanceTokenRetrieved(String token);
+ }
+}
diff --git a/src/playstore/java/eu/siacs/conversations/services/PushMessageReceiver.java b/src/playstore/java/eu/siacs/conversations/services/PushMessageReceiver.java
new file mode 100644
index 000000000..37c95e135
--- /dev/null
+++ b/src/playstore/java/eu/siacs/conversations/services/PushMessageReceiver.java
@@ -0,0 +1,20 @@
+package eu.siacs.conversations.services;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.google.android.gms.gcm.GcmListenerService;
+
+import eu.siacs.conversations.Config;
+
+public class PushMessageReceiver extends GcmListenerService {
+
+ @Override
+ public void onMessageReceived(String from, Bundle data) {
+ Intent intent = new Intent(this, XmppConnectionService.class);
+ intent.setAction(XmppConnectionService.ACTION_GCM_MESSAGE_RECEIVED);
+ intent.replaceExtras(data);
+ startService(intent);
+ }
+}