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