Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +9 −6 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import androidx.lifecycle.ViewModelProviders import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.material.snackbar.Snackbar import com.jakewharton.rxbinding2.view.clicks import com.jakewharton.rxbinding2.widget.editorActions import com.jakewharton.rxbinding2.widget.textChanges import com.moez.QKSMS.R import com.moez.QKSMS.common.Navigator Loading Loading @@ -86,9 +87,9 @@ class ComposeActivity : QkThemedActivity(), ComposeView { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory override val activityVisibleIntent: Subject<Boolean> = PublishSubject.create() override val queryChangedIntent: Observable<CharSequence> by lazy { chipsAdapter.textChanges } override val queryBackspaceIntent: Observable<*> by lazy { chipsAdapter.backspaces } override val queryEditorActionIntent: Observable<Int> by lazy { chipsAdapter.actions } override val queryChangedIntent: Observable<CharSequence> by lazy { search.textChanges() } 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 menuReadyIntent: Observable<Unit> = menu.map { Unit } Loading Loading @@ -191,14 +192,16 @@ class ComposeActivity : QkThemedActivity(), ComposeView { toolbarSubtitle.text = getString(R.string.compose_subtitle_results, state.searchSelectionPosition, state.searchResults) toolbarTitle.setVisible(!state.editingMode) chips.setVisible(state.editingMode) contacts.setVisible(state.contactsVisible) composeBar.setVisible(!state.contactsVisible && !state.loading) chips.setVisible(state.editingMode && !state.searching) search.setVisible(state.editingMode && state.searching) contacts.setVisible(state.editingMode && state.searching) composeBar.setVisible(!state.searching && !state.loading) // Don't set the adapters unless needed if (state.editingMode && chips.adapter == null) chips.adapter = chipsAdapter if (state.editingMode && contacts.adapter == null) contacts.adapter = contactsAdapter toolbar.menu.findItem(R.id.add)?.isVisible = state.editingMode && !state.searching toolbar.menu.findItem(R.id.call)?.isVisible = !state.editingMode && state.selectedMessages == 0 && state.query.isEmpty() toolbar.menu.findItem(R.id.info)?.isVisible = !state.editingMode && state.selectedMessages == 0 && state.query.isEmpty() toolbar.menu.findItem(R.id.copy)?.isVisible = !state.editingMode && state.selectedMessages == 1 Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeState.kt +1 −1 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ import io.realm.RealmResults data class ComposeState( val hasError: Boolean = false, val editingMode: Boolean = false, val searching: Boolean = false, val composeItems: List<ComposeItem> = ArrayList(), val contactsVisible: Boolean = false, val selectedConversation: Long = 0, val selectedContacts: List<Contact> = ArrayList(), val sendAsGroup: Boolean = true, Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +7 −8 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ class ComposeViewModel @Inject constructor( private val syncContacts: ContactSync ) : QkViewModel<ComposeView, ComposeState>(ComposeState( editingMode = threadId == 0L && address.isBlank(), searching = threadId == 0L && address.isBlank(), selectedConversation = threadId, query = query) ) { Loading Loading @@ -243,17 +244,13 @@ class ComposeViewModel @Inject constructor( override fun bindView(view: ComposeView) { super.bindView(view) // Set the contact suggestions list to visible at all times when in editing mode and there are no contacts // selected yet, and also visible while in editing mode and there is text entered in the query field Observables .combineLatest(view.queryChangedIntent, selectedContacts) { query, selectedContacts -> selectedContacts.isEmpty() || query.isNotEmpty() } // Set the contact suggestions list to visible when the add button is pressed view.optionsItemIntent .filter { it == R.id.add } .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .distinctUntilChanged() .autoDisposable(view.scope()) .subscribe { contactsVisible -> newState { copy(contactsVisible = contactsVisible && editingMode) } } .subscribe { newState { copy(searching = true) } } // Update the list of contact suggestions based on the query input, while also filtering out any contacts // that have already been selected Loading Loading @@ -318,6 +315,7 @@ class ComposeViewModel @Inject constructor( .withLatestFrom(state) { _, state -> state } .autoDisposable(view.scope()) .subscribe { state -> newState { copy(searching = false) } state.composeItems.firstOrNull()?.let { composeItem -> contactsReducer.onNext { contacts -> contacts + composeItem.getContacts() } } Loading @@ -329,6 +327,7 @@ class ComposeViewModel @Inject constructor( contactsReducer.onNext { contacts -> contacts.filterNot { it == contact } } }, view.chipSelectedIntent.doOnNext { composeItem -> newState { copy(searching = false) } contactsReducer.onNext { contacts -> contacts.toMutableList().apply { addAll(composeItem.getContacts()) } } Loading presentation/src/main/java/com/moez/QKSMS/feature/compose/editing/ChipsAdapter.kt +17 −71 Original line number Diff line number Diff line Loading @@ -20,83 +20,35 @@ package com.moez.QKSMS.feature.compose.editing import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.RelativeLayout import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager import com.jakewharton.rxbinding2.widget.editorActions import com.jakewharton.rxbinding2.widget.textChanges import com.moez.QKSMS.R import com.moez.QKSMS.common.base.QkAdapter import com.moez.QKSMS.common.base.QkViewHolder import com.moez.QKSMS.common.util.extensions.dpToPx import com.moez.QKSMS.common.util.extensions.resolveThemeColor import com.moez.QKSMS.common.util.extensions.showKeyboard import com.moez.QKSMS.common.widget.QkEditText import com.moez.QKSMS.model.Contact import io.reactivex.subjects.PublishSubject import kotlinx.android.synthetic.main.contact_chip.view.* import javax.inject.Inject class ChipsAdapter @Inject constructor(private val context: Context) : QkAdapter<Contact>() { companion object { private const val TYPE_EDIT_TEXT = 0 private const val TYPE_ITEM = 1 } private val hint: String = context.getString(R.string.title_compose) private val editText = View.inflate(context, R.layout.chip_input_list_item, null) as QkEditText class ChipsAdapter @Inject constructor() : QkAdapter<Contact>() { var view: RecyclerView? = null val chipDeleted: PublishSubject<Contact> = PublishSubject.create<Contact>() val textChanges = editText.textChanges() val actions = editText.editorActions() val backspaces = editText.backspaces init { val wrap = ViewGroup.LayoutParams.WRAP_CONTENT editText.layoutParams = FlexboxLayoutManager.LayoutParams(wrap, wrap).apply { minHeight = 36.dpToPx(context) minWidth = 56.dpToPx(context) flexGrow = 8f } editText.hint = hint } override fun onDatasetChanged() { editText.text = null editText.hint = if (itemCount == 1) hint else null if (itemCount != 2) { editText.showKeyboard() } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { TYPE_EDIT_TEXT -> { editText.setTextColor(parent.context.resolveThemeColor(android.R.attr.textColorPrimary)) editText.setHintTextColor(parent.context.resolveThemeColor(android.R.attr.textColorTertiary)) QkViewHolder(editText) } else -> { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder { val inflater = LayoutInflater.from(parent.context) val view = inflater.inflate(R.layout.contact_chip, parent, false) QkViewHolder(view).apply { return QkViewHolder(view).apply { view.setOnClickListener { val contact = getItem(adapterPosition) showDetailedChip(view.context, contact) } } } } override fun onBindViewHolder(holder: QkViewHolder, position: Int) { when (getItemViewType(position)) { TYPE_ITEM -> { val contact = getItem(position) val view = holder.containerView Loading @@ -110,12 +62,6 @@ class ChipsAdapter @Inject constructor(private val context: Context) : QkAdapter contact.numbers.firstOrNull { it.address.isNotBlank() }?.address ?: "" } } } } override fun getItemCount() = super.getItemCount() + 1 override fun getItemViewType(position: Int) = if (position == itemCount - 1) TYPE_EDIT_TEXT else TYPE_ITEM /** * The [context] has to come from a view, because we're inflating a view that used themed attrs Loading presentation/src/main/res/layout/compose_activity.xml +14 −0 Original line number Diff line number Diff line Loading @@ -376,6 +376,20 @@ android:scrollbars="vertical" tools:visibility="gone" /> <com.moez.QKSMS.common.widget.QkEditText android:id="@+id/search" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null" android:hint="@string/title_compose" android:imeOptions="flagNoExtractUi" android:inputType="textFilter|textNoSuggestions" android:privateImeOptions="nm" android:textColor="?android:attr/textColorPrimary" android:textColorHint="?android:attr/textColorTertiary" android:visibility="gone" app:textSize="primary" /> </LinearLayout> </androidx.appcompat.widget.Toolbar> Loading Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +9 −6 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import androidx.lifecycle.ViewModelProviders import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.material.snackbar.Snackbar import com.jakewharton.rxbinding2.view.clicks import com.jakewharton.rxbinding2.widget.editorActions import com.jakewharton.rxbinding2.widget.textChanges import com.moez.QKSMS.R import com.moez.QKSMS.common.Navigator Loading Loading @@ -86,9 +87,9 @@ class ComposeActivity : QkThemedActivity(), ComposeView { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory override val activityVisibleIntent: Subject<Boolean> = PublishSubject.create() override val queryChangedIntent: Observable<CharSequence> by lazy { chipsAdapter.textChanges } override val queryBackspaceIntent: Observable<*> by lazy { chipsAdapter.backspaces } override val queryEditorActionIntent: Observable<Int> by lazy { chipsAdapter.actions } override val queryChangedIntent: Observable<CharSequence> by lazy { search.textChanges() } 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 menuReadyIntent: Observable<Unit> = menu.map { Unit } Loading Loading @@ -191,14 +192,16 @@ class ComposeActivity : QkThemedActivity(), ComposeView { toolbarSubtitle.text = getString(R.string.compose_subtitle_results, state.searchSelectionPosition, state.searchResults) toolbarTitle.setVisible(!state.editingMode) chips.setVisible(state.editingMode) contacts.setVisible(state.contactsVisible) composeBar.setVisible(!state.contactsVisible && !state.loading) chips.setVisible(state.editingMode && !state.searching) search.setVisible(state.editingMode && state.searching) contacts.setVisible(state.editingMode && state.searching) composeBar.setVisible(!state.searching && !state.loading) // Don't set the adapters unless needed if (state.editingMode && chips.adapter == null) chips.adapter = chipsAdapter if (state.editingMode && contacts.adapter == null) contacts.adapter = contactsAdapter toolbar.menu.findItem(R.id.add)?.isVisible = state.editingMode && !state.searching toolbar.menu.findItem(R.id.call)?.isVisible = !state.editingMode && state.selectedMessages == 0 && state.query.isEmpty() toolbar.menu.findItem(R.id.info)?.isVisible = !state.editingMode && state.selectedMessages == 0 && state.query.isEmpty() toolbar.menu.findItem(R.id.copy)?.isVisible = !state.editingMode && state.selectedMessages == 1 Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeState.kt +1 −1 Original line number Diff line number Diff line Loading @@ -29,8 +29,8 @@ import io.realm.RealmResults data class ComposeState( val hasError: Boolean = false, val editingMode: Boolean = false, val searching: Boolean = false, val composeItems: List<ComposeItem> = ArrayList(), val contactsVisible: Boolean = false, val selectedConversation: Long = 0, val selectedContacts: List<Contact> = ArrayList(), val sendAsGroup: Boolean = true, Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +7 −8 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ class ComposeViewModel @Inject constructor( private val syncContacts: ContactSync ) : QkViewModel<ComposeView, ComposeState>(ComposeState( editingMode = threadId == 0L && address.isBlank(), searching = threadId == 0L && address.isBlank(), selectedConversation = threadId, query = query) ) { Loading Loading @@ -243,17 +244,13 @@ class ComposeViewModel @Inject constructor( override fun bindView(view: ComposeView) { super.bindView(view) // Set the contact suggestions list to visible at all times when in editing mode and there are no contacts // selected yet, and also visible while in editing mode and there is text entered in the query field Observables .combineLatest(view.queryChangedIntent, selectedContacts) { query, selectedContacts -> selectedContacts.isEmpty() || query.isNotEmpty() } // Set the contact suggestions list to visible when the add button is pressed view.optionsItemIntent .filter { it == R.id.add } .skipUntil(state.filter { state -> state.editingMode }) .takeUntil(state.filter { state -> !state.editingMode }) .distinctUntilChanged() .autoDisposable(view.scope()) .subscribe { contactsVisible -> newState { copy(contactsVisible = contactsVisible && editingMode) } } .subscribe { newState { copy(searching = true) } } // Update the list of contact suggestions based on the query input, while also filtering out any contacts // that have already been selected Loading Loading @@ -318,6 +315,7 @@ class ComposeViewModel @Inject constructor( .withLatestFrom(state) { _, state -> state } .autoDisposable(view.scope()) .subscribe { state -> newState { copy(searching = false) } state.composeItems.firstOrNull()?.let { composeItem -> contactsReducer.onNext { contacts -> contacts + composeItem.getContacts() } } Loading @@ -329,6 +327,7 @@ class ComposeViewModel @Inject constructor( contactsReducer.onNext { contacts -> contacts.filterNot { it == contact } } }, view.chipSelectedIntent.doOnNext { composeItem -> newState { copy(searching = false) } contactsReducer.onNext { contacts -> contacts.toMutableList().apply { addAll(composeItem.getContacts()) } } Loading
presentation/src/main/java/com/moez/QKSMS/feature/compose/editing/ChipsAdapter.kt +17 −71 Original line number Diff line number Diff line Loading @@ -20,83 +20,35 @@ package com.moez.QKSMS.feature.compose.editing import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.RelativeLayout import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager import com.jakewharton.rxbinding2.widget.editorActions import com.jakewharton.rxbinding2.widget.textChanges import com.moez.QKSMS.R import com.moez.QKSMS.common.base.QkAdapter import com.moez.QKSMS.common.base.QkViewHolder import com.moez.QKSMS.common.util.extensions.dpToPx import com.moez.QKSMS.common.util.extensions.resolveThemeColor import com.moez.QKSMS.common.util.extensions.showKeyboard import com.moez.QKSMS.common.widget.QkEditText import com.moez.QKSMS.model.Contact import io.reactivex.subjects.PublishSubject import kotlinx.android.synthetic.main.contact_chip.view.* import javax.inject.Inject class ChipsAdapter @Inject constructor(private val context: Context) : QkAdapter<Contact>() { companion object { private const val TYPE_EDIT_TEXT = 0 private const val TYPE_ITEM = 1 } private val hint: String = context.getString(R.string.title_compose) private val editText = View.inflate(context, R.layout.chip_input_list_item, null) as QkEditText class ChipsAdapter @Inject constructor() : QkAdapter<Contact>() { var view: RecyclerView? = null val chipDeleted: PublishSubject<Contact> = PublishSubject.create<Contact>() val textChanges = editText.textChanges() val actions = editText.editorActions() val backspaces = editText.backspaces init { val wrap = ViewGroup.LayoutParams.WRAP_CONTENT editText.layoutParams = FlexboxLayoutManager.LayoutParams(wrap, wrap).apply { minHeight = 36.dpToPx(context) minWidth = 56.dpToPx(context) flexGrow = 8f } editText.hint = hint } override fun onDatasetChanged() { editText.text = null editText.hint = if (itemCount == 1) hint else null if (itemCount != 2) { editText.showKeyboard() } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { TYPE_EDIT_TEXT -> { editText.setTextColor(parent.context.resolveThemeColor(android.R.attr.textColorPrimary)) editText.setHintTextColor(parent.context.resolveThemeColor(android.R.attr.textColorTertiary)) QkViewHolder(editText) } else -> { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QkViewHolder { val inflater = LayoutInflater.from(parent.context) val view = inflater.inflate(R.layout.contact_chip, parent, false) QkViewHolder(view).apply { return QkViewHolder(view).apply { view.setOnClickListener { val contact = getItem(adapterPosition) showDetailedChip(view.context, contact) } } } } override fun onBindViewHolder(holder: QkViewHolder, position: Int) { when (getItemViewType(position)) { TYPE_ITEM -> { val contact = getItem(position) val view = holder.containerView Loading @@ -110,12 +62,6 @@ class ChipsAdapter @Inject constructor(private val context: Context) : QkAdapter contact.numbers.firstOrNull { it.address.isNotBlank() }?.address ?: "" } } } } override fun getItemCount() = super.getItemCount() + 1 override fun getItemViewType(position: Int) = if (position == itemCount - 1) TYPE_EDIT_TEXT else TYPE_ITEM /** * The [context] has to come from a view, because we're inflating a view that used themed attrs Loading
presentation/src/main/res/layout/compose_activity.xml +14 −0 Original line number Diff line number Diff line Loading @@ -376,6 +376,20 @@ android:scrollbars="vertical" tools:visibility="gone" /> <com.moez.QKSMS.common.widget.QkEditText android:id="@+id/search" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null" android:hint="@string/title_compose" android:imeOptions="flagNoExtractUi" android:inputType="textFilter|textNoSuggestions" android:privateImeOptions="nm" android:textColor="?android:attr/textColorPrimary" android:textColorHint="?android:attr/textColorTertiary" android:visibility="gone" app:textSize="primary" /> </LinearLayout> </androidx.appcompat.widget.Toolbar> Loading