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