forked from upstream-mirrors/quik
#1112 - Mark conversations read/unread
This commit is contained in:
@@ -33,7 +33,7 @@ class MarkReadReceiver : BroadcastReceiver() {
|
||||
AndroidInjection.inject(this, context)
|
||||
|
||||
val threadId = intent.getLongExtra("threadId", 0)
|
||||
markRead.execute(threadId)
|
||||
markRead.execute(listOf(threadId))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
@@ -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()
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -216,7 +216,7 @@ class QkReplyViewModel @Inject constructor(
|
||||
threadId
|
||||
})
|
||||
.doOnNext { threadId ->
|
||||
markRead.execute(threadId) {
|
||||
markRead.execute(listOf(threadId)) {
|
||||
newState { copy(hasError = true) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user