postpone notification actions (mark as read, reply) until after messages are loaded

This commit is contained in:
Daniel Gultsch 2018-01-20 20:05:39 +01:00
parent 4600b3982e
commit 2b39acf352
3 changed files with 59 additions and 24 deletions

View file

@ -62,6 +62,7 @@ import java.util.ListIterator;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -159,6 +160,7 @@ public class XmppConnectionService extends Service {
private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor("VideoCompression"); private final SerialSingleThreadExecutor mVideoCompressionExecutor = new SerialSingleThreadExecutor("VideoCompression");
private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter"); private final SerialSingleThreadExecutor mDatabaseWriterExecutor = new SerialSingleThreadExecutor("DatabaseWriter");
private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader"); private final SerialSingleThreadExecutor mDatabaseReaderExecutor = new SerialSingleThreadExecutor("DatabaseReader");
private final SerialSingleThreadExecutor mNotificationExecutor = new SerialSingleThreadExecutor("NotificationExecutor");
private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true); private ReplacingSerialSingleThreadExecutor mContactMergerExecutor = new ReplacingSerialSingleThreadExecutor(true);
private final IBinder mBinder = new XmppConnectionBinder(); private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
@ -420,14 +422,14 @@ public class XmppConnectionService extends Service {
private LruCache<String, Bitmap> mBitmapCache; private LruCache<String, Bitmap> mBitmapCache;
private EventReceiver mEventReceiver = new EventReceiver(); private EventReceiver mEventReceiver = new EventReceiver();
private boolean mRestoredFromDatabase = false; public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1);
private static String generateFetchKey(Account account, final Avatar avatar) { private static String generateFetchKey(Account account, final Avatar avatar) {
return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum; return account.getJid().toBareJid() + "_" + avatar.owner + "_" + avatar.sha1sum;
} }
public boolean areMessagesInitialized() { public boolean areMessagesInitialized() {
return this.mRestoredFromDatabase; return this.restoredFromDatabaseLatch.getCount() == 0;
} }
public PgpEngine getPgpEngine() { public PgpEngine getPgpEngine() {
@ -569,7 +571,6 @@ public class XmppConnectionService extends Service {
boolean interactive = false; boolean interactive = false;
if (action != null) { if (action != null) {
final String uuid = intent.getStringExtra("uuid"); final String uuid = intent.getStringExtra("uuid");
final Conversation c = findConversationByUuid(uuid);
switch (action) { switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION: case ConnectivityManager.CONNECTIVITY_ACTION:
if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
@ -577,7 +578,7 @@ public class XmppConnectionService extends Service {
} }
break; break;
case ACTION_MERGE_PHONE_CONTACTS: case ACTION_MERGE_PHONE_CONTACTS:
if (mRestoredFromDatabase) { if (restoredFromDatabaseLatch.getCount() == 0) {
loadPhoneContacts(); loadPhoneContacts();
} }
return START_STICKY; return START_STICKY;
@ -585,11 +586,20 @@ public class XmppConnectionService extends Service {
logoutAndSave(true); logoutAndSave(true);
return START_NOT_STICKY; return START_NOT_STICKY;
case ACTION_CLEAR_NOTIFICATION: case ACTION_CLEAR_NOTIFICATION:
mNotificationExecutor.execute(() -> {
try {
final Conversation c = findConversationByUuid(uuid);
if (c != null) { if (c != null) {
mNotificationService.clear(c); mNotificationService.clear(c);
} else { } else {
mNotificationService.clear(); mNotificationService.clear();
} }
restoredFromDatabaseLatch.await();
} catch (InterruptedException e) {
Log.d(Config.LOGTAG,"unable to process clear notification");
}
});
break; break;
case ACTION_DISMISS_ERROR_NOTIFICATIONS: case ACTION_DISMISS_ERROR_NOTIFICATIONS:
dismissErrorNotifications(); dismissErrorNotifications();
@ -600,19 +610,41 @@ public class XmppConnectionService extends Service {
break; break;
case ACTION_REPLY_TO_CONVERSATION: case ACTION_REPLY_TO_CONVERSATION:
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null && c != null) { if (remoteInput == null) {
break;
}
final CharSequence body = remoteInput.getCharSequence("text_reply"); final CharSequence body = remoteInput.getCharSequence("text_reply");
if (body != null && body.length() > 0) { final boolean dismissNotification = intent.getBooleanExtra("dismiss_notification", false);
directReply(c, body.toString(), intent.getBooleanExtra("dismiss_notification", false)); if (body == null || body.length() <= 0) {
break;
} }
mNotificationExecutor.execute(()-> {
try {
restoredFromDatabaseLatch.await();
final Conversation c = findConversationByUuid(uuid);
if (c != null) {
directReply(c, body.toString(), dismissNotification);
} }
} catch (InterruptedException e) {
Log.d(Config.LOGTAG,"unable to process direct reply");
}
});
break; break;
case ACTION_MARK_AS_READ: case ACTION_MARK_AS_READ:
if (c != null) { mNotificationExecutor.execute(() -> {
sendReadMarker(c); final Conversation c = findConversationByUuid(uuid);
} else { if (c == null) {
Log.d(Config.LOGTAG, "received mark read intent for unknown conversation (" + uuid + ")"); Log.d(Config.LOGTAG, "received mark read intent for unknown conversation (" + uuid + ")");
return;
} }
try {
restoredFromDatabaseLatch.await();
sendReadMarker(c);
} catch (InterruptedException e) {
Log.d(Config.LOGTAG,"unable to process notification read marker for conversation "+c.getName());
}
});
break; break;
case AudioManager.RINGER_MODE_CHANGED_ACTION: case AudioManager.RINGER_MODE_CHANGED_ACTION:
if (dndOnSilentMode()) { if (dndOnSilentMode()) {
@ -1459,7 +1491,7 @@ public class XmppConnectionService extends Service {
}); });
} }
mNotificationService.finishBacklog(false); mNotificationService.finishBacklog(false);
mRestoredFromDatabase = true; restoredFromDatabaseLatch.countDown();
final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore; final long diffMessageRestore = SystemClock.elapsedRealtime() - startMessageRestore;
Log.d(Config.LOGTAG, "finished restoring messages in " + diffMessageRestore + "ms"); Log.d(Config.LOGTAG, "finished restoring messages in " + diffMessageRestore + "ms");
updateConversationUi(); updateConversationUi();

View file

@ -17,4 +17,5 @@ public final class Namespace {
public static final String PUBSUB_ERROR = "http://jabber.org/protocol/pubsub#errors"; public static final String PUBSUB_ERROR = "http://jabber.org/protocol/pubsub#errors";
public static final String NICK = "http://jabber.org/protocol/nick"; public static final String NICK = "http://jabber.org/protocol/nick";
public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline"; public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline";
public static final String BIND = "urn:ietf:params:xml:ns:xmpp-bind";
} }

View file

@ -1043,14 +1043,16 @@ public class XmppConnection implements Runnable {
} }
private void sendBindRequest() { private void sendBindRequest() {
while (!mXmppConnectionService.areMessagesInitialized() && socket != null && !socket.isClosed()) { try {
uninterruptedSleep(500); mXmppConnectionService.restoredFromDatabaseLatch.await();
} catch (InterruptedException e) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": interrupted while waiting for DB restore during bind");
return;
} }
needsBinding = false; needsBinding = false;
clearIqCallbacks(); clearIqCallbacks();
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind") iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(account.getResource());
.addChild("resource").setContent(account.getResource());
this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() { this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(final Account account, final IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {