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