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; 028 029import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 030import org.jivesoftware.smackx.message_fastening.element.FasteningElement; 031import org.jivesoftware.smackx.message_retraction.element.RetractElement; 032import org.jivesoftware.smackx.sid.element.OriginIdElement; 033 034/** 035 * Smacks API for XEP-0424: Message Retraction. 036 * 037 * To enable / disable auto-announcing support for this feature, call {@link #setEnabledByDefault(boolean)}. 038 * Auto-announcing is enabled by default. 039 * 040 * To retract a message, call {@link #retractMessage(OriginIdElement)}, passing in the {@link OriginIdElement Origin ID} 041 * of the message to be retracted. 042 */ 043public final class MessageRetractionManager extends Manager { 044 045 private static final Map<XMPPConnection, MessageRetractionManager> INSTANCES = new WeakHashMap<>(); 046 047 private static boolean ENABLED_BY_DEFAULT = false; 048 049 static { 050 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 051 @Override 052 public void connectionCreated(XMPPConnection connection) { 053 if (ENABLED_BY_DEFAULT) { 054 getInstanceFor(connection).announceSupport(); 055 } 056 } 057 }); 058 } 059 060 private MessageRetractionManager(XMPPConnection connection) { 061 super(connection); 062 } 063 064 public static synchronized MessageRetractionManager getInstanceFor(XMPPConnection connection) { 065 MessageRetractionManager manager = INSTANCES.get(connection); 066 if (manager == null) { 067 manager = new MessageRetractionManager(connection); 068 INSTANCES.put(connection, manager); 069 } 070 return manager; 071 } 072 073 /** 074 * Enable or disable auto-announcing support for Message Retraction. 075 * Default is disabled. 076 * 077 * @param enabled enabled 078 */ 079 public static synchronized void setEnabledByDefault(boolean enabled) { 080 ENABLED_BY_DEFAULT = enabled; 081 } 082 083 /** 084 * Announce support for Message Retraction to the server. 085 * 086 * @see <a href="https://xmpp.org/extensions/xep-0424.html#disco">XEP-0424: Message Retraction: ยง2. Discovering Support</a> 087 */ 088 public void announceSupport() { 089 ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(RetractElement.NAMESPACE); 090 } 091 092 /** 093 * Stop announcing support for Message Retraction. 094 */ 095 public void stopAnnouncingSupport() { 096 ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(RetractElement.NAMESPACE); 097 } 098 099 /** 100 * Append a {@link RetractElement} wrapped inside a {@link FasteningElement} which contains 101 * the {@link OriginIdElement Origin-ID} of the message that will be retracted to the given {@link MessageBuilder}. 102 * 103 * @param retractedMessageId {@link OriginIdElement OriginID} of the message that the user wants to retract 104 * @param carrierMessageBuilder message used to transmit the message retraction to the recipient 105 */ 106 public static void addRetractionElementToMessage(OriginIdElement retractedMessageId, MessageBuilder carrierMessageBuilder) { 107 FasteningElement fasteningElement = FasteningElement.builder() 108 .setOriginId(retractedMessageId) 109 .addWrappedPayload(new RetractElement()) 110 .build(); 111 fasteningElement.applyTo(carrierMessageBuilder); 112 } 113 114 /** 115 * Retract a message by appending a {@link RetractElement} wrapped inside a {@link FasteningElement} which contains 116 * the {@link OriginIdElement Origin-ID} of the message that will be retracted to a new message and send it to the 117 * server. 118 * 119 * @param retractedMessageId {@link OriginIdElement OriginID} of the message that the user wants to retract 120 * @throws SmackException.NotConnectedException in case the connection is not connected. 121 * @throws InterruptedException if the thread gets interrupted. 122 */ 123 public void retractMessage(OriginIdElement retractedMessageId) 124 throws SmackException.NotConnectedException, InterruptedException { 125 MessageBuilder message = connection().getStanzaFactory().buildMessageStanza(); 126 addRetractionElementToMessage(retractedMessageId, message); 127 connection().sendStanza(message.build()); 128 } 129}