Skip to content

Commit

Permalink
#1112 - Mark conversations read/unread
Browse files Browse the repository at this point in the history
  • Loading branch information
moezbhatti committed Jun 12, 2018
1 parent c41d40c commit 5ede84a
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 23 deletions.
2 changes: 1 addition & 1 deletion data/src/main/java/receiver/MarkReadReceiver.kt
Expand Up @@ -33,7 +33,7 @@ class MarkReadReceiver : BroadcastReceiver() {
AndroidInjection.inject(this, context)

val threadId = intent.getLongExtra("threadId", 0)
markRead.execute(threadId)
markRead.execute(listOf(threadId))
}

}
2 changes: 1 addition & 1 deletion data/src/main/java/receiver/RemoteMessagingReceiver.kt
Expand Up @@ -44,7 +44,7 @@ class RemoteMessagingReceiver : BroadcastReceiver() {

val threadId = bundle.getLong("threadId")
val body = remoteInput.getCharSequence("body").toString()
markRead.execute(threadId)
markRead.execute(listOf(threadId))

val lastMessage = messageRepo.getMessages(threadId).lastOrNull()
val subId = subscriptionManager.activeSubscriptionInfoList.firstOrNull { it.subscriptionId == lastMessage?.subId }?.subscriptionId ?: -1
Expand Down
28 changes: 22 additions & 6 deletions data/src/main/java/repository/MessageRepositoryImpl.kt
Expand Up @@ -49,6 +49,7 @@ import model.Conversation
import model.Message
import model.MmsPart
import model.SearchResult
import timber.log.Timber
import util.Preferences
import util.extensions.anyOf
import util.extensions.map
Expand Down Expand Up @@ -351,10 +352,10 @@ class MessageRepositoryImpl @Inject constructor(
realm.close()
}

override fun markRead(threadId: Long) {
override fun markRead(vararg threadIds: Long) {
Realm.getDefaultInstance()?.use { realm ->
val messages = realm.where(Message::class.java)
.equalTo("threadId", threadId)
.anyOf("threadId", threadIds)
.beginGroup()
.equalTo("read", false)
.or()
Expand All @@ -374,11 +375,26 @@ class MessageRepositoryImpl @Inject constructor(
values.put(Telephony.Sms.SEEN, true)
values.put(Telephony.Sms.READ, true)

try {
val uri = ContentUris.withAppendedId(Telephony.MmsSms.CONTENT_CONVERSATIONS_URI, threadId)
context.contentResolver.update(uri, values, "${Telephony.Sms.READ} = 0", null)
} catch (exception: Exception) {
threadIds.forEach { threadId ->
try {
val uri = ContentUris.withAppendedId(Telephony.MmsSms.CONTENT_CONVERSATIONS_URI, threadId)
context.contentResolver.update(uri, values, "${Telephony.Sms.READ} = 0", null)
} catch (exception: Exception) {
Timber.w(exception)
}
}
}

override fun markUnread(vararg threadIds: Long) {
Realm.getDefaultInstance()?.use { realm ->
val conversation = realm.where(Conversation::class.java)
.anyOf("id", threadIds)
.equalTo("read", true)
.findAll()

realm.executeTransaction {
conversation.forEach { it.read = false }
}
}
}

Expand Down
12 changes: 6 additions & 6 deletions domain/src/main/java/interactor/MarkRead.kt
Expand Up @@ -27,13 +27,13 @@ class MarkRead @Inject constructor(
private val messageRepo: MessageRepository,
private val notificationManager: NotificationManager,
private val updateBadge: UpdateBadge
) : Interactor<Long>() {
) : Interactor<List<Long>>() {

override fun buildObservable(params: Long): Flowable<*> {
return Flowable.just(Unit)
.doOnNext { messageRepo.markRead(params) }
.doOnNext { messageRepo.updateConversations(params) } // Update the conversation
.doOnNext { notificationManager.update(params) }
override fun buildObservable(params: List<Long>): Flowable<*> {
return Flowable.just(params.toLongArray())
.doOnNext { threadIds -> messageRepo.markRead(*threadIds) }
.doOnNext { threadIds -> messageRepo.updateConversations(*threadIds) } // Update the conversation
.doOnNext { threadIds -> threadIds.forEach(notificationManager::update) }
.flatMap { updateBadge.buildObservable(Unit) } // Update the badge
}

Expand Down
36 changes: 36 additions & 0 deletions domain/src/main/java/interactor/MarkUnread.kt
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
*
* This file is part of QKSMS.
*
* QKSMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QKSMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
*/
package interactor

import io.reactivex.Flowable
import repository.MessageRepository
import javax.inject.Inject

class MarkUnread @Inject constructor(
private val messageRepo: MessageRepository,
private val updateBadge: UpdateBadge
) : Interactor<List<Long>>() {

override fun buildObservable(params: List<Long>): Flowable<*> {
return Flowable.just(params.toLongArray())
.doOnNext { threadId -> messageRepo.markUnread(*threadId) }
.flatMap { updateBadge.buildObservable(Unit) } // Update the badge
}

}
4 changes: 3 additions & 1 deletion domain/src/main/java/repository/MessageRepository.kt
Expand Up @@ -90,7 +90,9 @@ interface MessageRepository {

fun markSeen(threadId: Long)

fun markRead(threadId: Long)
fun markRead(vararg threadIds: Long)

fun markUnread(vararg threadIds: Long)

/**
* Persists the SMS and attempts to send it
Expand Down
Expand Up @@ -441,7 +441,7 @@ class ComposeViewModel @Inject constructor(
.mapNotNull { conversation -> conversation.takeIf { it.isValid }?.id }
.debounce(200, TimeUnit.MILLISECONDS)
.autoDisposable(view.scope())
.subscribe { threadId -> markRead.execute(threadId) }
.subscribe { threadId -> markRead.execute(listOf(threadId)) }

// Open the attachment options
view.attachIntent
Expand Down
8 changes: 8 additions & 0 deletions presentation/src/main/java/feature/main/MainActivity.kt
Expand Up @@ -169,6 +169,12 @@ class MainActivity : QkThemedActivity(), MainView {
return
}

val markRead = when (state.page) {
is Inbox -> state.page.markRead
is Archived -> state.page.markRead
else -> true
}

val selectedConversations = when (state.page) {
is Inbox -> state.page.selected
is Archived -> state.page.selected
Expand All @@ -178,6 +184,8 @@ class MainActivity : QkThemedActivity(), MainView {
toolbarSearch.setVisible(state.page is Inbox && state.page.selected == 0 || state.page is Searching)
toolbarTitle.setVisible(toolbarSearch.visibility != View.VISIBLE)

toolbar.menu.findItem(R.id.read)?.isVisible = markRead && selectedConversations != 0
toolbar.menu.findItem(R.id.unread)?.isVisible = !markRead && selectedConversations != 0
toolbar.menu.findItem(R.id.archive)?.isVisible = state.page is Inbox && selectedConversations != 0
toolbar.menu.findItem(R.id.unarchive)?.isVisible = state.page is Archived && selectedConversations != 0
toolbar.menu.findItem(R.id.block)?.isVisible = selectedConversations != 0
Expand Down
2 changes: 2 additions & 0 deletions presentation/src/main/java/feature/main/MainState.kt
Expand Up @@ -38,6 +38,7 @@ sealed class MainPage

data class Inbox(
val showClearButton: Boolean = false,
val markRead: Boolean = false,
val data: RealmResults<Conversation>? = null,
val selected: Int = 0,
val showArchivedSnackbar: Boolean = false) : MainPage()
Expand All @@ -49,6 +50,7 @@ data class Searching(

data class Archived(
val showClearButton: Boolean = false,
val markRead: Boolean = false,
val data: RealmResults<Conversation>? = null,
val selected: Int = 0) : MainPage()

Expand Down
28 changes: 23 additions & 5 deletions presentation/src/main/java/feature/main/MainViewModel.kt
Expand Up @@ -27,7 +27,9 @@ import interactor.DeleteConversations
import interactor.MarkAllSeen
import interactor.MarkArchived
import interactor.MarkBlocked
import interactor.MarkRead
import interactor.MarkUnarchived
import interactor.MarkUnread
import interactor.MigratePreferences
import interactor.SyncMessages
import io.reactivex.Observable
Expand All @@ -50,6 +52,8 @@ class MainViewModel @Inject constructor(
private val messageRepo: MessageRepository,
private val markAllSeen: MarkAllSeen,
private val deleteConversations: DeleteConversations,
private val markRead: MarkRead,
private val markUnread: MarkUnread,
private val markArchived: MarkArchived,
private val markUnarchived: MarkUnarchived,
private val markBlocked: MarkBlocked,
Expand Down Expand Up @@ -195,6 +199,16 @@ class MainViewModel @Inject constructor(
view.optionsItemIntent
.withLatestFrom(view.conversationsSelectedIntent, { itemId, conversations ->
when (itemId) {
R.id.read -> {
markRead.execute(conversations)
view.clearSelection()
}

R.id.unread -> {
markUnread.execute(conversations)
view.clearSelection()
}

R.id.archive -> {
markArchived.execute(conversations)
view.clearSelection()
Expand Down Expand Up @@ -228,16 +242,20 @@ class MainViewModel @Inject constructor(
.subscribe { ratingManager.dismiss() }

view.conversationsSelectedIntent
.map { selection -> selection.size }
.withLatestFrom(state, { selected, state ->
.withLatestFrom(state, { selection, state ->
val read = selection
.mapNotNull(messageRepo::getConversation)
.sumBy { if (it.read) -1 else 1 } >= 0
val selected = selection.size

when (state.page) {
is Inbox -> {
val page = state.page.copy(selected = selected, showClearButton = selected > 0)
newState { copy(page = page) }
val page = state.page.copy(markRead = read, selected = selected, showClearButton = selected > 0)
newState { copy(page = page.copy(markRead = read, selected = selected, showClearButton = selected > 0)) }
}

is Archived -> {
val page = state.page.copy(selected = selected, showClearButton = selected > 0)
val page = state.page.copy(markRead = read, selected = selected, showClearButton = selected > 0)
newState { copy(page = page) }
}
}
Expand Down
Expand Up @@ -113,7 +113,7 @@ class QkReplyViewModel @Inject constructor(
.map { conversation -> conversation.id }
.autoDisposable(view.scope())
.subscribe { threadId ->
markRead.execute(threadId) { newState { copy(hasError = true) } }
markRead.execute(listOf(threadId)) { newState { copy(hasError = true) } }
}

// Call
Expand Down Expand Up @@ -216,7 +216,7 @@ class QkReplyViewModel @Inject constructor(
threadId
})
.doOnNext { threadId ->
markRead.execute(threadId) {
markRead.execute(listOf(threadId)) {
newState { copy(hasError = true) }
}
}
Expand Down
27 changes: 27 additions & 0 deletions presentation/src/main/res/drawable/ic_drafts_black_24dp.xml
@@ -0,0 +1,27 @@
<!--
~ Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
~
~ This file is part of QKSMS.
~
~ QKSMS is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ QKSMS is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21.99,8c0,-0.72 -0.37,-1.35 -0.94,-1.7L12,1 2.95,6.3C2.38,6.65 2,7.28 2,8v10c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2l-0.01,-10zM12,13L3.74,7.84 12,3l8.26,4.84L12,13z"/>
</vector>
27 changes: 27 additions & 0 deletions presentation/src/main/res/drawable/ic_markunread_black_24dp.xml
@@ -0,0 +1,27 @@
<!--
~ Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
~
~ This file is part of QKSMS.
~
~ QKSMS is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ QKSMS is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with QKSMS. If not, see <http://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>
14 changes: 14 additions & 0 deletions presentation/src/main/res/menu/main.xml
Expand Up @@ -19,6 +19,20 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<item
android:id="@+id/read"
android:icon="@drawable/ic_drafts_black_24dp"
android:title="@string/main_menu_read"
android:visible="false"
app:showAsAction="ifRoom" />

<item
android:id="@+id/unread"
android:icon="@drawable/ic_markunread_black_24dp"
android:title="@string/main_menu_unread"
android:visible="false"
app:showAsAction="ifRoom" />

<item
android:id="@+id/archive"
android:icon="@drawable/ic_archive_black_24dp"
Expand Down
2 changes: 2 additions & 0 deletions presentation/src/main/res/values/strings.xml
Expand Up @@ -38,6 +38,8 @@

<string name="main_title_selected">%d selected</string>
<string name="main_menu_clear">Clear</string>
<string name="main_menu_read">Mark read</string>
<string name="main_menu_unread">Mark unread</string>
<string name="main_menu_archive">Archive</string>
<string name="main_menu_unarchive">Unarchive</string>
<string name="main_menu_block">Block</string>
Expand Down

0 comments on commit 5ede84a

Please sign in to comment.