2014-02-28 17:46:01 +00:00
package eu.siacs.conversations.services ;
2014-01-24 01:04:05 +00:00
2014-11-10 00:24:35 +00:00
import android.annotation.SuppressLint ;
2016-05-28 12:44:22 +00:00
import android.annotation.TargetApi ;
2014-11-10 00:24:35 +00:00
import android.app.AlarmManager ;
import android.app.PendingIntent ;
import android.app.Service ;
import android.content.Context ;
import android.content.Intent ;
2015-10-07 22:35:04 +00:00
import android.content.IntentFilter ;
2014-11-10 00:24:35 +00:00
import android.content.SharedPreferences ;
import android.database.ContentObserver ;
import android.graphics.Bitmap ;
2015-10-07 22:35:04 +00:00
import android.media.AudioManager ;
2014-11-10 00:24:35 +00:00
import android.net.ConnectivityManager ;
import android.net.NetworkInfo ;
import android.net.Uri ;
import android.os.Binder ;
2015-10-07 22:35:04 +00:00
import android.os.Build ;
2014-11-10 00:24:35 +00:00
import android.os.Bundle ;
2016-07-23 14:12:45 +00:00
import android.os.Environment ;
2014-11-10 00:24:35 +00:00
import android.os.IBinder ;
2017-01-24 19:17:36 +00:00
import android.os.ParcelFileDescriptor ;
2014-11-10 00:24:35 +00:00
import android.os.PowerManager ;
import android.os.PowerManager.WakeLock ;
import android.os.SystemClock ;
import android.preference.PreferenceManager ;
import android.provider.ContactsContract ;
2015-10-09 11:37:08 +00:00
import android.security.KeyChain ;
2016-08-25 15:30:44 +00:00
import android.support.v4.app.RemoteInput ;
2015-10-16 07:58:31 +00:00
import android.util.DisplayMetrics ;
2014-11-10 00:24:35 +00:00
import android.util.Log ;
import android.util.LruCache ;
2015-10-11 13:48:58 +00:00
import android.util.Pair ;
2014-11-10 00:24:35 +00:00
import net.java.otr4j.OtrException ;
import net.java.otr4j.session.Session ;
import net.java.otr4j.session.SessionID ;
2015-07-05 09:59:38 +00:00
import net.java.otr4j.session.SessionImpl ;
2014-11-10 00:24:35 +00:00
import net.java.otr4j.session.SessionStatus ;
2017-01-24 19:17:36 +00:00
import net.ypresto.androidtranscoder.MediaTranscoder ;
import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets ;
2014-11-10 00:24:35 +00:00
2015-10-29 13:03:41 +00:00
import org.openintents.openpgp.IOpenPgpService2 ;
2014-11-10 00:24:35 +00:00
import org.openintents.openpgp.util.OpenPgpApi ;
import org.openintents.openpgp.util.OpenPgpServiceConnection ;
2017-01-24 19:17:36 +00:00
import java.io.FileDescriptor ;
import java.io.FileNotFoundException ;
2014-11-20 17:20:42 +00:00
import java.math.BigInteger ;
2017-02-07 18:32:12 +00:00
import java.net.URL ;
2014-06-20 15:30:19 +00:00
import java.security.SecureRandom ;
2015-10-11 13:48:58 +00:00
import java.security.cert.CertificateException ;
2015-10-09 11:37:08 +00:00
import java.security.cert.X509Certificate ;
2014-07-10 17:42:37 +00:00
import java.util.ArrayList ;
2015-04-21 20:17:58 +00:00
import java.util.Arrays ;
2014-12-21 20:43:58 +00:00
import java.util.Collection ;
2014-03-19 15:16:40 +00:00
import java.util.Collections ;
2015-12-27 16:29:32 +00:00
import java.util.HashMap ;
2016-05-02 12:31:30 +00:00
import java.util.HashSet ;
2014-02-01 14:07:20 +00:00
import java.util.Hashtable ;
2015-05-05 08:29:41 +00:00
import java.util.Iterator ;
2014-01-25 18:33:12 +00:00
import java.util.List ;
2017-01-22 17:58:49 +00:00
import java.util.ListIterator ;
2014-03-21 21:14:52 +00:00
import java.util.Locale ;
2014-12-21 20:43:58 +00:00
import java.util.Map ;
2014-07-12 10:41:37 +00:00
import java.util.concurrent.CopyOnWriteArrayList ;
2017-01-23 16:14:30 +00:00
import java.util.concurrent.atomic.AtomicLong ;
2014-02-13 22:40:08 +00:00
2014-07-22 15:27:44 +00:00
import de.duenndns.ssl.MemorizingTrustManager ;
2014-08-31 14:28:21 +00:00
import eu.siacs.conversations.Config ;
2014-08-04 23:36:17 +00:00
import eu.siacs.conversations.R ;
2016-06-13 11:32:14 +00:00
import eu.siacs.conversations.crypto.PgpDecryptionService ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.crypto.PgpEngine ;
2015-10-16 21:48:42 +00:00
import eu.siacs.conversations.crypto.axolotl.AxolotlService ;
2016-11-17 19:09:42 +00:00
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus ;
2015-07-20 21:13:28 +00:00
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.entities.Account ;
2014-12-21 20:43:58 +00:00
import eu.siacs.conversations.entities.Blockable ;
2014-07-14 09:47:42 +00:00
import eu.siacs.conversations.entities.Bookmark ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.entities.Contact ;
import eu.siacs.conversations.entities.Conversation ;
2016-07-23 14:12:45 +00:00
import eu.siacs.conversations.entities.DownloadableFile ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.entities.Message ;
2014-03-03 04:01:02 +00:00
import eu.siacs.conversations.entities.MucOptions ;
import eu.siacs.conversations.entities.MucOptions.OnRenameListener ;
2016-01-17 21:28:38 +00:00
import eu.siacs.conversations.entities.Presence ;
2016-04-22 19:25:06 +00:00
import eu.siacs.conversations.entities.PresenceTemplate ;
2016-02-03 09:40:02 +00:00
import eu.siacs.conversations.entities.Roster ;
import eu.siacs.conversations.entities.ServiceDiscoveryResult ;
2015-07-20 12:26:29 +00:00
import eu.siacs.conversations.entities.Transferable ;
import eu.siacs.conversations.entities.TransferablePlaceholder ;
2016-06-04 14:16:14 +00:00
import eu.siacs.conversations.generator.AbstractGenerator ;
2014-07-23 12:30:27 +00:00
import eu.siacs.conversations.generator.IqGenerator ;
2014-06-22 15:24:47 +00:00
import eu.siacs.conversations.generator.MessageGenerator ;
2014-07-12 01:44:23 +00:00
import eu.siacs.conversations.generator.PresenceGenerator ;
2014-10-13 23:06:45 +00:00
import eu.siacs.conversations.http.HttpConnectionManager ;
2017-02-07 19:07:57 +00:00
import eu.siacs.conversations.http.AesGcmURLStreamHandlerFactory ;
2016-05-17 12:25:58 +00:00
import eu.siacs.conversations.parser.AbstractParser ;
2014-07-12 10:28:28 +00:00
import eu.siacs.conversations.parser.IqParser ;
2014-05-14 10:56:34 +00:00
import eu.siacs.conversations.parser.MessageParser ;
2014-06-06 16:26:40 +00:00
import eu.siacs.conversations.parser.PresenceParser ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.persistance.DatabaseBackend ;
2014-04-05 19:06:10 +00:00
import eu.siacs.conversations.persistance.FileBackend ;
2016-11-21 10:03:38 +00:00
import eu.siacs.conversations.ui.SettingsActivity ;
2014-05-12 12:59:46 +00:00
import eu.siacs.conversations.ui.UiCallback ;
2017-01-24 19:17:36 +00:00
import eu.siacs.conversations.ui.UiInformableCallback ;
2016-07-23 14:12:45 +00:00
import eu.siacs.conversations.utils.ConversationsFileObserver ;
2014-06-20 15:30:19 +00:00
import eu.siacs.conversations.utils.CryptoHelper ;
2014-03-09 12:21:28 +00:00
import eu.siacs.conversations.utils.ExceptionHelper ;
2016-12-30 20:48:39 +00:00
import eu.siacs.conversations.utils.MimeUtils ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener ;
2014-06-20 15:30:19 +00:00
import eu.siacs.conversations.utils.PRNGFixes ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.utils.PhoneHelper ;
2016-05-31 15:20:21 +00:00
import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor ;
2015-06-05 06:46:06 +00:00
import eu.siacs.conversations.utils.SerialSingleThreadExecutor ;
2014-12-21 01:13:13 +00:00
import eu.siacs.conversations.utils.Xmlns ;
2016-11-17 19:09:42 +00:00
import eu.siacs.conversations.utils.XmppUri ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.xml.Element ;
2014-03-14 19:43:54 +00:00
import eu.siacs.conversations.xmpp.OnBindListener ;
2014-05-23 08:54:40 +00:00
import eu.siacs.conversations.xmpp.OnContactStatusChanged ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.xmpp.OnIqPacketReceived ;
2015-08-25 10:40:22 +00:00
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated ;
2014-08-26 14:52:42 +00:00
import eu.siacs.conversations.xmpp.OnMessageAcknowledged ;
2014-12-21 20:43:58 +00:00
import eu.siacs.conversations.xmpp.OnMessagePacketReceived ;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.xmpp.OnStatusChanged ;
2014-12-21 20:43:58 +00:00
import eu.siacs.conversations.xmpp.OnUpdateBlocklist ;
2017-02-19 12:05:40 +00:00
import eu.siacs.conversations.xmpp.Patches ;
2014-02-28 17:46:01 +00:00
import eu.siacs.conversations.xmpp.XmppConnection ;
2015-02-21 10:06:52 +00:00
import eu.siacs.conversations.xmpp.chatstate.ChatState ;
2014-11-20 17:33:04 +00:00
import eu.siacs.conversations.xmpp.forms.Data ;
import eu.siacs.conversations.xmpp.forms.Field ;
2014-11-05 20:55:47 +00:00
import eu.siacs.conversations.xmpp.jid.InvalidJidException ;
import eu.siacs.conversations.xmpp.jid.Jid ;
2014-04-08 21:15:55 +00:00
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager ;
import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived ;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket ;
2014-08-03 18:28:13 +00:00
import eu.siacs.conversations.xmpp.pep.Avatar ;
2014-03-10 18:22:13 +00:00
import eu.siacs.conversations.xmpp.stanzas.IqPacket ;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket ;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket ;
2015-05-20 10:47:04 +00:00
import me.leolin.shortcutbadger.ShortcutBadger ;
2014-01-24 01:04:05 +00:00
2016-05-31 15:20:21 +00:00
public class XmppConnectionService extends Service {
2014-02-03 17:38:47 +00:00
2017-02-07 18:32:12 +00:00
static {
2017-02-07 19:07:57 +00:00
URL . setURLStreamHandlerFactory ( new AesGcmURLStreamHandlerFactory ( ) ) ;
2017-02-07 18:32:12 +00:00
}
2016-08-25 15:30:44 +00:00
public static final String ACTION_REPLY_TO_CONVERSATION = " reply_to_conversations " ;
2014-12-21 20:43:58 +00:00
public static final String ACTION_CLEAR_NOTIFICATION = " clear_notification " ;
public static final String ACTION_DISABLE_FOREGROUND = " disable_foreground " ;
2016-10-04 09:16:59 +00:00
public static final String ACTION_DISMISS_ERROR_NOTIFICATIONS = " dismiss_error " ;
2015-02-10 16:13:34 +00:00
public static final String ACTION_TRY_AGAIN = " try_again " ;
2016-05-28 12:44:22 +00:00
public static final String ACTION_IDLE_PING = " idle_ping " ;
2015-10-16 07:58:31 +00:00
private static final String ACTION_MERGE_PHONE_CONTACTS = " merge_phone_contacts " ;
2016-02-12 10:39:27 +00:00
public static final String ACTION_GCM_TOKEN_REFRESH = " gcm_token_refresh " ;
public static final String ACTION_GCM_MESSAGE_RECEIVED = " gcm_message_received " ;
2015-10-16 07:58:31 +00:00
private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor ( ) ;
private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor ( ) ;
2016-05-31 15:20:21 +00:00
private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor ( true ) ;
2015-10-16 07:58:31 +00:00
private final IBinder mBinder = new XmppConnectionBinder ( ) ;
private final List < Conversation > conversations = new CopyOnWriteArrayList < > ( ) ;
private final IqGenerator mIqGenerator = new IqGenerator ( this ) ;
private final List < String > mInProgressAvatarFetches = new ArrayList < > ( ) ;
2016-10-20 16:02:11 +00:00
private final HashSet < Jid > mLowPingTimeoutMode = new HashSet < > ( ) ;
2016-06-04 14:16:14 +00:00
private long mLastActivity = 0 ;
2015-10-16 07:58:31 +00:00
public DatabaseBackend databaseBackend ;
2014-11-10 00:24:35 +00:00
private ContentObserver contactObserver = new ContentObserver ( null ) {
@Override
public void onChange ( boolean selfChange ) {
super . onChange ( selfChange ) ;
Intent intent = new Intent ( getApplicationContext ( ) ,
XmppConnectionService . class ) ;
intent . setAction ( ACTION_MERGE_PHONE_CONTACTS ) ;
startService ( intent ) ;
}
} ;
2015-10-16 07:58:31 +00:00
private FileBackend fileBackend = new FileBackend ( this ) ;
private MemorizingTrustManager mMemorizingTrustManager ;
private NotificationService mNotificationService = new NotificationService (
this ) ;
private OnMessagePacketReceived mMessageParser = new MessageParser ( this ) ;
private OnPresencePacketReceived mPresenceParser = new PresenceParser ( this ) ;
private IqParser mIqParser = new IqParser ( this ) ;
private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived ( ) {
2015-01-19 10:17:27 +00:00
@Override
2015-10-16 07:58:31 +00:00
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) ! = IqPacket . TYPE . RESULT ) {
Element error = packet . findChild ( " error " ) ;
String text = error ! = null ? error . findChildContent ( " text " ) : null ;
if ( text ! = null ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : received iq error - " + text ) ;
2015-01-19 10:17:27 +00:00
}
}
}
} ;
2015-10-16 07:58:31 +00:00
private MessageGenerator mMessageGenerator = new MessageGenerator ( this ) ;
private PresenceGenerator mPresenceGenerator = new PresenceGenerator ( this ) ;
private List < Account > accounts ;
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager (
this ) ;
2014-11-10 00:24:35 +00:00
public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged ( ) {
2014-08-26 14:52:42 +00:00
2014-11-10 00:24:35 +00:00
@Override
public void onContactStatusChanged ( Contact contact , boolean online ) {
Conversation conversation = find ( getConversations ( ) , contact ) ;
if ( conversation ! = null ) {
2015-04-21 20:17:58 +00:00
if ( online ) {
2014-11-10 00:24:35 +00:00
conversation . endOtrIfNeeded ( ) ;
2015-04-21 20:17:58 +00:00
if ( contact . getPresences ( ) . size ( ) = = 1 ) {
sendUnsentMessages ( conversation ) ;
}
2014-11-10 00:24:35 +00:00
} else {
2016-08-13 10:43:06 +00:00
//check if the resource we are haveing a conversation with is still online
if ( conversation . hasValidOtrSession ( ) ) {
String otrResource = conversation . getOtrSession ( ) . getSessionID ( ) . getUserID ( ) ;
if ( ! ( Arrays . asList ( contact . getPresences ( ) . toResourceArray ( ) ) . contains ( otrResource ) ) ) {
conversation . endOtrIfNeeded ( ) ;
2015-04-21 20:17:58 +00:00
}
}
2014-11-10 00:24:35 +00:00
}
}
}
} ;
2015-10-16 07:58:31 +00:00
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager (
2014-10-21 12:57:16 +00:00
this ) ;
2015-10-16 07:58:31 +00:00
private AvatarService mAvatarService = new AvatarService ( this ) ;
private MessageArchiveService mMessageArchiveService = new MessageArchiveService ( this ) ;
2016-02-12 10:39:27 +00:00
private PushManagementService mPushManagementService = new PushManagementService ( this ) ;
2015-10-16 07:58:31 +00:00
private OnConversationUpdate mOnConversationUpdate = null ;
2016-07-23 14:12:45 +00:00
private final ConversationsFileObserver fileObserver = new ConversationsFileObserver (
Environment . getExternalStorageDirectory ( ) . getAbsolutePath ( )
) {
2015-05-26 09:31:33 +00:00
@Override
2015-10-16 07:58:31 +00:00
public void onEvent ( int event , String path ) {
2016-07-23 14:12:45 +00:00
markFileDeleted ( path ) ;
2015-10-16 07:58:31 +00:00
}
} ;
private final OnJinglePacketReceived jingleListener = new OnJinglePacketReceived ( ) {
@Override
public void onJinglePacketReceived ( Account account , JinglePacket packet ) {
mJingleConnectionManager . deliverPacket ( account , packet ) ;
}
} ;
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged ( ) {
@Override
public void onMessageAcknowledged ( Account account , String uuid ) {
for ( final Conversation conversation : getConversations ( ) ) {
if ( conversation . getAccount ( ) = = account ) {
Message message = conversation . findUnsentMessageWithUuid ( uuid ) ;
if ( message ! = null ) {
markMessage ( message , Message . STATUS_SEND ) ;
}
2015-05-26 09:31:33 +00:00
}
}
}
} ;
2015-05-26 10:00:38 +00:00
private int convChangedListenerCount = 0 ;
2015-07-10 11:28:50 +00:00
private OnShowErrorToast mOnShowErrorToast = null ;
private int showErrorToastListenerCount = 0 ;
2015-07-03 20:08:23 +00:00
private int unreadCount = - 1 ;
2014-07-12 11:42:17 +00:00
private OnAccountUpdate mOnAccountUpdate = null ;
2015-10-11 11:11:50 +00:00
private OnCaptchaRequested mOnCaptchaRequested = null ;
2015-10-16 07:58:31 +00:00
private int accountChangedListenerCount = 0 ;
private int captchaRequestedListenerCount = 0 ;
private OnRosterUpdate mOnRosterUpdate = null ;
private OnUpdateBlocklist mOnUpdateBlocklist = null ;
private int updateBlocklistListenerCount = 0 ;
private int rosterChangedListenerCount = 0 ;
private OnMucRosterUpdate mOnMucRosterUpdate = null ;
private int mucRosterChangedListenerCount = 0 ;
private OnKeyStatusUpdated mOnKeyStatusUpdated = null ;
private int keyStatusUpdatedListenerCount = 0 ;
2017-01-23 16:14:30 +00:00
private AtomicLong mLastExpiryRun = new AtomicLong ( 0 ) ;
2015-10-16 07:58:31 +00:00
private SecureRandom mRandom ;
2016-02-03 09:40:02 +00:00
private LruCache < Pair < String , String > , ServiceDiscoveryResult > discoCache = new LruCache < > ( 20 ) ;
2015-10-16 07:58:31 +00:00
private final OnBindListener mOnBindListener = new OnBindListener ( ) {
@Override
public void onBind ( final Account account ) {
2016-03-03 12:33:02 +00:00
synchronized ( mInProgressAvatarFetches ) {
for ( Iterator < String > iterator = mInProgressAvatarFetches . iterator ( ) ; iterator . hasNext ( ) ; ) {
final String KEY = iterator . next ( ) ;
if ( KEY . startsWith ( account . getJid ( ) . toBareJid ( ) + " _ " ) ) {
iterator . remove ( ) ;
}
}
}
2015-10-16 07:58:31 +00:00
account . getRoster ( ) . clearPresences ( ) ;
2015-12-12 15:01:33 +00:00
mJingleConnectionManager . cancelInTransmission ( ) ;
2015-10-16 07:58:31 +00:00
fetchRosterFromServer ( account ) ;
fetchBookmarks ( account ) ;
sendPresence ( account ) ;
2016-02-12 23:03:57 +00:00
if ( mPushManagementService . available ( account ) ) {
2016-02-12 22:37:42 +00:00
mPushManagementService . registerPushTokenOnServer ( account ) ;
}
2015-10-16 07:58:31 +00:00
connectMultiModeConversations ( account ) ;
syncDirtyContacts ( account ) ;
}
} ;
2014-02-04 14:09:50 +00:00
private OnStatusChanged statusListener = new OnStatusChanged ( ) {
2014-02-05 21:33:39 +00:00
2014-02-04 14:09:50 +00:00
@Override
2016-02-12 10:39:27 +00:00
public void onStatusChanged ( final Account account ) {
2014-08-28 20:23:18 +00:00
XmppConnection connection = account . getXmppConnection ( ) ;
2014-07-12 11:42:17 +00:00
if ( mOnAccountUpdate ! = null ) {
2014-08-26 14:52:42 +00:00
mOnAccountUpdate . onAccountUpdate ( ) ;
2014-02-04 14:09:50 +00:00
}
2014-11-15 16:09:02 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2016-11-19 12:34:27 +00:00
synchronized ( mLowPingTimeoutMode ) {
2016-11-02 08:36:14 +00:00
if ( mLowPingTimeoutMode . remove ( account . getJid ( ) . toBareJid ( ) ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : leaving low ping timeout mode " ) ;
}
}
2016-10-04 09:16:59 +00:00
if ( account . setShowErrorNotification ( true ) ) {
databaseBackend . updateAccount ( account ) ;
}
2016-02-26 08:46:25 +00:00
mMessageArchiveService . executePendingQueries ( account ) ;
2015-08-13 16:25:10 +00:00
if ( connection ! = null & & connection . getFeatures ( ) . csi ( ) ) {
if ( checkListeners ( ) ) {
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " sending csi//inactive " ) ;
2015-08-13 16:25:10 +00:00
connection . sendInactive ( ) ;
} else {
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " sending csi//active " ) ;
2015-08-13 16:25:10 +00:00
connection . sendActive ( ) ;
}
2014-07-18 19:57:10 +00:00
}
2014-03-21 18:58:47 +00:00
List < Conversation > conversations = getConversations ( ) ;
2014-11-10 00:24:35 +00:00
for ( Conversation conversation : conversations ) {
2016-02-17 15:50:48 +00:00
if ( conversation . getAccount ( ) = = account
& & ! account . pendingConferenceJoins . contains ( conversation ) ) {
2016-02-16 08:15:41 +00:00
if ( ! conversation . startOtrIfNeeded ( ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : couldn't start OTR with " + conversation . getContact ( ) . getJid ( ) + " when needed " ) ;
}
2014-12-14 17:10:46 +00:00
sendUnsentMessages ( conversation ) ;
2014-11-10 00:24:35 +00:00
}
}
2015-10-01 14:01:19 +00:00
for ( Conversation conversation : account . pendingConferenceLeaves ) {
leaveMuc ( conversation ) ;
}
account . pendingConferenceLeaves . clear ( ) ;
for ( Conversation conversation : account . pendingConferenceJoins ) {
joinMuc ( conversation ) ;
}
account . pendingConferenceJoins . clear ( ) ;
2016-11-02 10:04:33 +00:00
scheduleWakeUpCall ( Config . PING_MAX_INTERVAL , account . getUuid ( ) . hashCode ( ) ) ;
2016-11-18 12:14:26 +00:00
} else {
2016-11-19 12:34:27 +00:00
if ( account . getStatus ( ) = = Account . State . OFFLINE | | account . getStatus ( ) = = Account . State . DISABLED ) {
resetSendingToWaiting ( account ) ;
if ( ! account . isOptionSet ( Account . OPTION_DISABLED ) ) {
synchronized ( mLowPingTimeoutMode ) {
2016-11-18 12:14:26 +00:00
if ( mLowPingTimeoutMode . contains ( account . getJid ( ) . toBareJid ( ) ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : went into offline state during low ping mode. reconnecting now " ) ;
reconnectAccount ( account , true , false ) ;
} else {
2016-11-18 12:30:12 +00:00
int timeToReconnect = mRandom . nextInt ( 10 ) + 2 ;
2016-11-18 12:14:26 +00:00
scheduleWakeUpCall ( timeToReconnect , account . getUuid ( ) . hashCode ( ) ) ;
}
}
2016-11-19 12:34:27 +00:00
}
} else if ( account . getStatus ( ) = = Account . State . REGISTRATION_SUCCESSFUL ) {
databaseBackend . updateAccount ( account ) ;
reconnectAccount ( account , true , false ) ;
} else if ( ( account . getStatus ( ) ! = Account . State . CONNECTING )
& & ( account . getStatus ( ) ! = Account . State . NO_INTERNET ) ) {
resetSendingToWaiting ( account ) ;
if ( connection ! = null ) {
int next = connection . getTimeToNextAttempt ( ) ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( )
+ " : error connecting account. try again in "
+ next + " s for the "
+ ( connection . getAttempt ( ) + 1 ) + " time " ) ;
scheduleWakeUpCall ( next , account . getUuid ( ) . hashCode ( ) ) ;
2016-10-23 07:03:17 +00:00
}
2014-03-06 02:30:03 +00:00
}
2015-10-16 07:58:31 +00:00
}
2014-11-18 14:26:28 +00:00
getNotificationService ( ) . updateErrorNotification ( ) ;
2014-02-04 14:09:50 +00:00
}
} ;
2014-02-27 23:22:56 +00:00
private OpenPgpServiceConnection pgpServiceConnection ;
private PgpEngine mPgpEngine = null ;
2014-03-23 13:15:14 +00:00
private WakeLock wakeLock ;
2014-04-03 08:41:21 +00:00
private PowerManager pm ;
2014-10-21 12:57:16 +00:00
private LruCache < String , Bitmap > mBitmapCache ;
2015-10-07 22:35:04 +00:00
private EventReceiver mEventReceiver = new EventReceiver ( ) ;
2014-02-27 23:22:56 +00:00
2015-02-17 10:51:11 +00:00
private boolean mRestoredFromDatabase = false ;
2015-10-16 07:58:31 +00:00
private static String generateFetchKey ( Account account , final Avatar avatar ) {
return account . getJid ( ) . toBareJid ( ) + " _ " + avatar . owner + " _ " + avatar . sha1sum ;
}
2015-02-12 17:53:00 +00:00
public boolean areMessagesInitialized ( ) {
2015-02-17 10:51:11 +00:00
return this . mRestoredFromDatabase ;
2015-02-12 17:53:00 +00:00
}
2014-02-27 23:22:56 +00:00
public PgpEngine getPgpEngine ( ) {
2016-02-24 13:47:49 +00:00
if ( ! Config . supportOpenPgp ( ) ) {
return null ;
} else if ( pgpServiceConnection ! = null & & pgpServiceConnection . isBound ( ) ) {
2014-02-27 23:22:56 +00:00
if ( this . mPgpEngine = = null ) {
this . mPgpEngine = new PgpEngine ( new OpenPgpApi (
2015-10-16 07:58:31 +00:00
getApplicationContext ( ) ,
pgpServiceConnection . getService ( ) ) , this ) ;
2014-02-27 23:22:56 +00:00
}
return mPgpEngine ;
} else {
return null ;
}
}
2016-06-13 11:32:14 +00:00
public OpenPgpApi getOpenPgpApi ( ) {
if ( ! Config . supportOpenPgp ( ) ) {
return null ;
} else if ( pgpServiceConnection ! = null & & pgpServiceConnection . isBound ( ) ) {
return new OpenPgpApi ( this , pgpServiceConnection . getService ( ) ) ;
} else {
return null ;
}
}
2014-04-05 19:06:10 +00:00
public FileBackend getFileBackend ( ) {
return this . fileBackend ;
}
2014-05-01 20:33:49 +00:00
2014-10-20 19:08:33 +00:00
public AvatarService getAvatarService ( ) {
return this . mAvatarService ;
}
2015-03-07 13:15:38 +00:00
public void attachLocationToConversation ( final Conversation conversation ,
final Uri uri ,
final UiCallback < Message > callback ) {
2015-07-30 22:52:46 +00:00
int encryption = conversation . getNextEncryption ( ) ;
2015-03-07 13:15:38 +00:00
if ( encryption = = Message . ENCRYPTION_PGP ) {
encryption = Message . ENCRYPTION_DECRYPTED ;
}
2015-10-16 07:58:31 +00:00
Message message = new Message ( conversation , uri . toString ( ) , encryption ) ;
2015-03-07 13:15:38 +00:00
if ( conversation . getNextCounterpart ( ) ! = null ) {
message . setCounterpart ( conversation . getNextCounterpart ( ) ) ;
}
if ( encryption = = Message . ENCRYPTION_DECRYPTED ) {
2015-05-05 08:29:41 +00:00
getPgpEngine ( ) . encrypt ( message , callback ) ;
2015-03-07 13:15:38 +00:00
} else {
callback . success ( message ) ;
}
}
2014-12-23 22:19:00 +00:00
public void attachFileToConversation ( final Conversation conversation ,
2015-10-16 07:58:31 +00:00
final Uri uri ,
final UiCallback < Message > callback ) {
2016-04-13 09:14:36 +00:00
if ( FileBackend . weOwnFile ( this , uri ) ) {
2016-04-07 18:29:40 +00:00
Log . d ( Config . LOGTAG , " trying to attach file that belonged to us " ) ;
callback . error ( R . string . security_error_invalid_file_access , null ) ;
return ;
}
2014-11-13 23:28:39 +00:00
final Message message ;
2015-07-30 22:52:46 +00:00
if ( conversation . getNextEncryption ( ) = = Message . ENCRYPTION_PGP ) {
message = new Message ( conversation , " " , Message . ENCRYPTION_DECRYPTED ) ;
2014-11-13 23:28:39 +00:00
} else {
2015-07-30 22:52:46 +00:00
message = new Message ( conversation , " " , conversation . getNextEncryption ( ) ) ;
2014-11-13 23:28:39 +00:00
}
message . setCounterpart ( conversation . getNextCounterpart ( ) ) ;
message . setType ( Message . TYPE_FILE ) ;
2016-07-12 22:20:38 +00:00
mFileAddingExecutor . execute ( new Runnable ( ) {
2017-01-24 19:17:36 +00:00
private void processAsFile ( ) {
final String path = getFileBackend ( ) . getOriginalPath ( uri ) ;
2016-07-12 22:20:38 +00:00
if ( path ! = null ) {
message . setRelativeFilePath ( path ) ;
getFileBackend ( ) . updateFileParams ( message ) ;
if ( message . getEncryption ( ) = = Message . ENCRYPTION_DECRYPTED ) {
getPgpEngine ( ) . encrypt ( message , callback ) ;
} else {
callback . success ( message ) ;
}
} else {
2014-11-13 23:28:39 +00:00
try {
getFileBackend ( ) . copyFileToPrivateStorage ( message , uri ) ;
getFileBackend ( ) . updateFileParams ( message ) ;
2014-11-14 02:27:18 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_DECRYPTED ) {
2016-06-19 09:08:17 +00:00
final PgpEngine pgpEngine = getPgpEngine ( ) ;
if ( pgpEngine ! = null ) {
pgpEngine . encrypt ( message , callback ) ;
2016-07-12 22:20:38 +00:00
} else if ( callback ! = null ) {
2016-06-19 09:08:17 +00:00
callback . error ( R . string . unable_to_connect_to_keychain , null ) ;
}
2014-11-14 02:27:18 +00:00
} else {
callback . success ( message ) ;
}
2014-11-13 23:28:39 +00:00
} catch ( FileBackend . FileCopyException e ) {
2015-06-28 09:19:07 +00:00
callback . error ( e . getResId ( ) , message ) ;
2014-11-13 23:28:39 +00:00
}
}
2016-07-12 22:20:38 +00:00
}
2017-01-24 19:17:36 +00:00
private void processAsVideo ( ) throws FileNotFoundException {
Log . d ( Config . LOGTAG , " processing file as video " ) ;
message . setRelativeFilePath ( message . getUuid ( ) + " .mp4 " ) ;
final DownloadableFile file = getFileBackend ( ) . getFile ( message ) ;
file . getParentFile ( ) . mkdirs ( ) ;
ParcelFileDescriptor parcelFileDescriptor = getContentResolver ( ) . openFileDescriptor ( uri , " r " ) ;
FileDescriptor fileDescriptor = parcelFileDescriptor . getFileDescriptor ( ) ;
final ArrayList < Integer > progressTracker = new ArrayList < > ( ) ;
final UiInformableCallback < Message > informableCallback ;
if ( callback instanceof UiInformableCallback ) {
informableCallback = ( UiInformableCallback < Message > ) callback ;
} else {
informableCallback = null ;
}
MediaTranscoder . Listener listener = new MediaTranscoder . Listener ( ) {
@Override
public void onTranscodeProgress ( double progress ) {
int p = ( ( int ) Math . round ( progress * 100 ) / 20 ) * 20 ;
if ( ! progressTracker . contains ( p ) & & p ! = 100 & & p ! = 0 ) {
progressTracker . add ( p ) ;
if ( informableCallback ! = null ) {
informableCallback . inform ( getString ( R . string . transcoding_video_progress , p ) ) ;
}
}
}
@Override
public void onTranscodeCompleted ( ) {
if ( message . getEncryption ( ) = = Message . ENCRYPTION_DECRYPTED ) {
getPgpEngine ( ) . encrypt ( message , callback ) ;
} else {
callback . success ( message ) ;
}
}
@Override
public void onTranscodeCanceled ( ) {
processAsFile ( ) ;
}
@Override
public void onTranscodeFailed ( Exception e ) {
Log . d ( Config . LOGTAG , " video transcoding failed " + e . getMessage ( ) ) ;
processAsFile ( ) ;
}
} ;
MediaTranscoder . getInstance ( ) . transcodeVideo ( fileDescriptor , file . getAbsolutePath ( ) ,
MediaFormatStrategyPresets . createAndroid720pStrategy ( ) , listener ) ;
}
@Override
public void run ( ) {
final String mimeType = MimeUtils . guessMimeTypeFromUri ( XmppConnectionService . this , uri ) ;
if ( mimeType ! = null & & mimeType . startsWith ( " video/ " ) & & Build . VERSION . SDK_INT > = Build . VERSION_CODES . JELLY_BEAN_MR2 ) {
try {
processAsVideo ( ) ;
} catch ( Throwable e ) {
processAsFile ( ) ;
}
} else {
processAsFile ( ) ;
}
}
2016-07-12 22:20:38 +00:00
} ) ;
2014-11-13 20:04:05 +00:00
}
2015-08-11 14:50:00 +00:00
public void attachImageToConversation ( final Conversation conversation , final Uri uri , final UiCallback < Message > callback ) {
2016-04-13 09:14:36 +00:00
if ( FileBackend . weOwnFile ( this , uri ) ) {
2016-04-07 18:29:40 +00:00
Log . d ( Config . LOGTAG , " trying to attach file that belonged to us " ) ;
callback . error ( R . string . security_error_invalid_file_access , null ) ;
return ;
}
2016-12-30 20:48:39 +00:00
final String mimeType = MimeUtils . guessMimeTypeFromUri ( this , uri ) ;
2016-01-09 15:17:39 +00:00
final String compressPictures = getCompressPicturesPreference ( ) ;
2016-12-30 20:48:39 +00:00
2016-01-09 15:17:39 +00:00
if ( " never " . equals ( compressPictures )
2016-12-30 20:48:39 +00:00
| | ( " auto " . equals ( compressPictures ) & & getFileBackend ( ) . useImageAsIs ( uri ) )
| | ( mimeType ! = null & & mimeType . endsWith ( " /gif " ) ) ) {
2016-01-09 15:17:39 +00:00
Log . d ( Config . LOGTAG , conversation . getAccount ( ) . getJid ( ) . toBareJid ( ) + " : not compressing picture. sending as file " ) ;
2015-08-11 14:50:00 +00:00
attachFileToConversation ( conversation , uri , callback ) ;
return ;
}
2014-05-12 12:59:46 +00:00
final Message message ;
2015-07-30 22:52:46 +00:00
if ( conversation . getNextEncryption ( ) = = Message . ENCRYPTION_PGP ) {
message = new Message ( conversation , " " , Message . ENCRYPTION_DECRYPTED ) ;
2014-05-12 12:59:46 +00:00
} else {
2015-10-16 07:58:31 +00:00
message = new Message ( conversation , " " , conversation . getNextEncryption ( ) ) ;
2014-05-12 12:59:46 +00:00
}
2014-11-09 15:21:13 +00:00
message . setCounterpart ( conversation . getNextCounterpart ( ) ) ;
2014-05-06 19:34:30 +00:00
message . setType ( Message . TYPE_IMAGE ) ;
2015-06-05 06:46:06 +00:00
mFileAddingExecutor . execute ( new Runnable ( ) {
2014-05-01 20:33:49 +00:00
2014-04-15 13:19:02 +00:00
@Override
public void run ( ) {
2014-05-14 16:32:58 +00:00
try {
2014-12-21 20:43:58 +00:00
getFileBackend ( ) . copyImageToPrivateStorage ( message , uri ) ;
2015-07-30 22:52:46 +00:00
if ( conversation . getNextEncryption ( ) = = Message . ENCRYPTION_PGP ) {
2016-06-19 09:08:17 +00:00
final PgpEngine pgpEngine = getPgpEngine ( ) ;
if ( pgpEngine ! = null ) {
pgpEngine . encrypt ( message , callback ) ;
} else if ( callback ! = null ) {
callback . error ( R . string . unable_to_connect_to_keychain , null ) ;
}
2014-05-12 12:59:46 +00:00
} else {
2014-06-07 11:25:27 +00:00
callback . success ( message ) ;
2014-05-12 12:59:46 +00:00
}
2014-12-21 20:43:58 +00:00
} catch ( final FileBackend . FileCopyException e ) {
2014-06-11 19:53:25 +00:00
callback . error ( e . getResId ( ) , message ) ;
2014-04-15 13:19:02 +00:00
}
}
2015-06-05 06:46:06 +00:00
} ) ;
2014-05-06 19:34:30 +00:00
}
2014-05-14 10:56:34 +00:00
2014-07-20 00:26:23 +00:00
public Conversation find ( Bookmark bookmark ) {
2014-08-26 14:52:42 +00:00
return find ( bookmark . getAccount ( ) , bookmark . getJid ( ) ) ;
2014-07-14 09:47:42 +00:00
}
2014-08-26 14:52:42 +00:00
2014-11-05 20:55:47 +00:00
public Conversation find ( final Account account , final Jid jid ) {
2014-08-26 14:52:42 +00:00
return find ( getConversations ( ) , account , jid ) ;
2014-03-03 04:01:02 +00:00
}
2014-02-03 17:38:47 +00:00
@Override
public int onStartCommand ( Intent intent , int flags , int startId ) {
2015-01-05 17:45:39 +00:00
final String action = intent = = null ? null : intent . getAction ( ) ;
2016-10-06 16:09:55 +00:00
String pushedAccountHash = null ;
2015-09-29 17:24:52 +00:00
boolean interactive = false ;
2015-01-05 17:45:39 +00:00
if ( action ! = null ) {
2016-08-25 15:30:44 +00:00
final Conversation c = findConversationByUuid ( intent . getStringExtra ( " uuid " ) ) ;
2015-01-08 13:45:44 +00:00
switch ( action ) {
2015-05-08 04:50:28 +00:00
case ConnectivityManager . CONNECTIVITY_ACTION :
if ( hasInternetConnection ( ) & & Config . RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE ) {
2016-11-18 12:55:02 +00:00
resetAllAttemptCounts ( true , false ) ;
2015-05-08 04:50:28 +00:00
}
break ;
2015-01-08 13:45:44 +00:00
case ACTION_MERGE_PHONE_CONTACTS :
2015-02-17 10:51:11 +00:00
if ( mRestoredFromDatabase ) {
2015-12-06 23:33:50 +00:00
loadPhoneContacts ( ) ;
2015-02-17 10:51:11 +00:00
}
2015-01-08 13:45:44 +00:00
return START_STICKY ;
case Intent . ACTION_SHUTDOWN :
2016-04-14 19:45:36 +00:00
logoutAndSave ( true ) ;
2015-01-08 13:45:44 +00:00
return START_NOT_STICKY ;
case ACTION_CLEAR_NOTIFICATION :
2016-08-25 13:20:06 +00:00
if ( c ! = null ) {
mNotificationService . clear ( c ) ;
} else {
mNotificationService . clear ( ) ;
}
2015-01-08 13:45:44 +00:00
break ;
case ACTION_DISABLE_FOREGROUND :
2016-11-21 10:03:38 +00:00
getPreferences ( ) . edit ( ) . putBoolean ( SettingsActivity . KEEP_FOREGROUND_SERVICE , false ) . commit ( ) ;
2015-01-08 13:45:44 +00:00
toggleForegroundService ( ) ;
break ;
2016-10-04 09:16:59 +00:00
case ACTION_DISMISS_ERROR_NOTIFICATIONS :
dismissErrorNotifications ( ) ;
break ;
2015-02-10 16:13:34 +00:00
case ACTION_TRY_AGAIN :
2016-11-18 12:55:02 +00:00
resetAllAttemptCounts ( false , true ) ;
2015-09-29 17:24:52 +00:00
interactive = true ;
2015-02-10 16:13:34 +00:00
break ;
2016-08-25 15:30:44 +00:00
case ACTION_REPLY_TO_CONVERSATION :
Bundle remoteInput = RemoteInput . getResultsFromIntent ( intent ) ;
if ( remoteInput ! = null & & c ! = null ) {
2016-09-21 17:04:16 +00:00
final CharSequence body = remoteInput . getCharSequence ( " text_reply " ) ;
if ( body ! = null & & body . length ( ) > 0 ) {
2016-09-19 19:35:54 +00:00
directReply ( c , body . toString ( ) , intent . getBooleanExtra ( " dismiss_notification " , false ) ) ;
2016-09-21 17:04:16 +00:00
}
2016-08-25 15:30:44 +00:00
}
break ;
2015-10-07 22:35:04 +00:00
case AudioManager . RINGER_MODE_CHANGED_ACTION :
if ( xaOnSilentMode ( ) ) {
refreshAllPresences ( ) ;
}
break ;
case Intent . ACTION_SCREEN_ON :
2016-06-01 19:51:46 +00:00
deactivateGracePeriod ( ) ;
case Intent . ACTION_SCREEN_OFF :
2015-10-07 22:35:04 +00:00
if ( awayWhenScreenOff ( ) ) {
refreshAllPresences ( ) ;
}
break ;
2016-02-12 10:39:27 +00:00
case ACTION_GCM_TOKEN_REFRESH :
refreshAllGcmTokens ( ) ;
break ;
2016-05-28 12:44:22 +00:00
case ACTION_IDLE_PING :
2016-11-02 10:04:33 +00:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . M ) {
2016-05-28 12:44:22 +00:00
scheduleNextIdlePing ( ) ;
}
break ;
2016-02-12 10:39:27 +00:00
case ACTION_GCM_MESSAGE_RECEIVED :
Log . d ( Config . LOGTAG , " gcm push message arrived in service. extras= " + intent . getExtras ( ) ) ;
2016-10-06 16:09:55 +00:00
pushedAccountHash = intent . getStringExtra ( " account " ) ;
2016-05-28 12:44:22 +00:00
break ;
2014-10-02 15:36:02 +00:00
}
2014-05-19 13:15:09 +00:00
}
2016-11-18 12:14:26 +00:00
synchronized ( this ) {
this . wakeLock . acquire ( ) ;
2016-11-18 12:47:39 +00:00
boolean pingNow = ConnectivityManager . CONNECTIVITY_ACTION . equals ( action ) ;
2016-11-18 12:14:26 +00:00
HashSet < Account > pingCandidates = new HashSet < > ( ) ;
for ( Account account : accounts ) {
pingNow | = processAccountState ( account ,
interactive ,
" ui " . equals ( action ) ,
CryptoHelper . getAccountFingerprint ( account ) . equals ( pushedAccountHash ) ,
pingCandidates ) ;
}
if ( pingNow ) {
for ( Account account : pingCandidates ) {
final boolean lowTimeout = mLowPingTimeoutMode . contains ( account . getJid ( ) . toBareJid ( ) ) ;
account . getXmppConnection ( ) . sendPing ( ) ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " send ping (action= " + action + " ,lowTimeout= " + Boolean . toString ( lowTimeout ) + " ) " ) ;
scheduleWakeUpCall ( lowTimeout ? Config . LOW_PING_TIMEOUT : Config . PING_TIMEOUT , account . getUuid ( ) . hashCode ( ) ) ;
}
}
if ( wakeLock . isHeld ( ) ) {
try {
wakeLock . release ( ) ;
} catch ( final RuntimeException ignored ) {
}
}
}
2017-01-23 16:14:30 +00:00
if ( SystemClock . elapsedRealtime ( ) - mLastExpiryRun . get ( ) > = Config . EXPIRY_INTERVAL ) {
expireOldMessages ( ) ;
}
2016-11-18 12:14:26 +00:00
return START_STICKY ;
}
2014-10-20 19:08:33 +00:00
2016-11-18 12:14:26 +00:00
private boolean processAccountState ( Account account , boolean interactive , boolean isUiAction , boolean isAccountPushed , HashSet < Account > pingCandidates ) {
2016-05-02 12:31:30 +00:00
boolean pingNow = false ;
2016-11-18 12:14:26 +00:00
if ( ! account . isOptionSet ( Account . OPTION_DISABLED ) ) {
if ( ! hasInternetConnection ( ) ) {
account . setStatus ( Account . State . NO_INTERNET ) ;
if ( statusListener ! = null ) {
statusListener . onStatusChanged ( account ) ;
}
} else {
if ( account . getStatus ( ) = = Account . State . NO_INTERNET ) {
account . setStatus ( Account . State . OFFLINE ) ;
2014-03-19 15:16:40 +00:00
if ( statusListener ! = null ) {
2014-03-11 15:49:42 +00:00
statusListener . onStatusChanged ( account ) ;
}
2016-11-18 12:14:26 +00:00
}
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
synchronized ( mLowPingTimeoutMode ) {
long lastReceived = account . getXmppConnection ( ) . getLastPacketReceived ( ) ;
long lastSent = account . getXmppConnection ( ) . getLastPingSent ( ) ;
long pingInterval = isUiAction ? Config . PING_MIN_INTERVAL * 1000 : Config . PING_MAX_INTERVAL * 1000 ;
long msToNextPing = ( Math . max ( lastReceived , lastSent ) + pingInterval ) - SystemClock . elapsedRealtime ( ) ;
int pingTimeout = mLowPingTimeoutMode . contains ( account . getJid ( ) . toBareJid ( ) ) ? Config . LOW_PING_TIMEOUT * 1000 : Config . PING_TIMEOUT * 1000 ;
long pingTimeoutIn = ( lastSent + pingTimeout ) - SystemClock . elapsedRealtime ( ) ;
if ( lastSent > lastReceived ) {
if ( pingTimeoutIn < 0 ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : ping timeout " ) ;
this . reconnectAccount ( account , true , interactive ) ;
} else {
int secs = ( int ) ( pingTimeoutIn / 1000 ) ;
this . scheduleWakeUpCall ( secs , account . getUuid ( ) . hashCode ( ) ) ;
}
} else {
pingCandidates . add ( account ) ;
if ( isAccountPushed ) {
pingNow = true ;
if ( mLowPingTimeoutMode . add ( account . getJid ( ) . toBareJid ( ) ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : entering low ping timeout mode " ) ;
2016-10-20 16:02:11 +00:00
}
2016-11-18 12:14:26 +00:00
} else if ( msToNextPing < = 0 ) {
pingNow = true ;
2016-05-02 12:31:30 +00:00
} else {
2016-11-18 12:14:26 +00:00
this . scheduleWakeUpCall ( ( int ) ( msToNextPing / 1000 ) , account . getUuid ( ) . hashCode ( ) ) ;
if ( mLowPingTimeoutMode . remove ( account . getJid ( ) . toBareJid ( ) ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : leaving low ping timeout mode " ) ;
2016-10-20 18:04:16 +00:00
}
2016-05-02 12:31:30 +00:00
}
2014-03-11 14:44:22 +00:00
}
2016-11-18 12:14:26 +00:00
}
} else if ( account . getStatus ( ) = = Account . State . OFFLINE ) {
reconnectAccount ( account , true , interactive ) ;
} else if ( account . getStatus ( ) = = Account . State . CONNECTING ) {
long secondsSinceLastConnect = ( SystemClock . elapsedRealtime ( ) - account . getXmppConnection ( ) . getLastConnect ( ) ) / 1000 ;
long secondsSinceLastDisco = ( SystemClock . elapsedRealtime ( ) - account . getXmppConnection ( ) . getLastDiscoStarted ( ) ) / 1000 ;
long discoTimeout = Config . CONNECT_DISCO_TIMEOUT - secondsSinceLastDisco ;
long timeout = Config . CONNECT_TIMEOUT - secondsSinceLastConnect ;
if ( timeout < 0 ) {
2016-11-18 12:47:39 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) + " : time out during connect reconnecting (secondsSinceLast= " + secondsSinceLastConnect + " ) " ) ;
2016-11-18 12:14:26 +00:00
account . getXmppConnection ( ) . resetAttemptCount ( false ) ;
2015-10-16 07:58:31 +00:00
reconnectAccount ( account , true , interactive ) ;
2016-11-18 12:14:26 +00:00
} else if ( discoTimeout < 0 ) {
account . getXmppConnection ( ) . sendDiscoTimeout ( ) ;
scheduleWakeUpCall ( ( int ) Math . min ( timeout , discoTimeout ) , account . getUuid ( ) . hashCode ( ) ) ;
2014-03-11 14:44:22 +00:00
} else {
2016-11-18 12:14:26 +00:00
scheduleWakeUpCall ( ( int ) Math . min ( timeout , discoTimeout ) , account . getUuid ( ) . hashCode ( ) ) ;
}
} else {
if ( account . getXmppConnection ( ) . getTimeToNextAttempt ( ) < = 0 ) {
reconnectAccount ( account , true , interactive ) ;
2014-03-08 19:14:47 +00:00
}
2016-08-09 17:21:54 +00:00
}
2016-05-02 12:31:30 +00:00
}
}
2016-11-18 12:14:26 +00:00
return pingNow ;
2014-02-03 17:38:47 +00:00
}
2014-10-20 19:08:33 +00:00
2016-08-27 11:35:52 +00:00
public boolean isDataSaverDisabled ( ) {
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
ConnectivityManager connectivityManager = ( ConnectivityManager ) getSystemService ( CONNECTIVITY_SERVICE ) ;
return ! connectivityManager . isActiveNetworkMetered ( )
| | connectivityManager . getRestrictBackgroundStatus ( ) = = ConnectivityManager . RESTRICT_BACKGROUND_STATUS_DISABLED ;
} else {
return true ;
}
}
2016-09-19 19:35:54 +00:00
private void directReply ( Conversation conversation , String body , final boolean dismissAfterReply ) {
2016-08-25 15:30:44 +00:00
Message message = new Message ( conversation , body , conversation . getNextEncryption ( ) ) ;
2016-08-25 20:41:33 +00:00
message . markUnread ( ) ;
2016-08-25 15:30:44 +00:00
if ( message . getEncryption ( ) = = Message . ENCRYPTION_PGP ) {
getPgpEngine ( ) . encrypt ( message , new UiCallback < Message > ( ) {
@Override
public void success ( Message message ) {
message . setEncryption ( Message . ENCRYPTION_DECRYPTED ) ;
sendMessage ( message ) ;
2016-09-19 19:35:54 +00:00
if ( dismissAfterReply ) {
markRead ( message . getConversation ( ) , true ) ;
} else {
mNotificationService . pushFromDirectReply ( message ) ;
}
2016-08-25 15:30:44 +00:00
}
@Override
public void error ( int errorCode , Message object ) {
}
@Override
public void userInputRequried ( PendingIntent pi , Message object ) {
}
} ) ;
} else {
sendMessage ( message ) ;
2016-09-19 19:35:54 +00:00
if ( dismissAfterReply ) {
markRead ( conversation , true ) ;
} else {
mNotificationService . pushFromDirectReply ( message ) ;
}
2016-08-25 15:30:44 +00:00
}
}
2015-10-07 22:35:04 +00:00
private boolean xaOnSilentMode ( ) {
return getPreferences ( ) . getBoolean ( " xa_on_silent_mode " , false ) ;
}
2016-04-22 19:25:06 +00:00
private boolean manuallyChangePresence ( ) {
2016-11-21 10:03:38 +00:00
return getPreferences ( ) . getBoolean ( SettingsActivity . MANUALLY_CHANGE_PRESENCE , false ) ;
2016-04-22 19:25:06 +00:00
}
2016-03-01 18:00:18 +00:00
private boolean treatVibrateAsSilent ( ) {
2016-11-21 10:03:38 +00:00
return getPreferences ( ) . getBoolean ( SettingsActivity . TREAT_VIBRATE_AS_SILENT , false ) ;
2016-03-01 18:00:18 +00:00
}
2015-10-07 22:35:04 +00:00
private boolean awayWhenScreenOff ( ) {
2016-11-21 10:03:38 +00:00
return getPreferences ( ) . getBoolean ( SettingsActivity . AWAY_WHEN_SCREEN_IS_OFF , false ) ;
2015-10-07 22:35:04 +00:00
}
2016-01-09 15:17:39 +00:00
private String getCompressPicturesPreference ( ) {
return getPreferences ( ) . getString ( " picture_compression " , " auto " ) ;
}
2016-01-17 21:28:38 +00:00
private Presence . Status getTargetPresence ( ) {
2015-10-07 22:35:04 +00:00
if ( xaOnSilentMode ( ) & & isPhoneSilenced ( ) ) {
2016-01-17 21:28:38 +00:00
return Presence . Status . XA ;
2015-10-07 22:35:04 +00:00
} else if ( awayWhenScreenOff ( ) & & ! isInteractive ( ) ) {
2016-01-17 21:28:38 +00:00
return Presence . Status . AWAY ;
2015-10-07 22:35:04 +00:00
} else {
2016-01-17 21:28:38 +00:00
return Presence . Status . ONLINE ;
2015-10-07 22:35:04 +00:00
}
}
@SuppressLint ( " NewApi " )
@SuppressWarnings ( " deprecation " )
public boolean isInteractive ( ) {
final PowerManager pm = ( PowerManager ) getSystemService ( Context . POWER_SERVICE ) ;
final boolean isScreenOn ;
if ( Build . VERSION . SDK_INT < Build . VERSION_CODES . LOLLIPOP ) {
isScreenOn = pm . isScreenOn ( ) ;
} else {
isScreenOn = pm . isInteractive ( ) ;
}
return isScreenOn ;
}
private boolean isPhoneSilenced ( ) {
AudioManager audioManager = ( AudioManager ) getSystemService ( Context . AUDIO_SERVICE ) ;
2016-08-02 08:58:31 +00:00
try {
if ( treatVibrateAsSilent ( ) ) {
return audioManager . getRingerMode ( ) ! = AudioManager . RINGER_MODE_NORMAL ;
} else {
return audioManager . getRingerMode ( ) = = AudioManager . RINGER_MODE_SILENT ;
}
} catch ( Throwable throwable ) {
Log . d ( Config . LOGTAG , " platform bug in isPhoneSilenced ( " + throwable . getMessage ( ) + " ) " ) ;
return false ;
2016-03-01 18:00:18 +00:00
}
2015-10-07 22:35:04 +00:00
}
2016-11-18 12:55:02 +00:00
private void resetAllAttemptCounts ( boolean reallyAll , boolean retryImmediately ) {
2016-03-22 09:54:45 +00:00
Log . d ( Config . LOGTAG , " resetting all attempt counts " ) ;
2015-10-16 07:58:31 +00:00
for ( Account account : accounts ) {
2015-05-08 04:50:28 +00:00
if ( account . hasErrorStatus ( ) | | reallyAll ) {
final XmppConnection connection = account . getXmppConnection ( ) ;
if ( connection ! = null ) {
2016-11-18 12:55:02 +00:00
connection . resetAttemptCount ( retryImmediately ) ;
2015-05-08 04:50:28 +00:00
}
}
2016-10-04 09:16:59 +00:00
if ( account . setShowErrorNotification ( true ) ) {
databaseBackend . updateAccount ( account ) ;
}
}
2016-10-06 20:05:40 +00:00
mNotificationService . updateErrorNotification ( ) ;
2016-10-04 09:16:59 +00:00
}
private void dismissErrorNotifications ( ) {
for ( final Account account : this . accounts ) {
if ( account . hasErrorStatus ( ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : dismissing error notification " ) ;
if ( account . setShowErrorNotification ( false ) ) {
databaseBackend . updateAccount ( account ) ;
}
}
2015-05-08 04:50:28 +00:00
}
}
2017-01-23 16:14:30 +00:00
private void expireOldMessages ( ) {
2017-01-25 17:35:22 +00:00
expireOldMessages ( false ) ;
}
public void expireOldMessages ( final boolean resetHasMessagesLeftOnServer ) {
2017-01-23 16:14:30 +00:00
mLastExpiryRun . set ( SystemClock . elapsedRealtime ( ) ) ;
2017-01-25 12:22:20 +00:00
mDatabaseExecutor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
long timestamp = getAutomaticMessageDeletionDate ( ) ;
if ( timestamp > 0 ) {
databaseBackend . expireOldMessages ( timestamp ) ;
synchronized ( XmppConnectionService . this . conversations ) {
for ( Conversation conversation : XmppConnectionService . this . conversations ) {
conversation . expireOldMessages ( timestamp ) ;
2017-01-25 17:35:22 +00:00
if ( resetHasMessagesLeftOnServer ) {
conversation . messagesLoaded . set ( true ) ;
conversation . setHasMessagesLeftOnServer ( true ) ;
}
2017-01-25 12:22:20 +00:00
}
}
updateConversationUi ( ) ;
2017-01-23 16:14:30 +00:00
}
}
2017-01-25 12:22:20 +00:00
} ) ;
2017-01-23 16:14:30 +00:00
}
2014-10-17 11:09:02 +00:00
public boolean hasInternetConnection ( ) {
ConnectivityManager cm = ( ConnectivityManager ) getApplicationContext ( )
2015-10-16 07:58:31 +00:00
. getSystemService ( Context . CONNECTIVITY_SERVICE ) ;
2014-10-17 11:09:02 +00:00
NetworkInfo activeNetwork = cm . getActiveNetworkInfo ( ) ;
return activeNetwork ! = null & & activeNetwork . isConnected ( ) ;
}
2014-02-03 17:38:47 +00:00
2014-06-20 15:30:19 +00:00
@SuppressLint ( " TrulyRandom " )
2014-02-03 17:38:47 +00:00
@Override
public void onCreate ( ) {
2014-03-09 12:21:28 +00:00
ExceptionHelper . init ( getApplicationContext ( ) ) ;
2014-06-20 15:30:19 +00:00
PRNGFixes . apply ( ) ;
this . mRandom = new SecureRandom ( ) ;
2015-04-02 11:35:42 +00:00
updateMemorizingTrustmanager ( ) ;
2014-12-21 01:13:13 +00:00
final int maxMemory = ( int ) ( Runtime . getRuntime ( ) . maxMemory ( ) / 1024 ) ;
final int cacheSize = maxMemory / 8 ;
2014-10-21 12:57:16 +00:00
this . mBitmapCache = new LruCache < String , Bitmap > ( cacheSize ) {
@Override
2014-12-21 01:13:13 +00:00
protected int sizeOf ( final String key , final Bitmap bitmap ) {
2014-10-21 12:57:16 +00:00
return bitmap . getByteCount ( ) / 1024 ;
}
} ;
2014-11-18 02:10:59 +00:00
this . databaseBackend = DatabaseBackend . getInstance ( getApplicationContext ( ) ) ;
2014-02-03 17:38:47 +00:00
this . accounts = databaseBackend . getAccounts ( ) ;
2014-02-11 22:55:03 +00:00
2016-12-30 19:24:35 +00:00
if ( Config . FREQUENT_RESTARTS_THRESHOLD ! = 0
& & Config . FREQUENT_RESTARTS_DETECTION_WINDOW ! = 0
& & ! keepForegroundService ( )
& & databaseBackend . startTimeCountExceedsThreshold ( ) ) {
2016-11-21 10:03:38 +00:00
getPreferences ( ) . edit ( ) . putBoolean ( SettingsActivity . KEEP_FOREGROUND_SERVICE , true ) . commit ( ) ;
2016-11-08 11:20:07 +00:00
Log . d ( Config . LOGTAG , " number of restarts exceeds threshold. enabling foreground service " ) ;
}
2015-02-17 10:51:11 +00:00
restoreFromDatabase ( ) ;
2014-05-01 20:33:49 +00:00
2014-11-18 02:10:59 +00:00
getContentResolver ( ) . registerContentObserver ( ContactsContract . Contacts . CONTENT_URI , true , contactObserver ) ;
2016-08-13 10:40:48 +00:00
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
fileObserver . startWatching ( ) ;
}
} ) . start ( ) ;
2016-02-24 13:47:49 +00:00
if ( Config . supportOpenPgp ( ) ) {
this . pgpServiceConnection = new OpenPgpServiceConnection ( getApplicationContext ( ) , " org.sufficientlysecure.keychain " , new OpenPgpServiceConnection . OnBound ( ) {
@Override
public void onBound ( IOpenPgpService2 service ) {
for ( Account account : accounts ) {
2016-06-13 11:32:14 +00:00
final PgpDecryptionService pgp = account . getPgpDecryptionService ( ) ;
if ( pgp ! = null ) {
pgp . continueDecryption ( true ) ;
2016-02-24 13:47:49 +00:00
}
2015-10-15 22:21:47 +00:00
}
}
2016-02-24 13:47:49 +00:00
@Override
public void onError ( Exception e ) {
}
} ) ;
this . pgpServiceConnection . bindToService ( ) ;
}
2014-03-11 14:44:22 +00:00
2014-04-03 08:41:21 +00:00
this . pm = ( PowerManager ) getSystemService ( Context . POWER_SERVICE ) ;
2015-10-16 07:58:31 +00:00
this . wakeLock = pm . newWakeLock ( PowerManager . PARTIAL_WAKE_LOCK , " XmppConnectionService " ) ;
2016-11-08 11:20:07 +00:00
2014-11-12 13:41:43 +00:00
toggleForegroundService ( ) ;
2015-07-03 20:08:23 +00:00
updateUnreadCountBadge ( ) ;
2015-10-07 22:35:04 +00:00
toggleScreenEventReceiver ( ) ;
2016-11-02 10:04:33 +00:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . M ) {
2016-05-28 14:07:16 +00:00
scheduleNextIdlePing ( ) ;
}
2016-08-27 10:15:25 +00:00
if ( Build . VERSION . SDK_INT > = Build . VERSION_CODES . N ) {
registerReceiver ( this . mEventReceiver , new IntentFilter ( ConnectivityManager . CONNECTIVITY_ACTION ) ) ;
}
2015-10-07 22:35:04 +00:00
}
2015-10-14 20:55:59 +00:00
@Override
public void onTrimMemory ( int level ) {
super . onTrimMemory ( level ) ;
if ( level > = TRIM_MEMORY_COMPLETE ) {
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , " clear cache due to low memory " ) ;
2015-10-14 20:55:59 +00:00
getBitmapCache ( ) . evictAll ( ) ;
}
}
2015-10-07 22:35:04 +00:00
@Override
public void onDestroy ( ) {
2015-10-07 22:52:04 +00:00
try {
unregisterReceiver ( this . mEventReceiver ) ;
} catch ( IllegalArgumentException e ) {
//ignored
}
2016-07-23 14:12:45 +00:00
fileObserver . stopWatching ( ) ;
2015-10-07 22:35:04 +00:00
super . onDestroy ( ) ;
}
public void toggleScreenEventReceiver ( ) {
2016-04-22 19:25:06 +00:00
if ( awayWhenScreenOff ( ) & & ! manuallyChangePresence ( ) ) {
2015-10-07 22:35:04 +00:00
final IntentFilter filter = new IntentFilter ( Intent . ACTION_SCREEN_ON ) ;
filter . addAction ( Intent . ACTION_SCREEN_OFF ) ;
registerReceiver ( this . mEventReceiver , filter ) ;
} else {
2015-10-07 22:52:04 +00:00
try {
unregisterReceiver ( this . mEventReceiver ) ;
} catch ( IllegalArgumentException e ) {
//ignored
}
2015-10-07 22:35:04 +00:00
}
2014-02-03 17:38:47 +00:00
}
2014-02-05 21:33:39 +00:00
2014-11-12 13:41:43 +00:00
public void toggleForegroundService ( ) {
2016-11-08 11:20:07 +00:00
if ( keepForegroundService ( ) ) {
2014-11-12 13:41:43 +00:00
startForeground ( NotificationService . FOREGROUND_NOTIFICATION_ID , this . mNotificationService . createForegroundNotification ( ) ) ;
} else {
stopForeground ( true ) ;
}
2014-05-19 19:05:17 +00:00
}
2014-05-22 07:36:00 +00:00
2016-11-08 11:20:07 +00:00
private boolean keepForegroundService ( ) {
2016-11-21 10:03:38 +00:00
return getPreferences ( ) . getBoolean ( SettingsActivity . KEEP_FOREGROUND_SERVICE , false ) ;
2016-11-08 11:20:07 +00:00
}
2014-05-19 19:05:17 +00:00
@Override
2014-12-21 01:13:13 +00:00
public void onTaskRemoved ( final Intent rootIntent ) {
2014-05-19 19:05:17 +00:00
super . onTaskRemoved ( rootIntent ) ;
2016-11-08 11:20:07 +00:00
if ( ! keepForegroundService ( ) ) {
2016-04-14 19:45:36 +00:00
this . logoutAndSave ( false ) ;
2016-07-31 20:32:10 +00:00
} else {
Log . d ( Config . LOGTAG , " ignoring onTaskRemoved because foreground service is activated " ) ;
2014-11-12 13:41:43 +00:00
}
2014-05-19 19:05:17 +00:00
}
2014-05-22 07:36:00 +00:00
2016-04-14 19:45:36 +00:00
private void logoutAndSave ( boolean stop ) {
int activeAccounts = 0 ;
2016-11-21 09:48:59 +00:00
databaseBackend . clearStartTimeCounter ( true ) ; // regular swipes don't count towards restart counter
2014-12-21 01:13:13 +00:00
for ( final Account account : accounts ) {
2016-04-14 19:45:36 +00:00
if ( account . getStatus ( ) ! = Account . State . DISABLED ) {
activeAccounts + + ;
}
2014-05-19 13:15:09 +00:00
databaseBackend . writeRoster ( account . getRoster ( ) ) ;
2014-02-13 22:40:08 +00:00
if ( account . getXmppConnection ( ) ! = null ) {
2015-10-17 14:10:56 +00:00
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
disconnect ( account , false ) ;
}
} ) . start ( ) ;
2014-02-13 22:40:08 +00:00
}
}
2016-04-14 19:45:36 +00:00
if ( stop | | activeAccounts = = 0 ) {
Log . d ( Config . LOGTAG , " good bye " ) ;
stopSelf ( ) ;
}
2014-02-13 22:40:08 +00:00
}
2014-03-11 14:44:22 +00:00
2015-12-15 18:14:38 +00:00
public void scheduleWakeUpCall ( int seconds , int requestCode ) {
2015-05-25 02:49:36 +00:00
final long timeToWake = SystemClock . elapsedRealtime ( ) + ( seconds < 0 ? 1 : seconds + 1 ) * 1000 ;
2016-05-28 12:44:22 +00:00
AlarmManager alarmManager = ( AlarmManager ) getSystemService ( Context . ALARM_SERVICE ) ;
Intent intent = new Intent ( this , EventReceiver . class ) ;
2015-01-05 17:45:39 +00:00
intent . setAction ( " ping " ) ;
2016-05-28 12:44:22 +00:00
PendingIntent alarmIntent = PendingIntent . getBroadcast ( this , requestCode , intent , 0 ) ;
2015-01-05 17:45:39 +00:00
alarmManager . set ( AlarmManager . ELAPSED_REALTIME_WAKEUP , timeToWake , alarmIntent ) ;
2014-03-06 02:30:03 +00:00
}
2014-02-13 22:40:08 +00:00
2016-05-28 12:44:22 +00:00
@TargetApi ( Build . VERSION_CODES . M )
private void scheduleNextIdlePing ( ) {
2016-05-28 14:07:16 +00:00
Log . d ( Config . LOGTAG , " schedule next idle ping " ) ;
2016-05-28 12:44:22 +00:00
AlarmManager alarmManager = ( AlarmManager ) getSystemService ( Context . ALARM_SERVICE ) ;
Intent intent = new Intent ( this , EventReceiver . class ) ;
intent . setAction ( ACTION_IDLE_PING ) ;
alarmManager . setAndAllowWhileIdle ( AlarmManager . ELAPSED_REALTIME_WAKEUP ,
SystemClock . elapsedRealtime ( ) + ( Config . IDLE_PING_INTERVAL * 1000 ) ,
PendingIntent . getBroadcast ( this , 0 , intent , 0 )
) ;
}
2014-12-21 20:43:58 +00:00
public XmppConnection createConnection ( final Account account ) {
final SharedPreferences sharedPref = getPreferences ( ) ;
2016-10-17 07:53:08 +00:00
String resource ;
try {
resource = sharedPref . getString ( " resource " , getString ( R . string . default_resource ) ) . toLowerCase ( Locale . ENGLISH ) ;
if ( resource . trim ( ) . isEmpty ( ) ) {
throw new Exception ( ) ;
}
} catch ( Exception e ) {
resource = " conversations " ;
}
account . setResource ( resource ) ;
2014-12-21 20:43:58 +00:00
final XmppConnection connection = new XmppConnection ( account , this ) ;
2014-07-12 00:36:37 +00:00
connection . setOnMessagePacketReceivedListener ( this . mMessageParser ) ;
2014-02-05 21:33:39 +00:00
connection . setOnStatusChangedListener ( this . statusListener ) ;
2014-07-12 00:36:37 +00:00
connection . setOnPresencePacketReceivedListener ( this . mPresenceParser ) ;
2014-08-26 14:52:42 +00:00
connection . setOnUnregisteredIqPacketReceivedListener ( this . mIqParser ) ;
2014-03-27 01:02:59 +00:00
connection . setOnJinglePacketReceivedListener ( this . jingleListener ) ;
2014-07-18 19:57:10 +00:00
connection . setOnBindListener ( this . mOnBindListener ) ;
2014-12-05 00:54:16 +00:00
connection . setOnMessageAcknowledgeListener ( this . mOnMessageAcknowledgedListener ) ;
2014-12-08 20:59:14 +00:00
connection . addOnAdvancedStreamFeaturesAvailableListener ( this . mMessageArchiveService ) ;
2016-04-11 20:20:32 +00:00
connection . addOnAdvancedStreamFeaturesAvailableListener ( this . mAvatarService ) ;
2015-10-16 21:48:42 +00:00
AxolotlService axolotlService = account . getAxolotlService ( ) ;
if ( axolotlService ! = null ) {
connection . addOnAdvancedStreamFeaturesAvailableListener ( axolotlService ) ;
}
2014-02-04 14:09:50 +00:00
return connection ;
}
2014-02-19 00:35:23 +00:00
2015-02-21 10:06:52 +00:00
public void sendChatState ( Conversation conversation ) {
if ( sendChatStates ( ) ) {
MessagePacket packet = mMessageGenerator . generateChatState ( conversation ) ;
sendMessagePacket ( conversation . getAccount ( ) , packet ) ;
}
}
2015-07-20 16:11:33 +00:00
private void sendFileMessage ( final Message message , final boolean delay ) {
2015-06-28 09:19:07 +00:00
Log . d ( Config . LOGTAG , " send file message " ) ;
final Account account = message . getConversation ( ) . getAccount ( ) ;
2016-03-31 22:03:14 +00:00
if ( account . httpUploadAvailable ( fileBackend . getFile ( message , false ) . getSize ( ) ) ) {
2015-07-20 16:11:33 +00:00
mHttpConnectionManager . createNewUploadConnection ( message , delay ) ;
2015-06-28 09:19:07 +00:00
} else {
mJingleConnectionManager . createNewConnection ( message ) ;
}
}
2014-12-21 20:43:58 +00:00
public void sendMessage ( final Message message ) {
2015-07-20 16:11:33 +00:00
sendMessage ( message , false , false ) ;
2015-07-05 09:59:38 +00:00
}
2015-07-20 16:11:33 +00:00
private void sendMessage ( final Message message , final boolean resend , final boolean delay ) {
2014-12-21 20:43:58 +00:00
final Account account = message . getConversation ( ) . getAccount ( ) ;
2016-10-04 09:16:59 +00:00
if ( account . setShowErrorNotification ( true ) ) {
databaseBackend . updateAccount ( account ) ;
mNotificationService . updateErrorNotification ( ) ;
}
2015-07-05 09:59:38 +00:00
final Conversation conversation = message . getConversation ( ) ;
2014-10-17 09:01:38 +00:00
account . deactivateGracePeriod ( ) ;
2014-04-11 07:13:56 +00:00
MessagePacket packet = null ;
2016-02-15 22:15:04 +00:00
final boolean addToConversation = ( conversation . getMode ( ) ! = Conversation . MODE_MULTI
2017-02-19 12:05:40 +00:00
| | ! Patches . BAD_MUC_REFLECTION . contains ( account . getServerIdentity ( ) ) )
2016-02-15 22:15:04 +00:00
& & ! message . edited ( ) ;
2015-11-01 13:50:06 +00:00
boolean saveInDb = addToConversation ;
2015-07-05 09:59:38 +00:00
message . setStatus ( Message . STATUS_WAITING ) ;
if ( ! resend & & message . getEncryption ( ) ! = Message . ENCRYPTION_OTR ) {
message . getConversation ( ) . endOtrIfNeeded ( ) ;
2015-06-29 12:22:26 +00:00
message . getConversation ( ) . findUnsentMessagesWithEncryption ( Message . ENCRYPTION_OTR ,
new Conversation . OnMessageFound ( ) {
2015-10-16 07:58:31 +00:00
@Override
public void onMessageFound ( Message message ) {
markMessage ( message , Message . STATUS_SEND_FAILED ) ;
}
} ) ;
2015-07-05 09:59:38 +00:00
}
if ( account . isOnlineAndConnected ( ) ) {
switch ( message . getEncryption ( ) ) {
case Message . ENCRYPTION_NONE :
if ( message . needsUploading ( ) ) {
2016-03-31 22:03:14 +00:00
if ( account . httpUploadAvailable ( fileBackend . getFile ( message , false ) . getSize ( ) )
| | message . fixCounterpart ( ) ) {
2015-10-16 07:58:31 +00:00
this . sendFileMessage ( message , delay ) ;
2015-07-05 09:59:38 +00:00
} else {
break ;
2015-06-28 09:19:07 +00:00
}
2014-06-22 11:57:57 +00:00
} else {
2015-07-20 16:11:33 +00:00
packet = mMessageGenerator . generateChat ( message ) ;
2014-10-29 23:31:44 +00:00
}
2015-07-05 09:59:38 +00:00
break ;
case Message . ENCRYPTION_PGP :
case Message . ENCRYPTION_DECRYPTED :
if ( message . needsUploading ( ) ) {
2016-03-31 22:03:14 +00:00
if ( account . httpUploadAvailable ( fileBackend . getFile ( message , false ) . getSize ( ) )
| | message . fixCounterpart ( ) ) {
2015-10-16 07:58:31 +00:00
this . sendFileMessage ( message , delay ) ;
2014-11-09 16:46:00 +00:00
} else {
2015-07-05 09:59:38 +00:00
break ;
2014-11-09 16:46:00 +00:00
}
} else {
2015-07-20 16:11:33 +00:00
packet = mMessageGenerator . generatePgpChat ( message ) ;
2014-04-07 18:05:45 +00:00
}
2015-07-05 09:59:38 +00:00
break ;
case Message . ENCRYPTION_OTR :
SessionImpl otrSession = conversation . getOtrSession ( ) ;
if ( otrSession ! = null & & otrSession . getSessionStatus ( ) = = SessionStatus . ENCRYPTED ) {
try {
message . setCounterpart ( Jid . fromSessionID ( otrSession . getSessionID ( ) ) ) ;
} catch ( InvalidJidException e ) {
break ;
2014-12-14 17:10:46 +00:00
}
2015-07-05 09:59:38 +00:00
if ( message . needsUploading ( ) ) {
mJingleConnectionManager . createNewConnection ( message ) ;
} else {
2015-07-20 16:11:33 +00:00
packet = mMessageGenerator . generateOtrChat ( message ) ;
2014-12-14 17:10:46 +00:00
}
2015-07-05 09:59:38 +00:00
} else if ( otrSession = = null ) {
if ( message . fixCounterpart ( ) ) {
conversation . startOtrSession ( message . getCounterpart ( ) . getResourcepart ( ) , true ) ;
} else {
2016-02-16 08:15:41 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : could not fix counterpart for OTR message to contact " + message . getContact ( ) . getJid ( ) ) ;
2015-07-05 09:59:38 +00:00
break ;
}
2016-02-16 08:15:41 +00:00
} else {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " OTR session with " + message . getContact ( ) + " is in wrong state: " + otrSession . getSessionStatus ( ) . toString ( ) ) ;
2015-07-05 09:59:38 +00:00
}
break ;
2015-06-25 15:01:42 +00:00
case Message . ENCRYPTION_AXOLOTL :
2016-03-31 19:15:49 +00:00
message . setFingerprint ( account . getAxolotlService ( ) . getOwnFingerprint ( ) ) ;
2015-07-17 17:44:05 +00:00
if ( message . needsUploading ( ) ) {
2016-03-31 22:03:14 +00:00
if ( account . httpUploadAvailable ( fileBackend . getFile ( message , false ) . getSize ( ) )
| | message . fixCounterpart ( ) ) {
2015-10-16 07:58:31 +00:00
this . sendFileMessage ( message , delay ) ;
2015-07-17 17:44:05 +00:00
} else {
break ;
}
} else {
2015-07-20 21:13:28 +00:00
XmppAxolotlMessage axolotlMessage = account . getAxolotlService ( ) . fetchAxolotlMessageFromCache ( message ) ;
if ( axolotlMessage = = null ) {
2015-07-31 19:12:34 +00:00
account . getAxolotlService ( ) . preparePayloadMessage ( message , delay ) ;
2015-07-20 21:13:28 +00:00
} else {
packet = mMessageGenerator . generateAxolotlChat ( message , axolotlMessage ) ;
2015-07-17 17:44:05 +00:00
}
2015-07-03 11:31:14 +00:00
}
2015-06-25 15:01:42 +00:00
break ;
2015-07-05 09:59:38 +00:00
}
if ( packet ! = null ) {
2016-10-09 17:40:30 +00:00
if ( account . getXmppConnection ( ) . getFeatures ( ) . sm ( )
| | ( conversation . getMode ( ) = = Conversation . MODE_MULTI & & message . getCounterpart ( ) . isBareJid ( ) ) ) {
2015-07-05 09:59:38 +00:00
message . setStatus ( Message . STATUS_UNSEND ) ;
} else {
message . setStatus ( Message . STATUS_SEND ) ;
2014-02-11 22:55:03 +00:00
}
}
} else {
2015-10-16 07:58:31 +00:00
switch ( message . getEncryption ( ) ) {
2015-07-05 09:59:38 +00:00
case Message . ENCRYPTION_DECRYPTED :
if ( ! message . needsUploading ( ) ) {
String pgpBody = message . getEncryptedBody ( ) ;
String decryptedBody = message . getBody ( ) ;
message . setBody ( pgpBody ) ;
message . setEncryption ( Message . ENCRYPTION_PGP ) ;
2016-09-04 20:59:15 +00:00
if ( message . edited ( ) ) {
message . setBody ( decryptedBody ) ;
message . setEncryption ( Message . ENCRYPTION_DECRYPTED ) ;
databaseBackend . updateMessage ( message , message . getEditedId ( ) ) ;
updateConversationUi ( ) ;
return ;
} else {
databaseBackend . createMessage ( message ) ;
saveInDb = false ;
message . setBody ( decryptedBody ) ;
message . setEncryption ( Message . ENCRYPTION_DECRYPTED ) ;
}
2015-07-05 09:59:38 +00:00
}
break ;
case Message . ENCRYPTION_OTR :
if ( ! conversation . hasValidOtrSession ( ) & & message . getCounterpart ( ) ! = null ) {
2016-02-16 08:15:41 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : create otr session without starting for " + message . getContact ( ) . getJid ( ) ) ;
2015-07-05 09:59:38 +00:00
conversation . startOtrSession ( message . getCounterpart ( ) . getResourcepart ( ) , false ) ;
}
break ;
2015-07-15 14:32:42 +00:00
case Message . ENCRYPTION_AXOLOTL :
2016-03-31 19:15:49 +00:00
message . setFingerprint ( account . getAxolotlService ( ) . getOwnFingerprint ( ) ) ;
2015-07-15 14:32:42 +00:00
break ;
2014-05-16 11:42:20 +00:00
}
2014-02-13 22:40:08 +00:00
}
2015-07-05 09:59:38 +00:00
if ( resend ) {
2015-11-01 13:50:06 +00:00
if ( packet ! = null & & addToConversation ) {
2016-10-09 17:40:30 +00:00
if ( account . getXmppConnection ( ) . getFeatures ( ) . sm ( )
| | ( conversation . getMode ( ) = = Conversation . MODE_MULTI & & message . getCounterpart ( ) . isBareJid ( ) ) ) {
2015-10-16 07:58:31 +00:00
markMessage ( message , Message . STATUS_UNSEND ) ;
2015-07-05 09:59:38 +00:00
} else {
2015-10-16 07:58:31 +00:00
markMessage ( message , Message . STATUS_SEND ) ;
2015-07-05 09:59:38 +00:00
}
}
} else {
2015-11-01 13:50:06 +00:00
if ( addToConversation ) {
conversation . add ( message ) ;
}
2017-01-23 16:14:30 +00:00
if ( saveInDb ) {
databaseBackend . createMessage ( message ) ;
} else if ( message . edited ( ) ) {
databaseBackend . updateMessage ( message , message . getEditedId ( ) ) ;
2015-07-05 09:59:38 +00:00
}
updateConversationUi ( ) ;
2014-02-13 22:40:08 +00:00
}
2015-07-05 09:59:38 +00:00
if ( packet ! = null ) {
2015-07-20 16:11:33 +00:00
if ( delay ) {
2015-10-16 07:58:31 +00:00
mMessageGenerator . addDelay ( packet , message . getTimeSent ( ) ) ;
2015-07-20 16:11:33 +00:00
}
2015-07-05 09:59:38 +00:00
if ( conversation . setOutgoingChatState ( Config . DEFAULT_CHATSTATE ) ) {
2015-02-21 10:06:52 +00:00
if ( this . sendChatStates ( ) ) {
2015-07-05 09:59:38 +00:00
packet . addChild ( ChatState . toElement ( conversation . getOutgoingChatState ( ) ) ) ;
2015-02-21 10:06:52 +00:00
}
}
2014-07-12 01:44:23 +00:00
sendMessagePacket ( account , packet ) ;
2014-04-11 07:13:56 +00:00
}
2014-02-11 22:55:03 +00:00
}
2014-12-21 20:43:58 +00:00
private void sendUnsentMessages ( final Conversation conversation ) {
2014-12-14 17:10:46 +00:00
conversation . findWaitingMessages ( new Conversation . OnMessageFound ( ) {
@Override
public void onMessageFound ( Message message ) {
2015-07-20 21:13:28 +00:00
resendMessage ( message , true ) ;
2014-05-16 11:42:20 +00:00
}
2014-12-14 17:10:46 +00:00
} ) ;
2014-05-16 11:42:20 +00:00
}
2014-05-18 09:25:04 +00:00
2015-07-20 16:11:33 +00:00
public void resendMessage ( final Message message , final boolean delay ) {
sendMessage ( message , true , delay ) ;
2014-02-11 22:55:03 +00:00
}
2014-12-21 20:43:58 +00:00
public void fetchRosterFromServer ( final Account account ) {
2014-12-30 13:16:25 +00:00
final IqPacket iqPacket = new IqPacket ( IqPacket . TYPE . GET ) ;
2014-03-08 01:06:00 +00:00
if ( ! " " . equals ( account . getRosterVersion ( ) ) ) {
2014-11-09 15:57:22 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( )
2014-08-31 14:28:21 +00:00
+ " : fetching roster version " + account . getRosterVersion ( ) ) ;
2014-03-08 01:06:00 +00:00
} else {
2014-11-09 15:57:22 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : fetching roster " ) ;
2014-03-08 01:06:00 +00:00
}
2015-08-11 14:50:00 +00:00
iqPacket . query ( Xmlns . ROSTER ) . setAttribute ( " ver " , account . getRosterVersion ( ) ) ;
2015-08-13 16:25:10 +00:00
sendIqPacket ( account , iqPacket , mIqParser ) ;
2014-02-10 02:34:00 +00:00
}
2014-08-26 14:52:42 +00:00
2014-12-21 20:43:58 +00:00
public void fetchBookmarks ( final Account account ) {
2014-12-30 13:16:25 +00:00
final IqPacket iqPacket = new IqPacket ( IqPacket . TYPE . GET ) ;
2014-12-21 20:43:58 +00:00
final Element query = iqPacket . query ( " jabber:iq:private " ) ;
2014-07-14 09:47:42 +00:00
query . addChild ( " storage " , " storage:bookmarks " ) ;
2015-01-04 11:09:39 +00:00
final OnIqPacketReceived callback = new OnIqPacketReceived ( ) {
2014-08-26 14:52:42 +00:00
2014-07-14 09:47:42 +00:00
@Override
2014-12-21 20:43:58 +00:00
public void onIqPacketReceived ( final Account account , final IqPacket packet ) {
2015-08-23 06:27:05 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
final Element query = packet . query ( ) ;
2015-12-27 16:29:32 +00:00
final HashMap < Jid , Bookmark > bookmarks = new HashMap < > ( ) ;
2015-08-23 06:27:05 +00:00
final Element storage = query . findChild ( " storage " , " storage:bookmarks " ) ;
2016-02-01 12:54:08 +00:00
final boolean autojoin = respectAutojoin ( ) ;
2015-08-23 06:27:05 +00:00
if ( storage ! = null ) {
for ( final Element item : storage . getChildren ( ) ) {
if ( item . getName ( ) . equals ( " conference " ) ) {
final Bookmark bookmark = Bookmark . parse ( item , account ) ;
2015-12-27 16:29:32 +00:00
Bookmark old = bookmarks . put ( bookmark . getJid ( ) , bookmark ) ;
if ( old ! = null & & old . getBookmarkName ( ) ! = null & & bookmark . getBookmarkName ( ) = = null ) {
bookmark . setBookmarkName ( old . getBookmarkName ( ) ) ;
}
2015-08-23 06:27:05 +00:00
Conversation conversation = find ( bookmark ) ;
if ( conversation ! = null ) {
conversation . setBookmark ( bookmark ) ;
2016-02-01 12:54:08 +00:00
} else if ( bookmark . autojoin ( ) & & bookmark . getJid ( ) ! = null & & autojoin ) {
2017-02-14 15:50:33 +00:00
conversation = findOrCreateConversation ( account , bookmark . getJid ( ) , true , true ) ;
2015-08-23 06:27:05 +00:00
conversation . setBookmark ( bookmark ) ;
}
2014-07-14 09:47:42 +00:00
}
}
}
2015-12-27 16:29:32 +00:00
account . setBookmarks ( new ArrayList < > ( bookmarks . values ( ) ) ) ;
2015-08-23 06:27:05 +00:00
} else {
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : could not fetch bookmarks " ) ;
2014-07-14 09:47:42 +00:00
}
}
} ;
sendIqPacket ( account , iqPacket , callback ) ;
}
2014-08-26 14:52:42 +00:00
2014-07-15 12:32:19 +00:00
public void pushBookmarks ( Account account ) {
2015-12-11 18:28:44 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : pushing bookmarks " ) ;
2014-12-30 13:16:25 +00:00
IqPacket iqPacket = new IqPacket ( IqPacket . TYPE . SET ) ;
2014-07-15 12:32:19 +00:00
Element query = iqPacket . query ( " jabber:iq:private " ) ;
Element storage = query . addChild ( " storage " , " storage:bookmarks " ) ;
2014-08-26 14:52:42 +00:00
for ( Bookmark bookmark : account . getBookmarks ( ) ) {
2014-10-05 22:33:52 +00:00
storage . addChild ( bookmark ) ;
2014-07-15 12:32:19 +00:00
}
2015-05-26 09:31:33 +00:00
sendIqPacket ( account , iqPacket , mDefaultIqHandler ) ;
2014-07-15 12:32:19 +00:00
}
2014-02-10 02:34:00 +00:00
2015-02-17 10:51:11 +00:00
private void restoreFromDatabase ( ) {
2014-11-18 02:10:59 +00:00
synchronized ( this . conversations ) {
2014-12-21 20:43:58 +00:00
final Map < String , Account > accountLookupTable = new Hashtable < > ( ) ;
2014-02-03 17:38:47 +00:00
for ( Account account : this . accounts ) {
accountLookupTable . put ( account . getUuid ( ) , account ) ;
}
2014-11-18 02:10:59 +00:00
this . conversations . addAll ( databaseBackend . getConversations ( Conversation . STATUS_AVAILABLE ) ) ;
2014-12-15 22:06:29 +00:00
for ( Conversation conversation : this . conversations ) {
Account account = accountLookupTable . get ( conversation . getAccountUuid ( ) ) ;
conversation . setAccount ( account ) ;
2014-02-03 17:38:47 +00:00
}
2015-10-16 07:58:31 +00:00
Runnable runnable = new Runnable ( ) {
2015-02-12 17:53:00 +00:00
@Override
public void run ( ) {
2017-01-23 16:14:30 +00:00
long deletionDate = getAutomaticMessageDeletionDate ( ) ;
mLastExpiryRun . set ( SystemClock . elapsedRealtime ( ) ) ;
if ( deletionDate > 0 ) {
Log . d ( Config . LOGTAG , " deleting messages that are older than " + AbstractGenerator . getTimestamp ( deletionDate ) ) ;
databaseBackend . expireOldMessages ( deletionDate ) ;
}
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , " restoring roster " ) ;
for ( Account account : accounts ) {
2015-10-16 21:48:42 +00:00
databaseBackend . readRoster ( account . getRoster ( ) ) ;
2015-10-30 11:05:21 +00:00
account . initAccountServices ( XmppConnectionService . this ) ; //roster needs to be loaded at this stage
2015-02-17 10:51:11 +00:00
}
getBitmapCache ( ) . evictAll ( ) ;
2015-12-06 23:33:50 +00:00
loadPhoneContacts ( ) ;
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , " restoring messages " ) ;
2015-02-12 17:53:00 +00:00
for ( Conversation conversation : conversations ) {
conversation . addAll ( 0 , databaseBackend . getMessages ( conversation , Config . PAGE_SIZE ) ) ;
checkDeletedFiles ( conversation ) ;
2016-10-07 12:54:35 +00:00
conversation . findUnsentTextMessages ( new Conversation . OnMessageFound ( ) {
@Override
public void onMessageFound ( Message message ) {
markMessage ( message , Message . STATUS_WAITING ) ;
}
} ) ;
2015-10-14 19:18:34 +00:00
conversation . findUnreadMessages ( new Conversation . OnMessageFound ( ) {
@Override
public void onMessageFound ( Message message ) {
mNotificationService . pushFromBacklog ( message ) ;
}
} ) ;
2015-02-12 17:53:00 +00:00
}
2015-12-10 22:05:11 +00:00
mNotificationService . finishBacklog ( false ) ;
2015-02-17 10:51:11 +00:00
mRestoredFromDatabase = true ;
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , " restored all messages " ) ;
2015-02-12 17:53:00 +00:00
updateConversationUi ( ) ;
}
2015-06-05 06:46:06 +00:00
} ;
mDatabaseExecutor . execute ( runnable ) ;
2014-02-03 17:38:47 +00:00
}
2014-11-18 02:10:59 +00:00
}
2015-12-06 23:33:50 +00:00
public void loadPhoneContacts ( ) {
2016-05-31 15:20:21 +00:00
mContactMergerExecutor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
PhoneHelper . loadPhoneContacts ( XmppConnectionService . this , new OnPhoneContactsLoadedListener ( ) {
@Override
public void onPhoneContactsLoaded ( List < Bundle > phoneContacts ) {
Log . d ( Config . LOGTAG , " start merging phone contacts with roster " ) ;
for ( Account account : accounts ) {
List < Contact > withSystemAccounts = account . getRoster ( ) . getWithSystemAccounts ( ) ;
for ( Bundle phoneContact : phoneContacts ) {
Jid jid ;
try {
jid = Jid . fromString ( phoneContact . getString ( " jid " ) ) ;
} catch ( final InvalidJidException e ) {
continue ;
}
final Contact contact = account . getRoster ( ) . getContact ( jid ) ;
String systemAccount = phoneContact . getInt ( " phoneid " )
+ " # "
+ phoneContact . getString ( " lookup " ) ;
contact . setSystemAccount ( systemAccount ) ;
2017-02-10 19:26:21 +00:00
boolean needsCacheClean = contact . setPhotoUri ( phoneContact . getString ( " photouri " ) ) ;
needsCacheClean | = contact . setSystemName ( phoneContact . getString ( " displayname " ) ) ;
if ( needsCacheClean ) {
2016-05-31 15:20:21 +00:00
getAvatarService ( ) . clear ( contact ) ;
}
withSystemAccounts . remove ( contact ) ;
}
for ( Contact contact : withSystemAccounts ) {
contact . setSystemAccount ( null ) ;
2017-02-10 19:26:21 +00:00
boolean needsCacheClean = contact . setPhotoUri ( null ) ;
needsCacheClean | = contact . setSystemName ( null ) ;
if ( needsCacheClean ) {
2016-05-31 15:20:21 +00:00
getAvatarService ( ) . clear ( contact ) ;
}
}
}
Log . d ( Config . LOGTAG , " finished merging phone contacts " ) ;
updateAccountUi ( ) ;
}
} ) ;
}
} ) ;
2015-12-06 23:33:50 +00:00
}
2014-11-18 02:10:59 +00:00
public List < Conversation > getConversations ( ) {
2014-07-12 10:41:37 +00:00
return this . conversations ;
}
2014-10-20 19:08:33 +00:00
2014-10-15 20:08:13 +00:00
private void checkDeletedFiles ( Conversation conversation ) {
2014-12-14 17:10:46 +00:00
conversation . findMessagesWithFiles ( new Conversation . OnMessageFound ( ) {
@Override
public void onMessageFound ( Message message ) {
2014-10-15 20:08:13 +00:00
if ( ! getFileBackend ( ) . isFileAvailable ( message ) ) {
2015-07-10 13:11:03 +00:00
message . setTransferable ( new TransferablePlaceholder ( Transferable . STATUS_DELETED ) ) ;
2015-08-11 14:50:00 +00:00
final int s = message . getStatus ( ) ;
2015-10-16 07:58:31 +00:00
if ( s = = Message . STATUS_WAITING | | s = = Message . STATUS_OFFERED | | s = = Message . STATUS_UNSEND ) {
markMessage ( message , Message . STATUS_SEND_FAILED ) ;
2015-08-11 14:50:00 +00:00
}
2014-10-15 20:08:13 +00:00
}
2014-12-14 17:10:46 +00:00
}
} ) ;
2014-10-15 20:08:13 +00:00
}
2014-10-20 19:08:33 +00:00
2016-07-23 14:12:45 +00:00
private void markFileDeleted ( final String path ) {
2016-07-26 18:43:05 +00:00
Log . d ( Config . LOGTAG , " deleted file " + path ) ;
2014-10-20 19:08:33 +00:00
for ( Conversation conversation : getConversations ( ) ) {
2016-07-23 14:12:45 +00:00
conversation . findMessagesWithFiles ( new Conversation . OnMessageFound ( ) {
@Override
public void onMessageFound ( Message message ) {
DownloadableFile file = fileBackend . getFile ( message ) ;
2016-07-26 18:43:05 +00:00
if ( file . getAbsolutePath ( ) . equals ( path ) ) {
2016-07-26 18:44:28 +00:00
if ( ! file . exists ( ) ) {
2016-07-26 18:43:05 +00:00
message . setTransferable ( new TransferablePlaceholder ( Transferable . STATUS_DELETED ) ) ;
final int s = message . getStatus ( ) ;
if ( s = = Message . STATUS_WAITING | | s = = Message . STATUS_OFFERED | | s = = Message . STATUS_UNSEND ) {
markMessage ( message , Message . STATUS_SEND_FAILED ) ;
} else {
updateConversationUi ( ) ;
}
2016-07-23 14:12:45 +00:00
} else {
2016-07-26 18:43:05 +00:00
Log . d ( Config . LOGTAG , " found matching message for file " + path + " but file still exists " ) ;
2016-07-23 14:12:45 +00:00
}
2015-08-11 14:50:00 +00:00
}
2014-12-14 17:10:46 +00:00
}
2016-07-23 14:12:45 +00:00
} ) ;
2014-10-16 00:39:02 +00:00
}
}
2014-09-05 11:29:20 +00:00
2014-11-18 02:10:59 +00:00
public void populateWithOrderedConversations ( final List < Conversation > list ) {
2014-09-05 11:29:20 +00:00
populateWithOrderedConversations ( list , true ) ;
2014-09-02 13:51:20 +00:00
}
2015-07-02 21:51:59 +00:00
public void populateWithOrderedConversations ( final List < Conversation > list , boolean includeNoFileUpload ) {
2014-07-12 10:41:37 +00:00
list . clear ( ) ;
2015-07-02 21:51:59 +00:00
if ( includeNoFileUpload ) {
2014-09-02 13:51:20 +00:00
list . addAll ( getConversations ( ) ) ;
} else {
2014-09-05 11:29:20 +00:00
for ( Conversation conversation : getConversations ( ) ) {
2015-07-02 21:51:59 +00:00
if ( conversation . getMode ( ) = = Conversation . MODE_SINGLE
| | conversation . getAccount ( ) . httpUploadAvailable ( ) ) {
2014-09-02 13:51:20 +00:00
list . add ( conversation ) ;
}
}
}
2016-06-09 12:50:13 +00:00
try {
Collections . sort ( list ) ;
} catch ( IllegalArgumentException e ) {
//ignore
}
2014-02-03 17:38:47 +00:00
}
2014-09-27 09:37:02 +00:00
2014-12-17 08:32:51 +00:00
public void loadMoreMessages ( final Conversation conversation , final long timestamp , final OnMoreMessagesLoaded callback ) {
2015-10-16 07:58:31 +00:00
if ( XmppConnectionService . this . getMessageArchiveService ( ) . queryInProgress ( conversation , callback ) ) {
2014-12-17 09:50:51 +00:00
return ;
2016-02-04 13:39:16 +00:00
} else if ( timestamp = = 0 ) {
return ;
2014-12-17 09:50:51 +00:00
}
2015-10-04 22:45:16 +00:00
Log . d ( Config . LOGTAG , " load more messages for " + conversation . getName ( ) + " prior to " + MessageGenerator . getTimestamp ( timestamp ) ) ;
2015-06-05 06:46:06 +00:00
Runnable runnable = new Runnable ( ) {
2014-12-17 08:32:51 +00:00
@Override
public void run ( ) {
final Account account = conversation . getAccount ( ) ;
2015-10-16 07:58:31 +00:00
List < Message > messages = databaseBackend . getMessages ( conversation , 50 , timestamp ) ;
2014-12-17 08:32:51 +00:00
if ( messages . size ( ) > 0 ) {
conversation . addAll ( 0 , messages ) ;
2015-01-19 10:23:05 +00:00
checkDeletedFiles ( conversation ) ;
2014-12-17 08:32:51 +00:00
callback . onMoreMessagesLoaded ( messages . size ( ) , conversation ) ;
2015-01-02 23:47:22 +00:00
} else if ( conversation . hasMessagesLeftOnServer ( )
2016-02-04 15:29:17 +00:00
& & account . isOnlineAndConnected ( )
& & conversation . getLastClearHistory ( ) = = 0 ) {
2015-10-04 22:37:19 +00:00
if ( ( conversation . getMode ( ) = = Conversation . MODE_SINGLE & & account . getXmppConnection ( ) . getFeatures ( ) . mam ( ) )
| | ( conversation . getMode ( ) = = Conversation . MODE_MULTI & & conversation . getMucOptions ( ) . mamSupport ( ) ) ) {
2016-02-04 10:55:42 +00:00
MessageArchiveService . Query query = getMessageArchiveService ( ) . query ( conversation , 0 , timestamp ) ;
2015-10-04 22:37:19 +00:00
if ( query ! = null ) {
query . setCallback ( callback ) ;
2017-01-23 16:14:30 +00:00
callback . informUser ( R . string . fetching_history_from_server ) ;
} else {
callback . informUser ( R . string . not_fetching_history_retention_period ) ;
2015-10-04 22:37:19 +00:00
}
2017-01-23 16:14:30 +00:00
2014-12-17 08:32:51 +00:00
}
2015-01-19 10:23:05 +00:00
}
2014-12-15 22:06:29 +00:00
}
2015-06-05 06:46:06 +00:00
} ;
mDatabaseExecutor . execute ( runnable ) ;
2014-12-15 22:06:29 +00:00
}
2014-02-03 17:38:47 +00:00
public List < Account > getAccounts ( ) {
return this . accounts ;
}
2014-06-03 13:48:51 +00:00
2016-06-14 15:11:31 +00:00
public List < Conversation > findAllConferencesWith ( Contact contact ) {
ArrayList < Conversation > results = new ArrayList < > ( ) ;
for ( Conversation conversation : conversations ) {
if ( conversation . getMode ( ) = = Conversation . MODE_MULTI
& & conversation . getMucOptions ( ) . isContactInRoom ( contact ) ) {
results . add ( conversation ) ;
}
}
return results ;
}
2014-12-21 20:43:58 +00:00
public Conversation find ( final Iterable < Conversation > haystack , final Contact contact ) {
for ( final Conversation conversation : haystack ) {
2014-05-23 08:54:40 +00:00
if ( conversation . getContact ( ) = = contact ) {
return conversation ;
}
}
return null ;
}
2014-02-11 22:55:03 +00:00
2014-12-21 20:43:58 +00:00
public Conversation find ( final Iterable < Conversation > haystack , final Account account , final Jid jid ) {
2015-01-19 10:17:27 +00:00
if ( jid = = null ) {
2014-11-29 18:09:28 +00:00
return null ;
}
2014-12-21 20:43:58 +00:00
for ( final Conversation conversation : haystack ) {
2014-10-28 16:15:35 +00:00
if ( ( account = = null | | conversation . getAccount ( ) = = account )
2014-12-21 20:43:58 +00:00
& & ( conversation . getJid ( ) . toBareJid ( ) . equals ( jid . toBareJid ( ) ) ) ) {
2014-07-20 00:26:23 +00:00
return conversation ;
2015-01-19 10:17:27 +00:00
}
2014-07-20 00:26:23 +00:00
}
return null ;
}
2014-08-26 14:52:42 +00:00
2017-02-14 15:50:33 +00:00
public Conversation findOrCreateConversation ( Account account , Jid jid , boolean muc ) {
return this . findOrCreateConversation ( account , jid , muc , false ) ;
2014-12-13 11:25:52 +00:00
}
2017-02-14 15:50:33 +00:00
public Conversation findOrCreateConversation ( final Account account , final Jid jid , final boolean muc , final boolean joinAfterCreate ) {
return this . findOrCreateConversation ( account , jid , muc , joinAfterCreate , null ) ;
}
public Conversation findOrCreateConversation ( final Account account , final Jid jid , final boolean muc , final boolean joinAfterCreate , final MessageArchiveService . Query query ) {
2014-11-18 02:10:59 +00:00
synchronized ( this . conversations ) {
Conversation conversation = find ( account , jid ) ;
if ( conversation ! = null ) {
return conversation ;
2014-02-10 14:24:34 +00:00
}
2014-11-18 02:10:59 +00:00
conversation = databaseBackend . findConversation ( account , jid ) ;
2017-02-07 13:03:23 +00:00
final boolean loadMessagesFromDb ;
2014-11-18 02:10:59 +00:00
if ( conversation ! = null ) {
conversation . setStatus ( Conversation . STATUS_AVAILABLE ) ;
conversation . setAccount ( account ) ;
if ( muc ) {
conversation . setMode ( Conversation . MODE_MULTI ) ;
2015-01-21 15:18:38 +00:00
conversation . setContactJid ( jid ) ;
2014-11-18 02:10:59 +00:00
} else {
conversation . setMode ( Conversation . MODE_SINGLE ) ;
2015-01-21 15:18:38 +00:00
conversation . setContactJid ( jid . toBareJid ( ) ) ;
2014-11-18 02:10:59 +00:00
}
2017-02-07 13:03:23 +00:00
databaseBackend . updateConversation ( conversation ) ;
loadMessagesFromDb = conversation . messagesLoaded . compareAndSet ( true , false ) ;
2014-02-07 15:50:29 +00:00
} else {
2014-11-18 02:10:59 +00:00
String conversationName ;
Contact contact = account . getRoster ( ) . getContact ( jid ) ;
if ( contact ! = null ) {
conversationName = contact . getDisplayName ( ) ;
} else {
conversationName = jid . getLocalpart ( ) ;
}
if ( muc ) {
conversation = new Conversation ( conversationName , account , jid ,
Conversation . MODE_MULTI ) ;
} else {
2015-01-21 15:18:38 +00:00
conversation = new Conversation ( conversationName , account , jid . toBareJid ( ) ,
2014-11-18 02:10:59 +00:00
Conversation . MODE_SINGLE ) ;
}
this . databaseBackend . createConversation ( conversation ) ;
2017-02-07 13:03:23 +00:00
loadMessagesFromDb = false ;
2014-02-07 15:50:29 +00:00
}
2017-02-07 13:03:23 +00:00
final Conversation c = conversation ;
mDatabaseExecutor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
if ( loadMessagesFromDb ) {
c . addAll ( 0 , databaseBackend . getMessages ( c , Config . PAGE_SIZE ) ) ;
updateConversationUi ( ) ;
c . messagesLoaded . set ( true ) ;
2015-01-04 17:16:55 +00:00
}
2017-02-07 13:03:23 +00:00
if ( account . getXmppConnection ( ) ! = null
& & account . getXmppConnection ( ) . getFeatures ( ) . mam ( )
& & ! muc ) {
if ( query = = null ) {
mMessageArchiveService . query ( c ) ;
} else {
if ( query . getConversation ( ) = = null ) {
mMessageArchiveService . query ( c , query . getStart ( ) ) ;
}
}
}
checkDeletedFiles ( c ) ;
2017-02-14 15:50:33 +00:00
if ( joinAfterCreate ) {
joinMuc ( c ) ;
}
2014-12-13 11:25:52 +00:00
}
2017-02-07 13:03:23 +00:00
} ) ;
2014-11-18 02:10:59 +00:00
this . conversations . add ( conversation ) ;
updateConversationUi ( ) ;
return conversation ;
2014-02-03 17:38:47 +00:00
}
}
public void archiveConversation ( Conversation conversation ) {
2015-04-02 22:06:37 +00:00
getNotificationService ( ) . clear ( conversation ) ;
2015-01-07 11:20:39 +00:00
conversation . setStatus ( Conversation . STATUS_ARCHIVED ) ;
2014-11-18 02:10:59 +00:00
synchronized ( this . conversations ) {
if ( conversation . getMode ( ) = = Conversation . MODE_MULTI ) {
if ( conversation . getAccount ( ) . getStatus ( ) = = Account . State . ONLINE ) {
Bookmark bookmark = conversation . getBookmark ( ) ;
2016-02-01 12:54:08 +00:00
if ( bookmark ! = null & & bookmark . autojoin ( ) & & respectAutojoin ( ) ) {
2014-11-18 02:10:59 +00:00
bookmark . setAutojoin ( false ) ;
pushBookmarks ( bookmark . getAccount ( ) ) ;
}
2014-10-08 12:10:37 +00:00
}
2014-11-18 02:10:59 +00:00
leaveMuc ( conversation ) ;
} else {
conversation . endOtrIfNeeded ( ) ;
2015-11-05 02:56:45 +00:00
if ( conversation . getContact ( ) . getOption ( Contact . Options . PENDING_SUBSCRIPTION_REQUEST ) ) {
Log . d ( Config . LOGTAG , " Canceling presence request from " + conversation . getJid ( ) . toString ( ) ) ;
sendPresencePacket (
conversation . getAccount ( ) ,
mPresenceGenerator . stopPresenceUpdatesTo ( conversation . getContact ( ) )
) ;
}
2014-07-15 12:32:19 +00:00
}
2016-10-18 11:06:24 +00:00
updateConversation ( conversation ) ;
2014-11-18 02:10:59 +00:00
this . conversations . remove ( conversation ) ;
updateConversationUi ( ) ;
2014-02-13 22:40:08 +00:00
}
2014-02-03 17:38:47 +00:00
}
2014-05-01 20:33:49 +00:00
2014-12-23 22:19:00 +00:00
public void createAccount ( final Account account ) {
2015-05-26 02:36:32 +00:00
account . initAccountServices ( this ) ;
2014-01-28 18:21:54 +00:00
databaseBackend . createAccount ( account ) ;
2014-02-04 14:09:50 +00:00
this . accounts . add ( account ) ;
2015-03-05 14:46:33 +00:00
this . reconnectAccountInBackground ( account ) ;
2014-07-12 11:42:17 +00:00
updateAccountUi ( ) ;
2014-01-28 18:21:54 +00:00
}
2014-02-23 20:33:37 +00:00
2015-10-09 11:37:08 +00:00
public void createAccountFromKey ( final String alias , final OnAccountCreated callback ) {
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
try {
X509Certificate [ ] chain = KeyChain . getCertificateChain ( XmppConnectionService . this , alias ) ;
2015-10-16 07:58:31 +00:00
Pair < Jid , String > info = CryptoHelper . extractJidAndName ( chain [ 0 ] ) ;
2015-10-11 13:48:58 +00:00
if ( findAccountByJid ( info . first ) = = null ) {
Account account = new Account ( info . first , " " ) ;
2015-10-09 11:37:08 +00:00
account . setPrivateKeyAlias ( alias ) ;
account . setOption ( Account . OPTION_DISABLED , true ) ;
2015-10-29 12:41:08 +00:00
account . setDisplayName ( info . second ) ;
2015-10-09 11:37:08 +00:00
createAccount ( account ) ;
callback . onAccountCreated ( account ) ;
2015-10-12 11:18:20 +00:00
if ( Config . X509_VERIFICATION ) {
try {
2016-12-05 20:52:44 +00:00
getMemorizingTrustManager ( ) . getNonInteractive ( account . getJid ( ) . getDomainpart ( ) ) . checkClientTrusted ( chain , " RSA " ) ;
2015-10-12 11:18:20 +00:00
} catch ( CertificateException e ) {
callback . informUser ( R . string . certificate_chain_is_not_trusted ) ;
}
2015-10-11 13:48:58 +00:00
}
2015-10-09 11:37:08 +00:00
} else {
callback . informUser ( R . string . account_already_exists ) ;
}
2015-10-12 11:18:20 +00:00
} catch ( Exception e ) {
2015-10-16 07:58:31 +00:00
e . printStackTrace ( ) ;
2015-10-09 11:37:08 +00:00
callback . informUser ( R . string . unable_to_parse_certificate ) ;
}
}
} ) . start ( ) ;
}
2015-10-11 13:48:58 +00:00
public void updateKeyInAccount ( final Account account , final String alias ) {
2017-02-24 18:58:46 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : update key in account " + alias ) ;
2015-10-11 13:48:58 +00:00
try {
X509Certificate [ ] chain = KeyChain . getCertificateChain ( XmppConnectionService . this , alias ) ;
2017-02-24 18:58:46 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " loaded certificate chain " ) ;
2015-10-11 13:48:58 +00:00
Pair < Jid , String > info = CryptoHelper . extractJidAndName ( chain [ 0 ] ) ;
if ( account . getJid ( ) . toBareJid ( ) . equals ( info . first ) ) {
account . setPrivateKeyAlias ( alias ) ;
2015-10-29 12:41:08 +00:00
account . setDisplayName ( info . second ) ;
2015-10-11 13:48:58 +00:00
databaseBackend . updateAccount ( account ) ;
2015-10-12 11:18:20 +00:00
if ( Config . X509_VERIFICATION ) {
try {
2017-02-24 18:58:46 +00:00
getMemorizingTrustManager ( ) . getNonInteractive ( ) . checkClientTrusted ( chain , " RSA " ) ;
2015-10-12 11:18:20 +00:00
} catch ( CertificateException e ) {
showErrorToastInUi ( R . string . certificate_chain_is_not_trusted ) ;
}
account . getAxolotlService ( ) . regenerateKeys ( true ) ;
2015-10-11 13:48:58 +00:00
}
} else {
showErrorToastInUi ( R . string . jid_does_not_match_certificate ) ;
}
2015-10-16 07:58:31 +00:00
} catch ( Exception e ) {
2015-10-11 13:48:58 +00:00
e . printStackTrace ( ) ;
}
}
2016-09-07 12:34:58 +00:00
public boolean updateAccount ( final Account account ) {
if ( databaseBackend . updateAccount ( account ) ) {
2016-10-04 09:16:59 +00:00
account . setShowErrorNotification ( true ) ;
2016-09-07 12:34:58 +00:00
this . statusListener . onStatusChanged ( account ) ;
databaseBackend . updateAccount ( account ) ;
reconnectAccountInBackground ( account ) ;
updateAccountUi ( ) ;
getNotificationService ( ) . updateErrorNotification ( ) ;
return true ;
} else {
return false ;
}
2014-01-28 18:21:54 +00:00
}
2014-12-25 21:08:13 +00:00
public void updateAccountPasswordOnServer ( final Account account , final String newPassword , final OnAccountPasswordChanged callback ) {
final IqPacket iq = getIqGenerator ( ) . generateSetPassword ( account , newPassword ) ;
sendIqPacket ( account , iq , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( final Account account , final IqPacket packet ) {
2014-12-30 13:16:25 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-12-25 21:08:13 +00:00
account . setPassword ( newPassword ) ;
2016-04-26 21:23:48 +00:00
account . setOption ( Account . OPTION_MAGIC_CREATE , false ) ;
2014-12-25 21:08:13 +00:00
databaseBackend . updateAccount ( account ) ;
callback . onPasswordChangeSucceeded ( ) ;
} else {
callback . onPasswordChangeFailed ( ) ;
2014-12-23 22:19:00 +00:00
}
2014-12-25 21:08:13 +00:00
}
} ) ;
}
2014-12-23 22:19:00 +00:00
public void deleteAccount ( final Account account ) {
2014-11-18 02:10:59 +00:00
synchronized ( this . conversations ) {
2014-12-23 22:19:00 +00:00
for ( final Conversation conversation : conversations ) {
2014-11-18 02:10:59 +00:00
if ( conversation . getAccount ( ) = = account ) {
if ( conversation . getMode ( ) = = Conversation . MODE_MULTI ) {
leaveMuc ( conversation ) ;
} else if ( conversation . getMode ( ) = = Conversation . MODE_SINGLE ) {
conversation . endOtrIfNeeded ( ) ;
}
conversations . remove ( conversation ) ;
2014-07-21 16:20:26 +00:00
}
}
2014-11-18 02:10:59 +00:00
if ( account . getXmppConnection ( ) ! = null ) {
2016-06-03 12:18:43 +00:00
new Thread ( new Runnable ( ) {
@Override
public void run ( ) {
disconnect ( account , true ) ;
}
2016-09-07 12:34:58 +00:00
} ) . start ( ) ;
2014-11-18 02:10:59 +00:00
}
2015-10-29 16:20:01 +00:00
Runnable runnable = new Runnable ( ) {
@Override
public void run ( ) {
2016-09-07 12:34:58 +00:00
if ( ! databaseBackend . deleteAccount ( account ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : unable to delete account " ) ;
}
2015-10-29 16:20:01 +00:00
}
} ;
mDatabaseExecutor . execute ( runnable ) ;
2014-11-18 02:10:59 +00:00
this . accounts . remove ( account ) ;
updateAccountUi ( ) ;
2014-11-18 14:26:28 +00:00
getNotificationService ( ) . updateErrorNotification ( ) ;
2014-07-21 16:20:26 +00:00
}
2014-01-28 18:21:54 +00:00
}
2014-02-03 17:38:47 +00:00
2014-12-03 13:55:09 +00:00
public void setOnConversationListChangedListener ( OnConversationUpdate listener ) {
synchronized ( this ) {
2016-06-04 14:16:14 +00:00
this . mLastActivity = System . currentTimeMillis ( ) ;
2014-10-15 12:41:27 +00:00
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnConversationUpdate = listener ;
this . mNotificationService . setIsInForeground ( true ) ;
2014-12-03 13:55:09 +00:00
if ( this . convChangedListenerCount < 2 ) {
this . convChangedListenerCount + + ;
2014-11-15 16:09:02 +00:00
}
2014-12-03 13:55:09 +00:00
}
}
2014-02-03 17:38:47 +00:00
2014-02-01 14:07:20 +00:00
public void removeOnConversationListChangedListener ( ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-10-15 12:41:27 +00:00
this . convChangedListenerCount - - ;
if ( this . convChangedListenerCount < = 0 ) {
this . convChangedListenerCount = 0 ;
this . mOnConversationUpdate = null ;
this . mNotificationService . setIsInForeground ( false ) ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
2014-08-26 15:43:44 +00:00
}
2014-03-29 19:29:03 +00:00
}
2014-02-01 14:07:20 +00:00
}
2014-02-05 21:33:39 +00:00
2015-07-10 11:28:50 +00:00
public void setOnShowErrorToastListener ( OnShowErrorToast onShowErrorToast ) {
synchronized ( this ) {
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnShowErrorToast = onShowErrorToast ;
if ( this . showErrorToastListenerCount < 2 ) {
this . showErrorToastListenerCount + + ;
}
}
this . mOnShowErrorToast = onShowErrorToast ;
}
public void removeOnShowErrorToastListener ( ) {
synchronized ( this ) {
this . showErrorToastListenerCount - - ;
if ( this . showErrorToastListenerCount < = 0 ) {
this . showErrorToastListenerCount = 0 ;
this . mOnShowErrorToast = null ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
}
}
}
2014-07-12 11:42:17 +00:00
public void setOnAccountListChangedListener ( OnAccountUpdate listener ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-10-15 12:41:27 +00:00
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnAccountUpdate = listener ;
2014-12-03 13:55:09 +00:00
if ( this . accountChangedListenerCount < 2 ) {
this . accountChangedListenerCount + + ;
}
2014-08-26 15:43:44 +00:00
}
2014-02-04 14:09:50 +00:00
}
2014-02-05 21:33:39 +00:00
2014-02-04 14:09:50 +00:00
public void removeOnAccountListChangedListener ( ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-10-15 12:41:27 +00:00
this . accountChangedListenerCount - - ;
if ( this . accountChangedListenerCount < = 0 ) {
this . mOnAccountUpdate = null ;
this . accountChangedListenerCount = 0 ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
2014-08-26 15:43:44 +00:00
}
2014-08-15 15:31:24 +00:00
}
2014-02-04 14:09:50 +00:00
}
2014-08-26 14:52:42 +00:00
2015-10-11 11:11:50 +00:00
public void setOnCaptchaRequestedListener ( OnCaptchaRequested listener ) {
synchronized ( this ) {
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnCaptchaRequested = listener ;
if ( this . captchaRequestedListenerCount < 2 ) {
this . captchaRequestedListenerCount + + ;
}
}
}
public void removeOnCaptchaRequestedListener ( ) {
synchronized ( this ) {
this . captchaRequestedListenerCount - - ;
if ( this . captchaRequestedListenerCount < = 0 ) {
this . mOnCaptchaRequested = null ;
this . captchaRequestedListenerCount = 0 ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
}
}
}
2014-12-21 20:43:58 +00:00
public void setOnRosterUpdateListener ( final OnRosterUpdate listener ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-10-15 12:41:27 +00:00
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnRosterUpdate = listener ;
2014-12-03 13:55:09 +00:00
if ( this . rosterChangedListenerCount < 2 ) {
this . rosterChangedListenerCount + + ;
}
2014-08-26 15:43:44 +00:00
}
2014-07-18 13:35:31 +00:00
}
public void removeOnRosterUpdateListener ( ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-10-15 12:41:27 +00:00
this . rosterChangedListenerCount - - ;
if ( this . rosterChangedListenerCount < = 0 ) {
this . rosterChangedListenerCount = 0 ;
this . mOnRosterUpdate = null ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
2014-09-10 15:59:57 +00:00
}
2014-08-26 15:43:44 +00:00
}
}
2014-08-30 07:24:58 +00:00
2014-12-21 20:43:58 +00:00
public void setOnUpdateBlocklistListener ( final OnUpdateBlocklist listener ) {
synchronized ( this ) {
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnUpdateBlocklist = listener ;
2015-07-20 12:12:24 +00:00
if ( this . updateBlocklistListenerCount < 2 ) {
this . updateBlocklistListenerCount + + ;
2014-12-21 20:43:58 +00:00
}
}
}
public void removeOnUpdateBlocklistListener ( ) {
synchronized ( this ) {
2015-07-20 12:12:24 +00:00
this . updateBlocklistListenerCount - - ;
if ( this . updateBlocklistListenerCount < = 0 ) {
this . updateBlocklistListenerCount = 0 ;
2014-12-21 20:43:58 +00:00
this . mOnUpdateBlocklist = null ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
}
}
}
2015-07-21 12:18:16 +00:00
public void setOnKeyStatusUpdatedListener ( final OnKeyStatusUpdated listener ) {
2015-07-19 16:36:28 +00:00
synchronized ( this ) {
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
2015-07-21 12:18:16 +00:00
this . mOnKeyStatusUpdated = listener ;
if ( this . keyStatusUpdatedListenerCount < 2 ) {
this . keyStatusUpdatedListenerCount + + ;
2015-07-19 16:36:28 +00:00
}
}
}
public void removeOnNewKeysAvailableListener ( ) {
synchronized ( this ) {
2015-07-21 12:18:16 +00:00
this . keyStatusUpdatedListenerCount - - ;
if ( this . keyStatusUpdatedListenerCount < = 0 ) {
this . keyStatusUpdatedListenerCount = 0 ;
this . mOnKeyStatusUpdated = null ;
2015-07-19 16:36:28 +00:00
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
}
}
}
2015-07-21 12:18:16 +00:00
2014-11-23 14:19:44 +00:00
public void setOnMucRosterUpdateListener ( OnMucRosterUpdate listener ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-11-23 14:19:44 +00:00
if ( checkListeners ( ) ) {
switchToForeground ( ) ;
}
this . mOnMucRosterUpdate = listener ;
2014-12-03 13:55:09 +00:00
if ( this . mucRosterChangedListenerCount < 2 ) {
this . mucRosterChangedListenerCount + + ;
}
2014-11-23 14:19:44 +00:00
}
}
public void removeOnMucRosterUpdateListener ( ) {
2014-12-03 13:55:09 +00:00
synchronized ( this ) {
2014-11-23 14:19:44 +00:00
this . mucRosterChangedListenerCount - - ;
if ( this . mucRosterChangedListenerCount < = 0 ) {
this . mucRosterChangedListenerCount = 0 ;
this . mOnMucRosterUpdate = null ;
if ( checkListeners ( ) ) {
switchToBackground ( ) ;
}
}
}
}
2016-05-19 08:41:56 +00:00
public boolean checkListeners ( ) {
2014-08-30 07:24:58 +00:00
return ( this . mOnAccountUpdate = = null
2015-01-02 11:04:33 +00:00
& & this . mOnConversationUpdate = = null
& & this . mOnRosterUpdate = = null
2015-10-11 11:11:50 +00:00
& & this . mOnCaptchaRequested = = null
2015-07-10 11:28:50 +00:00
& & this . mOnUpdateBlocklist = = null
2015-07-19 16:36:28 +00:00
& & this . mOnShowErrorToast = = null
2015-07-21 12:18:16 +00:00
& & this . mOnKeyStatusUpdated = = null ) ;
2014-08-26 15:43:44 +00:00
}
2014-08-30 07:24:58 +00:00
2014-08-26 15:43:44 +00:00
private void switchToForeground ( ) {
2016-06-04 14:16:14 +00:00
final boolean broadcastLastActivity = broadcastLastActivity ( ) ;
2015-10-19 21:22:29 +00:00
for ( Conversation conversation : getConversations ( ) ) {
conversation . setIncomingChatState ( ChatState . ACTIVE ) ;
}
2014-08-30 07:24:58 +00:00
for ( Account account : getAccounts ( ) ) {
2014-11-15 16:09:02 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2016-06-01 19:30:50 +00:00
account . deactivateGracePeriod ( ) ;
2016-06-04 14:16:14 +00:00
final XmppConnection connection = account . getXmppConnection ( ) ;
if ( connection ! = null ) {
if ( connection . getFeatures ( ) . csi ( ) ) {
connection . sendActive ( ) ;
}
if ( broadcastLastActivity ) {
sendPresence ( account , false ) ; //send new presence but don't include idle because we are not
}
2014-08-26 15:43:44 +00:00
}
}
}
2014-10-20 19:08:33 +00:00
Log . d ( Config . LOGTAG , " app switched into foreground " ) ;
2014-08-26 15:43:44 +00:00
}
2014-08-30 07:24:58 +00:00
2014-08-26 15:43:44 +00:00
private void switchToBackground ( ) {
2016-06-04 14:16:14 +00:00
final boolean broadcastLastActivity = broadcastLastActivity ( ) ;
2014-08-30 07:24:58 +00:00
for ( Account account : getAccounts ( ) ) {
2014-11-15 16:09:02 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2014-08-26 15:43:44 +00:00
XmppConnection connection = account . getXmppConnection ( ) ;
2016-02-14 12:20:23 +00:00
if ( connection ! = null ) {
2016-06-04 14:16:14 +00:00
if ( broadcastLastActivity ) {
sendPresence ( account , broadcastLastActivity ) ;
}
2016-06-04 20:42:12 +00:00
if ( connection . getFeatures ( ) . csi ( ) ) {
connection . sendInactive ( ) ;
}
2014-08-26 15:43:44 +00:00
}
}
}
2014-10-16 17:10:37 +00:00
this . mNotificationService . setIsInForeground ( false ) ;
2014-10-20 19:08:33 +00:00
Log . d ( Config . LOGTAG , " app switched into background " ) ;
2014-07-18 13:35:31 +00:00
}
2014-10-15 12:41:27 +00:00
2014-12-15 22:06:29 +00:00
private void connectMultiModeConversations ( Account account ) {
2014-02-05 21:33:39 +00:00
List < Conversation > conversations = getConversations ( ) ;
2014-11-10 00:24:35 +00:00
for ( Conversation conversation : conversations ) {
2015-10-01 11:03:15 +00:00
if ( conversation . getMode ( ) = = Conversation . MODE_MULTI & & conversation . getAccount ( ) = = account ) {
2016-02-17 15:50:48 +00:00
joinMuc ( conversation ) ;
2015-01-19 10:17:27 +00:00
}
2014-11-10 00:24:35 +00:00
}
2014-02-05 21:33:39 +00:00
}
2014-02-08 23:47:11 +00:00
2014-02-13 22:40:08 +00:00
public void joinMuc ( Conversation conversation ) {
2017-01-09 18:54:27 +00:00
joinMuc ( conversation , null , false ) ;
}
public void joinMuc ( Conversation conversation , boolean followedInvite ) {
joinMuc ( conversation , null , followedInvite ) ;
2015-10-01 14:01:19 +00:00
}
2016-02-17 15:50:48 +00:00
private void joinMuc ( Conversation conversation , final OnConferenceJoined onConferenceJoined ) {
2017-01-09 18:54:27 +00:00
joinMuc ( conversation , onConferenceJoined , false ) ;
}
private void joinMuc ( Conversation conversation , final OnConferenceJoined onConferenceJoined , final boolean followedInvite ) {
2014-05-22 13:36:41 +00:00
Account account = conversation . getAccount ( ) ;
2014-07-18 19:57:10 +00:00
account . pendingConferenceJoins . remove ( conversation ) ;
account . pendingConferenceLeaves . remove ( conversation ) ;
2016-02-17 15:50:48 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2015-10-01 11:03:15 +00:00
conversation . resetMucOptions ( ) ;
2016-05-27 08:35:00 +00:00
if ( onConferenceJoined ! = null ) {
conversation . getMucOptions ( ) . flagNoAutoPushConfiguration ( ) ;
}
2016-02-23 15:15:55 +00:00
conversation . setHasMessagesLeftOnServer ( false ) ;
2015-10-04 22:45:16 +00:00
fetchConferenceConfiguration ( conversation , new OnConferenceConfigurationFetched ( ) {
2015-10-22 09:20:36 +00:00
private void join ( Conversation conversation ) {
2015-10-04 22:45:16 +00:00
Account account = conversation . getAccount ( ) ;
2016-02-29 12:18:07 +00:00
final MucOptions mucOptions = conversation . getMucOptions ( ) ;
2016-02-29 15:32:24 +00:00
final Jid joinJid = mucOptions . getSelf ( ) . getFullJid ( ) ;
2015-10-04 22:45:16 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) . toString ( ) + " : joining conversation " + joinJid . toString ( ) ) ;
2016-10-19 10:31:11 +00:00
PresencePacket packet = mPresenceGenerator . selfPresence ( account , Presence . Status . ONLINE , mucOptions . nonanonymous ( ) ) ;
2015-10-04 22:45:16 +00:00
packet . setTo ( joinJid ) ;
Element x = packet . addChild ( " x " , " http://jabber.org/protocol/muc " ) ;
if ( conversation . getMucOptions ( ) . getPassword ( ) ! = null ) {
2016-10-19 10:31:11 +00:00
x . addChild ( " password " ) . setContent ( mucOptions . getPassword ( ) ) ;
2015-10-04 22:45:16 +00:00
}
2016-02-29 12:18:07 +00:00
if ( mucOptions . mamSupport ( ) ) {
2015-10-04 22:45:16 +00:00
// Use MAM instead of the limited muc history to get history
x . addChild ( " history " ) . setAttribute ( " maxchars " , " 0 " ) ;
} else {
// Fallback to muc history
x . addChild ( " history " ) . setAttribute ( " since " , PresenceGenerator . getTimestamp ( conversation . getLastMessageTransmitted ( ) ) ) ;
}
sendPresencePacket ( account , packet ) ;
2015-11-25 19:47:02 +00:00
if ( onConferenceJoined ! = null ) {
onConferenceJoined . onConferenceJoined ( conversation ) ;
}
2015-10-04 22:45:16 +00:00
if ( ! joinJid . equals ( conversation . getJid ( ) ) ) {
conversation . setContactJid ( joinJid ) ;
databaseBackend . updateConversation ( conversation ) ;
}
2016-02-29 12:18:07 +00:00
if ( mucOptions . mamSupport ( ) ) {
2015-10-06 14:58:56 +00:00
getMessageArchiveService ( ) . catchupMUC ( conversation ) ;
}
2016-02-29 12:18:07 +00:00
if ( mucOptions . membersOnly ( ) & & mucOptions . nonanonymous ( ) ) {
fetchConferenceMembers ( conversation ) ;
2017-01-09 18:54:27 +00:00
if ( followedInvite & & conversation . getBookmark ( ) = = null ) {
saveConversationAsBookmark ( conversation , null ) ;
}
2016-02-29 12:18:07 +00:00
}
2016-02-10 08:53:48 +00:00
sendUnsentMessages ( conversation ) ;
2015-10-04 22:45:16 +00:00
}
2015-10-22 09:20:36 +00:00
@Override
public void onConferenceConfigurationFetched ( Conversation conversation ) {
join ( conversation ) ;
}
@Override
public void onFetchFailed ( final Conversation conversation , Element error ) {
2016-09-06 10:15:08 +00:00
if ( error ! = null & & " remote-server-not-found " . equals ( error . getName ( ) ) ) {
2016-09-09 09:04:05 +00:00
conversation . getMucOptions ( ) . setError ( MucOptions . Error . SERVER_NOT_FOUND ) ;
2016-09-06 10:15:08 +00:00
} else {
join ( conversation ) ;
fetchConferenceConfiguration ( conversation ) ;
}
2015-10-22 09:20:36 +00:00
}
2015-10-04 22:45:16 +00:00
} ) ;
2016-02-22 19:19:58 +00:00
updateConversationUi ( ) ;
2014-07-18 19:57:10 +00:00
} else {
account . pendingConferenceJoins . add ( conversation ) ;
2016-02-22 19:19:58 +00:00
conversation . resetMucOptions ( ) ;
2016-02-23 15:15:55 +00:00
conversation . setHasMessagesLeftOnServer ( false ) ;
2016-02-22 19:19:58 +00:00
updateConversationUi ( ) ;
2014-02-11 14:34:24 +00:00
}
2014-02-07 15:50:29 +00:00
}
2014-03-11 14:44:22 +00:00
2016-02-29 12:18:07 +00:00
private void fetchConferenceMembers ( final Conversation conversation ) {
final Account account = conversation . getAccount ( ) ;
final String [ ] affiliations = { " member " , " admin " , " owner " } ;
OnIqPacketReceived callback = new OnIqPacketReceived ( ) {
private int i = 0 ;
2017-01-22 17:58:49 +00:00
private boolean success = true ;
2016-02-29 12:18:07 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2016-05-16 12:10:40 +00:00
2016-02-29 12:18:07 +00:00
Element query = packet . query ( " http://jabber.org/protocol/muc#admin " ) ;
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT & & query ! = null ) {
for ( Element child : query . getChildren ( ) ) {
if ( " item " . equals ( child . getName ( ) ) ) {
2016-05-17 12:25:58 +00:00
MucOptions . User user = AbstractParser . parseItem ( conversation , child ) ;
if ( ! user . realJidMatchesAccount ( ) ) {
2016-11-13 18:25:58 +00:00
conversation . getMucOptions ( ) . updateUser ( user ) ;
2016-05-16 12:10:40 +00:00
}
2016-02-29 12:18:07 +00:00
}
}
} else {
2017-01-22 17:58:49 +00:00
success = false ;
2016-02-29 12:18:07 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : could not request affiliation " + affiliations [ i ] + " in " + conversation . getJid ( ) . toBareJid ( ) ) ;
}
+ + i ;
if ( i > = affiliations . length ) {
2017-01-22 17:58:49 +00:00
List < Jid > members = conversation . getMucOptions ( ) . getMembers ( ) ;
if ( success ) {
List < Jid > cryptoTargets = conversation . getAcceptedCryptoTargets ( ) ;
boolean changed = false ;
for ( ListIterator < Jid > iterator = cryptoTargets . listIterator ( ) ; iterator . hasNext ( ) ; ) {
Jid jid = iterator . next ( ) ;
if ( ! members . contains ( jid ) ) {
iterator . remove ( ) ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : removed " + jid + " from crypto targets of " + conversation . getName ( ) ) ;
changed = true ;
}
}
if ( changed ) {
conversation . setAcceptedCryptoTargets ( cryptoTargets ) ;
updateConversation ( conversation ) ;
}
}
2016-02-29 12:18:07 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : retrieved members for " + conversation . getJid ( ) . toBareJid ( ) + " : " + conversation . getMucOptions ( ) . getMembers ( ) ) ;
2016-11-12 19:21:11 +00:00
getAvatarService ( ) . clear ( conversation ) ;
updateMucRosterUi ( ) ;
updateConversationUi ( ) ;
2016-02-29 12:18:07 +00:00
}
}
} ;
for ( String affiliation : affiliations ) {
sendIqPacket ( account , mIqGenerator . queryAffiliation ( conversation , affiliation ) , callback ) ;
}
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : fetching members for " + conversation . getName ( ) ) ;
}
2014-09-03 17:35:45 +00:00
public void providePasswordForMuc ( Conversation conversation , String password ) {
if ( conversation . getMode ( ) = = Conversation . MODE_MULTI ) {
conversation . getMucOptions ( ) . setPassword ( password ) ;
2014-10-05 22:33:52 +00:00
if ( conversation . getBookmark ( ) ! = null ) {
2016-02-01 12:54:08 +00:00
if ( respectAutojoin ( ) ) {
conversation . getBookmark ( ) . setAutojoin ( true ) ;
}
2014-09-07 12:06:23 +00:00
pushBookmarks ( conversation . getAccount ( ) ) ;
}
2016-10-18 11:06:24 +00:00
updateConversation ( conversation ) ;
2014-09-03 17:35:45 +00:00
joinMuc ( conversation ) ;
}
}
2014-03-11 14:44:22 +00:00
2014-11-20 17:20:42 +00:00
public void renameInMuc ( final Conversation conversation , final String nick , final UiCallback < Conversation > callback ) {
2014-03-03 04:01:02 +00:00
final MucOptions options = conversation . getMucOptions ( ) ;
2014-11-20 17:20:42 +00:00
final Jid joinJid = options . createJoinJid ( nick ) ;
2014-03-03 04:01:02 +00:00
if ( options . online ( ) ) {
2014-06-03 09:04:17 +00:00
Account account = conversation . getAccount ( ) ;
2014-03-03 04:01:02 +00:00
options . setOnRenameListener ( new OnRenameListener ( ) {
2014-03-11 14:44:22 +00:00
2014-03-03 04:01:02 +00:00
@Override
2014-11-20 17:20:42 +00:00
public void onSuccess ( ) {
conversation . setContactJid ( joinJid ) ;
databaseBackend . updateConversation ( conversation ) ;
Bookmark bookmark = conversation . getBookmark ( ) ;
if ( bookmark ! = null ) {
bookmark . setNick ( nick ) ;
pushBookmarks ( bookmark . getAccount ( ) ) ;
2014-03-04 03:09:15 +00:00
}
2014-11-20 17:20:42 +00:00
callback . success ( conversation ) ;
}
@Override
public void onFailure ( ) {
2014-12-13 11:25:52 +00:00
callback . error ( R . string . nick_in_use , conversation ) ;
2014-03-03 04:01:02 +00:00
}
} ) ;
2014-11-20 17:20:42 +00:00
2014-03-03 04:01:02 +00:00
PresencePacket packet = new PresencePacket ( ) ;
2014-11-20 17:20:42 +00:00
packet . setTo ( joinJid ) ;
2014-11-09 15:57:22 +00:00
packet . setFrom ( conversation . getAccount ( ) . getJid ( ) ) ;
2014-06-03 13:48:51 +00:00
2014-06-03 09:04:17 +00:00
String sig = account . getPgpSignature ( ) ;
if ( sig ! = null ) {
packet . addChild ( " status " ) . setContent ( " online " ) ;
packet . addChild ( " x " , " jabber:x:signed " ) . setContent ( sig ) ;
}
2014-08-26 14:52:42 +00:00
sendPresencePacket ( account , packet ) ;
2014-03-03 04:01:02 +00:00
} else {
2014-11-20 17:20:42 +00:00
conversation . setContactJid ( joinJid ) ;
2014-03-03 04:01:02 +00:00
databaseBackend . updateConversation ( conversation ) ;
2014-11-15 16:09:02 +00:00
if ( conversation . getAccount ( ) . getStatus ( ) = = Account . State . ONLINE ) {
2014-07-15 15:11:43 +00:00
Bookmark bookmark = conversation . getBookmark ( ) ;
2014-08-26 14:52:42 +00:00
if ( bookmark ! = null ) {
2014-07-15 15:11:43 +00:00
bookmark . setNick ( nick ) ;
pushBookmarks ( bookmark . getAccount ( ) ) ;
}
2014-03-03 04:01:02 +00:00
joinMuc ( conversation ) ;
}
}
}
2014-02-05 21:33:39 +00:00
2014-02-13 22:40:08 +00:00
public void leaveMuc ( Conversation conversation ) {
2015-10-16 07:58:31 +00:00
leaveMuc ( conversation , false ) ;
}
private void leaveMuc ( Conversation conversation , boolean now ) {
2014-07-18 19:57:10 +00:00
Account account = conversation . getAccount ( ) ;
account . pendingConferenceJoins . remove ( conversation ) ;
account . pendingConferenceLeaves . remove ( conversation ) ;
2015-10-16 07:58:31 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE | | now ) {
2014-07-18 19:57:10 +00:00
PresencePacket packet = new PresencePacket ( ) ;
2016-02-29 15:32:24 +00:00
packet . setTo ( conversation . getMucOptions ( ) . getSelf ( ) . getFullJid ( ) ) ;
2014-11-09 15:57:22 +00:00
packet . setFrom ( conversation . getAccount ( ) . getJid ( ) ) ;
2014-07-18 19:57:10 +00:00
packet . setAttribute ( " type " , " unavailable " ) ;
2014-08-26 14:52:42 +00:00
sendPresencePacket ( conversation . getAccount ( ) , packet ) ;
2014-07-18 19:57:10 +00:00
conversation . getMucOptions ( ) . setOffline ( ) ;
conversation . deregisterWithBookmark ( ) ;
2014-11-09 15:57:22 +00:00
Log . d ( Config . LOGTAG , conversation . getAccount ( ) . getJid ( ) . toBareJid ( )
2014-12-21 20:43:58 +00:00
+ " : leaving muc " + conversation . getJid ( ) ) ;
2014-07-18 19:57:10 +00:00
} else {
account . pendingConferenceLeaves . add ( conversation ) ;
}
2014-02-13 22:40:08 +00:00
}
2014-02-05 21:33:39 +00:00
2017-02-13 16:32:26 +00:00
public String findConferenceServer ( final Account account ) {
2014-11-20 17:20:42 +00:00
String server ;
if ( account . getXmppConnection ( ) ! = null ) {
server = account . getXmppConnection ( ) . getMucServer ( ) ;
if ( server ! = null ) {
return server ;
}
}
2015-01-19 10:17:27 +00:00
for ( Account other : getAccounts ( ) ) {
2014-11-20 17:20:42 +00:00
if ( other ! = account & & other . getXmppConnection ( ) ! = null ) {
server = other . getXmppConnection ( ) . getMucServer ( ) ;
if ( server ! = null ) {
return server ;
}
}
}
return null ;
}
2017-02-13 16:32:26 +00:00
public boolean createAdhocConference ( final Account account ,
2016-05-26 10:39:31 +00:00
final String subject ,
final Iterable < Jid > jids ,
final UiCallback < Conversation > callback ) {
2014-12-21 01:13:13 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) . toString ( ) + " : creating adhoc conference with " + jids . toString ( ) ) ;
2014-11-20 17:20:42 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
try {
String server = findConferenceServer ( account ) ;
if ( server = = null ) {
if ( callback ! = null ) {
2015-01-19 10:17:27 +00:00
callback . error ( R . string . no_conference_server_found , null ) ;
2014-11-20 17:20:42 +00:00
}
2017-02-13 16:32:26 +00:00
return false ;
2014-11-20 17:20:42 +00:00
}
2016-05-26 20:53:55 +00:00
final Jid jid = Jid . fromParts ( new BigInteger ( 64 , getRNG ( ) ) . toString ( Character . MAX_RADIX ) , server , null ) ;
2017-02-14 15:50:33 +00:00
final Conversation conversation = findOrCreateConversation ( account , jid , true , false ) ;
2016-02-17 15:50:48 +00:00
joinMuc ( conversation , new OnConferenceJoined ( ) {
2014-11-20 17:20:42 +00:00
@Override
2015-11-25 19:47:02 +00:00
public void onConferenceJoined ( final Conversation conversation ) {
2017-02-24 18:58:46 +00:00
pushConferenceConfiguration ( conversation , IqGenerator . defaultRoomConfiguration ( ) , new OnConfigurationPushed ( ) {
2015-11-25 19:47:02 +00:00
@Override
public void onPushSucceeded ( ) {
2016-05-26 10:39:31 +00:00
if ( subject ! = null & & ! subject . trim ( ) . isEmpty ( ) ) {
2016-05-26 20:53:55 +00:00
pushSubjectToConference ( conversation , subject . trim ( ) ) ;
2016-05-26 10:39:31 +00:00
}
2015-11-25 19:47:02 +00:00
for ( Jid invite : jids ) {
invite ( conversation , invite ) ;
}
if ( account . countPresences ( ) > 1 ) {
directInvite ( conversation , account . getJid ( ) . toBareJid ( ) ) ;
}
2016-05-26 20:53:55 +00:00
saveConversationAsBookmark ( conversation , subject ) ;
2015-11-25 19:47:02 +00:00
if ( callback ! = null ) {
callback . success ( conversation ) ;
}
}
2014-11-20 17:20:42 +00:00
2015-11-25 19:47:02 +00:00
@Override
public void onPushFailed ( ) {
2016-05-26 20:53:55 +00:00
archiveConversation ( conversation ) ;
2015-11-25 19:47:02 +00:00
if ( callback ! = null ) {
callback . error ( R . string . conference_creation_failed , conversation ) ;
}
}
} ) ;
2014-11-20 17:20:42 +00:00
}
} ) ;
2017-02-13 16:32:26 +00:00
return true ;
2014-11-20 17:20:42 +00:00
} catch ( InvalidJidException e ) {
if ( callback ! = null ) {
callback . error ( R . string . conference_creation_failed , null ) ;
}
2017-02-13 16:32:26 +00:00
return false ;
2014-11-20 17:20:42 +00:00
}
} else {
if ( callback ! = null ) {
2015-01-19 10:17:27 +00:00
callback . error ( R . string . not_connected_try_again , null ) ;
2014-11-20 17:20:42 +00:00
}
2017-02-13 16:32:26 +00:00
return false ;
2014-11-20 17:20:42 +00:00
}
}
2015-01-07 17:34:24 +00:00
public void fetchConferenceConfiguration ( final Conversation conversation ) {
2015-10-04 22:45:16 +00:00
fetchConferenceConfiguration ( conversation , null ) ;
}
public void fetchConferenceConfiguration ( final Conversation conversation , final OnConferenceConfigurationFetched callback ) {
2015-01-07 17:34:24 +00:00
IqPacket request = new IqPacket ( IqPacket . TYPE . GET ) ;
request . setTo ( conversation . getJid ( ) . toBareJid ( ) ) ;
request . query ( " http://jabber.org/protocol/disco#info " ) ;
sendIqPacket ( conversation . getAccount ( ) , request , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2016-02-29 15:32:24 +00:00
Element query = packet . findChild ( " query " , " http://jabber.org/protocol/disco#info " ) ;
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT & & query ! = null ) {
2015-01-08 13:45:44 +00:00
ArrayList < String > features = new ArrayList < > ( ) ;
2015-11-26 16:44:29 +00:00
for ( Element child : query . getChildren ( ) ) {
2015-01-07 17:34:24 +00:00
if ( child ! = null & & child . getName ( ) . equals ( " feature " ) ) {
String var = child . getAttribute ( " var " ) ;
if ( var ! = null ) {
features . add ( var ) ;
}
}
}
2016-01-25 20:17:53 +00:00
Element form = query . findChild ( " x " , " jabber:x:data " ) ;
2015-11-26 16:44:29 +00:00
if ( form ! = null ) {
conversation . getMucOptions ( ) . updateFormData ( Data . parse ( form ) ) ;
}
2015-01-07 17:34:24 +00:00
conversation . getMucOptions ( ) . updateFeatures ( features ) ;
2015-10-04 22:45:16 +00:00
if ( callback ! = null ) {
callback . onConferenceConfigurationFetched ( conversation ) ;
}
2016-02-29 15:32:24 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : fetched muc configuration for " + conversation . getJid ( ) . toBareJid ( ) + " - " + features . toString ( ) ) ;
2015-01-08 20:29:26 +00:00
updateConversationUi ( ) ;
2015-10-22 09:20:36 +00:00
} else if ( packet . getType ( ) = = IqPacket . TYPE . ERROR ) {
if ( callback ! = null ) {
callback . onFetchFailed ( conversation , packet . getError ( ) ) ;
}
2015-01-07 17:34:24 +00:00
}
}
} ) ;
}
2017-02-24 18:58:46 +00:00
public void pushNodeConfiguration ( Account account , final Jid jid , final String node , final Bundle options , final OnConfigurationPushed callback ) {
sendIqPacket ( account , mIqGenerator . requestPubsubConfiguration ( jid , node ) , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
Element pubsub = packet . findChild ( " pubsub " , " http://jabber.org/protocol/pubsub#owner " ) ;
Element configuration = pubsub = = null ? null : pubsub . findChild ( " configure " ) ;
Element x = configuration = = null ? null : configuration . findChild ( " x " , " jabber:x:data " ) ;
if ( x ! = null ) {
Data data = Data . parse ( x ) ;
data . submit ( options ) ;
sendIqPacket ( account , mIqGenerator . publishPubsubConfiguration ( jid , node , data ) , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
callback . onPushSucceeded ( ) ;
} else {
Log . d ( Config . LOGTAG , packet . toString ( ) ) ;
}
}
} ) ;
} else {
callback . onPushFailed ( ) ;
}
} else {
callback . onPushFailed ( ) ;
}
}
} ) ;
}
public void pushConferenceConfiguration ( final Conversation conversation , final Bundle options , final OnConfigurationPushed callback ) {
2014-12-30 13:16:25 +00:00
IqPacket request = new IqPacket ( IqPacket . TYPE . GET ) ;
2014-12-21 20:43:58 +00:00
request . setTo ( conversation . getJid ( ) . toBareJid ( ) ) ;
2014-11-20 17:20:42 +00:00
request . query ( " http://jabber.org/protocol/muc#owner " ) ;
2015-01-19 10:17:27 +00:00
sendIqPacket ( conversation . getAccount ( ) , request , new OnIqPacketReceived ( ) {
2014-11-20 17:20:42 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-08-23 15:53:23 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-11-20 17:20:42 +00:00
Data data = Data . parse ( packet . query ( ) . findChild ( " x " , " jabber:x:data " ) ) ;
2017-02-24 18:58:46 +00:00
data . submit ( options ) ;
2014-12-30 13:16:25 +00:00
IqPacket set = new IqPacket ( IqPacket . TYPE . SET ) ;
2014-12-21 20:43:58 +00:00
set . setTo ( conversation . getJid ( ) . toBareJid ( ) ) ;
2014-11-20 17:20:42 +00:00
set . query ( " http://jabber.org/protocol/muc#owner " ) . addChild ( data ) ;
sendIqPacket ( account , set , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-08-23 15:53:23 +00:00
if ( callback ! = null ) {
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-11-20 17:20:42 +00:00
callback . onPushSucceeded ( ) ;
2015-08-23 15:53:23 +00:00
} else {
2014-11-20 17:20:42 +00:00
callback . onPushFailed ( ) ;
}
}
}
} ) ;
} else {
if ( callback ! = null ) {
callback . onPushFailed ( ) ;
}
}
}
} ) ;
}
2015-01-10 22:10:32 +00:00
public void pushSubjectToConference ( final Conversation conference , final String subject ) {
MessagePacket packet = this . getMessageGenerator ( ) . conferenceSubject ( conference , subject ) ;
this . sendMessagePacket ( conference . getAccount ( ) , packet ) ;
final MucOptions mucOptions = conference . getMucOptions ( ) ;
final MucOptions . User self = mucOptions . getSelf ( ) ;
if ( ! mucOptions . persistent ( ) & & self . getAffiliation ( ) . ranks ( MucOptions . Affiliation . OWNER ) ) {
Bundle options = new Bundle ( ) ;
options . putString ( " muc#roomconfig_persistentroom " , " 1 " ) ;
2015-01-19 10:17:27 +00:00
this . pushConferenceConfiguration ( conference , options , null ) ;
2015-01-10 22:10:32 +00:00
}
}
2016-05-16 12:10:40 +00:00
public void changeAffiliationInConference ( final Conversation conference , Jid user , final MucOptions . Affiliation affiliation , final OnAffiliationChanged callback ) {
2015-01-07 14:03:29 +00:00
final Jid jid = user . toBareJid ( ) ;
IqPacket request = this . mIqGenerator . changeAffiliation ( conference , jid , affiliation . toString ( ) ) ;
2015-01-07 17:34:24 +00:00
sendIqPacket ( conference . getAccount ( ) , request , new OnIqPacketReceived ( ) {
2015-01-07 14:03:29 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2016-05-16 12:10:40 +00:00
conference . getMucOptions ( ) . changeAffiliation ( jid , affiliation ) ;
2016-05-16 13:50:57 +00:00
getAvatarService ( ) . clear ( conference ) ;
2015-01-07 14:03:29 +00:00
callback . onAffiliationChangedSuccessful ( jid ) ;
} else {
2015-01-07 17:34:24 +00:00
callback . onAffiliationChangeFailed ( jid , R . string . could_not_change_affiliation ) ;
2015-01-07 14:03:29 +00:00
}
}
} ) ;
}
2015-01-09 12:28:01 +00:00
public void changeAffiliationsInConference ( final Conversation conference , MucOptions . Affiliation before , MucOptions . Affiliation after ) {
List < Jid > jids = new ArrayList < > ( ) ;
2015-01-19 10:17:27 +00:00
for ( MucOptions . User user : conference . getMucOptions ( ) . getUsers ( ) ) {
2016-05-16 12:10:40 +00:00
if ( user . getAffiliation ( ) = = before & & user . getRealJid ( ) ! = null ) {
jids . add ( user . getRealJid ( ) ) ;
2015-01-09 12:28:01 +00:00
}
}
IqPacket request = this . mIqGenerator . changeAffiliation ( conference , jids , after . toString ( ) ) ;
2015-05-26 09:31:33 +00:00
sendIqPacket ( conference . getAccount ( ) , request , mDefaultIqHandler ) ;
2015-01-09 12:28:01 +00:00
}
2015-01-08 00:23:53 +00:00
public void changeRoleInConference ( final Conversation conference , final String nick , MucOptions . Role role , final OnRoleChanged callback ) {
IqPacket request = this . mIqGenerator . changeRole ( conference , nick , role . toString ( ) ) ;
2015-01-19 10:17:27 +00:00
Log . d ( Config . LOGTAG , request . toString ( ) ) ;
2015-01-08 00:23:53 +00:00
sendIqPacket ( conference . getAccount ( ) , request , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
Log . d ( Config . LOGTAG , packet . toString ( ) ) ;
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
callback . onRoleChangedSuccessful ( nick ) ;
} else {
callback . onRoleChangeFailed ( nick , R . string . could_not_change_role ) ;
}
}
} ) ;
}
2015-10-16 07:58:31 +00:00
private void disconnect ( Account account , boolean force ) {
2014-11-15 16:09:02 +00:00
if ( ( account . getStatus ( ) = = Account . State . ONLINE )
| | ( account . getStatus ( ) = = Account . State . DISABLED ) ) {
2016-06-04 14:16:14 +00:00
final XmppConnection connection = account . getXmppConnection ( ) ;
2014-03-16 13:12:30 +00:00
if ( ! force ) {
List < Conversation > conversations = getConversations ( ) ;
2014-11-10 00:24:35 +00:00
for ( Conversation conversation : conversations ) {
if ( conversation . getAccount ( ) = = account ) {
if ( conversation . getMode ( ) = = Conversation . MODE_MULTI ) {
2015-10-16 07:58:31 +00:00
leaveMuc ( conversation , true ) ;
2014-11-10 00:24:35 +00:00
} else {
if ( conversation . endOtrIfNeeded ( ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( )
+ " : ended otr session with "
2014-12-21 20:43:58 +00:00
+ conversation . getJid ( ) ) ;
2014-11-10 00:24:35 +00:00
}
}
}
}
2015-04-09 10:46:54 +00:00
sendOfflinePresence ( account ) ;
2014-02-13 22:40:08 +00:00
}
2016-06-04 14:16:14 +00:00
connection . disconnect ( force ) ;
2015-01-19 10:17:27 +00:00
}
2014-02-05 21:33:39 +00:00
}
2014-02-08 23:47:11 +00:00
@Override
public IBinder onBind ( Intent intent ) {
return mBinder ;
}
2014-02-16 15:32:15 +00:00
2014-02-27 23:22:56 +00:00
public void updateMessage ( Message message ) {
databaseBackend . updateMessage ( message ) ;
2014-10-14 16:16:03 +00:00
updateConversationUi ( ) ;
2014-02-27 23:22:56 +00:00
}
2014-06-03 13:48:51 +00:00
2016-02-19 23:01:39 +00:00
public void updateMessage ( Message message , String uuid ) {
databaseBackend . updateMessage ( message , uuid ) ;
updateConversationUi ( ) ;
}
2014-05-22 12:33:17 +00:00
protected void syncDirtyContacts ( Account account ) {
2014-06-03 13:48:51 +00:00
for ( Contact contact : account . getRoster ( ) . getContacts ( ) ) {
2014-05-22 12:33:17 +00:00
if ( contact . getOption ( Contact . Options . DIRTY_PUSH ) ) {
pushContactToServer ( contact ) ;
}
if ( contact . getOption ( Contact . Options . DIRTY_DELETE ) ) {
deleteContactOnServer ( contact ) ;
}
}
}
2014-02-27 23:22:56 +00:00
2014-02-20 16:00:50 +00:00
public void createContact ( Contact contact ) {
2015-11-28 19:11:38 +00:00
boolean autoGrant = getPreferences ( ) . getBoolean ( " grant_new_contacts " , true ) ;
2014-02-23 20:33:37 +00:00
if ( autoGrant ) {
2014-05-19 13:15:09 +00:00
contact . setOption ( Contact . Options . PREEMPTIVE_GRANT ) ;
contact . setOption ( Contact . Options . ASKING ) ;
2014-02-23 20:33:37 +00:00
}
2014-05-19 13:15:09 +00:00
pushContactToServer ( contact ) ;
2014-06-11 19:53:25 +00:00
}
public void onOtrSessionEstablished ( Conversation conversation ) {
2014-12-14 17:10:46 +00:00
final Account account = conversation . getAccount ( ) ;
final Session otrSession = conversation . getOtrSession ( ) ;
2014-08-31 14:28:21 +00:00
Log . d ( Config . LOGTAG ,
2014-11-09 15:57:22 +00:00
account . getJid ( ) . toBareJid ( ) + " otr session established with "
2015-01-19 10:17:27 +00:00
+ conversation . getJid ( ) + " / "
+ otrSession . getSessionID ( ) . getUserID ( ) ) ;
2015-06-29 12:22:26 +00:00
conversation . findUnsentMessagesWithEncryption ( Message . ENCRYPTION_OTR , new Conversation . OnMessageFound ( ) {
2014-12-14 17:10:46 +00:00
@Override
public void onMessageFound ( Message message ) {
2014-11-09 15:21:13 +00:00
SessionID id = otrSession . getSessionID ( ) ;
try {
2014-12-14 17:10:46 +00:00
message . setCounterpart ( Jid . fromString ( id . getAccountID ( ) + " / " + id . getUserID ( ) ) ) ;
2014-11-09 15:21:13 +00:00
} catch ( InvalidJidException e ) {
2014-12-14 17:10:46 +00:00
return ;
2014-11-09 15:21:13 +00:00
}
2015-06-28 09:19:07 +00:00
if ( message . needsUploading ( ) ) {
mJingleConnectionManager . createNewConnection ( message ) ;
} else {
2015-07-20 16:11:33 +00:00
MessagePacket outPacket = mMessageGenerator . generateOtrChat ( message ) ;
2014-11-10 00:24:35 +00:00
if ( outPacket ! = null ) {
2015-07-20 21:13:28 +00:00
mMessageGenerator . addDelay ( outPacket , message . getTimeSent ( ) ) ;
2014-12-14 17:10:46 +00:00
message . setStatus ( Message . STATUS_SEND ) ;
databaseBackend . updateMessage ( message ) ;
2014-11-10 00:24:35 +00:00
sendMessagePacket ( account , outPacket ) ;
}
}
2014-12-14 17:10:46 +00:00
updateConversationUi ( ) ;
}
} ) ;
2014-05-19 13:15:09 +00:00
}
2014-06-22 11:57:57 +00:00
2014-06-20 15:30:19 +00:00
public boolean renewSymmetricKey ( Conversation conversation ) {
Account account = conversation . getAccount ( ) ;
byte [ ] symmetricKey = new byte [ 32 ] ;
this . mRandom . nextBytes ( symmetricKey ) ;
Session otrSession = conversation . getOtrSession ( ) ;
2014-06-22 11:57:57 +00:00
if ( otrSession ! = null ) {
2014-06-20 15:30:19 +00:00
MessagePacket packet = new MessagePacket ( ) ;
packet . setType ( MessagePacket . TYPE_CHAT ) ;
2014-11-09 15:57:22 +00:00
packet . setFrom ( account . getJid ( ) ) ;
2015-09-01 20:37:52 +00:00
MessageGenerator . addMessageHints ( packet ) ;
2014-11-05 20:55:47 +00:00
packet . setAttribute ( " to " , otrSession . getSessionID ( ) . getAccountID ( ) + " / "
2014-11-09 15:21:13 +00:00
+ otrSession . getSessionID ( ) . getUserID ( ) ) ;
2014-06-20 15:30:19 +00:00
try {
2014-06-22 11:57:57 +00:00
packet . setBody ( otrSession
. transformSending ( CryptoHelper . FILETRANSFER
2015-01-20 17:01:39 +00:00
+ CryptoHelper . bytesToHex ( symmetricKey ) ) [ 0 ] ) ;
2014-08-26 14:52:42 +00:00
sendMessagePacket ( account , packet ) ;
2014-06-20 15:30:19 +00:00
conversation . setSymmetricKey ( symmetricKey ) ;
return true ;
} catch ( OtrException e ) {
return false ;
}
}
return false ;
}
2014-05-22 07:36:00 +00:00
2014-12-21 01:13:13 +00:00
public void pushContactToServer ( final Contact contact ) {
2014-05-22 12:33:17 +00:00
contact . resetOption ( Contact . Options . DIRTY_DELETE ) ;
2014-07-10 21:49:34 +00:00
contact . setOption ( Contact . Options . DIRTY_PUSH ) ;
2014-12-21 01:13:13 +00:00
final Account account = contact . getAccount ( ) ;
2014-11-15 16:09:02 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2014-12-21 01:13:13 +00:00
final boolean ask = contact . getOption ( Contact . Options . ASKING ) ;
final boolean sendUpdates = contact
2015-01-19 10:17:27 +00:00
. getOption ( Contact . Options . PENDING_SUBSCRIPTION_REQUEST )
& & contact . getOption ( Contact . Options . PREEMPTIVE_GRANT ) ;
2014-12-30 13:16:25 +00:00
final IqPacket iq = new IqPacket ( IqPacket . TYPE . SET ) ;
2014-12-21 01:13:13 +00:00
iq . query ( Xmlns . ROSTER ) . addChild ( contact . asElement ( ) ) ;
2015-05-26 09:31:33 +00:00
account . getXmppConnection ( ) . sendIqPacket ( iq , mDefaultIqHandler ) ;
2014-07-12 10:28:28 +00:00
if ( sendUpdates ) {
2014-08-26 14:52:42 +00:00
sendPresencePacket ( account ,
mPresenceGenerator . sendPresenceUpdatesTo ( contact ) ) ;
2014-05-22 07:36:00 +00:00
}
2014-07-12 10:28:28 +00:00
if ( ask ) {
2014-08-26 14:52:42 +00:00
sendPresencePacket ( account ,
mPresenceGenerator . requestPresenceUpdatesFrom ( contact ) ) ;
2014-07-12 10:28:28 +00:00
}
2014-05-21 20:22:36 +00:00
}
2014-05-19 13:15:09 +00:00
}
2014-05-22 07:36:00 +00:00
2016-04-11 20:20:32 +00:00
public void publishAvatar ( Account account , Uri image , UiCallback < Avatar > callback ) {
2014-08-31 14:28:21 +00:00
final Bitmap . CompressFormat format = Config . AVATAR_FORMAT ;
final int size = Config . AVATAR_SIZE ;
2016-01-04 14:17:02 +00:00
final Avatar avatar = getFileBackend ( ) . getPepAvatar ( image , size , format ) ;
2014-08-26 14:52:42 +00:00
if ( avatar ! = null ) {
2014-08-04 23:36:17 +00:00
avatar . height = size ;
avatar . width = size ;
if ( format . equals ( Bitmap . CompressFormat . WEBP ) ) {
avatar . type = " image/webp " ;
} else if ( format . equals ( Bitmap . CompressFormat . JPEG ) ) {
avatar . type = " image/jpeg " ;
} else if ( format . equals ( Bitmap . CompressFormat . PNG ) ) {
avatar . type = " image/png " ;
}
2014-08-06 16:36:33 +00:00
if ( ! getFileBackend ( ) . save ( avatar ) ) {
callback . error ( R . string . error_saving_avatar , avatar ) ;
return ;
}
2016-04-11 20:20:32 +00:00
publishAvatar ( account , avatar , callback ) ;
} else {
callback . error ( R . string . error_publish_avatar_converting , null ) ;
}
}
2014-08-26 14:52:42 +00:00
2016-04-11 20:20:32 +00:00
public void publishAvatar ( Account account , final Avatar avatar , final UiCallback < Avatar > callback ) {
2017-01-12 11:20:10 +00:00
IqPacket packet = this . mIqGenerator . publishAvatar ( avatar ) ;
2016-04-11 20:20:32 +00:00
this . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket result ) {
if ( result . getType ( ) = = IqPacket . TYPE . RESULT ) {
2017-01-12 11:20:10 +00:00
final IqPacket packet = XmppConnectionService . this . mIqGenerator . publishAvatarMetadata ( avatar ) ;
2016-04-11 20:20:32 +00:00
sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket result ) {
if ( result . getType ( ) = = IqPacket . TYPE . RESULT ) {
if ( account . setAvatar ( avatar . getFilename ( ) ) ) {
getAvatarService ( ) . clear ( account ) ;
databaseBackend . updateAccount ( account ) ;
}
2017-01-12 11:20:10 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : published avatar " + ( avatar . size / 1024 ) + " KiB " ) ;
2016-04-11 20:20:32 +00:00
if ( callback ! = null ) {
2014-08-04 23:36:17 +00:00
callback . success ( avatar ) ;
2016-04-11 20:20:32 +00:00
}
} else {
if ( callback ! = null ) {
2017-01-12 11:20:10 +00:00
callback . error ( R . string . error_publish_avatar_server_reject , avatar ) ;
2014-08-04 23:36:17 +00:00
}
}
2016-04-11 20:20:32 +00:00
}
} ) ;
} else {
2017-01-12 11:20:10 +00:00
Element error = result . findChild ( " error " ) ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : server rejected avatar " + ( avatar . size / 1024 ) + " KiB " + ( error ! = null ? error . toString ( ) : " " ) ) ;
2016-04-11 20:20:32 +00:00
if ( callback ! = null ) {
2017-01-12 11:20:10 +00:00
callback . error ( R . string . error_publish_avatar_server_reject , avatar ) ;
2014-08-04 23:36:17 +00:00
}
}
2016-04-11 20:20:32 +00:00
}
} ) ;
}
public void republishAvatarIfNeeded ( Account account ) {
if ( account . getAxolotlService ( ) . isPepBroken ( ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : skipping republication of avatar because pep is broken " ) ;
return ;
2014-08-03 18:28:13 +00:00
}
2016-04-11 20:20:32 +00:00
IqPacket packet = this . mIqGenerator . retrieveAvatarMetaData ( null ) ;
this . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
private Avatar parseAvatar ( IqPacket packet ) {
Element pubsub = packet . findChild ( " pubsub " , " http://jabber.org/protocol/pubsub " ) ;
if ( pubsub ! = null ) {
Element items = pubsub . findChild ( " items " ) ;
if ( items ! = null ) {
return Avatar . parseMetadata ( items ) ;
}
}
return null ;
}
private boolean errorIsItemNotFound ( IqPacket packet ) {
Element error = packet . findChild ( " error " ) ;
return packet . getType ( ) = = IqPacket . TYPE . ERROR
& & error ! = null
& & error . hasChild ( " item-not-found " ) ;
}
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT | | errorIsItemNotFound ( packet ) ) {
Avatar serverAvatar = parseAvatar ( packet ) ;
if ( serverAvatar = = null & & account . getAvatar ( ) ! = null ) {
Avatar avatar = fileBackend . getStoredPepAvatar ( account . getAvatar ( ) ) ;
if ( avatar ! = null ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : avatar on server was null. republishing " ) ;
publishAvatar ( account , fileBackend . getStoredPepAvatar ( account . getAvatar ( ) ) , null ) ;
} else {
Log . e ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : error rereading avatar " ) ;
}
}
}
}
} ) ;
2014-08-03 18:28:13 +00:00
}
2014-08-26 14:52:42 +00:00
2014-08-15 15:31:24 +00:00
public void fetchAvatar ( Account account , Avatar avatar ) {
fetchAvatar ( account , avatar , null ) ;
}
2014-08-26 14:52:42 +00:00
2015-05-05 04:17:34 +00:00
public void fetchAvatar ( Account account , final Avatar avatar , final UiCallback < Avatar > callback ) {
2015-05-05 08:29:41 +00:00
final String KEY = generateFetchKey ( account , avatar ) ;
2015-10-16 07:58:31 +00:00
synchronized ( this . mInProgressAvatarFetches ) {
2016-04-12 15:52:58 +00:00
if ( ! this . mInProgressAvatarFetches . contains ( KEY ) ) {
2015-05-05 08:29:41 +00:00
switch ( avatar . origin ) {
case PEP :
this . mInProgressAvatarFetches . add ( KEY ) ;
fetchAvatarPep ( account , avatar , callback ) ;
break ;
case VCARD :
this . mInProgressAvatarFetches . add ( KEY ) ;
fetchAvatarVcard ( account , avatar , callback ) ;
break ;
}
}
2015-05-05 04:17:34 +00:00
}
}
private void fetchAvatarPep ( Account account , final Avatar avatar , final UiCallback < Avatar > callback ) {
IqPacket packet = this . mIqGenerator . retrievePepAvatar ( avatar ) ;
2014-08-05 20:58:46 +00:00
sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
2014-08-26 14:52:42 +00:00
2014-08-05 20:58:46 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket result ) {
2015-05-05 08:29:41 +00:00
synchronized ( mInProgressAvatarFetches ) {
mInProgressAvatarFetches . remove ( generateFetchKey ( account , avatar ) ) ;
}
2014-11-09 15:57:22 +00:00
final String ERROR = account . getJid ( ) . toBareJid ( )
2015-01-19 10:17:27 +00:00
+ " : fetching avatar for " + avatar . owner + " failed " ;
2014-12-30 13:16:25 +00:00
if ( result . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-09-05 11:29:20 +00:00
avatar . image = mIqParser . avatarData ( result ) ;
if ( avatar . image ! = null ) {
if ( getFileBackend ( ) . save ( avatar ) ) {
2014-11-09 15:57:22 +00:00
if ( account . getJid ( ) . toBareJid ( ) . equals ( avatar . owner ) ) {
2014-09-05 11:29:20 +00:00
if ( account . setAvatar ( avatar . getFilename ( ) ) ) {
databaseBackend . updateAccount ( account ) ;
}
2014-10-21 12:57:16 +00:00
getAvatarService ( ) . clear ( account ) ;
2014-10-21 13:26:17 +00:00
updateConversationUi ( ) ;
updateAccountUi ( ) ;
2014-09-05 11:29:20 +00:00
} else {
Contact contact = account . getRoster ( )
2015-01-19 10:17:27 +00:00
. getContact ( avatar . owner ) ;
2015-05-05 04:17:34 +00:00
contact . setAvatar ( avatar ) ;
2014-10-21 12:57:16 +00:00
getAvatarService ( ) . clear ( contact ) ;
2014-10-21 13:26:17 +00:00
updateConversationUi ( ) ;
updateRosterUi ( ) ;
2014-08-21 10:32:50 +00:00
}
2014-09-05 11:29:20 +00:00
if ( callback ! = null ) {
callback . success ( avatar ) ;
}
2014-11-09 15:57:22 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( )
2016-05-04 08:29:29 +00:00
+ " : successfully fetched pep avatar for " + avatar . owner ) ;
2014-09-05 11:29:20 +00:00
return ;
2014-08-15 15:31:24 +00:00
}
2014-09-05 11:29:20 +00:00
} else {
2014-09-08 10:51:01 +00:00
2014-09-05 11:29:20 +00:00
Log . d ( Config . LOGTAG , ERROR + " (parsing error) " ) ;
}
} else {
Element error = result . findChild ( " error " ) ;
2014-09-08 10:51:01 +00:00
if ( error = = null ) {
2014-09-05 11:29:20 +00:00
Log . d ( Config . LOGTAG , ERROR + " (server error) " ) ;
} else {
Log . d ( Config . LOGTAG , ERROR + error . toString ( ) ) ;
2014-08-15 15:31:24 +00:00
}
}
2014-08-26 14:52:42 +00:00
if ( callback ! = null ) {
2014-08-15 15:31:24 +00:00
callback . error ( 0 , null ) ;
}
2014-09-05 11:29:20 +00:00
2014-08-15 15:31:24 +00:00
}
} ) ;
}
2014-08-26 14:52:42 +00:00
2015-05-05 04:17:34 +00:00
private void fetchAvatarVcard ( final Account account , final Avatar avatar , final UiCallback < Avatar > callback ) {
IqPacket packet = this . mIqGenerator . retrieveVcardAvatar ( avatar ) ;
2015-05-20 10:47:04 +00:00
this . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
2015-05-05 04:17:34 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2015-05-20 10:47:04 +00:00
synchronized ( mInProgressAvatarFetches ) {
mInProgressAvatarFetches . remove ( generateFetchKey ( account , avatar ) ) ;
2015-05-05 08:29:41 +00:00
}
2015-05-05 04:17:34 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2015-05-20 10:47:04 +00:00
Element vCard = packet . findChild ( " vCard " , " vcard-temp " ) ;
2015-05-05 04:17:34 +00:00
Element photo = vCard ! = null ? vCard . findChild ( " PHOTO " ) : null ;
2015-05-14 12:42:21 +00:00
String image = photo ! = null ? photo . findChildContent ( " BINVAL " ) : null ;
2015-05-07 09:07:15 +00:00
if ( image ! = null ) {
avatar . image = image ;
2015-05-05 04:17:34 +00:00
if ( getFileBackend ( ) . save ( avatar ) ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( )
+ " : successfully fetched vCard avatar for " + avatar . owner ) ;
2015-12-03 17:18:34 +00:00
if ( avatar . owner . isBareJid ( ) ) {
2016-09-09 09:04:05 +00:00
if ( account . getJid ( ) . toBareJid ( ) . equals ( avatar . owner ) & & account . getAvatar ( ) = = null ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : had no avatar. replacing with vcard " ) ;
account . setAvatar ( avatar . getFilename ( ) ) ;
databaseBackend . updateAccount ( account ) ;
getAvatarService ( ) . clear ( account ) ;
updateAccountUi ( ) ;
} else {
Contact contact = account . getRoster ( ) . getContact ( avatar . owner ) ;
contact . setAvatar ( avatar ) ;
getAvatarService ( ) . clear ( contact ) ;
updateRosterUi ( ) ;
}
2015-12-03 17:18:34 +00:00
updateConversationUi ( ) ;
} else {
2016-01-25 20:17:53 +00:00
Conversation conversation = find ( account , avatar . owner . toBareJid ( ) ) ;
2015-12-03 17:18:34 +00:00
if ( conversation ! = null & & conversation . getMode ( ) = = Conversation . MODE_MULTI ) {
2016-05-16 12:10:40 +00:00
MucOptions . User user = conversation . getMucOptions ( ) . findUserByFullJid ( avatar . owner ) ;
2015-12-03 17:18:34 +00:00
if ( user ! = null ) {
2015-12-04 20:36:48 +00:00
if ( user . setAvatar ( avatar ) ) {
getAvatarService ( ) . clear ( user ) ;
updateConversationUi ( ) ;
updateMucRosterUi ( ) ;
}
2015-12-03 17:18:34 +00:00
}
}
}
2015-05-05 04:17:34 +00:00
}
}
}
}
} ) ;
}
public void checkForAvatar ( Account account , final UiCallback < Avatar > callback ) {
2014-08-15 15:31:24 +00:00
IqPacket packet = this . mIqGenerator . retrieveAvatarMetaData ( null ) ;
this . sendIqPacket ( account , packet , new OnIqPacketReceived ( ) {
2014-08-26 14:52:42 +00:00
2014-08-15 15:31:24 +00:00
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2014-12-30 13:16:25 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2016-04-11 20:20:32 +00:00
Element pubsub = packet . findChild ( " pubsub " , " http://jabber.org/protocol/pubsub " ) ;
2014-08-26 14:52:42 +00:00
if ( pubsub ! = null ) {
2014-08-15 15:31:24 +00:00
Element items = pubsub . findChild ( " items " ) ;
2014-08-26 14:52:42 +00:00
if ( items ! = null ) {
2014-08-15 15:31:24 +00:00
Avatar avatar = Avatar . parseMetadata ( items ) ;
2014-08-26 14:52:42 +00:00
if ( avatar ! = null ) {
2014-11-09 15:57:22 +00:00
avatar . owner = account . getJid ( ) . toBareJid ( ) ;
2014-08-15 15:31:24 +00:00
if ( fileBackend . isAvatarCached ( avatar ) ) {
2014-08-21 10:32:50 +00:00
if ( account . setAvatar ( avatar . getFilename ( ) ) ) {
databaseBackend . updateAccount ( account ) ;
}
2014-10-21 12:57:16 +00:00
getAvatarService ( ) . clear ( account ) ;
2014-08-15 15:31:24 +00:00
callback . success ( avatar ) ;
} else {
2015-05-05 04:17:34 +00:00
fetchAvatarPep ( account , avatar , callback ) ;
2014-08-15 15:31:24 +00:00
}
return ;
}
}
2014-08-06 16:36:33 +00:00
}
2014-08-05 20:58:46 +00:00
}
2014-08-15 15:31:24 +00:00
callback . error ( 0 , null ) ;
2014-08-05 20:58:46 +00:00
}
} ) ;
}
2014-08-26 14:52:42 +00:00
2014-05-19 13:15:09 +00:00
public void deleteContactOnServer ( Contact contact ) {
2014-07-11 11:52:27 +00:00
contact . resetOption ( Contact . Options . PREEMPTIVE_GRANT ) ;
2014-05-22 12:33:17 +00:00
contact . resetOption ( Contact . Options . DIRTY_PUSH ) ;
2014-07-09 23:55:19 +00:00
contact . setOption ( Contact . Options . DIRTY_DELETE ) ;
2014-05-19 13:15:09 +00:00
Account account = contact . getAccount ( ) ;
2014-11-15 16:09:02 +00:00
if ( account . getStatus ( ) = = Account . State . ONLINE ) {
2014-12-30 13:16:25 +00:00
IqPacket iq = new IqPacket ( IqPacket . TYPE . SET ) ;
2014-12-21 01:13:13 +00:00
Element item = iq . query ( Xmlns . ROSTER ) . addChild ( " item " ) ;
2014-11-05 20:55:47 +00:00
item . setAttribute ( " jid " , contact . getJid ( ) . toString ( ) ) ;
2014-05-22 07:36:00 +00:00
item . setAttribute ( " subscription " , " remove " ) ;
2015-05-26 09:31:33 +00:00
account . getXmppConnection ( ) . sendIqPacket ( iq , mDefaultIqHandler ) ;
2014-05-22 07:36:00 +00:00
}
2014-02-20 16:00:50 +00:00
}
2014-02-21 20:35:23 +00:00
2016-10-18 11:06:24 +00:00
public void updateConversation ( final Conversation conversation ) {
mDatabaseExecutor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
databaseBackend . updateConversation ( conversation ) ;
}
} ) ;
2014-03-03 04:01:02 +00:00
}
2014-03-05 14:41:14 +00:00
2015-09-29 17:24:52 +00:00
private void reconnectAccount ( final Account account , final boolean force , final boolean interactive ) {
2015-03-05 14:46:33 +00:00
synchronized ( account ) {
2016-01-16 18:21:11 +00:00
XmppConnection connection = account . getXmppConnection ( ) ;
2016-03-03 12:31:59 +00:00
if ( connection = = null ) {
2016-01-16 18:21:11 +00:00
connection = createConnection ( account ) ;
account . setXmppConnection ( connection ) ;
2015-03-05 14:46:33 +00:00
}
2016-12-10 12:20:05 +00:00
boolean hasInternet = hasInternetConnection ( ) ;
if ( ! account . isOptionSet ( Account . OPTION_DISABLED ) & & hasInternet ) {
2016-01-16 18:21:11 +00:00
if ( ! force ) {
2016-03-03 12:31:59 +00:00
disconnect ( account , false ) ;
2015-03-05 14:46:33 +00:00
}
2016-01-16 18:21:11 +00:00
Thread thread = new Thread ( connection ) ;
connection . setInteractive ( interactive ) ;
2016-03-20 16:24:41 +00:00
connection . prepareNewConnection ( ) ;
2016-11-19 09:44:40 +00:00
connection . interrupt ( ) ;
2015-03-05 14:46:33 +00:00
thread . start ( ) ;
2016-01-15 13:26:23 +00:00
scheduleWakeUpCall ( Config . CONNECT_DISCO_TIMEOUT , account . getUuid ( ) . hashCode ( ) ) ;
2015-03-05 14:46:33 +00:00
} else {
2016-12-10 12:20:05 +00:00
disconnect ( account , force | | account . getTrueStatus ( ) . isError ( ) | | ! hasInternet ) ;
2015-03-05 14:46:33 +00:00
account . getRoster ( ) . clearPresences ( ) ;
2016-01-16 18:21:11 +00:00
connection . resetEverything ( ) ;
2017-02-17 09:26:42 +00:00
final AxolotlService axolotlService = account . getAxolotlService ( ) ;
if ( axolotlService ! = null ) {
axolotlService . resetBrokenness ( ) ;
}
2016-12-10 12:20:05 +00:00
if ( ! hasInternet ) {
account . setStatus ( Account . State . NO_INTERNET ) ;
}
2015-03-05 14:46:33 +00:00
}
}
}
2014-03-11 14:44:22 +00:00
2015-03-05 14:46:33 +00:00
public void reconnectAccountInBackground ( final Account account ) {
new Thread ( new Runnable ( ) {
2014-03-10 18:22:13 +00:00
@Override
public void run ( ) {
2015-10-16 07:58:31 +00:00
reconnectAccount ( account , false , true ) ;
2014-03-08 19:14:47 +00:00
}
} ) . start ( ) ;
}
2014-03-14 21:40:56 +00:00
2014-11-20 17:20:42 +00:00
public void invite ( Conversation conversation , Jid contact ) {
2015-10-16 07:58:31 +00:00
Log . d ( Config . LOGTAG , conversation . getAccount ( ) . getJid ( ) . toBareJid ( ) + " : inviting " + contact + " to " + conversation . getJid ( ) . toBareJid ( ) ) ;
2014-07-21 14:04:53 +00:00
MessagePacket packet = mMessageGenerator . invite ( conversation , contact ) ;
2014-08-26 14:52:42 +00:00
sendMessagePacket ( conversation . getAccount ( ) , packet ) ;
2014-03-15 03:59:18 +00:00
}
2014-08-30 07:24:58 +00:00
2015-04-23 15:37:47 +00:00
public void directInvite ( Conversation conversation , Jid jid ) {
2015-05-20 10:47:04 +00:00
MessagePacket packet = mMessageGenerator . directInvite ( conversation , jid ) ;
2015-10-16 07:58:31 +00:00
sendMessagePacket ( conversation . getAccount ( ) , packet ) ;
2015-04-23 15:37:47 +00:00
}
2014-08-27 17:25:58 +00:00
public void resetSendingToWaiting ( Account account ) {
2014-08-30 07:24:58 +00:00
for ( Conversation conversation : getConversations ( ) ) {
2014-08-27 17:25:58 +00:00
if ( conversation . getAccount ( ) = = account ) {
2014-12-14 17:10:46 +00:00
conversation . findUnsentTextMessages ( new Conversation . OnMessageFound ( ) {
@Override
public void onMessageFound ( Message message ) {
2014-08-27 17:25:58 +00:00
markMessage ( message , Message . STATUS_WAITING ) ;
2014-12-14 17:10:46 +00:00
}
} ) ;
2014-08-27 17:25:58 +00:00
}
}
2014-03-15 03:59:18 +00:00
}
2014-05-01 20:33:49 +00:00
2015-03-01 13:15:40 +00:00
public Message markMessage ( final Account account , final Jid recipient , final String uuid , final int status ) {
2016-10-26 10:26:04 +00:00
return markMessage ( account , recipient , uuid , status , null ) ;
}
public Message markMessage ( final Account account , final Jid recipient , final String uuid , final int status , String errorMessage ) {
2014-10-02 16:54:21 +00:00
if ( uuid = = null ) {
2015-03-01 13:15:40 +00:00
return null ;
}
for ( Conversation conversation : getConversations ( ) ) {
2015-03-16 22:23:51 +00:00
if ( conversation . getJid ( ) . toBareJid ( ) . equals ( recipient ) & & conversation . getAccount ( ) = = account ) {
2015-08-26 10:11:12 +00:00
final Message message = conversation . findSentMessageWithUuidOrRemoteId ( uuid ) ;
2015-03-01 13:15:40 +00:00
if ( message ! = null ) {
2016-10-26 10:26:04 +00:00
markMessage ( message , status , errorMessage ) ;
2015-01-19 10:17:27 +00:00
}
2015-03-01 13:15:40 +00:00
return message ;
2014-05-16 20:46:15 +00:00
}
}
2015-03-01 13:15:40 +00:00
return null ;
2014-05-16 20:46:15 +00:00
}
2014-05-18 09:25:04 +00:00
2015-08-26 10:11:12 +00:00
public boolean markMessage ( Conversation conversation , String uuid , int status ) {
2014-10-02 16:54:21 +00:00
if ( uuid = = null ) {
return false ;
} else {
2014-12-14 17:10:46 +00:00
Message message = conversation . findSentMessageWithUuid ( uuid ) ;
2015-01-19 10:17:27 +00:00
if ( message ! = null ) {
markMessage ( message , status ) ;
2014-12-14 17:10:46 +00:00
return true ;
} else {
return false ;
2014-04-11 07:13:56 +00:00
}
}
}
2014-05-01 20:33:49 +00:00
2014-04-11 07:13:56 +00:00
public void markMessage ( Message message , int status ) {
2016-10-26 10:26:04 +00:00
markMessage ( message , status , null ) ;
}
public void markMessage ( Message message , int status , String errorMessage ) {
2014-09-08 10:51:01 +00:00
if ( status = = Message . STATUS_SEND_FAILED
& & ( message . getStatus ( ) = = Message . STATUS_SEND_RECEIVED | | message
2016-11-07 20:57:08 +00:00
. getStatus ( ) = = Message . STATUS_SEND_DISPLAYED ) ) {
2014-09-08 10:51:01 +00:00
return ;
2015-01-19 10:17:27 +00:00
}
2016-10-26 10:26:04 +00:00
message . setErrorMessage ( errorMessage ) ;
2014-04-11 07:13:56 +00:00
message . setStatus ( status ) ;
databaseBackend . updateMessage ( message ) ;
2014-07-12 11:42:17 +00:00
updateConversationUi ( ) ;
2014-04-11 07:13:56 +00:00
}
2014-05-01 20:33:49 +00:00
2014-04-13 16:09:40 +00:00
public SharedPreferences getPreferences ( ) {
2014-05-01 20:33:49 +00:00
return PreferenceManager
2015-01-19 10:17:27 +00:00
. getDefaultSharedPreferences ( getApplicationContext ( ) ) ;
2014-04-13 16:09:40 +00:00
}
2014-09-08 21:58:37 +00:00
2017-01-23 16:14:30 +00:00
public long getAutomaticMessageDeletionDate ( ) {
try {
final long timeout = Long . parseLong ( getPreferences ( ) . getString ( SettingsActivity . AUTOMATIC_MESSAGE_DELETION , " 0 " ) ) * 1000 ;
return timeout = = 0 ? timeout : System . currentTimeMillis ( ) - timeout ;
} catch ( NumberFormatException e ) {
return 0 ;
}
}
2014-06-04 16:44:15 +00:00
public boolean confirmMessages ( ) {
return getPreferences ( ) . getBoolean ( " confirm_messages " , true ) ;
2016-02-16 08:57:59 +00:00
}
public boolean allowMessageCorrection ( ) {
2016-07-17 20:42:37 +00:00
return getPreferences ( ) . getBoolean ( " allow_message_correction " , true ) ;
2014-06-03 13:48:51 +00:00
}
2014-09-08 21:58:37 +00:00
2015-02-21 10:06:52 +00:00
public boolean sendChatStates ( ) {
return getPreferences ( ) . getBoolean ( " chat_states " , false ) ;
}
2016-02-01 12:54:08 +00:00
private boolean respectAutojoin ( ) {
return getPreferences ( ) . getBoolean ( " autojoin " , true ) ;
}
2014-09-20 13:49:25 +00:00
public boolean indicateReceived ( ) {
return getPreferences ( ) . getBoolean ( " indicate_received " , false ) ;
2015-11-28 19:11:38 +00:00
}
public boolean useTorToConnect ( ) {
2015-12-14 09:54:55 +00:00
return Config . FORCE_ORBOT | | getPreferences ( ) . getBoolean ( " use_tor " , false ) ;
2014-09-20 13:49:25 +00:00
}
2016-01-25 20:17:53 +00:00
public boolean showExtendedConnectionOptions ( ) {
return getPreferences ( ) . getBoolean ( " show_connection_options " , false ) ;
}
2016-06-04 14:16:14 +00:00
public boolean broadcastLastActivity ( ) {
return getPreferences ( ) . getBoolean ( " last_activity " , false ) ;
}
2015-03-02 10:53:15 +00:00
public int unreadCount ( ) {
int count = 0 ;
2015-10-16 07:58:31 +00:00
for ( Conversation conversation : getConversations ( ) ) {
2015-03-02 10:53:15 +00:00
count + = conversation . unreadCount ( ) ;
}
return count ;
}
2015-07-10 11:28:50 +00:00
public void showErrorToastInUi ( int resId ) {
if ( mOnShowErrorToast ! = null ) {
mOnShowErrorToast . onShowErrorToast ( resId ) ;
}
}
2014-07-12 11:42:17 +00:00
public void updateConversationUi ( ) {
if ( mOnConversationUpdate ! = null ) {
mOnConversationUpdate . onConversationUpdate ( ) ;
}
}
2014-08-26 14:52:42 +00:00
2014-07-12 11:42:17 +00:00
public void updateAccountUi ( ) {
if ( mOnAccountUpdate ! = null ) {
mOnAccountUpdate . onAccountUpdate ( ) ;
}
}
2014-08-26 14:52:42 +00:00
2014-07-18 13:35:31 +00:00
public void updateRosterUi ( ) {
if ( mOnRosterUpdate ! = null ) {
mOnRosterUpdate . onRosterUpdate ( ) ;
}
}
2014-05-22 07:36:00 +00:00
2015-10-11 11:11:50 +00:00
public boolean displayCaptchaRequest ( Account account , String id , Data data , Bitmap captcha ) {
if ( mOnCaptchaRequested ! = null ) {
DisplayMetrics metrics = getApplicationContext ( ) . getResources ( ) . getDisplayMetrics ( ) ;
2015-10-16 07:58:31 +00:00
Bitmap scaled = Bitmap . createScaledBitmap ( captcha , ( int ) ( captcha . getWidth ( ) * metrics . scaledDensity ) ,
( int ) ( captcha . getHeight ( ) * metrics . scaledDensity ) , false ) ;
2015-10-11 11:11:50 +00:00
mOnCaptchaRequested . onCaptchaRequested ( account , id , data , scaled ) ;
2016-05-10 07:41:30 +00:00
return true ;
2015-10-11 11:11:50 +00:00
}
2016-05-10 07:41:30 +00:00
return false ;
2015-10-11 11:11:50 +00:00
}
2014-12-21 20:43:58 +00:00
public void updateBlocklistUi ( final OnUpdateBlocklist . Status status ) {
if ( mOnUpdateBlocklist ! = null ) {
mOnUpdateBlocklist . OnUpdateBlocklist ( status ) ;
}
}
2014-11-23 14:19:44 +00:00
public void updateMucRosterUi ( ) {
if ( mOnMucRosterUpdate ! = null ) {
mOnMucRosterUpdate . onMucRosterUpdate ( ) ;
}
}
2015-10-17 12:09:26 +00:00
public void keyStatusUpdated ( AxolotlService . FetchStatus report ) {
2015-10-16 07:58:31 +00:00
if ( mOnKeyStatusUpdated ! = null ) {
2015-10-17 12:09:26 +00:00
mOnKeyStatusUpdated . onKeyStatusUpdated ( report ) ;
2015-07-19 16:36:28 +00:00
}
}
2014-11-05 20:55:47 +00:00
public Account findAccountByJid ( final Jid accountJid ) {
2014-05-19 13:15:09 +00:00
for ( Account account : this . accounts ) {
2014-11-16 15:04:45 +00:00
if ( account . getJid ( ) . toBareJid ( ) . equals ( accountJid . toBareJid ( ) ) ) {
2014-05-19 13:15:09 +00:00
return account ;
}
}
return null ;
}
2014-08-26 14:52:42 +00:00
2014-07-16 22:03:37 +00:00
public Conversation findConversationByUuid ( String uuid ) {
for ( Conversation conversation : getConversations ( ) ) {
if ( conversation . getUuid ( ) . equals ( uuid ) ) {
return conversation ;
}
}
return null ;
}
2014-06-04 16:44:15 +00:00
2016-02-16 13:22:21 +00:00
public boolean markRead ( final Conversation conversation ) {
2016-08-27 13:25:37 +00:00
return markRead ( conversation , true ) ;
}
public boolean markRead ( final Conversation conversation , boolean clear ) {
if ( clear ) {
mNotificationService . clear ( conversation ) ;
}
2015-10-29 16:20:01 +00:00
final List < Message > readMessages = conversation . markRead ( ) ;
if ( readMessages . size ( ) > 0 ) {
Runnable runnable = new Runnable ( ) {
@Override
public void run ( ) {
for ( Message message : readMessages ) {
databaseBackend . updateMessage ( message ) ;
}
}
} ;
mDatabaseExecutor . execute ( runnable ) ;
2016-02-16 13:22:21 +00:00
updateUnreadCountBadge ( ) ;
return true ;
} else {
return false ;
2015-10-14 19:18:34 +00:00
}
2015-05-20 10:47:04 +00:00
}
2015-05-26 10:00:38 +00:00
public synchronized void updateUnreadCountBadge ( ) {
2015-05-20 10:47:04 +00:00
int count = unreadCount ( ) ;
2015-05-26 10:00:38 +00:00
if ( unreadCount ! = count ) {
Log . d ( Config . LOGTAG , " update unread count to " + count ) ;
if ( count > 0 ) {
2016-03-27 18:02:36 +00:00
ShortcutBadger . applyCount ( getApplicationContext ( ) , count ) ;
2015-05-26 10:00:38 +00:00
} else {
2016-03-27 18:02:36 +00:00
ShortcutBadger . removeCount ( getApplicationContext ( ) ) ;
2015-05-26 10:00:38 +00:00
}
unreadCount = count ;
2015-05-20 10:47:04 +00:00
}
2015-01-02 11:04:33 +00:00
}
public void sendReadMarker ( final Conversation conversation ) {
final Message markable = conversation . getLatestMarkableMessage ( ) ;
2016-02-16 13:22:21 +00:00
if ( this . markRead ( conversation ) ) {
updateConversationUi ( ) ;
}
2016-10-07 08:05:08 +00:00
if ( confirmMessages ( )
& & markable ! = null
& & markable . trusted ( )
& & markable . getRemoteMsgId ( ) ! = null ) {
2015-01-19 10:17:27 +00:00
Log . d ( Config . LOGTAG , conversation . getAccount ( ) . getJid ( ) . toBareJid ( ) + " : sending read marker to " + markable . getCounterpart ( ) . toString ( ) ) ;
2014-07-12 01:44:23 +00:00
Account account = conversation . getAccount ( ) ;
2014-11-19 16:40:42 +00:00
final Jid to = markable . getCounterpart ( ) ;
MessagePacket packet = mMessageGenerator . confirm ( account , to , markable . getRemoteMsgId ( ) ) ;
2015-01-19 10:17:27 +00:00
this . sendMessagePacket ( conversation . getAccount ( ) , packet ) ;
2014-07-12 01:44:23 +00:00
}
2014-06-04 16:44:15 +00:00
}
2014-08-26 14:52:42 +00:00
2014-06-20 15:30:19 +00:00
public SecureRandom getRNG ( ) {
return this . mRandom ;
}
2014-08-26 14:52:42 +00:00
2014-07-22 15:27:44 +00:00
public MemorizingTrustManager getMemorizingTrustManager ( ) {
return this . mMemorizingTrustManager ;
}
2014-06-20 15:30:19 +00:00
2015-04-02 11:35:42 +00:00
public void setMemorizingTrustManager ( MemorizingTrustManager trustManager ) {
this . mMemorizingTrustManager = trustManager ;
}
public void updateMemorizingTrustmanager ( ) {
final MemorizingTrustManager tm ;
final boolean dontTrustSystemCAs = getPreferences ( ) . getBoolean ( " dont_trust_system_cas " , false ) ;
if ( dontTrustSystemCAs ) {
2015-10-16 07:58:31 +00:00
tm = new MemorizingTrustManager ( getApplicationContext ( ) , null ) ;
2015-04-02 11:35:42 +00:00
} else {
tm = new MemorizingTrustManager ( getApplicationContext ( ) ) ;
}
setMemorizingTrustManager ( tm ) ;
}
2014-06-20 15:30:19 +00:00
public PowerManager getPowerManager ( ) {
return this . pm ;
}
2014-06-25 14:55:47 +00:00
2014-10-21 12:57:16 +00:00
public LruCache < String , Bitmap > getBitmapCache ( ) {
return this . mBitmapCache ;
}
2014-06-30 08:46:46 +00:00
public void syncRosterToDisk ( final Account account ) {
2015-06-05 06:46:06 +00:00
Runnable runnable = new Runnable ( ) {
2014-07-11 11:52:27 +00:00
2014-06-30 08:46:46 +00:00
@Override
public void run ( ) {
databaseBackend . writeRoster ( account . getRoster ( ) ) ;
}
2015-06-05 06:46:06 +00:00
} ;
mDatabaseExecutor . execute ( runnable ) ;
2014-07-11 11:52:27 +00:00
2014-06-30 08:46:46 +00:00
}
2014-07-11 11:52:27 +00:00
2014-07-10 17:42:37 +00:00
public List < String > getKnownHosts ( ) {
2014-12-21 01:13:13 +00:00
final List < String > hosts = new ArrayList < > ( ) ;
for ( final Account account : getAccounts ( ) ) {
2014-11-05 20:55:47 +00:00
if ( ! hosts . contains ( account . getServer ( ) . toString ( ) ) ) {
hosts . add ( account . getServer ( ) . toString ( ) ) ;
2014-07-10 17:42:37 +00:00
}
2014-12-21 01:13:13 +00:00
for ( final Contact contact : account . getRoster ( ) . getContacts ( ) ) {
2014-07-10 17:42:37 +00:00
if ( contact . showInRoster ( ) ) {
2014-11-05 20:55:47 +00:00
final String server = contact . getServer ( ) . toString ( ) ;
2014-07-11 11:52:27 +00:00
if ( server ! = null & & ! hosts . contains ( server ) ) {
2014-07-10 17:42:37 +00:00
hosts . add ( server ) ;
}
}
}
}
2016-05-10 08:53:44 +00:00
if ( Config . DOMAIN_LOCK ! = null & & ! hosts . contains ( Config . DOMAIN_LOCK ) ) {
hosts . add ( Config . DOMAIN_LOCK ) ;
}
if ( Config . MAGIC_CREATE_DOMAIN ! = null & & ! hosts . contains ( Config . MAGIC_CREATE_DOMAIN ) ) {
hosts . add ( Config . MAGIC_CREATE_DOMAIN ) ;
}
2014-07-10 17:42:37 +00:00
return hosts ;
}
2014-07-11 17:48:41 +00:00
public List < String > getKnownConferenceHosts ( ) {
2014-12-21 01:13:13 +00:00
final ArrayList < String > mucServers = new ArrayList < > ( ) ;
for ( final Account account : accounts ) {
2014-07-11 17:48:41 +00:00
if ( account . getXmppConnection ( ) ! = null ) {
2014-12-21 01:13:13 +00:00
final String server = account . getXmppConnection ( ) . getMucServer ( ) ;
2014-07-24 15:21:21 +00:00
if ( server ! = null & & ! mucServers . contains ( server ) ) {
2014-07-11 17:48:41 +00:00
mucServers . add ( server ) ;
}
}
}
return mucServers ;
}
2014-08-26 14:52:42 +00:00
2014-07-12 01:44:23 +00:00
public void sendMessagePacket ( Account account , MessagePacket packet ) {
2014-10-08 12:10:37 +00:00
XmppConnection connection = account . getXmppConnection ( ) ;
if ( connection ! = null ) {
connection . sendMessagePacket ( packet ) ;
}
2014-07-12 01:44:23 +00:00
}
2014-08-26 14:52:42 +00:00
2014-07-12 01:44:23 +00:00
public void sendPresencePacket ( Account account , PresencePacket packet ) {
2014-10-08 12:10:37 +00:00
XmppConnection connection = account . getXmppConnection ( ) ;
if ( connection ! = null ) {
connection . sendPresencePacket ( packet ) ;
}
2014-07-12 01:44:23 +00:00
}
2014-08-26 14:52:42 +00:00
2015-10-11 11:11:50 +00:00
public void sendCreateAccountWithCaptchaPacket ( Account account , String id , Data data ) {
2016-05-05 07:58:35 +00:00
final XmppConnection connection = account . getXmppConnection ( ) ;
2015-10-11 11:11:50 +00:00
if ( connection ! = null ) {
2016-05-05 07:58:35 +00:00
IqPacket request = mIqGenerator . generateCreateAccountWithCaptcha ( account , id , data ) ;
2016-08-25 11:50:54 +00:00
connection . sendUnmodifiedIqPacket ( request , connection . registrationResponseListener ) ;
2015-10-11 11:11:50 +00:00
}
}
2015-01-04 11:09:39 +00:00
public void sendIqPacket ( final Account account , final IqPacket packet , final OnIqPacketReceived callback ) {
2014-12-21 20:43:58 +00:00
final XmppConnection connection = account . getXmppConnection ( ) ;
2014-10-08 12:10:37 +00:00
if ( connection ! = null ) {
connection . sendIqPacket ( packet , callback ) ;
}
2014-07-14 09:47:42 +00:00
}
2014-08-26 14:52:42 +00:00
2015-01-25 23:48:56 +00:00
public void sendPresence ( final Account account ) {
2016-06-04 14:16:14 +00:00
sendPresence ( account , checkListeners ( ) & & broadcastLastActivity ( ) ) ;
}
private void sendPresence ( final Account account , final boolean includeIdleTimestamp ) {
2016-04-22 19:25:06 +00:00
PresencePacket packet ;
if ( manuallyChangePresence ( ) ) {
packet = mPresenceGenerator . selfPresence ( account , account . getPresenceStatus ( ) ) ;
String message = account . getPresenceStatusMessage ( ) ;
if ( message ! = null & & ! message . isEmpty ( ) ) {
packet . addChild ( new Element ( " status " ) . setContent ( message ) ) ;
}
} else {
packet = mPresenceGenerator . selfPresence ( account , getTargetPresence ( ) ) ;
}
2016-06-04 14:16:14 +00:00
if ( mLastActivity > 0 & & includeIdleTimestamp ) {
long since = Math . min ( mLastActivity , System . currentTimeMillis ( ) ) ; //don't send future dates
packet . addChild ( " idle " , " urn:xmpp:idle:1 " ) . setAttribute ( " since " , AbstractGenerator . getTimestamp ( since ) ) ;
}
2016-04-22 19:25:06 +00:00
sendPresencePacket ( account , packet ) ;
2015-10-07 22:35:04 +00:00
}
2016-06-01 19:51:46 +00:00
private void deactivateGracePeriod ( ) {
for ( Account account : getAccounts ( ) ) {
account . deactivateGracePeriod ( ) ;
}
}
2015-10-07 22:35:04 +00:00
public void refreshAllPresences ( ) {
2016-06-04 14:16:14 +00:00
boolean includeIdleTimestamp = checkListeners ( ) & & broadcastLastActivity ( ) ;
2015-10-07 22:35:04 +00:00
for ( Account account : getAccounts ( ) ) {
if ( ! account . isOptionSet ( Account . OPTION_DISABLED ) ) {
2016-06-04 14:16:14 +00:00
sendPresence ( account , includeIdleTimestamp ) ;
2015-10-07 22:35:04 +00:00
}
}
2015-01-25 23:48:56 +00:00
}
2016-02-12 10:39:27 +00:00
private void refreshAllGcmTokens ( ) {
for ( Account account : getAccounts ( ) ) {
2016-02-12 23:03:57 +00:00
if ( account . isOnlineAndConnected ( ) & & mPushManagementService . available ( account ) ) {
2016-02-12 10:39:27 +00:00
mPushManagementService . registerPushTokenOnServer ( account ) ;
}
}
}
2016-09-07 12:34:58 +00:00
private void sendOfflinePresence ( final Account account ) {
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : sending offline presence " ) ;
2015-04-09 10:46:54 +00:00
sendPresencePacket ( account , mPresenceGenerator . sendOfflinePresence ( account ) ) ;
}
2014-07-12 01:44:23 +00:00
public MessageGenerator getMessageGenerator ( ) {
return this . mMessageGenerator ;
}
2014-08-26 14:52:42 +00:00
2014-07-12 01:44:23 +00:00
public PresenceGenerator getPresenceGenerator ( ) {
return this . mPresenceGenerator ;
}
2014-08-26 14:52:42 +00:00
2014-07-23 12:30:27 +00:00
public IqGenerator getIqGenerator ( ) {
2014-08-26 14:52:42 +00:00
return this . mIqGenerator ;
2014-07-23 12:30:27 +00:00
}
2014-08-26 14:52:42 +00:00
2015-01-19 10:17:27 +00:00
public IqParser getIqParser ( ) {
return this . mIqParser ;
}
2014-12-21 20:43:58 +00:00
2014-07-12 10:28:28 +00:00
public JingleConnectionManager getJingleConnectionManager ( ) {
return this . mJingleConnectionManager ;
}
2014-08-26 14:52:42 +00:00
2014-12-05 00:54:16 +00:00
public MessageArchiveService getMessageArchiveService ( ) {
return this . mMessageArchiveService ;
}
2014-11-17 19:02:46 +00:00
public List < Contact > findContacts ( Jid jid ) {
2014-11-05 20:55:47 +00:00
ArrayList < Contact > contacts = new ArrayList < > ( ) ;
2014-09-27 09:37:02 +00:00
for ( Account account : getAccounts ( ) ) {
if ( ! account . isOptionSet ( Account . OPTION_DISABLED ) ) {
2014-10-15 12:41:27 +00:00
Contact contact = account . getRoster ( ) . getContactFromRoster ( jid ) ;
2014-09-27 09:37:02 +00:00
if ( contact ! = null ) {
contacts . add ( contact ) ;
}
}
}
return contacts ;
}
2014-10-02 16:31:19 +00:00
2016-05-30 19:12:04 +00:00
public Conversation findFirstMuc ( Jid jid ) {
for ( Conversation conversation : getConversations ( ) ) {
if ( conversation . getJid ( ) . toBareJid ( ) . equals ( jid . toBareJid ( ) )
& & conversation . getMode ( ) = = Conversation . MODE_MULTI ) {
return conversation ;
}
}
return null ;
}
2014-09-29 16:28:13 +00:00
public NotificationService getNotificationService ( ) {
return this . mNotificationService ;
2014-09-28 13:21:56 +00:00
}
2014-10-13 23:06:45 +00:00
public HttpConnectionManager getHttpConnectionManager ( ) {
return this . mHttpConnectionManager ;
}
2014-10-20 19:08:33 +00:00
2014-12-21 20:43:58 +00:00
public void resendFailedMessages ( final Message message ) {
final Collection < Message > messages = new ArrayList < > ( ) ;
2014-11-10 00:24:35 +00:00
Message current = message ;
while ( current . getStatus ( ) = = Message . STATUS_SEND_FAILED ) {
messages . add ( current ) ;
if ( current . mergeable ( current . next ( ) ) ) {
current = current . next ( ) ;
} else {
break ;
}
}
2014-12-21 20:43:58 +00:00
for ( final Message msg : messages ) {
2015-07-28 10:54:54 +00:00
msg . setTime ( System . currentTimeMillis ( ) ) ;
2014-11-10 00:24:35 +00:00
markMessage ( msg , Message . STATUS_WAITING ) ;
2015-10-16 07:58:31 +00:00
this . resendMessage ( msg , false ) ;
2014-11-10 00:24:35 +00:00
}
}
2014-12-15 16:14:27 +00:00
public void clearConversationHistory ( final Conversation conversation ) {
conversation . clearMessages ( ) ;
2015-01-03 17:22:26 +00:00
conversation . setHasMessagesLeftOnServer ( false ) ; //avoid messages getting loaded through mam
2016-02-04 13:39:16 +00:00
conversation . setLastClearHistory ( System . currentTimeMillis ( ) ) ;
2015-10-29 16:20:01 +00:00
Runnable runnable = new Runnable ( ) {
2014-12-15 16:14:27 +00:00
@Override
public void run ( ) {
databaseBackend . deleteMessagesInConversation ( conversation ) ;
2016-09-16 09:07:52 +00:00
databaseBackend . updateConversation ( conversation ) ;
2014-12-15 16:14:27 +00:00
}
2015-10-29 16:20:01 +00:00
} ;
mDatabaseExecutor . execute ( runnable ) ;
2014-12-15 16:14:27 +00:00
}
2016-09-18 21:21:05 +00:00
public void sendBlockRequest ( final Blockable blockable , boolean reportSpam ) {
2014-12-21 20:43:58 +00:00
if ( blockable ! = null & & blockable . getBlockedJid ( ) ! = null ) {
final Jid jid = blockable . getBlockedJid ( ) ;
2016-09-18 21:21:05 +00:00
this . sendIqPacket ( blockable . getAccount ( ) , getIqGenerator ( ) . generateSetBlockRequest ( jid , reportSpam ) , new OnIqPacketReceived ( ) {
2014-12-21 20:43:58 +00:00
@Override
public void onIqPacketReceived ( final Account account , final IqPacket packet ) {
2014-12-30 13:16:25 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-12-21 20:43:58 +00:00
account . getBlocklist ( ) . add ( jid ) ;
updateBlocklistUi ( OnUpdateBlocklist . Status . BLOCKED ) ;
}
}
} ) ;
}
}
public void sendUnblockRequest ( final Blockable blockable ) {
if ( blockable ! = null & & blockable . getJid ( ) ! = null ) {
final Jid jid = blockable . getBlockedJid ( ) ;
this . sendIqPacket ( blockable . getAccount ( ) , getIqGenerator ( ) . generateSetUnblockRequest ( jid ) , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( final Account account , final IqPacket packet ) {
2014-12-30 13:16:25 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT ) {
2014-12-21 20:43:58 +00:00
account . getBlocklist ( ) . remove ( jid ) ;
updateBlocklistUi ( OnUpdateBlocklist . Status . UNBLOCKED ) ;
}
}
} ) ;
}
}
2015-01-19 10:17:27 +00:00
2015-10-29 12:41:08 +00:00
public void publishDisplayName ( Account account ) {
String displayName = account . getDisplayName ( ) ;
if ( displayName ! = null & & ! displayName . isEmpty ( ) ) {
IqPacket publish = mIqGenerator . publishNick ( displayName ) ;
sendIqPacket ( account , publish , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
if ( packet . getType ( ) = = IqPacket . TYPE . ERROR ) {
2016-02-03 09:40:02 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : could not publish nick " ) ;
2015-10-29 12:41:08 +00:00
}
}
} ) ;
}
}
2016-05-19 08:41:56 +00:00
public ServiceDiscoveryResult getCachedServiceDiscoveryResult ( Pair < String , String > key ) {
2016-02-03 09:40:02 +00:00
ServiceDiscoveryResult result = discoCache . get ( key ) ;
if ( result ! = null ) {
return result ;
} else {
result = databaseBackend . findDiscoveryResult ( key . first , key . second ) ;
if ( result ! = null ) {
discoCache . put ( key , result ) ;
}
return result ;
}
}
public void fetchCaps ( Account account , final Jid jid , final Presence presence ) {
final Pair < String , String > key = new Pair < > ( presence . getHash ( ) , presence . getVer ( ) ) ;
ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult ( key ) ;
if ( disco ! = null ) {
presence . setServiceDiscoveryResult ( disco ) ;
} else {
if ( ! account . inProgressDiscoFetches . contains ( key ) ) {
account . inProgressDiscoFetches . add ( key ) ;
IqPacket request = new IqPacket ( IqPacket . TYPE . GET ) ;
request . setTo ( jid ) ;
request . query ( " http://jabber.org/protocol/disco#info " ) ;
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : making disco request for " + key . second + " to " + jid ) ;
sendIqPacket ( account , request , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket discoPacket ) {
if ( discoPacket . getType ( ) = = IqPacket . TYPE . RESULT ) {
ServiceDiscoveryResult disco = new ServiceDiscoveryResult ( discoPacket ) ;
if ( presence . getVer ( ) . equals ( disco . getVer ( ) ) ) {
databaseBackend . insertDiscoveryResult ( disco ) ;
injectServiceDiscorveryResult ( account . getRoster ( ) , presence . getHash ( ) , presence . getVer ( ) , disco ) ;
} else {
2016-02-12 10:39:27 +00:00
Log . d ( Config . LOGTAG , account . getJid ( ) . toBareJid ( ) + " : mismatch in caps for contact " + jid + " " + presence . getVer ( ) + " vs " + disco . getVer ( ) ) ;
2016-02-03 09:40:02 +00:00
}
}
account . inProgressDiscoFetches . remove ( key ) ;
}
} ) ;
}
}
}
private void injectServiceDiscorveryResult ( Roster roster , String hash , String ver , ServiceDiscoveryResult disco ) {
for ( Contact contact : roster . getContacts ( ) ) {
for ( Presence presence : contact . getPresences ( ) . getPresences ( ) . values ( ) ) {
if ( hash . equals ( presence . getHash ( ) ) & & ver . equals ( presence . getVer ( ) ) ) {
presence . setServiceDiscoveryResult ( disco ) ;
}
}
}
}
2016-02-09 12:01:17 +00:00
public void fetchMamPreferences ( Account account , final OnMamPreferencesFetched callback ) {
2017-02-15 15:42:35 +00:00
final boolean lagecy = account . getXmppConnection ( ) . getFeatures ( ) . mamLegacy ( ) ;
2016-02-09 12:01:17 +00:00
IqPacket request = new IqPacket ( IqPacket . TYPE . GET ) ;
2017-02-15 15:42:35 +00:00
request . addChild ( " prefs " , lagecy ? Xmlns . MAM_LAGECY : Xmlns . MAM ) ;
2016-02-09 12:01:17 +00:00
sendIqPacket ( account , request , new OnIqPacketReceived ( ) {
@Override
public void onIqPacketReceived ( Account account , IqPacket packet ) {
2017-02-15 15:42:35 +00:00
Element prefs = packet . findChild ( " prefs " , lagecy ? Xmlns . MAM_LAGECY : Xmlns . MAM ) ;
2016-02-09 12:01:17 +00:00
if ( packet . getType ( ) = = IqPacket . TYPE . RESULT & & prefs ! = null ) {
callback . onPreferencesFetched ( prefs ) ;
} else {
callback . onPreferencesFetchFailed ( ) ;
}
}
} ) ;
}
2016-02-12 10:39:27 +00:00
public PushManagementService getPushManagementService ( ) {
return mPushManagementService ;
}
2016-04-19 16:03:24 +00:00
public Account getPendingAccount ( ) {
Account pending = null ;
for ( Account account : getAccounts ( ) ) {
if ( account . isOptionSet ( Account . OPTION_REGISTER ) ) {
pending = account ;
} else {
return null ;
}
}
return pending ;
}
2016-05-05 11:17:04 +00:00
public void changeStatus ( Account account , Presence . Status status , String statusMessage , boolean send ) {
2016-04-23 13:10:35 +00:00
if ( ! statusMessage . isEmpty ( ) ) {
databaseBackend . insertPresenceTemplate ( new PresenceTemplate ( status , statusMessage ) ) ;
}
2016-05-05 11:17:04 +00:00
changeStatusReal ( account , status , statusMessage , send ) ;
2016-04-22 19:25:06 +00:00
}
2016-05-05 11:17:04 +00:00
private void changeStatusReal ( Account account , Presence . Status status , String statusMessage , boolean send ) {
2016-04-22 19:25:06 +00:00
account . setPresenceStatus ( status ) ;
account . setPresenceStatusMessage ( statusMessage ) ;
databaseBackend . updateAccount ( account ) ;
2016-05-05 11:17:04 +00:00
if ( ! account . isOptionSet ( Account . OPTION_DISABLED ) & & send ) {
2016-04-22 19:25:06 +00:00
sendPresence ( account ) ;
}
}
public void changeStatus ( Presence . Status status , String statusMessage ) {
2016-04-23 13:10:35 +00:00
if ( ! statusMessage . isEmpty ( ) ) {
databaseBackend . insertPresenceTemplate ( new PresenceTemplate ( status , statusMessage ) ) ;
}
2016-04-22 19:25:06 +00:00
for ( Account account : getAccounts ( ) ) {
2016-05-05 11:17:04 +00:00
changeStatusReal ( account , status , statusMessage , true ) ;
2016-04-22 19:25:06 +00:00
}
}
2016-05-13 08:45:30 +00:00
public List < PresenceTemplate > getPresenceTemplates ( Account account ) {
List < PresenceTemplate > templates = databaseBackend . getPresenceTemplates ( ) ;
for ( PresenceTemplate template : account . getSelfContact ( ) . getPresences ( ) . asTemplates ( ) ) {
if ( ! templates . contains ( template ) ) {
templates . add ( 0 , template ) ;
}
}
return templates ;
}
2016-05-26 20:53:55 +00:00
public void saveConversationAsBookmark ( Conversation conversation , String name ) {
Account account = conversation . getAccount ( ) ;
Bookmark bookmark = new Bookmark ( account , conversation . getJid ( ) . toBareJid ( ) ) ;
if ( ! conversation . getJid ( ) . isBareJid ( ) ) {
bookmark . setNick ( conversation . getJid ( ) . getResourcepart ( ) ) ;
}
if ( name ! = null & & ! name . trim ( ) . isEmpty ( ) ) {
bookmark . setBookmarkName ( name . trim ( ) ) ;
}
bookmark . setAutojoin ( getPreferences ( ) . getBoolean ( " autojoin " , true ) ) ;
account . getBookmarks ( ) . add ( bookmark ) ;
pushBookmarks ( account ) ;
conversation . setBookmark ( bookmark ) ;
}
2016-11-08 20:37:44 +00:00
public void clearStartTimeCounter ( ) {
mDatabaseExecutor . execute ( new Runnable ( ) {
@Override
public void run ( ) {
2016-11-21 09:48:59 +00:00
databaseBackend . clearStartTimeCounter ( false ) ;
2016-11-08 20:37:44 +00:00
}
} ) ;
}
2016-11-22 11:03:21 +00:00
public boolean verifyFingerprints ( Contact contact , List < XmppUri . Fingerprint > fingerprints ) {
2016-11-17 19:09:42 +00:00
boolean needsRosterWrite = false ;
2016-11-22 11:03:21 +00:00
boolean performedVerification = false ;
2016-11-17 19:09:42 +00:00
final AxolotlService axolotlService = contact . getAccount ( ) . getAxolotlService ( ) ;
for ( XmppUri . Fingerprint fp : fingerprints ) {
if ( fp . type = = XmppUri . FingerprintType . OTR ) {
2016-11-22 11:03:21 +00:00
performedVerification | = contact . addOtrFingerprint ( fp . fingerprint ) ;
needsRosterWrite | = performedVerification ;
2016-11-17 19:09:42 +00:00
} else if ( fp . type = = XmppUri . FingerprintType . OMEMO ) {
String fingerprint = " 05 " + fp . fingerprint . replaceAll ( " \\ s " , " " ) ;
FingerprintStatus fingerprintStatus = axolotlService . getFingerprintTrust ( fingerprint ) ;
if ( fingerprintStatus ! = null ) {
if ( ! fingerprintStatus . isVerified ( ) ) {
2016-11-22 11:03:21 +00:00
performedVerification = true ;
2016-11-17 19:09:42 +00:00
axolotlService . setFingerprintTrust ( fingerprint , fingerprintStatus . toVerified ( ) ) ;
}
} else {
axolotlService . preVerifyFingerprint ( contact , fingerprint ) ;
}
}
}
if ( needsRosterWrite ) {
syncRosterToDisk ( contact . getAccount ( ) ) ;
}
2016-11-22 11:03:21 +00:00
return performedVerification ;
2016-11-17 19:09:42 +00:00
}
2016-11-17 21:28:45 +00:00
public boolean verifyFingerprints ( Account account , List < XmppUri . Fingerprint > fingerprints ) {
final AxolotlService axolotlService = account . getAxolotlService ( ) ;
boolean verifiedSomething = false ;
for ( XmppUri . Fingerprint fp : fingerprints ) {
if ( fp . type = = XmppUri . FingerprintType . OMEMO ) {
String fingerprint = " 05 " + fp . fingerprint . replaceAll ( " \\ s " , " " ) ;
Log . d ( Config . LOGTAG , " trying to verify own fp= " + fingerprint ) ;
FingerprintStatus fingerprintStatus = axolotlService . getFingerprintTrust ( fingerprint ) ;
if ( fingerprintStatus ! = null ) {
if ( ! fingerprintStatus . isVerified ( ) ) {
axolotlService . setFingerprintTrust ( fingerprint , fingerprintStatus . toVerified ( ) ) ;
verifiedSomething = true ;
}
} else {
axolotlService . preVerifyFingerprint ( account , fingerprint ) ;
verifiedSomething = true ;
}
}
}
return verifiedSomething ;
}
2016-11-23 09:42:27 +00:00
public boolean blindTrustBeforeVerification ( ) {
return getPreferences ( ) . getBoolean ( SettingsActivity . BLIND_TRUST_BEFORE_VERIFICATION , true ) ;
}
2016-02-09 12:01:17 +00:00
public interface OnMamPreferencesFetched {
void onPreferencesFetched ( Element prefs ) ;
void onPreferencesFetchFailed ( ) ;
}
public void pushMamPreferences ( Account account , Element prefs ) {
IqPacket set = new IqPacket ( IqPacket . TYPE . SET ) ;
set . addChild ( prefs ) ;
sendIqPacket ( account , set , null ) ;
}
2015-10-09 11:37:08 +00:00
public interface OnAccountCreated {
void onAccountCreated ( Account account ) ;
2015-10-16 07:58:31 +00:00
2015-10-09 11:37:08 +00:00
void informUser ( int r ) ;
}
2015-01-19 10:17:27 +00:00
public interface OnMoreMessagesLoaded {
2015-10-09 11:37:08 +00:00
void onMoreMessagesLoaded ( int count , Conversation conversation ) ;
2015-01-19 10:17:27 +00:00
2015-10-09 11:37:08 +00:00
void informUser ( int r ) ;
2015-01-19 10:17:27 +00:00
}
public interface OnAccountPasswordChanged {
2015-10-09 11:37:08 +00:00
void onPasswordChangeSucceeded ( ) ;
2015-01-19 10:17:27 +00:00
2015-10-09 11:37:08 +00:00
void onPasswordChangeFailed ( ) ;
2015-01-19 10:17:27 +00:00
}
public interface OnAffiliationChanged {
2015-10-09 11:37:08 +00:00
void onAffiliationChangedSuccessful ( Jid jid ) ;
2015-01-19 10:17:27 +00:00
2015-10-09 11:37:08 +00:00
void onAffiliationChangeFailed ( Jid jid , int resId ) ;
2015-01-19 10:17:27 +00:00
}
public interface OnRoleChanged {
2015-10-09 11:37:08 +00:00
void onRoleChangedSuccessful ( String nick ) ;
2015-01-19 10:17:27 +00:00
2015-10-09 11:37:08 +00:00
void onRoleChangeFailed ( String nick , int resid ) ;
2015-01-19 10:17:27 +00:00
}
public interface OnConversationUpdate {
2015-10-09 11:37:08 +00:00
void onConversationUpdate ( ) ;
2015-01-19 10:17:27 +00:00
}
public interface OnAccountUpdate {
2015-10-09 11:37:08 +00:00
void onAccountUpdate ( ) ;
2015-01-19 10:17:27 +00:00
}
2015-10-11 11:11:50 +00:00
public interface OnCaptchaRequested {
void onCaptchaRequested ( Account account ,
2015-10-16 07:58:31 +00:00
String id ,
Data data ,
Bitmap captcha ) ;
2015-10-11 11:11:50 +00:00
}
2015-01-19 10:17:27 +00:00
public interface OnRosterUpdate {
2015-10-09 11:37:08 +00:00
void onRosterUpdate ( ) ;
2015-01-19 10:17:27 +00:00
}
public interface OnMucRosterUpdate {
2015-10-09 11:37:08 +00:00
void onMucRosterUpdate ( ) ;
2015-01-19 10:17:27 +00:00
}
2015-10-04 22:45:16 +00:00
public interface OnConferenceConfigurationFetched {
2015-10-09 11:37:08 +00:00
void onConferenceConfigurationFetched ( Conversation conversation ) ;
2015-10-22 09:20:36 +00:00
void onFetchFailed ( Conversation conversation , Element error ) ;
2015-10-04 22:45:16 +00:00
}
2015-11-25 19:47:02 +00:00
public interface OnConferenceJoined {
void onConferenceJoined ( Conversation conversation ) ;
}
2017-02-24 18:58:46 +00:00
public interface OnConfigurationPushed {
2015-10-09 11:37:08 +00:00
void onPushSucceeded ( ) ;
2015-01-19 10:17:27 +00:00
2015-10-09 11:37:08 +00:00
void onPushFailed ( ) ;
2015-01-19 10:17:27 +00:00
}
2015-07-10 11:28:50 +00:00
public interface OnShowErrorToast {
void onShowErrorToast ( int resId ) ;
}
2015-01-19 10:17:27 +00:00
public class XmppConnectionBinder extends Binder {
public XmppConnectionService getService ( ) {
return XmppConnectionService . this ;
}
}
2014-04-07 21:58:59 +00:00
}