Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 058d12d3 authored by moezbhatti's avatar moezbhatti Committed by Moez Bhatti
Browse files

Create chip model to keep original contact and know which number was selected

parent c348fba3
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -54,11 +54,11 @@ import com.moez.QKSMS.common.util.extensions.setBackgroundTint
import com.moez.QKSMS.common.util.extensions.setTint
import com.moez.QKSMS.common.util.extensions.setVisible
import com.moez.QKSMS.common.util.extensions.showKeyboard
import com.moez.QKSMS.feature.compose.editing.Chip
import com.moez.QKSMS.feature.compose.editing.ChipsAdapter
import com.moez.QKSMS.feature.compose.editing.ComposeItem
import com.moez.QKSMS.feature.compose.editing.ComposeItemAdapter
import com.moez.QKSMS.model.Attachment
import com.moez.QKSMS.model.Contact
import com.uber.autodispose.android.lifecycle.scope
import com.uber.autodispose.autoDisposable
import dagger.android.AndroidInjection
@@ -91,7 +91,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
    override val queryBackspaceIntent: Observable<*> by lazy { search.backspaces }
    override val queryEditorActionIntent: Observable<Int> by lazy { search.editorActions() }
    override val chipSelectedIntent: Subject<ComposeItem> by lazy { contactsAdapter.itemSelected }
    override val chipDeletedIntent: Subject<Contact> by lazy { chipsAdapter.chipDeleted }
    override val chipDeletedIntent: Subject<Chip> by lazy { chipsAdapter.chipDeleted }
    override val menuReadyIntent: Observable<Unit> = menu.map { Unit }
    override val optionsItemIntent: Subject<Int> = PublishSubject.create()
    override val sendAsGroupIntent by lazy { sendAsGroupBackground.clicks() }
