001/** 002 * 003 * Copyright 2020 Paul Schaub 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.message_retraction; 018 019import java.util.Map; 020import java.util.WeakHashMap; 021 022import org.jivesoftware.smack.ConnectionCreationListener; 023import org.jivesoftware.smack.Manager; 024import org.jivesoftware.smack.SmackException; 025import org.jivesoftware.smack.XMPPConnection; 026import org.jivesoftware.smack.XMPPConnectionRegistry; 027import org.jivesoftware.smack.packet.MessageBuilder; 028import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 029import org.jivesoftware.smackx.message_fastening.element.FasteningElement; 030import org.jivesoftware.smackx.message_retraction.element.RetractElement; 031import org.jivesoftware.smackx.sid.element.OriginIdElement; 032 033/** 034 * Smacks API for XEP-0424: Message Retraction. 035 * 036 * To enable / disable auto-announcing support for this feature, call {@link #setEnabledByDefault(boolean)}. 037 * Auto-announcing is enabled by default. 038 * 039 * To retract a message, call {@link #retractMessage(OriginIdElement)}, passing in the {@link OriginIdElement Origin ID} 040 * of the message to be retracted. 041 */ 042public final class MessageRetractionManager extends Manager { 043 044 private static final Map<XMPPConnection, MessageRetractionManager> INSTANCES = new WeakHashMap<>(); 045 046 private static boolean ENABLED_BY_DEFAULT = false; 047 048 static { 049 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 050 @Override 051 public void connectionCreated(XMPPConnection connection) { 052 if (ENABLED_BY_DEFAULT) { 053 getInstanceFor(connection).announceSupport(); 054 } 055 } 056 }); 057 } 058 059 private MessageRetractionManager(XMPPConnection connection) { 060 super(connection); 061 } 062 063 public static synchronized MessageRetractionManager getInstanceFor(XMPPConnection connection) { 064 MessageRetractionManager manager = INSTANCES.get(connection); 065 if (manager == null) { 066 manager = new MessageRetractionManager(connection); 067 INSTANCES.put(connection, manager); 068 } 069 return manager; 070 } 071 072 /** 073 * Enable or disable auto-announcing support for Message Retraction. 074 * Default is disabled. 075 * 076 * @param enabled enabled 077 */ 078 public static synchronized void setEnabledByDefault(boolean enabled) { 079 ENABLED_BY_DEFAULT = enabled; 080 } 081 082 /** 083 * Announce support for Message Retraction to the server. 084 * 085 * @see <a href="https://xmpp.org/extensions/xep-0424.html#disco">XEP-0424: Message Retraction: ยง2. Discovering Support</a> 086 */ 087 public void announceSupport() { 088 ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(RetractElement.NAMESPACE); 089 } 090 091 /** 092 * Stop announcing support for Message Retraction. 093 */ 094 public void stopAnnouncingSupport() { 095 ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(RetractElement.NAMESPACE); 096 } 097 098 /** 099 * Append a {@link RetractElement} wrapped inside a {@link FasteningElement} which contains 100 * the {@link OriginIdElement Origin-ID} of the message that will be retracted to the given {@link MessageBuilder}. 101 * 102 * @param retractedMessageId {@link OriginIdElement OriginID} of the message that the user wants to retract 103 * @param carrierMessageBuilder message used to transmit the message retraction to the recipient 104 */ 105 public static void addRetractionElementToMessage(OriginIdElement retractedMessageId, MessageBuilder carrierMessageBuilder) { 106 FasteningElement fasteningElement = FasteningElement.builder() 107 .setOriginId(retractedMessageId) 108 .addWrappedPayload(new RetractElement()) 109 .build(); 110 fasteningElement.applyTo(carrierMessageBuilder); 111 } 112 113 /** 114 * Retract a message by appending a {@link RetractElement} wrapped inside a {@link FasteningElement} which contains 115 * the {@link OriginIdElement Origin-ID} of the message that will be retracted to a new message and send it to the 116 * server. 117 * 118 * @param retractedMessageId {@link OriginIdElement OriginID} of the message that the user wants to retract 119 * @throws SmackException.NotConnectedException in case the connection is not connected. 120 * @throws InterruptedException if the thread gets interrupted. 121 */ 122 public void retractMessage(OriginIdElement retractedMessageId) 123 throws SmackException.NotConnectedException, InterruptedException { 124 MessageBuilder message = connection().getStanzaFactory().buildMessageStanza(); 125 addRetractionElementToMessage(retractedMessageId, message); 126 connection().sendStanza(message.build()); 127 } 128}