2014-12-05 00:54:16 +00:00
package eu.siacs.conversations.services ;
import android.util.Log ;
2020-12-11 10:29:23 +00:00
import org.jetbrains.annotations.NotNull ;
2014-12-05 00:54:16 +00:00
import java.math.BigInteger ;
2014-12-14 22:23:32 +00:00
import java.util.ArrayList ;
2014-12-05 00:54:16 +00:00
import java.util.HashSet ;
2014-12-14 22:23:32 +00:00
import java.util.Iterator ;
2014-12-08 20:59:14 +00:00
import java.util.List ;
2014-12-05 00:54:16 +00:00
import eu.siacs.conversations.Config ;
2014-12-17 09:50:51 +00:00
import eu.siacs.conversations.R ;
2014-12-05 00:54:16 +00:00
import eu.siacs.conversations.entities.Account ;
import eu.siacs.conversations.entities.Conversation ;
2018-05-11 09:16:29 +00:00
import eu.siacs.conversations.entities.Conversational ;
2018-01-20 07:54:44 +00:00
import eu.siacs.conversations.entities.ReceiptRequest ;
2014-12-13 11:25:52 +00:00
import eu.siacs.conversations.generator.AbstractGenerator ;
2014-12-05 00:54:16 +00:00
import eu.siacs.conversations.xml.Element ;
2014-12-08 20:59:14 +00:00
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded ;
2017-05-07 19:05:35 +00:00
import eu.siacs.conversations.xmpp.mam.MamReference ;
2014-12-05 00:54:16 +00:00
import eu.siacs.conversations.xmpp.stanzas.IqPacket ;
2018-07-07 09:20:39 +00:00
import eu.siacs.conversations.xmpp.stanzas.MessagePacket ;
2020-05-15 15:06:16 +00:00
import eu.siacs.conversations.xmpp.Jid ;
2014-12-05 00:54:16 +00:00
2014-12-08 20:59:14 +00:00
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
2014-12-05 00:54:16 +00:00
private final XmppConnectionService mXmppConnectionService ;
2016-02-04 13:39:16 +00:00
private final HashSet < Query > queries = new HashSet < > ( ) ;
private final ArrayList < Query > pendingQueries = new ArrayList < > ( ) ;
2014-12-05 00:54:16 +00:00
2018-07-07 09:20:39 +00:00
public enum Version {
MAM_0 ( " urn:xmpp:mam:0 " , true ) ,
MAM_1 ( " urn:xmpp:mam:1 " , false ) ,
MAM_2 ( " urn:xmpp:mam:2 " , false ) ;
public final boolean legacy ;
public final String namespace ;
Version ( String namespace , boolean legacy ) {
this . namespace = namespace ;
this . legacy = legacy ;
}
public static Version get ( Account account ) {
return get ( account , null ) ;
}
public static Version get ( Account account , Conversation conversation ) {
if ( conversation = = null | | conversation . getMode ( ) = = Conversation . MODE_SINGLE ) {
return get ( account . getXmppConnection ( ) . getFeatures ( ) . getAccountFeatures ( ) ) ;
} else {
return get ( conversation . getMucOptions ( ) . getFeatures ( ) ) ;
}
}
private static Version get ( List < String > features ) {
final Version [ ] values = values ( ) ;
for ( int i = values . length - 1 ; i > = 0 ; - - i ) {
for ( String feature : features ) {
if ( values [ i ] . namespace . equals ( feature ) ) {
return values [ i ] ;
}
}
}
return MAM_0 ;
}
public static boolean has ( List < String > features ) {
for ( String feature : features ) {
for ( Version version : values ( ) ) {
if ( version . namespace . equals ( feature ) ) {
return true ;
}
}
}
return false ;
}
public static Element findResult ( MessagePacket packet ) {
for ( Version version : values ( ) ) {
Element result = packet . findChild ( " result " , version . namespace ) ;
if ( result ! = null ) {
return result ;
}
}
return null ;
}
} ;
2018-03-01 07:27:30 +00:00
MessageArchiveService ( final XmppConnectionService service ) {
2014-12-05 00:54:16 +00:00
this . mXmppConnectionService = service ;
}
2015-12-10 17:28:47 +00:00
private void catchup ( final Account account ) {
synchronized ( this . queries ) {
2018-03-01 07:27:30 +00:00
for ( Iterator < Query > iterator = this . queries . iterator ( ) ; iterator . hasNext ( ) ; ) {
2015-12-10 17:28:47 +00:00
Query query = iterator . next ( ) ;
if ( query . getAccount ( ) = = account ) {
iterator . remove ( ) ;
}
}
}
2017-05-07 19:05:35 +00:00
MamReference mamReference = MamReference . max (
mXmppConnectionService . databaseBackend . getLastMessageReceived ( account ) ,
mXmppConnectionService . databaseBackend . getLastClearDate ( account )
) ;
2018-03-01 07:27:30 +00:00
mamReference = MamReference . max ( mamReference , mXmppConnectionService . getAutomaticMessageDeletionDate ( ) ) ;
2014-12-13 11:25:52 +00:00
long endCatchup = account . getXmppConnection ( ) . getLastSessionEstablished ( ) ;
2016-04-09 08:29:34 +00:00
final Query query ;
2017-05-07 19:05:35 +00:00
if ( mamReference . getTimestamp ( ) = = 0 ) {
2014-12-13 11:25:52 +00:00
return ;
2017-05-07 19:05:35 +00:00
} else if ( endCatchup - mamReference . getTimestamp ( ) > = Config . MAM_MAX_CATCHUP ) {
long startCatchup = endCatchup - Config . MAM_MAX_CATCHUP ;
2014-12-13 11:25:52 +00:00
List < Conversation > conversations = mXmppConnectionService . getConversations ( ) ;
for ( Conversation conversation : conversations ) {
2017-05-07 19:05:35 +00:00
if ( conversation . getMode ( ) = = Conversation . MODE_SINGLE & & conversation . getAccount ( ) = = account & & startCatchup > conversation . getLastMessageTransmitted ( ) . getTimestamp ( ) ) {
2018-03-01 07:27:30 +00:00
this . query ( conversation , startCatchup , true ) ;
2014-12-13 11:25:52 +00:00
}
}
2018-05-11 09:16:29 +00:00
query = new Query ( account , new MamReference ( startCatchup ) , 0 ) ;
2016-04-09 08:29:34 +00:00
} else {
2018-05-11 09:16:29 +00:00
query = new Query ( account , mamReference , 0 ) ;
2014-12-13 11:25:52 +00:00
}
2017-05-07 13:47:18 +00:00
synchronized ( this . queries ) {
this . queries . add ( query ) ;
}
2014-12-13 11:25:52 +00:00
this . execute ( query ) ;
}
2018-03-01 07:27:30 +00:00
void catchupMUC ( final Conversation conversation ) {
2017-05-07 19:05:35 +00:00
if ( conversation . getLastMessageTransmitted ( ) . getTimestamp ( ) < 0 & & conversation . countMessages ( ) = = 0 ) {
2015-10-04 22:45:16 +00:00
query ( conversation ,
2017-05-07 19:05:35 +00:00
new MamReference ( 0 ) ,
2018-05-11 09:16:29 +00:00
0 ,
2017-05-04 20:11:46 +00:00
true ) ;
2015-10-04 22:45:16 +00:00
} else {
query ( conversation ,
conversation . getLastMessageTransmitted ( ) ,
2018-05-11 09:16:29 +00:00
0 ,
2017-05-04 20:11:46 +00:00
true ) ;
2015-10-04 22:45:16 +00:00
}
}
2014-12-15 22:06:29 +00:00
public Query query ( final Conversation conversation ) {
2017-05-07 19:05:35 +00:00
if ( conversation . getLastMessageTransmitted ( ) . getTimestamp ( ) < 0 & & conversation . countMessages ( ) = = 0 ) {
2015-08-30 09:24:37 +00:00
return query ( conversation ,
2017-05-07 19:05:35 +00:00
new MamReference ( 0 ) ,
2017-05-04 20:11:46 +00:00
System . currentTimeMillis ( ) ,
false ) ;
2015-08-30 09:24:37 +00:00
} else {
return query ( conversation ,
conversation . getLastMessageTransmitted ( ) ,
2017-05-04 20:11:46 +00:00
conversation . getAccount ( ) . getXmppConnection ( ) . getLastSessionEstablished ( ) ,
false ) ;
2015-08-30 09:24:37 +00:00
}
2014-12-13 11:25:52 +00:00
}
2017-05-19 11:39:44 +00:00
public boolean isCatchingUp ( Conversation conversation ) {
final Account account = conversation . getAccount ( ) ;
if ( account . getXmppConnection ( ) . isWaitingForSmCatchup ( ) ) {
return true ;
} else {
synchronized ( this . queries ) {
2018-03-01 07:27:30 +00:00
for ( Query query : this . queries ) {
2017-05-19 11:39:44 +00:00
if ( query . getAccount ( ) = = account & & query . isCatchup ( ) & & ( ( conversation . getMode ( ) = = Conversation . MODE_SINGLE & & query . getWith ( ) = = null ) | | query . getConversation ( ) = = conversation ) ) {
return true ;
}
}
}
return false ;
}
}
2017-05-04 20:11:46 +00:00
public Query query ( final Conversation conversation , long end , boolean allowCatchup ) {
2018-03-01 07:27:30 +00:00
return this . query ( conversation , conversation . getLastMessageTransmitted ( ) , end , allowCatchup ) ;
2014-12-15 22:06:29 +00:00
}
2017-05-07 19:05:35 +00:00
public Query query ( Conversation conversation , MamReference start , long end , boolean allowCatchup ) {
2014-12-05 00:54:16 +00:00
synchronized ( this . queries ) {
2017-02-14 15:50:33 +00:00
final Query query ;
2018-03-01 07:27:30 +00:00
final MamReference startActual = MamReference . max ( start , mXmppConnectionService . getAutomaticMessageDeletionDate ( ) ) ;
2017-05-07 19:05:35 +00:00
if ( start . getTimestamp ( ) = = 0 ) {
2017-02-14 15:50:33 +00:00
query = new Query ( conversation , startActual , end , false ) ;
2017-01-12 19:56:55 +00:00
query . reference = conversation . getFirstMamReference ( ) ;
2017-02-14 15:50:33 +00:00
} else {
2017-05-07 19:05:35 +00:00
if ( allowCatchup ) {
MamReference maxCatchup = MamReference . max ( startActual , System . currentTimeMillis ( ) - Config . MAM_MAX_CATCHUP ) ;
if ( maxCatchup . greaterThan ( startActual ) ) {
Query reverseCatchup = new Query ( conversation , startActual , maxCatchup . getTimestamp ( ) , false ) ;
this . queries . add ( reverseCatchup ) ;
this . execute ( reverseCatchup ) ;
}
2018-03-01 07:27:30 +00:00
query = new Query ( conversation , maxCatchup , end , true ) ;
2017-05-07 19:05:35 +00:00
} else {
query = new Query ( conversation , startActual , end , false ) ;
2017-02-14 15:50:33 +00:00
}
2017-01-23 16:14:30 +00:00
}
2018-05-11 09:16:29 +00:00
if ( end ! = 0 & & start . greaterThan ( end ) ) {
2017-01-23 16:14:30 +00:00
return null ;
2017-01-12 19:56:55 +00:00
}
2014-12-05 00:54:16 +00:00
this . queries . add ( query ) ;
2014-12-13 11:25:52 +00:00
this . execute ( query ) ;
2014-12-15 22:06:29 +00:00
return query ;
2014-12-13 11:25:52 +00:00
}
}
2018-03-01 07:27:30 +00:00
void executePendingQueries ( final Account account ) {
2014-12-14 22:23:32 +00:00
List < Query > pending = new ArrayList < > ( ) ;
2018-03-01 07:27:30 +00:00
synchronized ( this . pendingQueries ) {
for ( Iterator < Query > iterator = this . pendingQueries . iterator ( ) ; iterator . hasNext ( ) ; ) {
2014-12-14 22:23:32 +00:00
Query query = iterator . next ( ) ;
if ( query . getAccount ( ) = = account ) {
pending . add ( query ) ;
iterator . remove ( ) ;
}
}
}
2018-03-01 07:27:30 +00:00
for ( Query query : pending ) {
2014-12-14 22:23:32 +00:00
this . execute ( query ) ;
}
}
2014-12-13 11:25:52 +00:00
private void execute ( final Query query ) {
2018-03-01 07:27:30 +00:00
final Account account = query . getAccount ( ) ;
2014-12-14 22:23:32 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2020-07-26 14:39:48 +00:00
final Conversation conversation = query . getConversation ( ) ;
if ( conversation ! = null & & conversation . getStatus ( ) = = Conversation . STATUS_ARCHIVED ) {
throw new IllegalStateException ( " Attempted to run MAM query for archived conversation " ) ;
}
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) . toString ( ) + " : running mam query " + query . toString ( ) ) ;
2020-07-26 14:39:48 +00:00
final IqPacket packet = this . mXmppConnectionService . getIqGenerator ( ) . queryMessageArchiveManagement ( query ) ;
2018-03-01 07:27:30 +00:00
this . mXmppConnectionService . sendIqPacket ( account , packet , ( a , p ) - > {
2020-07-26 14:39:48 +00:00
final Element fin = p . findChild ( " fin " , query . version . namespace ) ;
2018-03-01 07:27:30 +00:00
if ( p . getType ( ) = = IqPacket . TYPE . TIMEOUT ) {
2020-07-26 14:39:48 +00:00
synchronized ( this . queries ) {
this . queries . remove ( query ) ;
2018-03-01 07:27:30 +00:00
if ( query . hasCallback ( ) ) {
query . callback ( false ) ;
2015-12-10 17:28:47 +00:00
}
2014-12-08 20:59:14 +00:00
}
2018-03-01 07:27:30 +00:00
} else if ( p . getType ( ) = = IqPacket . TYPE . RESULT & & fin ! = null ) {
2020-07-26 14:39:48 +00:00
final boolean running ;
synchronized ( this . queries ) {
running = this . queries . contains ( query ) ;
}
if ( running ) {
processFin ( query , fin ) ;
} else {
Log . d ( Config . LOGTAG , account . getJid ( ) . asBareJid ( ) + " : ignoring MAM iq result because query had been killed " ) ;
}
2018-03-01 07:27:30 +00:00
} else if ( p . getType ( ) = = IqPacket . TYPE . RESULT & & query . isLegacy ( ) ) {
//do nothing
} else {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , a . getJid ( ) . asBareJid ( ) . toString ( ) + " : error executing mam: " + p . toString ( ) ) ;
2018-03-01 07:27:30 +00:00
finalizeQuery ( query , true ) ;
2014-12-05 00:54:16 +00:00
}
} ) ;
2014-12-14 22:23:32 +00:00
} else {
synchronized ( this . pendingQueries ) {
this . pendingQueries . add ( query ) ;
}
}
2014-12-05 00:54:16 +00:00
}
2020-07-26 14:39:48 +00:00
private void finalizeQuery ( final Query query , boolean done ) {
2014-12-08 20:59:14 +00:00
synchronized ( this . queries ) {
2020-07-26 14:39:48 +00:00
if ( ! this . queries . remove ( query ) ) {
throw new IllegalStateException ( " Unable to remove query from queries " ) ;
}
2014-12-08 20:59:14 +00:00
}
2014-12-09 21:50:53 +00:00
final Conversation conversation = query . getConversation ( ) ;
2014-12-13 11:25:52 +00:00
if ( conversation ! = null ) {
conversation . sort ( ) ;
2016-02-04 10:55:42 +00:00
conversation . setHasMessagesLeftOnServer ( ! done ) ;
2014-12-13 11:25:52 +00:00
} else {
2018-03-01 07:27:30 +00:00
for ( Conversation tmp : this . mXmppConnectionService . getConversations ( ) ) {
2014-12-13 11:25:52 +00:00
if ( tmp . getAccount ( ) = = query . getAccount ( ) ) {
tmp . sort ( ) ;
}
}
2014-12-09 21:50:53 +00:00
}
2015-12-11 12:52:04 +00:00
if ( query . hasCallback ( ) ) {
2016-02-04 13:39:16 +00:00
query . callback ( done ) ;
2015-12-11 12:52:04 +00:00
} else {
this . mXmppConnectionService . updateConversationUi ( ) ;
}
2014-12-08 20:59:14 +00:00
}
2018-03-01 07:27:30 +00:00
boolean inCatchup ( Account account ) {
2018-01-19 17:17:13 +00:00
synchronized ( this . queries ) {
2018-03-01 07:27:30 +00:00
for ( Query query : queries ) {
2018-01-19 17:17:13 +00:00
if ( query . account = = account & & query . isCatchup ( ) & & query . getWith ( ) = = null ) {
return true ;
}
}
}
return false ;
}
2018-05-11 09:16:29 +00:00
public boolean isCatchupInProgress ( Conversation conversation ) {
synchronized ( this . queries ) {
for ( Query query : queries ) {
if ( query . account = = conversation . getAccount ( ) & & query . isCatchup ( ) ) {
final Jid with = query . getWith ( ) = = null ? null : query . getWith ( ) . asBareJid ( ) ;
if ( ( conversation . getMode ( ) = = Conversational . MODE_SINGLE & & with = = null ) | | ( conversation . getJid ( ) . asBareJid ( ) . equals ( with ) ) ) {
return true ;
}
}
}
}
return false ;
}
2018-03-01 07:27:30 +00:00
boolean queryInProgress ( Conversation conversation , XmppConnectionService . OnMoreMessagesLoaded callback ) {
2014-12-13 14:32:11 +00:00
synchronized ( this . queries ) {
2018-03-01 07:27:30 +00:00
for ( Query query : queries ) {
2014-12-13 14:32:11 +00:00
if ( query . conversation = = conversation ) {
2014-12-20 11:52:08 +00:00
if ( ! query . hasCallback ( ) & & callback ! = null ) {
query . setCallback ( callback ) ;
}
2014-12-13 14:32:11 +00:00
return true ;
}
}
return false ;
}
}
2016-02-21 16:32:46 +00:00
public boolean queryInProgress ( Conversation conversation ) {
return queryInProgress ( conversation , null ) ;
}
2017-02-19 13:47:57 +00:00
public void processFinLegacy ( Element fin , Jid from ) {
2017-02-15 15:42:35 +00:00
Query query = findQuery ( fin . getAttribute ( " queryid " ) ) ;
if ( query ! = null & & query . validFrom ( from ) ) {
2018-02-23 22:11:54 +00:00
processFin ( query , fin ) ;
2017-02-15 15:42:35 +00:00
}
}
2018-02-23 22:11:54 +00:00
private void processFin ( Query query , Element fin ) {
2014-12-05 00:54:16 +00:00
boolean complete = fin . getAttributeAsBoolean ( " complete " ) ;
2018-03-01 07:27:30 +00:00
Element set = fin . findChild ( " set " , " http://jabber.org/protocol/rsm " ) ;
2014-12-05 00:54:16 +00:00
Element last = set = = null ? null : set . findChild ( " last " ) ;
2018-02-23 22:11:54 +00:00
String count = set = = null ? null : set . findChildContent ( " count " ) ;
2014-12-13 14:32:11 +00:00
Element first = set = = null ? null : set . findChild ( " first " ) ;
Element relevant = query . getPagingOrder ( ) = = PagingOrder . NORMAL ? last : first ;
2017-02-14 15:50:33 +00:00
boolean abort = ( ! query . isCatchup ( ) & & query . getTotalCount ( ) > = Config . PAGE_SIZE ) | | query . getTotalCount ( ) > = Config . MAM_MAX_MESSAGES ;
2016-02-04 10:55:42 +00:00
if ( query . getConversation ( ) ! = null ) {
query . getConversation ( ) . setFirstMamReference ( first = = null ? null : first . getContent ( ) ) ;
}
2014-12-15 22:06:29 +00:00
if ( complete | | relevant = = null | | abort ) {
2018-12-22 12:02:45 +00:00
//TODO: FIX done logic to look at complete. using count is probably unreliable because it can be ommited and doesn’ t work with paging.
2018-02-24 09:57:11 +00:00
boolean done ;
if ( query . isCatchup ( ) ) {
done = false ;
} else {
if ( count ! = null ) {
try {
done = Integer . parseInt ( count ) < = query . getTotalCount ( ) ;
} catch ( NumberFormatException e ) {
done = false ;
}
} else {
done = query . getTotalCount ( ) = = 0 ;
2018-02-23 22:11:54 +00:00
}
}
done = done | | ( query . getActualMessageCount ( ) = = 0 & & ! query . isCatchup ( ) ) ;
2016-02-04 10:55:42 +00:00
this . finalizeQuery ( query , done ) ;
2018-02-23 22:11:54 +00:00
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , query . getAccount ( ) . getJid ( ) . asBareJid ( ) + " : finished mam after " + query . getTotalCount ( ) + " ( " + query . getActualMessageCount ( ) + " ) messages. messages left= " + Boolean . toString ( ! done ) + " count= " + count ) ;
2017-03-08 21:02:09 +00:00
if ( query . isCatchup ( ) & & query . getActualMessageCount ( ) > 0 ) {
2018-03-01 07:27:30 +00:00
mXmppConnectionService . getNotificationService ( ) . finishBacklog ( true , query . getAccount ( ) ) ;
2015-12-10 22:05:11 +00:00
}
2018-01-20 07:54:44 +00:00
processPostponed ( query ) ;
2014-12-05 00:54:16 +00:00
} else {
2014-12-13 14:32:11 +00:00
final Query nextQuery ;
if ( query . getPagingOrder ( ) = = PagingOrder . NORMAL ) {
nextQuery = query . next ( last = = null ? null : last . getContent ( ) ) ;
} else {
nextQuery = query . prev ( first = = null ? null : first . getContent ( ) ) ;
}
2014-12-13 11:25:52 +00:00
this . execute ( nextQuery ) ;
2016-02-04 10:55:42 +00:00
this . finalizeQuery ( query , false ) ;
2014-12-05 00:54:16 +00:00
synchronized ( this . queries ) {
this . queries . add ( nextQuery ) ;
}
}
}
2018-03-01 07:27:30 +00:00
void kill ( Conversation conversation ) {
final ArrayList < Query > toBeKilled = new ArrayList < > ( ) ;
2018-02-24 08:39:17 +00:00
synchronized ( this . queries ) {
2020-07-26 14:39:48 +00:00
for ( final Query q : queries ) {
2018-02-24 08:39:17 +00:00
if ( q . conversation = = conversation ) {
2018-03-01 07:27:30 +00:00
toBeKilled . add ( q ) ;
2018-02-24 08:39:17 +00:00
}
}
}
2018-03-01 07:27:30 +00:00
for ( Query q : toBeKilled ) {
kill ( q ) ;
}
2018-02-24 08:39:17 +00:00
}
private void kill ( Query query ) {
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , query . getAccount ( ) . getJid ( ) . asBareJid ( ) + " : killing mam query prematurely " ) ;
2018-02-24 08:39:17 +00:00
query . callback = null ;
2018-03-01 07:27:30 +00:00
this . finalizeQuery ( query , false ) ;
2018-02-24 08:39:17 +00:00
if ( query . isCatchup ( ) & & query . getActualMessageCount ( ) > 0 ) {
2018-03-01 07:27:30 +00:00
mXmppConnectionService . getNotificationService ( ) . finishBacklog ( true , query . getAccount ( ) ) ;
2018-02-24 08:39:17 +00:00
}
this . processPostponed ( query ) ;
}
2018-01-20 07:54:44 +00:00
private void processPostponed ( Query query ) {
query . account . getAxolotlService ( ) . processPostponed ( ) ;
2018-03-25 15:22:39 +00:00
query . pendingReceiptRequests . removeAll ( query . receiptRequests ) ;
2018-03-05 17:30:40 +00:00
Log . d ( Config . LOGTAG , query . getAccount ( ) . getJid ( ) . asBareJid ( ) + " : found " + query . pendingReceiptRequests . size ( ) + " pending receipt requests " ) ;
2018-01-20 07:54:44 +00:00
Iterator < ReceiptRequest > iterator = query . pendingReceiptRequests . iterator ( ) ;
while ( iterator . hasNext ( ) ) {
ReceiptRequest rr = iterator . next ( ) ;
2018-03-01 07:27:30 +00:00
mXmppConnectionService . sendMessagePacket ( query . account , mXmppConnectionService . getMessageGenerator ( ) . received ( query . account , rr . getJid ( ) , rr . getId ( ) ) ) ;
2018-01-20 07:54:44 +00:00
iterator . remove ( ) ;
}
}
2014-12-13 11:25:52 +00:00
public Query findQuery ( String id ) {
2014-12-05 00:54:16 +00:00
if ( id = = null ) {
return null ;
}
synchronized ( this . queries ) {
2018-03-01 07:27:30 +00:00
for ( Query query : this . queries ) {
2014-12-05 00:54:16 +00:00
if ( query . getQueryId ( ) . equals ( id ) ) {
return query ;
}
}
return null ;
}
}
2014-12-08 20:59:14 +00:00
@Override
public void onAdvancedStreamFeaturesAvailable ( Account account ) {
if ( account . getXmppConnection ( ) ! = null & & account . getXmppConnection ( ) . getFeatures ( ) . mam ( ) ) {
2014-12-13 11:25:52 +00:00
this . catchup ( account ) ;
2014-12-08 20:59:14 +00:00
}
}
2018-03-01 07:27:30 +00:00
public enum PagingOrder {
NORMAL ,
REVERSE
}
2014-12-05 00:54:16 +00:00
public class Query {
2018-03-25 15:22:39 +00:00
private HashSet < ReceiptRequest > pendingReceiptRequests = new HashSet < > ( ) ;
private HashSet < ReceiptRequest > receiptRequests = new HashSet < > ( ) ;
2016-02-03 09:40:44 +00:00
private int totalCount = 0 ;
2017-02-14 15:50:33 +00:00
private int actualCount = 0 ;
2018-02-23 22:11:54 +00:00
private int actualInThisQuery = 0 ;
2014-12-05 00:54:16 +00:00
private long start ;
private long end ;
private String queryId ;
2014-12-13 14:32:11 +00:00
private String reference = null ;
2014-12-13 11:25:52 +00:00
private Account account ;
2014-12-05 00:54:16 +00:00
private Conversation conversation ;
2014-12-13 14:32:11 +00:00
private PagingOrder pagingOrder = PagingOrder . NORMAL ;
2014-12-15 22:06:29 +00:00
private XmppConnectionService . OnMoreMessagesLoaded callback = null ;
2017-02-14 15:50:33 +00:00
private boolean catchup = true ;
2018-07-07 09:20:39 +00:00
public final Version version ;
2014-12-13 14:32:11 +00:00
2014-12-05 00:54:16 +00:00
2018-03-01 07:27:30 +00:00
Query ( Conversation conversation , MamReference start , long end , boolean catchup ) {
2018-07-07 09:20:39 +00:00
this ( conversation . getAccount ( ) , Version . get ( conversation . getAccount ( ) , conversation ) , catchup ? start : start . timeOnly ( ) , end ) ;
2014-12-05 00:54:16 +00:00
this . conversation = conversation ;
2017-02-14 15:50:33 +00:00
this . pagingOrder = catchup ? PagingOrder . NORMAL : PagingOrder . REVERSE ;
this . catchup = catchup ;
2014-12-13 14:32:11 +00:00
}
2018-03-01 07:27:30 +00:00
Query ( Account account , MamReference start , long end ) {
2018-07-07 09:20:39 +00:00
this ( account , Version . get ( account ) , start , end ) ;
}
Query ( Account account , Version version , MamReference start , long end ) {
2014-12-13 11:25:52 +00:00
this . account = account ;
2017-05-07 19:05:35 +00:00
if ( start . getReference ( ) ! = null ) {
this . reference = start . getReference ( ) ;
} else {
this . start = start . getTimestamp ( ) ;
}
2014-12-05 00:54:16 +00:00
this . end = end ;
this . queryId = new BigInteger ( 50 , mXmppConnectionService . getRNG ( ) ) . toString ( 32 ) ;
2018-07-07 09:20:39 +00:00
this . version = version ;
2014-12-05 00:54:16 +00:00
}
2018-03-01 07:27:30 +00:00
2014-12-13 14:32:11 +00:00
private Query page ( String reference ) {
2018-07-07 09:20:39 +00:00
Query query = new Query ( this . account , this . version , new MamReference ( this . start , reference ) , this . end ) ;
2014-12-13 11:25:52 +00:00
query . conversation = conversation ;
2016-02-03 09:40:44 +00:00
query . totalCount = totalCount ;
2017-02-14 15:50:33 +00:00
query . actualCount = actualCount ;
2018-01-20 07:54:44 +00:00
query . pendingReceiptRequests = pendingReceiptRequests ;
2018-03-25 15:22:39 +00:00
query . receiptRequests = receiptRequests ;
2014-12-15 22:06:29 +00:00
query . callback = callback ;
2017-02-14 15:50:33 +00:00
query . catchup = catchup ;
2014-12-05 00:54:16 +00:00
return query ;
}
2018-03-25 15:22:39 +00:00
public void removePendingReceiptRequest ( ReceiptRequest receiptRequest ) {
if ( ! this . pendingReceiptRequests . remove ( receiptRequest ) ) {
this . receiptRequests . add ( receiptRequest ) ;
}
}
public void addPendingReceiptRequest ( ReceiptRequest receiptRequest ) {
this . pendingReceiptRequests . add ( receiptRequest ) ;
}
2017-02-15 15:42:35 +00:00
public boolean isLegacy ( ) {
2018-07-07 09:20:39 +00:00
return version . legacy ;
2017-02-15 15:42:35 +00:00
}
2017-12-02 17:11:03 +00:00
public boolean safeToExtractTrueCounterpart ( ) {
return muc ( ) & & ! isLegacy ( ) ;
}
2014-12-13 14:32:11 +00:00
public Query next ( String reference ) {
Query query = page ( reference ) ;
query . pagingOrder = PagingOrder . NORMAL ;
return query ;
}
2018-03-01 07:27:30 +00:00
Query prev ( String reference ) {
2014-12-13 14:32:11 +00:00
Query query = page ( reference ) ;
query . pagingOrder = PagingOrder . REVERSE ;
return query ;
}
public String getReference ( ) {
return reference ;
}
public PagingOrder getPagingOrder ( ) {
return this . pagingOrder ;
2014-12-05 00:54:16 +00:00
}
public String getQueryId ( ) {
return queryId ;
}
public Jid getWith ( ) {
2018-03-05 17:30:40 +00:00
return conversation = = null ? null : conversation . getJid ( ) . asBareJid ( ) ;
2015-01-23 23:22:51 +00:00
}
public boolean muc ( ) {
return conversation ! = null & & conversation . getMode ( ) = = Conversation . MODE_MULTI ;
2014-12-05 00:54:16 +00:00
}
public long getStart ( ) {
return start ;
}
2017-02-14 15:50:33 +00:00
public boolean isCatchup ( ) {
return catchup ;
}
2014-12-15 22:06:29 +00:00
public void setCallback ( XmppConnectionService . OnMoreMessagesLoaded callback ) {
this . callback = callback ;
}
2016-02-04 13:39:16 +00:00
public void callback ( boolean done ) {
2014-12-15 22:06:29 +00:00
if ( this . callback ! = null ) {
2018-03-01 07:27:30 +00:00
this . callback . onMoreMessagesLoaded ( actualCount , conversation ) ;
2016-02-04 13:39:16 +00:00
if ( done ) {
2014-12-17 09:50:51 +00:00
this . callback . informUser ( R . string . no_more_history_on_server ) ;
}
2014-12-15 22:06:29 +00:00
}
}
2014-12-05 00:54:16 +00:00
public long getEnd ( ) {
return end ;
}
public Conversation getConversation ( ) {
return conversation ;
}
2014-12-13 11:25:52 +00:00
public Account getAccount ( ) {
return this . account ;
}
2014-12-17 05:59:58 +00:00
public void incrementMessageCount ( ) {
2016-02-03 15:04:21 +00:00
this . totalCount + + ;
2014-12-17 05:59:58 +00:00
}
2017-02-14 15:50:33 +00:00
public void incrementActualMessageCount ( ) {
2018-02-23 22:11:54 +00:00
this . actualInThisQuery + + ;
2017-02-14 15:50:33 +00:00
this . actualCount + + ;
}
2018-03-01 07:27:30 +00:00
int getTotalCount ( ) {
2016-02-03 09:40:44 +00:00
return this . totalCount ;
}
2018-03-01 07:27:30 +00:00
int getActualMessageCount ( ) {
2017-02-14 15:50:33 +00:00
return this . actualCount ;
2015-01-02 23:47:22 +00:00
}
2018-02-23 22:11:54 +00:00
public int getActualInThisQuery ( ) {
return this . actualInThisQuery ;
}
2015-05-15 10:29:45 +00:00
public boolean validFrom ( Jid from ) {
if ( muc ( ) ) {
return getWith ( ) . equals ( from ) ;
} else {
2018-03-05 17:30:40 +00:00
return ( from = = null ) | | account . getJid ( ) . asBareJid ( ) . equals ( from . asBareJid ( ) ) ;
2015-05-15 10:29:45 +00:00
}
}
2020-12-11 10:29:23 +00:00
@NotNull
2014-12-13 11:25:52 +00:00
@Override
public String toString ( ) {
StringBuilder builder = new StringBuilder ( ) ;
2015-01-23 23:22:51 +00:00
if ( this . muc ( ) ) {
2016-02-04 13:39:16 +00:00
builder . append ( " to= " ) ;
builder . append ( this . getWith ( ) . toString ( ) ) ;
2014-12-13 11:25:52 +00:00
} else {
2015-01-23 23:22:51 +00:00
builder . append ( " with= " ) ;
if ( this . getWith ( ) = = null ) {
builder . append ( " * " ) ;
} else {
builder . append ( getWith ( ) . toString ( ) ) ;
}
2014-12-13 11:25:52 +00:00
}
2017-05-07 19:05:35 +00:00
if ( this . start ! = 0 ) {
builder . append ( " , start= " ) ;
builder . append ( AbstractGenerator . getTimestamp ( this . start ) ) ;
}
2018-05-11 09:16:29 +00:00
if ( this . end ! = 0 ) {
builder . append ( " , end= " ) ;
builder . append ( AbstractGenerator . getTimestamp ( this . end ) ) ;
}
2018-03-01 07:27:30 +00:00
builder . append ( " , order= " ) . append ( pagingOrder . toString ( ) ) ;
if ( this . reference ! = null ) {
2014-12-13 14:32:11 +00:00
if ( this . pagingOrder = = PagingOrder . NORMAL ) {
builder . append ( " , after= " ) ;
} else {
builder . append ( " , before= " ) ;
}
builder . append ( this . reference ) ;
2014-12-13 11:25:52 +00:00
}
2018-03-01 07:27:30 +00:00
builder . append ( " , catchup= " ) . append ( Boolean . toString ( catchup ) ) ;
2018-07-07 09:20:39 +00:00
builder . append ( " , ns= " ) . append ( version . namespace ) ;
2014-12-13 11:25:52 +00:00
return builder . toString ( ) ;
}
2014-12-15 22:06:29 +00:00
2018-03-01 07:27:30 +00:00
boolean hasCallback ( ) {
2014-12-15 22:06:29 +00:00
return this . callback ! = null ;
}
2014-12-05 00:54:16 +00:00
}
}