1 /** 2 * Copyright 2013 Georg Lukas 3 * 4 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.jivesoftware.smackx.receipts; 18 19 import java.util.Collections; 20 import java.util.HashSet; 21 import java.util.Map; 22 import java.util.Set; 23 import java.util.WeakHashMap; 24 25 import org.jivesoftware.smack.Connection; 26 import org.jivesoftware.smack.ConnectionCreationListener; 27 import org.jivesoftware.smack.PacketListener; 28 import org.jivesoftware.smack.XMPPException; 29 import org.jivesoftware.smack.filter.PacketExtensionFilter; 30 import org.jivesoftware.smack.packet.Message; 31 import org.jivesoftware.smack.packet.Packet; 32 import org.jivesoftware.smackx.ServiceDiscoveryManager; 33 import org.jivesoftware.smackx.packet.DiscoverInfo; 34 35 /** 36 * Manager for XEP-0184: Message Delivery Receipts. This class implements 37 * the manager for {@link DeliveryReceipt} support, enabling and disabling of 38 * automatic DeliveryReceipt transmission. 39 * 40 * @author Georg Lukas 41 */ 42 public class DeliveryReceiptManager implements PacketListener { 43 44 private static Map<Connection, DeliveryReceiptManager> instances = 45 Collections.synchronizedMap(new WeakHashMap<Connection, DeliveryReceiptManager>()); 46 47 static { Connection.addConnectionCreationListener(new ConnectionCreationListener() { public void connectionCreated(Connection connection) { new DeliveryReceiptManager(connection); } })48 Connection.addConnectionCreationListener(new ConnectionCreationListener() { 49 public void connectionCreated(Connection connection) { 50 new DeliveryReceiptManager(connection); 51 } 52 }); 53 } 54 55 private Connection connection; 56 private boolean auto_receipts_enabled = false; 57 private Set<ReceiptReceivedListener> receiptReceivedListeners = Collections 58 .synchronizedSet(new HashSet<ReceiptReceivedListener>()); 59 DeliveryReceiptManager(Connection connection)60 private DeliveryReceiptManager(Connection connection) { 61 ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); 62 sdm.addFeature(DeliveryReceipt.NAMESPACE); 63 this.connection = connection; 64 instances.put(connection, this); 65 66 // register listener for delivery receipts and requests 67 connection.addPacketListener(this, new PacketExtensionFilter(DeliveryReceipt.NAMESPACE)); 68 } 69 70 /** 71 * Obtain the DeliveryReceiptManager responsible for a connection. 72 * 73 * @param connection the connection object. 74 * 75 * @return the DeliveryReceiptManager instance for the given connection 76 */ getInstanceFor(Connection connection)77 synchronized public static DeliveryReceiptManager getInstanceFor(Connection connection) { 78 DeliveryReceiptManager receiptManager = instances.get(connection); 79 80 if (receiptManager == null) { 81 receiptManager = new DeliveryReceiptManager(connection); 82 } 83 84 return receiptManager; 85 } 86 87 /** 88 * Returns true if Delivery Receipts are supported by a given JID 89 * 90 * @param jid 91 * @return true if supported 92 */ isSupported(String jid)93 public boolean isSupported(String jid) { 94 try { 95 DiscoverInfo result = 96 ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(jid); 97 return result.containsFeature(DeliveryReceipt.NAMESPACE); 98 } 99 catch (XMPPException e) { 100 return false; 101 } 102 } 103 104 // handle incoming receipts and receipt requests 105 @Override processPacket(Packet packet)106 public void processPacket(Packet packet) { 107 DeliveryReceipt dr = (DeliveryReceipt)packet.getExtension( 108 DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE); 109 if (dr != null) { 110 // notify listeners of incoming receipt 111 for (ReceiptReceivedListener l : receiptReceivedListeners) { 112 l.onReceiptReceived(packet.getFrom(), packet.getTo(), dr.getId()); 113 } 114 115 } 116 117 // if enabled, automatically send a receipt 118 if (auto_receipts_enabled) { 119 DeliveryReceiptRequest drr = (DeliveryReceiptRequest)packet.getExtension( 120 DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE); 121 if (drr != null) { 122 Message ack = new Message(packet.getFrom(), Message.Type.normal); 123 ack.addExtension(new DeliveryReceipt(packet.getPacketID())); 124 connection.sendPacket(ack); 125 } 126 } 127 } 128 129 /** 130 * Configure whether the {@link DeliveryReceiptManager} should automatically 131 * reply to incoming {@link DeliveryReceipt}s. By default, this feature is off. 132 * 133 * @param new_state whether automatic transmission of 134 * DeliveryReceipts should be enabled or disabled 135 */ setAutoReceiptsEnabled(boolean new_state)136 public void setAutoReceiptsEnabled(boolean new_state) { 137 auto_receipts_enabled = new_state; 138 } 139 140 /** 141 * Helper method to enable automatic DeliveryReceipt transmission. 142 */ enableAutoReceipts()143 public void enableAutoReceipts() { 144 setAutoReceiptsEnabled(true); 145 } 146 147 /** 148 * Helper method to disable automatic DeliveryReceipt transmission. 149 */ disableAutoReceipts()150 public void disableAutoReceipts() { 151 setAutoReceiptsEnabled(false); 152 } 153 154 /** 155 * Check if AutoReceipts are enabled on this connection. 156 */ getAutoReceiptsEnabled()157 public boolean getAutoReceiptsEnabled() { 158 return this.auto_receipts_enabled; 159 } 160 161 /** 162 * Get informed about incoming delivery receipts with a {@link ReceiptReceivedListener}. 163 * 164 * @param listener the listener to be informed about new receipts 165 */ addReceiptReceivedListener(ReceiptReceivedListener listener)166 public void addReceiptReceivedListener(ReceiptReceivedListener listener) { 167 receiptReceivedListeners.add(listener); 168 } 169 170 /** 171 * Stop getting informed about incoming delivery receipts. 172 * 173 * @param listener the listener to be removed 174 */ removeReceiptReceivedListener(ReceiptReceivedListener listener)175 public void removeReceiptReceivedListener(ReceiptReceivedListener listener) { 176 receiptReceivedListeners.remove(listener); 177 } 178 179 /** 180 * Test if a packet requires a delivery receipt. 181 * 182 * @param p Packet object to check for a DeliveryReceiptRequest 183 * 184 * @return true if a delivery receipt was requested 185 */ hasDeliveryReceiptRequest(Packet p)186 public static boolean hasDeliveryReceiptRequest(Packet p) { 187 return (p.getExtension(DeliveryReceiptRequest.ELEMENT, 188 DeliveryReceipt.NAMESPACE) != null); 189 } 190 191 /** 192 * Add a delivery receipt request to an outgoing packet. 193 * 194 * Only message packets may contain receipt requests as of XEP-0184, 195 * therefore only allow Message as the parameter type. 196 * 197 * @param m Message object to add a request to 198 */ addDeliveryReceiptRequest(Message m)199 public static void addDeliveryReceiptRequest(Message m) { 200 m.addExtension(new DeliveryReceiptRequest()); 201 } 202 } 203