@@ -212,16 +212,16 @@ class ComposeActivity : QkThemedActivity(), ComposeView {
        toolbar.menu.findItem(R.id.next)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty()
        toolbar.menu.findItem(R.id.clear)?.isVisible = state.selectedMessages == 0 && state.query.isNotEmpty()

        if (chipsAdapter.data.isEmpty() && state.selectedContacts.isNotEmpty()) {
        if (chipsAdapter.data.isEmpty() && state.selectedChips.isNotEmpty()) {
            message.showKeyboard()
        }

        chipsAdapter.data = state.selectedContacts
        chipsAdapter.data = state.selectedChips
        contactsAdapter.data = state.composeItems

        loading.setVisible(state.loading)

        sendAsGroup.setVisible(state.editingMode && state.selectedContacts.size >= 2)
        sendAsGroup.setVisible(state.editingMode && state.selectedChips.size >= 2)
        sendAsGroupSwitch.isChecked = state.sendAsGroup

        messageList.setVisible(state.sendAsGroup)
+2 −2
Original line number Diff line number Diff line
@@ -19,9 +19,9 @@
package com.moez.QKSMS.feature.compose

import com.moez.QKSMS.compat.SubscriptionInfoCompat
import com.moez.QKSMS.feature.compose.editing.Chip
import com.moez.QKSMS.feature.compose.editing.ComposeItem
import com.moez.QKSMS.model.Attachment
import com.moez.QKSMS.model.Contact
import com.moez.QKSMS.model.Conversation
import com.moez.QKSMS.model.Message
import io.realm.RealmResults
@@ -32,7 +32,7 @@ data class ComposeState(
    val searching: Boolean = false,
    val composeItems: List<ComposeItem> = ArrayList(),
    val selectedConversation: Long = 0,
    val selectedContacts: List<Contact> = ArrayList(),
    val selectedChips: List<Chip> = ArrayList(),
    val sendAsGroup: Boolean = true,
    val conversationtitle: String = "",
    val loading: Boolean = false,
+2 −2
Original line number Diff line number Diff line
@@ -22,9 +22,9 @@ import android.net.Uri
import androidx.annotation.StringRes
import androidx.core.view.inputmethod.InputContentInfoCompat
import com.moez.QKSMS.common.base.QkView
import com.moez.QKSMS.feature.compose.editing.Chip
import com.moez.QKSMS.feature.compose.editing.ComposeItem
import com.moez.QKSMS.model.Attachment
import com.moez.QKSMS.model.Contact
import io.reactivex.Observable
import io.reactivex.subjects.Subject

@@ -35,7 +35,7 @@ interface ComposeView : QkView<ComposeState> {
    val queryBackspaceIntent: Observable<*>
    val queryEditorActionIntent: Observable<Int>
    val chipSelectedIntent: Subject<ComposeItem>
    val chipDeletedIntent: Subject<Contact>
    val chipDeletedIntent: Subject<Chip>
    val menuReadyIntent: Observable<Unit>
    val optionsItemIntent: Observable<Int>
    val sendAsGroupIntent: Observable<*>
+32 −36
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.moez.QKSMS.extensions.isImage
import com.moez.QKSMS.extensions.isVideo
import com.moez.QKSMS.extensions.mapNotNull
import com.moez.QKSMS.extensions.removeAccents
import com.moez.QKSMS.feature.compose.editing.Chip
import com.moez.QKSMS.feature.compose.editing.ComposeItem
import com.moez.QKSMS.feature.compose.editing.ComposeItem.*
import com.moez.QKSMS.filter.ContactFilter
@@ -118,11 +119,11 @@ class ComposeViewModel @Inject constructor(
    private val attachments: Subject<List<Attachment>> = BehaviorSubject.createDefault(sharedAttachments)
    private val contactGroups: Observable<List<ContactGroup>> by lazy { contactsRepo.getUnmanagedContactGroups() }
    private val contacts: Observable<List<Contact>> by lazy { contactsRepo.getUnmanagedContacts() }
    private val contactsReducer: Subject<(List<Contact>) -> List<Contact>> = PublishSubject.create()
    private val chipsReducer: Subject<(List<Chip>) -> List<Chip>> = PublishSubject.create()
    private val conversation: Subject<Conversation> = BehaviorSubject.create()
    private val messages: Subject<List<Message>> = BehaviorSubject.create()
    private val recents: Observable<List<Conversation>> by lazy { conversationRepo.getUnmanagedConversations() }
    private val selectedContacts: Subject<List<Contact>> = BehaviorSubject.createDefault(listOf())
    private val selectedChips: Subject<List<Chip>> = BehaviorSubject.createDefault(listOf())
    private val searchResults: Subject<List<Message>> = BehaviorSubject.create()
    private val searchSelection: Subject<Long> = BehaviorSubject.createDefault(-1)
    private val starredContacts: Observable<List<Contact>> by lazy { contactsRepo.getUnmanagedContacts(true) }
@@ -133,9 +134,9 @@ class ComposeViewModel @Inject constructor(
                ?.asObservable()
                ?: Observable.empty()

        val selectedConversation = selectedContacts
        val selectedConversation = selectedChips
                .skipWhile { it.isEmpty() }
                .map { contacts -> contacts.map { it.numbers.firstOrNull()?.address ?: "" } }
                .map { chips -> chips.map { it.address } }
                .distinctUntilChanged()
                .doOnNext { newState { copy(loading = true) } }
                .observeOn(Schedulers.io())
@@ -177,15 +178,15 @@ class ComposeViewModel @Inject constructor(
                .subscribe(conversation::onNext)

        if (address.isNotBlank()) {
            selectedContacts.onNext(listOf(Contact(numbers = RealmList(PhoneNumber(address)))))
            selectedChips.onNext(listOf(Chip(address)))
        }

        disposables += contactsReducer
                .scan(listOf<Contact>()) { previousState, reducer -> reducer(previousState) }
                .doOnNext { contacts -> newState { copy(selectedContacts = contacts) } }
        disposables += chipsReducer
                .scan(listOf<Chip>()) { previousState, reducer -> reducer(previousState) }
                .doOnNext { chips -> newState { copy(selectedChips = chips) } }
                .skipUntil(state.filter { state -> state.editingMode })
                .takeUntil(state.filter { state -> !state.editingMode })
                .subscribe(selectedContacts::onNext)
                .subscribe(selectedChips::onNext)

        // When the conversation changes, mark read, and update the threadId and the messages for the adapter
        disposables += conversation
@@ -256,8 +257,8 @@ class ComposeViewModel @Inject constructor(
        // that have already been selected
        Observables
                .combineLatest(
                        view.queryChangedIntent, recents, starredContacts, contactGroups, contacts, selectedContacts
                ) { query, recents, starredContacts, contactGroups, contacts, selectedContacts ->
                        view.queryChangedIntent, recents, starredContacts, contactGroups, contacts, selectedChips
                ) { query, recents, starredContacts, contactGroups, contacts, selectedChips ->
                    val composeItems = mutableListOf<ComposeItem>()
                    if (query.isBlank()) {
                        composeItems += recents.map(::Recent)
@@ -276,7 +277,7 @@ class ComposeViewModel @Inject constructor(
                        // cache the result instead of doing it for each contact
                        val normalizedQuery = query.removeAccents()
                        composeItems += starredContacts
                                .filterNot { contact -> selectedContacts.contains(contact) }
                                .filterNot { contact -> selectedChips.map { it.contact }.contains(contact) }
                                .filter { contact -> contactFilter.filter(contact, normalizedQuery) }
                                .map(::Starred)

@@ -285,7 +286,7 @@ class ComposeViewModel @Inject constructor(
                                .map(::Group)

                        composeItems += contacts
                                .filterNot { contact -> selectedContacts.contains(contact) }
                                .filterNot { contact -> selectedChips.map { it.contact }.contains(contact) }
                                .filter { contact -> contactFilter.filter(contact, normalizedQuery) }
                                .map(::Person)
                    }
@@ -301,47 +302,42 @@ class ComposeViewModel @Inject constructor(
        // Backspaces should delete the most recent contact if there's no text input
        // Close the activity if user presses back
        view.queryBackspaceIntent
                .withLatestFrom(selectedContacts, view.queryChangedIntent) { event, contacts, query ->
                .withLatestFrom(selectedChips, view.queryChangedIntent) { event, contacts, query ->
                    if (contacts.isNotEmpty() && query.isEmpty()) {
                        contactsReducer.onNext { it.dropLast(1) }
                        chipsReducer.onNext { it.dropLast(1) }
                    }
                }
                .autoDisposable(view.scope())
                .subscribe()

        // Enter the first contact suggestion if the enter button is pressed
        // Enter the first contact suggestion if the enter button is pressed, and enter contacts that are selected
        view.queryEditorActionIntent
                .filter { actionId -> actionId == EditorInfo.IME_ACTION_DONE }
                .withLatestFrom(state) { _, state -> state }
                .mapNotNull { state -> state.composeItems.firstOrNull() }
                .mergeWith(view.chipSelectedIntent)
                .autoDisposable(view.scope())
                .subscribe { state ->
                .subscribe { composeItem ->
                    newState { copy(searching = false) }
                    state.composeItems.firstOrNull()?.let { composeItem ->
                        contactsReducer.onNext { contacts -> contacts + composeItem.getContacts() }
                    chipsReducer.onNext { chips ->
                        chips + composeItem.getContacts().map { Chip(it.numbers.first()?.address.orEmpty(), it) }
                    }
                }

        // Update the list of selected contacts when a new contact is selected or an existing one is deselected
        Observable.merge(
                view.chipDeletedIntent.doOnNext { contact ->
                    contactsReducer.onNext { contacts ->
        view.chipDeletedIntent
                .skipUntil(state.filter { state -> state.editingMode })
                .takeUntil(state.filter { state -> !state.editingMode })
                .autoDisposable(view.scope())
                .subscribe { contact ->
                    chipsReducer.onNext { contacts ->
                        val result = contacts.filterNot { it == contact }
                        if (result.isEmpty()) {
                            newState { copy(searching = true) }
                        }
                        result
                    }
                },
                view.chipSelectedIntent.doOnNext { composeItem ->
                    newState { copy(searching = false) }
                    contactsReducer.onNext { contacts ->
                        contacts.toMutableList().apply { addAll(composeItem.getContacts()) }
                }
                })
                .skipUntil(state.filter { state -> state.editingMode })
                .takeUntil(state.filter { state -> !state.editingMode })
                .autoDisposable(view.scope())
                .subscribe()

        // When the menu is loaded, trigger a new state so that the menu options can be rendered correctly
        view.menuReadyIntent
@@ -667,12 +663,12 @@ class ComposeViewModel @Inject constructor(
                .filter { permissionManager.hasSendSms().also { if (!it) view.requestSmsPermission() } }
                .withLatestFrom(view.textChangedIntent) { _, body -> body }
                .map { body -> body.toString() }
                .withLatestFrom(state, attachments, conversation, selectedContacts) { body, state, attachments,
                                                                                      conversation, contacts ->
                .withLatestFrom(state, attachments, conversation, selectedChips) { body, state, attachments,
                                                                                   conversation, chips ->
                    val subId = state.subscription?.subscriptionId ?: -1
                    val addresses = when (conversation.recipients.isNotEmpty()) {
                        true -> conversation.recipients.map { it.address }
                        false -> contacts.mapNotNull { it.numbers.firstOrNull()?.address }
                        false -> chips.map { chip -> chip.address }
                    }
                    val delay = when (prefs.sendDelay.get()) {
                        Preferences.SEND_DELAY_SHORT -> 3000
+26 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 com.moez.QKSMS.feature.compose.editing

import com.moez.QKSMS.model.Contact

data class Chip(
    val address: String,
    val contact: Contact? = null
)
Loading