1package eu.siacs.conversations.services;
2
3import android.util.Log;
4
5import com.google.android.gms.common.ConnectionResult;
6import com.google.android.gms.common.GoogleApiAvailability;
7import com.google.firebase.iid.FirebaseInstanceId;
8import com.google.firebase.iid.InstanceIdResult;
9
10import eu.siacs.conversations.Config;
11import eu.siacs.conversations.R;
12import eu.siacs.conversations.entities.Account;
13import eu.siacs.conversations.utils.PhoneHelper;
14import eu.siacs.conversations.xml.Element;
15import eu.siacs.conversations.xml.Namespace;
16import eu.siacs.conversations.xmpp.Jid;
17import eu.siacs.conversations.xmpp.XmppConnection;
18import eu.siacs.conversations.xmpp.forms.Data;
19import eu.siacs.conversations.xmpp.stanzas.IqPacket;
20
21public class PushManagementService {
22
23 protected final XmppConnectionService mXmppConnectionService;
24
25 PushManagementService(XmppConnectionService service) {
26 this.mXmppConnectionService = service;
27 }
28
29 private static Data findResponseData(IqPacket response) {
30 final Element command = response.findChild("command", Namespace.COMMANDS);
31 final Element x = command == null ? null : command.findChild("x", Namespace.DATA);
32 return x == null ? null : Data.parse(x);
33 }
34
35 private Jid getAppServer() {
36 return Jid.of(mXmppConnectionService.getString(R.string.app_server));
37 }
38
39 void registerPushTokenOnServer(final Account account) {
40 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": has push support");
41 retrieveFcmInstanceToken(token -> {
42 final String androidId = PhoneHelper.getAndroidId(mXmppConnectionService);
43 final IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(getAppServer(), token, androidId);
44 mXmppConnectionService.sendIqPacket(account, packet, (a, response) -> {
45 final Data data = findResponseData(response);
46 if (response.getType() == IqPacket.TYPE.RESULT && data != null) {
47 try {
48 String node = data.getValue("node");
49 String secret = data.getValue("secret");
50 Jid jid = Jid.of(data.getValue("jid"));
51 if (node != null && secret != null) {
52 enablePushOnServer(a, jid, node, secret);
53 }
54 } catch (IllegalArgumentException e) {
55 e.printStackTrace();
56 }
57 } else {
58 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": invalid response from app server");
59 }
60 });
61 });
62 }
63
64 private void enablePushOnServer(final Account account, final Jid appServer, final String node, final String secret) {
65 final IqPacket enable = mXmppConnectionService.getIqGenerator().enablePush(appServer, node, secret);
66 mXmppConnectionService.sendIqPacket(account, enable, (a, p) -> {
67 if (p.getType() == IqPacket.TYPE.RESULT) {
68 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": successfully enabled push on server");
69 } else if (p.getType() == IqPacket.TYPE.ERROR) {
70 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": enabling push on server failed");
71 }
72 });
73 }
74
75 private void retrieveFcmInstanceToken(final OnGcmInstanceTokenRetrieved instanceTokenRetrieved) {
76 final FirebaseInstanceId firebaseInstanceId;
77 try {
78 firebaseInstanceId = FirebaseInstanceId.getInstance();
79 } catch (IllegalStateException e) {
80 Log.d(Config.LOGTAG, "unable to get firebase instance token ",e);
81 return;
82 }
83 firebaseInstanceId.getInstanceId().addOnCompleteListener(task -> {
84 if (!task.isSuccessful()) {
85 Log.d(Config.LOGTAG, "unable to get Firebase instance token", task.getException());
86 }
87 final InstanceIdResult result;
88 try {
89 result = task.getResult();
90 } catch (Exception e) {
91 Log.d(Config.LOGTAG, "unable to get Firebase instance token due to bug in library ", e);
92 return;
93 }
94 if (result != null) {
95 instanceTokenRetrieved.onGcmInstanceTokenRetrieved(result.getToken());
96 }
97 });
98
99 }
100
101
102 public boolean available(Account account) {
103 final XmppConnection connection = account.getXmppConnection();
104 return connection != null
105 && connection.getFeatures().sm()
106 && connection.getFeatures().push()
107 && playServicesAvailable();
108 }
109
110 private boolean playServicesAvailable() {
111 return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mXmppConnectionService) == ConnectionResult.SUCCESS;
112 }
113
114 public boolean isStub() {
115 return false;
116 }
117
118 interface OnGcmInstanceTokenRetrieved {
119 void onGcmInstanceTokenRetrieved(String token);
120 }
121}