make unread status and notifications presistent across restarts

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java          | 28 
src/main/java/eu/siacs/conversations/entities/Message.java               | 16 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java    |  6 
src/main/java/eu/siacs/conversations/services/NotificationService.java   | 42 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 13 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java        |  3 
6 files changed, 74 insertions(+), 34 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -111,6 +111,16 @@ public class Conversation extends AbstractEntity implements Blockable {
 		}
 	}
 
+	public void findUnreadMessages(OnMessageFound onMessageFound) {
+		synchronized (this.messages) {
+			for(Message message : this.messages) {
+				if (!message.isRead()) {
+					onMessageFound.onMessageFound(message);
+				}
+			}
+		}
+	}
+
 	public void findMessagesWithFiles(final OnMessageFound onMessageFound) {
 		synchronized (this.messages) {
 			for (final Message message : this.messages) {
@@ -266,9 +276,8 @@ public class Conversation extends AbstractEntity implements Blockable {
 		}
 	}
 
-
 	public interface OnMessageFound {
-		public void onMessageFound(final Message message);
+		void onMessageFound(final Message message);
 	}
 
 	public Conversation(final String name, final Account account, final Jid contactJid,
@@ -301,13 +310,18 @@ public class Conversation extends AbstractEntity implements Blockable {
 		return (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
 	}
 
-	public void markRead() {
-		for (int i = this.messages.size() - 1; i >= 0; --i) {
-			if (messages.get(i).isRead()) {
-				break;
+	public List<Message> markRead() {
+		final List<Message> unread = new ArrayList<>();
+		synchronized (this.messages) {
+			for (int i = this.messages.size() - 1; i >= 0; --i) {
+				if (this.messages.get(i).isRead()) {
+					break;
+				}
+				this.messages.get(i).markRead();
+				unread.add(this.messages.get(i));
 			}
-			this.messages.get(i).markRead();
 		}
+		return unread;
 	}
 
 	public Message getLatestMarkableMessage() {

src/main/java/eu/siacs/conversations/entities/Message.java 🔗

@@ -56,6 +56,7 @@ public class Message extends AbstractEntity {
 	public static final String SERVER_MSG_ID = "serverMsgId";
 	public static final String RELATIVE_FILE_PATH = "relativeFilePath";
 	public static final String FINGERPRINT = "axolotl_fingerprint";
+	public static final String READ = "read";
 	public static final String ME_COMMAND = "/me ";
 
 
@@ -87,11 +88,8 @@ public class Message extends AbstractEntity {
 	public Message(Conversation conversation, String body, int encryption) {
 		this(conversation, body, encryption, STATUS_UNSEND);
 	}
-	public Message(Conversation conversation, String body, int encryption, int status) {
-		this(conversation, body, encryption, status, false);
-	}
 
-	public Message(Conversation conversation, String body, int encryption, int status, boolean carbon) {
+	public Message(Conversation conversation, String body, int encryption, int status) {
 		this(java.util.UUID.randomUUID().toString(),
 				conversation.getUuid(),
 				conversation.getJid() == null ? null : conversation.getJid().toBareJid(),
@@ -105,7 +103,8 @@ public class Message extends AbstractEntity {
 				null,
 				null,
 				null,
-				null);
+				null,
+				true);
 		this.conversation = conversation;
 	}
 
@@ -113,7 +112,7 @@ public class Message extends AbstractEntity {
 					final Jid trueCounterpart, final String body, final long timeSent,
 					final int encryption, final int status, final int type, final boolean carbon,
 					final String remoteMsgId, final String relativeFilePath,
-					final String serverMsgId, final String fingerprint) {
+					final String serverMsgId, final String fingerprint, final boolean read) {
 		this.uuid = uuid;
 		this.conversationUuid = conversationUUid;
 		this.counterpart = counterpart;
@@ -128,6 +127,7 @@ public class Message extends AbstractEntity {
 		this.relativeFilePath = relativeFilePath;
 		this.serverMsgId = serverMsgId;
 		this.axolotlFingerprint = fingerprint;
+		this.read = read;
 	}
 
 	public static Message fromCursor(Cursor cursor) {
@@ -166,7 +166,8 @@ public class Message extends AbstractEntity {
 				cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
 				cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
 				cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)),
-				cursor.getString(cursor.getColumnIndex(FINGERPRINT)));
+				cursor.getString(cursor.getColumnIndex(FINGERPRINT)),
+				cursor.getInt(cursor.getColumnIndex(READ)) > 0);
 	}
 
 	public static Message createStatusMessage(Conversation conversation, String body) {
@@ -202,6 +203,7 @@ public class Message extends AbstractEntity {
 		values.put(RELATIVE_FILE_PATH, relativeFilePath);
 		values.put(SERVER_MSG_ID, serverMsgId);
 		values.put(FINGERPRINT, axolotlFingerprint);
+		values.put(READ,read);
 		return values;
 	}
 

src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java 🔗

@@ -43,7 +43,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 	private static DatabaseBackend instance = null;
 
 	private static final String DATABASE_NAME = "history";
-	private static final int DATABASE_VERSION = 17;
+	private static final int DATABASE_VERSION = 18;
 
 	private static String CREATE_CONTATCS_STATEMENT = "create table "
 			+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -143,6 +143,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 				+ Message.SERVER_MSG_ID + " TEXT, "
 				+ Message.FINGERPRINT + " TEXT, "
 				+ Message.CARBON + " INTEGER, "
+				+ Message.READ + " NUMBER DEFAULT 1, "
 				+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
 				+ Message.CONVERSATION + ") REFERENCES "
 				+ Conversation.TABLENAME + "(" + Conversation.UUID
@@ -320,6 +321,9 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 				}
 			}
 		}
+		if (oldVersion < 18 && newVersion >= 18) {
+			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "+ Message.READ+ " NUMBER DEFAULT 1");
+		}
 	}
 
 	public static synchronized DatabaseBackend getInstance(Context context) {

src/main/java/eu/siacs/conversations/services/NotificationService.java 🔗

@@ -1,6 +1,5 @@
 package eu.siacs.conversations.services;
 
-import android.annotation.SuppressLint;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -10,7 +9,6 @@ import android.content.SharedPreferences;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
-import android.os.PowerManager;
 import android.os.SystemClock;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationCompat.BigPictureStyle;
@@ -115,31 +113,45 @@ public class NotificationService {
 		return mXmppConnectionService.getPreferences().getBoolean("always_notify_in_conference", false);
 	}
 
+	public void pushFromBacklog(final Message message) {
+		if (notify(message)) {
+			pushToStack(message);
+		}
+	}
+
+	public void finishBacklog() {
+		synchronized (notifications) {
+			mXmppConnectionService.updateUnreadCountBadge();
+			updateNotification(false);
+		}
+	}
+
+	private void pushToStack(final Message message) {
+		final String conversationUuid = message.getConversationUuid();
+		if (notifications.containsKey(conversationUuid)) {
+			notifications.get(conversationUuid).add(message);
+		} else {
+			final ArrayList<Message> mList = new ArrayList<>();
+			mList.add(message);
+			notifications.put(conversationUuid, mList);
+		}
+	}
+
 	public void push(final Message message) {
 		mXmppConnectionService.updateUnreadCountBadge();
 		if (!notify(message)) {
 			return;
 		}
-
 		final boolean isScreenOn = mXmppConnectionService.isInteractive();
-
 		if (this.mIsInForeground && isScreenOn && this.mOpenConversation == message.getConversation()) {
 			return;
 		}
-
 		synchronized (notifications) {
-			final String conversationUuid = message.getConversationUuid();
-			if (notifications.containsKey(conversationUuid)) {
-				notifications.get(conversationUuid).add(message);
-			} else {
-				final ArrayList<Message> mList = new ArrayList<>();
-				mList.add(message);
-				notifications.put(conversationUuid, mList);
-			}
+			pushToStack(message);
 			final Account account = message.getConversation().getAccount();
 			final boolean doNotify = (!(this.mIsInForeground && this.mOpenConversation == null) || !isScreenOn)
-				&& !account.inGracePeriod()
-				&& !this.inMiniGracePeriod(account);
+					&& !account.inGracePeriod()
+					&& !this.inMiniGracePeriod(account);
 			updateNotification(doNotify);
 			if (doNotify) {
 				notifyPebble(message);

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -1072,7 +1072,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 					for (Conversation conversation : conversations) {
 						conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE));
 						checkDeletedFiles(conversation);
+						conversation.findUnreadMessages(new Conversation.OnMessageFound() {
+							@Override
+							public void onMessageFound(Message message) {
+								mNotificationService.pushFromBacklog(message);
+							}
+						});
 					}
+					mNotificationService.finishBacklog();
 					mRestoredFromDatabase = true;
 					Log.d(Config.LOGTAG,"restored all messages");
 					updateConversationUi();
@@ -1330,7 +1337,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 	}
 
 	public void updateKeyInAccount(final Account account, final String alias) {
-		Log.d(Config.LOGTAG,"update key in account "+alias);
+		Log.d(Config.LOGTAG, "update key in account " + alias);
 		try {
 			X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
 			Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
@@ -2566,7 +2573,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 
 	public void markRead(final Conversation conversation) {
 		mNotificationService.clear(conversation);
-		conversation.markRead();
+		for(Message message : conversation.markRead()) {
+			databaseBackend.updateMessage(message);
+		}
 		updateUnreadCountBadge();
 	}
 

src/main/java/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -216,8 +216,7 @@ public class ConversationActivity extends XmppActivity
 					return null;
 				}
 				listAdapter.remove(swipedConversation);
-				swipedConversation.markRead();
-				xmppConnectionService.getNotificationService().clear(swipedConversation);
+				xmppConnectionService.markRead(swipedConversation);
 
 				final boolean formerlySelected = (getSelectedConversation() == swipedConversation);
 				if (position == 0 && listAdapter.getCount() == 0) {